app/models/wiki_content.rb in instiki-0.10.0 vs app/models/wiki_content.rb in instiki-0.10.1
- old
+ new
@@ -1,207 +1,207 @@
-require 'cgi'
-require 'chunks/engines'
-require 'chunks/category'
-require 'chunks/include'
-require 'chunks/wiki'
-require 'chunks/literal'
-require 'chunks/uri'
-require 'chunks/nowiki'
-
-# Wiki content is just a string that can process itself with a chain of
-# actions. The actions can modify wiki content so that certain parts of
-# it are protected from being rendered by later actions.
-#
-# When wiki content is rendered, it can be interrogated to find out
-# which chunks were rendered. This means things like categories, wiki
-# links, can be determined.
-#
-# Exactly how wiki content is rendered is determined by a number of
-# settings that are optionally passed in to a constructor. The current
-# options are:
-# * :engine
-# => The structural markup engine to use (Textile, Markdown, RDoc)
-# * :engine_opts
-# => A list of options to pass to the markup engines (safe modes, etc)
-# * :pre_engine_actions
-# => A list of render actions or chunks to be processed before the
-# markup engine is applied. By default this is:
-# Category, Include, URIChunk, WikiChunk::Link, WikiChunk::Word
-# * :post_engine_actions
-# => A list of render actions or chunks to apply after the markup
-# engine. By default these are:
-# Literal::Pre, Literal::Tags
-# * :mode
-# => How should the content be rendered? For normal display (show),
-# publishing (:publish) or export (:export)?
-#
-# AUTHOR: Mark Reid <mark @ threewordslong . com>
-# CREATED: 15th May 2004
-# UPDATED: 22nd May 2004
-
-module ChunkManager
- attr_reader :chunks_by_type, :chunks_by_id, :chunks, :chunk_id
-
- ACTIVE_CHUNKS = [ NoWiki, Category, WikiChunk::Link, URIChunk, LocalURIChunk,
- WikiChunk::Word ]
-
- HIDE_CHUNKS = [ Literal::Pre, Literal::Tags ]
-
- MASK_RE = {
- ACTIVE_CHUNKS => Chunk::Abstract.mask_re(ACTIVE_CHUNKS),
- HIDE_CHUNKS => Chunk::Abstract.mask_re(HIDE_CHUNKS)
- }
-
- def init_chunk_manager
- @chunks_by_type = Hash.new
- Chunk::Abstract::derivatives.each{|chunk_type|
- @chunks_by_type[chunk_type] = Array.new
- }
- @chunks_by_id = Hash.new
- @chunks = []
- @chunk_id = 0
- end
-
- def add_chunk(c)
- @chunks_by_type[c.class] << c
- @chunks_by_id[c.id] = c
- @chunks << c
- @chunk_id += 1
- end
-
- def delete_chunk(c)
- @chunks_by_type[c.class].delete(c)
- @chunks_by_id.delete(c.id)
- @chunks.delete(c)
- end
-
- def merge_chunks(other)
- other.chunks.each{|c| add_chunk(c)}
- end
-
- def scan_chunkid(text)
- text.scan(MASK_RE[ACTIVE_CHUNKS]){|a| yield a[0] }
- end
-
- def find_chunks(chunk_type)
- @chunks.select { |chunk| chunk.kind_of?(chunk_type) and chunk.rendered? }
- end
-
- # for testing and WikiContentStub; we need a page_id even if we have no page
- def page_id
- 0
- end
-
-end
-
-# A simplified version of WikiContent. Useful to avoid recursion problems in
-# WikiContent.new
-class WikiContentStub < String
- attr_reader :options
- include ChunkManager
- def initialize(content, options)
- super(content)
- @options = options
- init_chunk_manager
- end
-
- # Detects the mask strings contained in the text of chunks of type chunk_types
- # and yields the corresponding chunk ids
- # example: content = "chunk123categorychunk <pre>chunk456categorychunk</pre>"
- # inside_chunks(Literal::Pre) ==> yield 456
- def inside_chunks(chunk_types)
- chunk_types.each{|chunk_type| chunk_type.apply_to(self) }
-
- chunk_types.each{|chunk_type| @chunks_by_type[chunk_type].each{|hide_chunk|
- scan_chunkid(hide_chunk.text){|id| yield id }
- }
- }
- end
-end
-
-class WikiContent < String
-
- include ChunkManager
-
- DEFAULT_OPTS = {
- :active_chunks => ACTIVE_CHUNKS,
- :engine => Engines::Textile,
- :engine_opts => [],
- :mode => :show
- }.freeze
-
- attr_reader :web, :options, :revision, :not_rendered, :pre_rendered
-
- # Create a new wiki content string from the given one.
- # The options are explained at the top of this file.
- def initialize(revision, options = {})
- @revision = revision
- @web = @revision.page.web
-
- @options = DEFAULT_OPTS.dup.merge(options)
- @options[:engine] = Engines::MAP[@web.markup]
- @options[:engine_opts] = [:filter_html, :filter_styles] if @web.safe_mode
- @options[:active_chunks] = (ACTIVE_CHUNKS - [WikiChunk::Word] ) if @web.brackets_only
-
- super(@revision.content)
- init_chunk_manager
- build_chunks
- @not_rendered = String.new(self)
- end
-
- # Call @web.page_link using current options.
- def page_link(name, text, link_type)
- @options[:link_type] = (link_type || :show)
- @web.make_link(name, text, @options)
- end
-
- def build_chunks
- # create and mask Includes and "active_chunks" chunks
- Include.apply_to(self)
- @options[:active_chunks].each{|chunk_type| chunk_type.apply_to(self)}
-
- # Handle hiding contexts like "pre" and "code" etc..
- # The markup (textile, rdoc etc) can produce such contexts with its own syntax.
- # 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
- # These chunks are reverted on the original content string.
-
- copy = WikiContentStub.new(self, @options)
- @options[:engine].apply_to(copy)
-
- copy.inside_chunks(HIDE_CHUNKS) do |id|
- @chunks_by_id[id].revert
- end
- end
-
- def pre_render!
- unless @pre_rendered
- @chunks_by_type[Include].each{|chunk| chunk.unmask }
- @pre_rendered = String.new(self)
- end
- @pre_rendered
- end
-
- def render!
- pre_render!
- @options[:engine].apply_to(self)
- # unmask in one go. $~[1] is the chunk id
- gsub!(MASK_RE[ACTIVE_CHUNKS]){
- if chunk = @chunks_by_id[$~[1]]
- chunk.unmask_text
- # if we match a chunkmask that existed in the original content string
- # just keep it as it is
- else
- $~[0]
- end}
- self
- end
-
- def page_name
- @revision.page.name
- end
-
- def page_id
- @revision.page.id
- end
-
-end
+require 'cgi'
+require 'chunks/engines'
+require 'chunks/category'
+require 'chunks/include'
+require 'chunks/wiki'
+require 'chunks/literal'
+require 'chunks/uri'
+require 'chunks/nowiki'
+
+# Wiki content is just a string that can process itself with a chain of
+# actions. The actions can modify wiki content so that certain parts of
+# it are protected from being rendered by later actions.
+#
+# When wiki content is rendered, it can be interrogated to find out
+# which chunks were rendered. This means things like categories, wiki
+# links, can be determined.
+#
+# Exactly how wiki content is rendered is determined by a number of
+# settings that are optionally passed in to a constructor. The current
+# options are:
+# * :engine
+# => The structural markup engine to use (Textile, Markdown, RDoc)
+# * :engine_opts
+# => A list of options to pass to the markup engines (safe modes, etc)
+# * :pre_engine_actions
+# => A list of render actions or chunks to be processed before the
+# markup engine is applied. By default this is:
+# Category, Include, URIChunk, WikiChunk::Link, WikiChunk::Word
+# * :post_engine_actions
+# => A list of render actions or chunks to apply after the markup
+# engine. By default these are:
+# Literal::Pre, Literal::Tags
+# * :mode
+# => How should the content be rendered? For normal display (show),
+# publishing (:publish) or export (:export)?
+#
+# AUTHOR: Mark Reid <mark @ threewordslong . com>
+# CREATED: 15th May 2004
+# UPDATED: 22nd May 2004
+
+module ChunkManager
+ attr_reader :chunks_by_type, :chunks_by_id, :chunks, :chunk_id
+
+ ACTIVE_CHUNKS = [ NoWiki, Category, WikiChunk::Link, URIChunk, LocalURIChunk,
+ WikiChunk::Word ]
+
+ HIDE_CHUNKS = [ Literal::Pre, Literal::Tags ]
+
+ MASK_RE = {
+ ACTIVE_CHUNKS => Chunk::Abstract.mask_re(ACTIVE_CHUNKS),
+ HIDE_CHUNKS => Chunk::Abstract.mask_re(HIDE_CHUNKS)
+ }
+
+ def init_chunk_manager
+ @chunks_by_type = Hash.new
+ Chunk::Abstract::derivatives.each{|chunk_type|
+ @chunks_by_type[chunk_type] = Array.new
+ }
+ @chunks_by_id = Hash.new
+ @chunks = []
+ @chunk_id = 0
+ end
+
+ def add_chunk(c)
+ @chunks_by_type[c.class] << c
+ @chunks_by_id[c.id] = c
+ @chunks << c
+ @chunk_id += 1
+ end
+
+ def delete_chunk(c)
+ @chunks_by_type[c.class].delete(c)
+ @chunks_by_id.delete(c.id)
+ @chunks.delete(c)
+ end
+
+ def merge_chunks(other)
+ other.chunks.each{|c| add_chunk(c)}
+ end
+
+ def scan_chunkid(text)
+ text.scan(MASK_RE[ACTIVE_CHUNKS]){|a| yield a[0] }
+ end
+
+ def find_chunks(chunk_type)
+ @chunks.select { |chunk| chunk.kind_of?(chunk_type) and chunk.rendered? }
+ end
+
+ # for testing and WikiContentStub; we need a page_id even if we have no page
+ def page_id
+ 0
+ end
+
+end
+
+# A simplified version of WikiContent. Useful to avoid recursion problems in
+# WikiContent.new
+class WikiContentStub < String
+ attr_reader :options
+ include ChunkManager
+ def initialize(content, options)
+ super(content)
+ @options = options
+ init_chunk_manager
+ end
+
+ # Detects the mask strings contained in the text of chunks of type chunk_types
+ # and yields the corresponding chunk ids
+ # example: content = "chunk123categorychunk <pre>chunk456categorychunk</pre>"
+ # inside_chunks(Literal::Pre) ==> yield 456
+ def inside_chunks(chunk_types)
+ chunk_types.each{|chunk_type| chunk_type.apply_to(self) }
+
+ chunk_types.each{|chunk_type| @chunks_by_type[chunk_type].each{|hide_chunk|
+ scan_chunkid(hide_chunk.text){|id| yield id }
+ }
+ }
+ end
+end
+
+class WikiContent < String
+
+ include ChunkManager
+
+ DEFAULT_OPTS = {
+ :active_chunks => ACTIVE_CHUNKS,
+ :engine => Engines::Textile,
+ :engine_opts => [],
+ :mode => :show
+ }.freeze
+
+ attr_reader :web, :options, :revision, :not_rendered, :pre_rendered
+
+ # Create a new wiki content string from the given one.
+ # The options are explained at the top of this file.
+ def initialize(revision, options = {})
+ @revision = revision
+ @web = @revision.page.web
+
+ @options = DEFAULT_OPTS.dup.merge(options)
+ @options[:engine] = Engines::MAP[@web.markup]
+ @options[:engine_opts] = [:filter_html, :filter_styles] if @web.safe_mode
+ @options[:active_chunks] = (ACTIVE_CHUNKS - [WikiChunk::Word] ) if @web.brackets_only
+
+ super(@revision.content)
+ init_chunk_manager
+ build_chunks
+ @not_rendered = String.new(self)
+ end
+
+ # Call @web.page_link using current options.
+ def page_link(name, text, link_type)
+ @options[:link_type] = (link_type || :show)
+ @web.make_link(name, text, @options)
+ end
+
+ def build_chunks
+ # create and mask Includes and "active_chunks" chunks
+ Include.apply_to(self)
+ @options[:active_chunks].each{|chunk_type| chunk_type.apply_to(self)}
+
+ # Handle hiding contexts like "pre" and "code" etc..
+ # The markup (textile, rdoc etc) can produce such contexts with its own syntax.
+ # 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
+ # These chunks are reverted on the original content string.
+
+ copy = WikiContentStub.new(self, @options)
+ @options[:engine].apply_to(copy)
+
+ copy.inside_chunks(HIDE_CHUNKS) do |id|
+ @chunks_by_id[id].revert
+ end
+ end
+
+ def pre_render!
+ unless @pre_rendered
+ @chunks_by_type[Include].each{|chunk| chunk.unmask }
+ @pre_rendered = String.new(self)
+ end
+ @pre_rendered
+ end
+
+ def render!
+ pre_render!
+ @options[:engine].apply_to(self)
+ # unmask in one go. $~[1] is the chunk id
+ gsub!(MASK_RE[ACTIVE_CHUNKS]){
+ if chunk = @chunks_by_id[$~[1]]
+ chunk.unmask_text
+ # if we match a chunkmask that existed in the original content string
+ # just keep it as it is
+ else
+ $~[0]
+ end}
+ self
+ end
+
+ def page_name
+ @revision.page.name
+ end
+
+ def page_id
+ @revision.page.id
+ end
+
+end