Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 344
- Log:
Massive changeset which brings the old, ROOL customised Instiki
version up to date, but without any ROOL customisations in this
latest checked-in version (which is 0.19.1). This is deliberate,
so that it's easy to see the changes made for the ROOL version
in a subsequent changeset. The 'app/views/shared' directory is not
part of Instiki but is kept to maintain the change history with
updated ROOL customisations, some of which involve the same files
in that same directory.
- Author:
- rool
- Date:
- Sat Mar 19 19:52:13 +0000 2011
- Size:
- 6866 Bytes
1 | require 'cgi' |
2 | require 'chunks/engines' |
3 | require 'chunks/category' |
4 | require_dependency 'chunks/include' |
5 | require_dependency 'chunks/redirect' |
6 | require_dependency 'chunks/wiki' |
7 | require_dependency 'chunks/literal' |
8 | require 'chunks/nowiki' |
9 | require 'sanitizer' |
10 | require 'instiki_stringsupport' |
11 | |
12 | |
13 | # Wiki content is just a string that can process itself with a chain of |
14 | # actions. The actions can modify wiki content so that certain parts of |
15 | # it are protected from being rendered by later actions. |
16 | # |
17 | # When wiki content is rendered, it can be interrogated to find out |
18 | # which chunks were rendered. This means things like categories, wiki |
19 | # links, can be determined. |
20 | # |
21 | # Exactly how wiki content is rendered is determined by a number of |
22 | # settings that are optionally passed in to a constructor. The current |
23 | # options are: |
24 | # * :engine |
25 | # => The structural markup engine to use (Textile, Markdown, RDoc) |
26 | # * :engine_opts |
27 | # => A list of options to pass to the markup engines (safe modes, etc) |
28 | # * :pre_engine_actions |
29 | # => A list of render actions or chunks to be processed before the |
30 | # markup engine is applied. By default this is: |
31 | # Category, Include, URIChunk, WikiChunk::Link, WikiChunk::Word |
32 | # * :post_engine_actions |
33 | # => A list of render actions or chunks to apply after the markup |
34 | # engine. By default these are: |
35 | # Literal::Pre, Literal::Tags |
36 | # * :mode |
37 | # => How should the content be rendered? For normal display (show), |
38 | # publishing (:publish) or export (:export)? |
39 | |
40 | module ChunkManager |
41 | attr_reader :chunks_by_type, :chunks_by_id, :chunks, :chunk_id |
42 | |
43 | ACTIVE_CHUNKS = [ NoWiki, Category, Redirect, WikiChunk::Link, |
44 | WikiChunk::Word ] |
45 | |
46 | HIDE_CHUNKS = [ Literal::Pre, Literal::Tags, Literal::Math ] |
47 | |
48 | MASK_RE = { |
49 | ACTIVE_CHUNKS => Chunk::Abstract.mask_re(ACTIVE_CHUNKS), |
50 | HIDE_CHUNKS => Chunk::Abstract.mask_re(HIDE_CHUNKS) |
51 | } |
52 | |
53 | def init_chunk_manager |
54 | @chunks_by_type = Hash.new |
55 | Chunk::Abstract::derivatives.each{|chunk_type| |
56 | @chunks_by_type[chunk_type] = Array.new |
57 | } |
58 | @chunks_by_id = Hash.new |
59 | @chunks = [] |
60 | @chunk_id = 0 |
61 | end |
62 | |
63 | def add_chunk(c) |
64 | @chunks_by_type[c.class] << c |
65 | @chunks_by_id[c.object_id] = c |
66 | @chunks << c |
67 | @chunk_id += 1 |
68 | end |
69 | |
70 | def delete_chunk(c) |
71 | @chunks_by_type[c.class].delete(c) |
72 | @chunks_by_id.delete(c.object_id) |
73 | @chunks.delete(c) |
74 | end |
75 | |
76 | def merge_chunks(other) |
77 | other.chunks.each{|c| add_chunk(c)} |
78 | end |
79 | |
80 | def scan_chunkid(text) |
81 | text.scan(MASK_RE[ACTIVE_CHUNKS]){|a| yield a[0] } |
82 | end |
83 | |
84 | def find_chunks(chunk_type) |
85 | @chunks.select { |chunk| chunk.kind_of?(chunk_type) and chunk.rendered? } |
86 | end |
87 | |
88 | end |
89 | |
90 | # A simplified version of WikiContent. Useful to avoid recursion problems in |
91 | # WikiContent.new |
92 | class WikiContentStub < String |
93 | |
94 | attr_reader :web, :options |
95 | include ChunkManager |
96 | |
97 | def initialize(content, web, options) |
98 | super(content) |
99 | @web = web |
100 | @options = options |
101 | init_chunk_manager |
102 | end |
103 | |
104 | # Detects the mask strings contained in the text of chunks of type chunk_types |
105 | # and yields the corresponding chunk ids |
106 | # example: content = "chunk123categorychunk <pre>chunk456categorychunk</pre>" |
107 | # inside_chunks(Literal::Pre) ==> yield 456 |
108 | def inside_chunks(chunk_types) |
109 | chunk_types.each{|chunk_type| chunk_type.apply_to(self) } |
110 | |
111 | chunk_types.each{|chunk_type| @chunks_by_type[chunk_type].each{|hide_chunk| |
112 | scan_chunkid(hide_chunk.text){|id| yield id } |
113 | } |
114 | } |
115 | end |
116 | end |
117 | |
118 | class WikiContent < ActiveSupport::SafeBuffer |
119 | |
120 | include ChunkManager |
121 | include Sanitizer |
122 | |
123 | DEFAULT_OPTS = { |
124 | :active_chunks => ACTIVE_CHUNKS, |
125 | :hide_chunks => HIDE_CHUNKS, |
126 | :engine => Engines::MarkdownMML, |
127 | :engine_opts => [], |
128 | :mode => :show |
129 | }.freeze |
130 | |
131 | attr_reader :web, :options, :revision, :not_rendered, :pre_rendered, :url_generator |
132 | |
133 | # Create a new wiki content string from the given one. |
134 | # The options are explained at the top of this file. |
135 | def initialize(revision, url_generator, options = {}) |
136 | @revision = revision |
137 | @url_generator = url_generator |
138 | @web = @revision.page.web |
139 | |
140 | @options = DEFAULT_OPTS.dup.merge(options) |
141 | @options[:engine] = Engines::MAP[@web.markup] |
142 | @options[:engine_opts] = [:filter_html, :filter_styles] if @web.safe_mode? |
143 | @options[:active_chunks] = (ACTIVE_CHUNKS - [WikiChunk::Word] ) if @web.brackets_only? |
144 | @options[:hide_chunks] = (HIDE_CHUNKS - [Literal::Math] ) unless |
145 | [Engines::MarkdownMML, Engines::MarkdownPNG].include?(@options[:engine]) |
146 | if @options[:engine] == Engines::MarkdownPNG |
147 | @options[:png_url] = |
148 | @options[:mode] == :export ? 'files/pngs/' : |
149 | (@url_generator.url_for :controller => 'file', :web => @web.address, |
150 | :action => 'file', :id => 'pngs', :only_path => true) + '/' |
151 | end |
152 | |
153 | @not_rendered = @pre_rendered = nil |
154 | |
155 | super(@revision.content) |
156 | init_chunk_manager |
157 | build_chunks |
158 | @not_rendered = String.new(self) |
159 | end |
160 | |
161 | # Call @web.page_link using current options. |
162 | def page_link(web_name, name, text, link_type) |
163 | web = Web.find_by_name(web_name) || Web.find_by_address(web_name) || @web |
164 | @options[:link_type] = (link_type || :show) |
165 | @url_generator.make_link(@web, name, web, text, @options) |
166 | end |
167 | |
168 | def build_chunks |
169 | # create and mask Includes and "active_chunks" chunks |
170 | NoWiki.apply_to(self) if @options[:active_chunks].include?(NoWiki) |
171 | Include.apply_to(self) |
172 | @options[:active_chunks].each{|chunk_type| chunk_type.apply_to(self) unless chunk_type == NoWiki} |
173 | |
174 | # Handle hiding contexts like "pre" and "code" etc.. |
175 | # The markup (textile, rdoc etc) can produce such contexts with its own syntax. |
176 | # To reveal them, we work on a copy of the content. |
177 | # The copy is rendered and used to detect the chunks that are inside protecting context |
178 | # These chunks are reverted on the original content string. |
179 | |
180 | copy = WikiContentStub.new(self, @web, @options) |
181 | @options[:engine].apply_to(copy) |
182 | |
183 | copy.inside_chunks(@options[:hide_chunks]) do |id| |
184 | @chunks_by_id[id.to_i].revert if @chunks_by_id[id.to_i] |
185 | end |
186 | end |
187 | |
188 | def pre_render! |
189 | unless @pre_rendered |
190 | @chunks_by_type[Include].each{|chunk| chunk.unmask } |
191 | @pre_rendered = String.new(self) |
192 | end |
193 | @pre_rendered |
194 | end |
195 | |
196 | def render! |
197 | pre_render! |
198 | @options[:engine].apply_to(self) |
199 | as_utf8 |
200 | # unmask in one go. $~[1] is the chunk id |
201 | gsub!(MASK_RE[ACTIVE_CHUNKS]) do |
202 | chunk = @chunks_by_id[$~[1].to_i] |
203 | if chunk.nil? |
204 | # if we match a chunkmask that existed in the original content string |
205 | # just keep it as it is |
206 | $~[0] |
207 | else |
208 | chunk.unmask_text |
209 | end |
210 | end |
211 | self.replace xhtml_sanitize(self) |
212 | self.html_safe |
213 | end |
214 | |
215 | def page_name |
216 | @revision.page.name |
217 | end |
218 | |
219 | end |
220 |