Support for InterWeb Links

This commit is contained in:
Jason Blevins 2007-10-06 09:06:55 -04:00
parent 8cdcbff13e
commit c1be34abcd
2 changed files with 72 additions and 51 deletions

View file

@ -13,18 +13,21 @@ module WikiChunk
# Name of the referenced page # Name of the referenced page
attr_reader :page_name attr_reader :page_name
# Name of the referenced page
attr_reader :web_name
# the referenced page # the referenced page
def refpage def refpage
@content.web.page(@page_name) @content.web.page(@page_name)
end end
end end
# A wiki link is the top-level class for links that refers to # A wiki link is the top-level class for links that refers to
# another wiki page. # another wiki page.
class WikiLink < WikiReference class WikiLink < WikiReference
attr_reader :link_text, :link_type attr_reader :link_text, :link_type
def initialize(match_data, content) def initialize(match_data, content)
@ -49,11 +52,16 @@ module WikiChunk
not @textile_link_suffix.nil? not @textile_link_suffix.nil?
end end
def interweb_link?
not @web_name.nil? and Web.find_by_name(@web_name) or
Web.find_by_address(@web_name)
end
# replace any sequence of whitespace characters with a single space # replace any sequence of whitespace characters with a single space
def normalize_whitespace(line) def normalize_whitespace(line)
line.gsub(/\s+/, ' ') line.gsub(/\s+/, ' ')
end end
end end
# This chunk matches a WikiWord. WikiWords can be escaped # This chunk matches a WikiWord. WikiWords can be escaped
@ -63,7 +71,7 @@ module WikiChunk
class Word < WikiLink class Word < WikiLink
attr_reader :escaped_text attr_reader :escaped_text
unless defined? WIKI_WORD unless defined? WIKI_WORD
WIKI_WORD = Regexp.new('(":)?(\\\\)?(' + WikiWords::WIKI_WORD_PATTERN + ')\b', 0, "utf-8") WIKI_WORD = Regexp.new('(":)?(\\\\)?(' + WikiWords::WIKI_WORD_PATTERN + ')\b', 0, "utf-8")
end end
@ -75,7 +83,7 @@ module WikiChunk
def initialize(match_data, content) def initialize(match_data, content)
super super
@textile_link_suffix, @escape, @page_name = match_data[1..3] @textile_link_suffix, @escape, @page_name = match_data[1..3]
if @escape if @escape
@unmask_mode = :escape @unmask_mode = :escape
@escaped_text = @page_name @escaped_text = @page_name
else else
@ -87,7 +95,7 @@ module WikiChunk
end end
# This chunk handles [[bracketted wiki words]] and # This chunk handles [[bracketted wiki words]] and
# [[AliasedWords|aliased wiki words]]. The first part of an # [[AliasedWords|aliased wiki words]]. The first part of an
# aliased wiki word must be a WikiWord. If the WikiWord # aliased wiki word must be a WikiWord. If the WikiWord
# is aliased, the +link_text+ field will contain the # is aliased, the +link_text+ field will contain the
@ -95,15 +103,16 @@ module WikiChunk
# contents within the double brackets. # contents within the double brackets.
# #
# NOTE: This chunk must be tested before WikiWord since # NOTE: This chunk must be tested before WikiWord since
# a WikiWords can be a substring of a WikiLink. # a WikiWords can be a substring of a WikiLink.
class Link < WikiLink class Link < WikiLink
unless defined? WIKI_LINK unless defined? WIKI_LINK
WIKI_LINK = /(":)?\[\[\s*([^\]\s][^\]]+?)\s*\]\]/ WIKI_LINK = /(":)?\[\[\s*([^\]\s][^\]]+?)\s*\]\]/
LINK_TYPE_SEPARATION = Regexp.new('^(.+):((file)|(pic))$', 0, 'utf-8') LINK_TYPE_SEPARATION = Regexp.new('^(.+):((file)|(pic))$', 0, 'utf-8')
ALIAS_SEPARATION = Regexp.new('^(.+)\|(.+)$', 0, 'utf-8') ALIAS_SEPARATION = Regexp.new('^(.+)\|(.+)$', 0, 'utf-8')
end WEB_SEPARATION = Regexp.new('^(.+):(.+)$', 0, 'utf-8')
end
def self.pattern() WIKI_LINK end def self.pattern() WIKI_LINK end
def initialize(match_data, content) def initialize(match_data, content)
@ -112,12 +121,13 @@ module WikiChunk
@link_text = @page_name = normalize_whitespace(match_data[2]) @link_text = @page_name = normalize_whitespace(match_data[2])
separate_link_type separate_link_type
separate_alias separate_alias
@unmask_text = @content.page_link(@page_name, @link_text, @link_type) separate_web
@unmask_text = @content.page_link(@web_name, @page_name, @link_text, @link_type)
end end
private private
# if link wihin the brackets has a form of [[filename:file]] or [[filename:pic]], # if link wihin the brackets has a form of [[filename:file]] or [[filename:pic]],
# this means a link to a picture or a file # this means a link to a picture or a file
def separate_link_type def separate_link_type
link_type_match = LINK_TYPE_SEPARATION.match(@page_name) link_type_match = LINK_TYPE_SEPARATION.match(@page_name)
@ -135,9 +145,19 @@ module WikiChunk
@link_text = alias_match[2] @link_text = alias_match[2]
end end
# note that [[filename|link text:file]] is also supported # note that [[filename|link text:file]] is also supported
end end
# Interweb links have the form [[Web Name:Page Name]] or
# [[address:PageName]]. Alternate text links of the form
# [[address:PageName|Other text]] are also supported.
def separate_web
web_match = WEB_SEPARATION.match(@page_name)
if web_match
@web_name = normalize_whitespace(web_match[1])
@page_name = web_match[2]
end
end
end end
end end

View file

@ -11,46 +11,46 @@ require 'chunks/nowiki'
# actions. The actions can modify wiki content so that certain parts of # actions. The actions can modify wiki content so that certain parts of
# it are protected from being rendered by later actions. # it are protected from being rendered by later actions.
# #
# When wiki content is rendered, it can be interrogated to find out # When wiki content is rendered, it can be interrogated to find out
# which chunks were rendered. This means things like categories, wiki # which chunks were rendered. This means things like categories, wiki
# links, can be determined. # links, can be determined.
# #
# Exactly how wiki content is rendered is determined by a number of # Exactly how wiki content is rendered is determined by a number of
# settings that are optionally passed in to a constructor. The current # settings that are optionally passed in to a constructor. The current
# options are: # options are:
# * :engine # * :engine
# => The structural markup engine to use (Textile, Markdown, RDoc) # => The structural markup engine to use (Textile, Markdown, RDoc)
# * :engine_opts # * :engine_opts
# => A list of options to pass to the markup engines (safe modes, etc) # => A list of options to pass to the markup engines (safe modes, etc)
# * :pre_engine_actions # * :pre_engine_actions
# => A list of render actions or chunks to be processed before the # => A list of render actions or chunks to be processed before the
# markup engine is applied. By default this is: # markup engine is applied. By default this is:
# Category, Include, URIChunk, WikiChunk::Link, WikiChunk::Word # Category, Include, URIChunk, WikiChunk::Link, WikiChunk::Word
# * :post_engine_actions # * :post_engine_actions
# => A list of render actions or chunks to apply after the markup # => A list of render actions or chunks to apply after the markup
# engine. By default these are: # engine. By default these are:
# Literal::Pre, Literal::Tags # Literal::Pre, Literal::Tags
# * :mode # * :mode
# => How should the content be rendered? For normal display (show), # => How should the content be rendered? For normal display (show),
# publishing (:publish) or export (:export)? # publishing (:publish) or export (:export)?
module ChunkManager module ChunkManager
attr_reader :chunks_by_type, :chunks_by_id, :chunks, :chunk_id attr_reader :chunks_by_type, :chunks_by_id, :chunks, :chunk_id
ACTIVE_CHUNKS = [ NoWiki, Category, WikiChunk::Link, URIChunk, LocalURIChunk, ACTIVE_CHUNKS = [ NoWiki, Category, WikiChunk::Link, URIChunk, LocalURIChunk,
WikiChunk::Word ] WikiChunk::Word ]
HIDE_CHUNKS = [ Literal::Pre, Literal::Tags ] HIDE_CHUNKS = [ Literal::Pre, Literal::Tags ]
MASK_RE = { MASK_RE = {
ACTIVE_CHUNKS => Chunk::Abstract.mask_re(ACTIVE_CHUNKS), ACTIVE_CHUNKS => Chunk::Abstract.mask_re(ACTIVE_CHUNKS),
HIDE_CHUNKS => Chunk::Abstract.mask_re(HIDE_CHUNKS) HIDE_CHUNKS => Chunk::Abstract.mask_re(HIDE_CHUNKS)
} }
def init_chunk_manager def init_chunk_manager
@chunks_by_type = Hash.new @chunks_by_type = Hash.new
Chunk::Abstract::derivatives.each{|chunk_type| Chunk::Abstract::derivatives.each{|chunk_type|
@chunks_by_type[chunk_type] = Array.new @chunks_by_type[chunk_type] = Array.new
} }
@chunks_by_id = Hash.new @chunks_by_id = Hash.new
@chunks = [] @chunks = []
@ -77,20 +77,20 @@ module ChunkManager
def scan_chunkid(text) def scan_chunkid(text)
text.scan(MASK_RE[ACTIVE_CHUNKS]){|a| yield a[0] } text.scan(MASK_RE[ACTIVE_CHUNKS]){|a| yield a[0] }
end end
def find_chunks(chunk_type) def find_chunks(chunk_type)
@chunks.select { |chunk| chunk.kind_of?(chunk_type) and chunk.rendered? } @chunks.select { |chunk| chunk.kind_of?(chunk_type) and chunk.rendered? }
end end
end end
# A simplified version of WikiContent. Useful to avoid recursion problems in # A simplified version of WikiContent. Useful to avoid recursion problems in
# WikiContent.new # WikiContent.new
class WikiContentStub < String class WikiContentStub < String
attr_reader :options attr_reader :options
include ChunkManager include ChunkManager
def initialize(content, options) def initialize(content, options)
super(content) super(content)
@options = options @options = options
@ -99,15 +99,15 @@ class WikiContentStub < String
# Detects the mask strings contained in the text of chunks of type chunk_types # Detects the mask strings contained in the text of chunks of type chunk_types
# and yields the corresponding chunk ids # and yields the corresponding chunk ids
# example: content = "chunk123categorychunk <pre>chunk456categorychunk</pre>" # example: content = "chunk123categorychunk <pre>chunk456categorychunk</pre>"
# inside_chunks(Literal::Pre) ==> yield 456 # inside_chunks(Literal::Pre) ==> yield 456
def inside_chunks(chunk_types) def inside_chunks(chunk_types)
chunk_types.each{|chunk_type| chunk_type.apply_to(self) } chunk_types.each{|chunk_type| chunk_type.apply_to(self) }
chunk_types.each{|chunk_type| @chunks_by_type[chunk_type].each{|hide_chunk| chunk_types.each{|chunk_type| @chunks_by_type[chunk_type].each{|hide_chunk|
scan_chunkid(hide_chunk.text){|id| yield id } scan_chunkid(hide_chunk.text){|id| yield id }
} }
} }
end end
end end
@ -132,12 +132,12 @@ class WikiContent < String
@web = @revision.page.web @web = @revision.page.web
@options = DEFAULT_OPTS.dup.merge(options) @options = DEFAULT_OPTS.dup.merge(options)
@options[:engine] = Engines::MAP[@web.markup] @options[:engine] = Engines::MAP[@web.markup]
@options[:engine_opts] = [:filter_html, :filter_styles] if @web.safe_mode? @options[:engine_opts] = [:filter_html, :filter_styles] if @web.safe_mode?
@options[:active_chunks] = (ACTIVE_CHUNKS - [WikiChunk::Word] ) if @web.brackets_only? @options[:active_chunks] = (ACTIVE_CHUNKS - [WikiChunk::Word] ) if @web.brackets_only?
@not_rendered = @pre_rendered = nil @not_rendered = @pre_rendered = nil
super(@revision.content) super(@revision.content)
init_chunk_manager init_chunk_manager
build_chunks build_chunks
@ -145,9 +145,10 @@ class WikiContent < String
end end
# Call @web.page_link using current options. # Call @web.page_link using current options.
def page_link(name, text, link_type) def page_link(web_name, name, text, link_type)
web = Web.find_by_name(web_name) || Web.find_by_address(web_name) || @web
@options[:link_type] = (link_type || :show) @options[:link_type] = (link_type || :show)
@url_generator.make_link(name, @web, text, @options) @url_generator.make_link(name, web, text, @options)
end end
def build_chunks def build_chunks
@ -158,7 +159,7 @@ class WikiContent < String
# Handle hiding contexts like "pre" and "code" etc.. # Handle hiding contexts like "pre" and "code" etc..
# The markup (textile, rdoc etc) can produce such contexts with its own syntax. # The markup (textile, rdoc etc) can produce such contexts with its own syntax.
# To reveal them, we work on a copy of the content. # To reveal them, we work on a copy of the content.
# The copy is rendered and used to detect the chunks that are inside protecting context # The copy is rendered and used to detect the chunks that are inside protecting context
# These chunks are reverted on the original content string. # These chunks are reverted on the original content string.
copy = WikiContentStub.new(self, @options) copy = WikiContentStub.new(self, @options)
@ -170,25 +171,25 @@ class WikiContent < String
end end
def pre_render! def pre_render!
unless @pre_rendered unless @pre_rendered
@chunks_by_type[Include].each{|chunk| chunk.unmask } @chunks_by_type[Include].each{|chunk| chunk.unmask }
@pre_rendered = String.new(self) @pre_rendered = String.new(self)
end end
@pre_rendered @pre_rendered
end end
def render! def render!
pre_render! pre_render!
@options[:engine].apply_to(self) @options[:engine].apply_to(self)
# unmask in one go. $~[1] is the chunk id # unmask in one go. $~[1] is the chunk id
gsub!(MASK_RE[ACTIVE_CHUNKS]) do gsub!(MASK_RE[ACTIVE_CHUNKS]) do
chunk = @chunks_by_id[$~[1].to_i] chunk = @chunks_by_id[$~[1].to_i]
if chunk.nil? if chunk.nil?
# if we match a chunkmask that existed in the original content string # if we match a chunkmask that existed in the original content string
# just keep it as it is # just keep it as it is
$~[0] $~[0]
else else
chunk.unmask_text chunk.unmask_text
end end
end end
self self