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