Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 367
- Log:
Final file in the Instiki changes for 0.19.1 and ROOL customisation;
looks like this got missed out in Changeset #344/Changeset #345. It
is part of the original 0.19.1 distribution.
- Author:
- rool
- Date:
- Sat Mar 19 22:08:12 +0000 2011
- Size:
- 8790 Bytes
1 | require 'xhtmldiff' |
2 | |
3 | # Temporary class containing all rendering stuff from a Revision |
4 | # I want to shift all rendering loguc to the controller eventually |
5 | |
6 | class PageRenderer |
7 | |
8 | def self.setup_url_generator(url_generator) |
9 | @@url_generator = url_generator |
10 | end |
11 | |
12 | def self.teardown_url_generator |
13 | @@url_generator = nil |
14 | end |
15 | |
16 | attr_reader :revision |
17 | |
18 | def initialize(revision = nil) |
19 | self.revision = revision |
20 | end |
21 | |
22 | def revision=(r) |
23 | @revision = r |
24 | @display_content = @display_published = @wiki_words_cache = @wiki_includes_cache = |
25 | @wiki_references_cache = nil |
26 | end |
27 | |
28 | def display_content(update_references = false) |
29 | @display_content ||= render(:update_references => update_references) |
30 | end |
31 | |
32 | def display_content_for_export |
33 | render :mode => :export |
34 | end |
35 | |
36 | def display_published |
37 | @display_published ||= render(:mode => :publish) |
38 | end |
39 | |
40 | require 'hpricot' |
41 | |
42 | def display_diff |
43 | previous_revision = @revision.page.previous_revision(@revision) |
44 | if previous_revision |
45 | |
46 | # Pass one: Normal Instiki behaviour |
47 | |
48 | previous_content = "<div>" + WikiContent.new(previous_revision, @@url_generator).render!.to_s + "</div>" |
49 | current_content = "<div>" + display_content.to_s + "</div>" |
50 | diff_doc = REXML::Document.new |
51 | div = REXML::Element.new('div', nil, {:respect_whitespace =>:all}) |
52 | div.attributes['class'] = 'xhtmldiff_wrapper' |
53 | diff_doc << div |
54 | |
55 | begin |
56 | |
57 | # Allow exceptions here. We expect them from REXML. |
58 | |
59 | parsed_previous_revision = REXML::HashableElementDelegator.new( |
60 | REXML::XPath.first(REXML::Document.new(previous_content), '/div')) |
61 | parsed_display_content = REXML::HashableElementDelegator.new( |
62 | REXML::XPath.first(REXML::Document.new(current_content), '/div')) |
63 | |
64 | hd = XHTMLDiff.new(div) |
65 | Diff::LCS.traverse_balanced(parsed_previous_revision, parsed_display_content, hd) |
66 | |
67 | diffs = '' |
68 | diff_doc.write(diffs, -1, true, true) |
69 | diffs.gsub(/\A<div class='xhtmldiff_wrapper'>(.*)<\/div>\Z/m, '\1').html_safe |
70 | |
71 | rescue |
72 | |
73 | # To reach here, REXML probably complained. Users probably wrote HTML |
74 | # in the page, but the markup was invalid XHTML - forgetting to close |
75 | # "<li>" tags is a common example. So, try the diff again, this time |
76 | # using Hpricot as a wrapper around the post-processed content to try |
77 | # and sanitise the markup at the possible expense of correctness. |
78 | |
79 | begin |
80 | |
81 | previous_content = "<div>" + Hpricot(WikiContent.new(previous_revision, @@url_generator).render!.to_s).to_s + "</div>" |
82 | current_content = "<div>" + Hpricot(display_content.to_s).to_s + "</div>" |
83 | |
84 | parsed_previous_revision = REXML::HashableElementDelegator.new( |
85 | REXML::XPath.first(REXML::Document.new(previous_content), '/div')) |
86 | parsed_display_content = REXML::HashableElementDelegator.new( |
87 | REXML::XPath.first(REXML::Document.new(current_content), '/div')) |
88 | |
89 | hd = XHTMLDiff.new(div) |
90 | Diff::LCS.traverse_balanced(parsed_previous_revision, parsed_display_content, hd) |
91 | |
92 | diffs = "<p><ins class=\"diffins\"><strong>Warning: The current and/or previous revision contained markup errors which prevented proper diff analysis. A guess at markup correction has been made but the diff may not be a truly accurate reflection of the real changes.</strong></ins></p>" |
93 | diff_doc.write(diffs, -1, true, true) |
94 | diffs.gsub(/\A<div class='xhtmldiff_wrapper'>(.*)<\/div>\Z/m, '\1').html_safe |
95 | |
96 | rescue |
97 | |
98 | # Further exceptions indicate a lost cause! |
99 | "<p><ins class=\"diffins\"><strong>The current and/or previous revision contain invalid markup which is too broken for the diff parser to understand.</strong></ins></p>".html_safe |
100 | |
101 | end |
102 | end |
103 | else |
104 | display_content |
105 | end |
106 | end |
107 | |
108 | attr :s5_theme |
109 | def s5_theme=(s) |
110 | @s5_theme = s |
111 | end |
112 | |
113 | # Renders an S5 slideshow |
114 | def display_s5 |
115 | @display_s5 ||= render(:mode => :s5, |
116 | :engine_opts => {:author => @author, :title => @plain_name}, |
117 | :renderer => self) |
118 | end |
119 | |
120 | # Returns an array of all the WikiIncludes present in the content of this revision. |
121 | def wiki_includes |
122 | unless @wiki_includes_cache |
123 | chunks = display_content.find_chunks(Include) |
124 | @wiki_includes_cache = chunks.map { |c| ( c.escaped? ? nil : c.page_name ) }.compact.uniq |
125 | end |
126 | @wiki_includes_cache |
127 | end |
128 | |
129 | # Returns an array of all the WikiReferences present in the content of this revision. |
130 | def wiki_references |
131 | unless @wiki_references_cache |
132 | chunks = display_content.find_chunks(WikiChunk::WikiReference) |
133 | @wiki_references_cache = chunks.map { |c| ( c.escaped? ? nil : c.page_name ) }.compact.uniq |
134 | end |
135 | @wiki_references_cache |
136 | end |
137 | |
138 | # Returns an array of all the WikiWords present in the content of this revision. |
139 | def wiki_words |
140 | @wiki_words_cache ||= find_wiki_words(display_content) |
141 | end |
142 | |
143 | def find_wiki_words(rendering_result) |
144 | the_wiki_words = wiki_links(rendering_result) |
145 | # Exclude backslash-escaped wiki words, such as \WikiWord, as well as links to files |
146 | # and pictures, such as [[foo.txt:file]] or [[foo.jpg:pic]] |
147 | the_wiki_words.delete_if { |link| link.escaped? or [:pic, :file, :audio, :video, :delete].include?(link.link_type) } |
148 | # convert to the list of unique page names |
149 | the_wiki_words.map { |link| ( link.page_name ) }.uniq |
150 | end |
151 | |
152 | # Returns an array of all the WikiWords present in the content of this revision. |
153 | def wiki_files |
154 | @wiki_files_cache ||= find_wiki_files(display_content) |
155 | end |
156 | |
157 | def find_wiki_files(rendering_result) |
158 | the_wiki_files = wiki_links(rendering_result) |
159 | the_wiki_files.delete_if { |link| ![:pic, :file, :audio, :video].include?(link.link_type) } |
160 | the_wiki_files.map { |link| ( link.page_name ) }.uniq |
161 | end |
162 | |
163 | def wiki_links(rendering_result) |
164 | rendering_result.find_chunks(WikiChunk::WikiLink) |
165 | end |
166 | |
167 | # Returns an array of all the WikiWords present in the content of this revision. |
168 | # that already exists as a page in the web. |
169 | def existing_pages |
170 | wiki_words.select { |wiki_word| @revision.page.web.page(wiki_word) } |
171 | end |
172 | |
173 | # Returns an array of all the WikiWords present in the content of this revision |
174 | # that *doesn't* already exists as a page in the web. |
175 | def unexisting_pages |
176 | wiki_words - existing_pages |
177 | end |
178 | |
179 | private |
180 | |
181 | def render(options = {}) |
182 | rendering_result = WikiContent.new(@revision, @@url_generator, options).render! |
183 | update_references(rendering_result) if options[:update_references] |
184 | rendering_result |
185 | end |
186 | |
187 | def update_references(rendering_result) |
188 | WikiReference.delete_all ['page_id = ?', @revision.page_id] |
189 | |
190 | references = @revision.page.wiki_references |
191 | |
192 | wiki_words = find_wiki_words(rendering_result) |
193 | # TODO it may be desirable to save links to files and pictures as WikiReference objects |
194 | # present version doesn't do it |
195 | |
196 | wiki_words.each do |referenced_name| |
197 | # Links to self are always considered linked |
198 | if referenced_name == @revision.page.name |
199 | link_type = WikiReference::LINKED_PAGE |
200 | else |
201 | link_type = WikiReference.link_type(@revision.page.web, referenced_name) |
202 | end |
203 | references.build :referenced_name => referenced_name, :link_type => link_type |
204 | end |
205 | |
206 | wiki_files = find_wiki_files(rendering_result) |
207 | wiki_files.each do |referenced_name| |
208 | references.build :referenced_name => referenced_name, :link_type => WikiReference::FILE |
209 | end |
210 | |
211 | include_chunks = rendering_result.find_chunks(Include) |
212 | includes = include_chunks.map { |c| ( c.escaped? ? nil : c.page_name ) }.compact.uniq |
213 | includes.each do |included_page_name| |
214 | references.build :referenced_name => included_page_name, |
215 | :link_type => WikiReference::INCLUDED_PAGE |
216 | end |
217 | |
218 | redirect_chunks = rendering_result.find_chunks(Redirect) |
219 | redirects = redirect_chunks.map { |c| ( c.escaped? ? nil : c.page_name ) }.compact.uniq |
220 | redirects.each do |redirected_page_name| |
221 | references.build :referenced_name => redirected_page_name, |
222 | :link_type => WikiReference::REDIRECTED_PAGE |
223 | end |
224 | |
225 | # ugly hack: store these in a thread-local variable, so that the cache-sweeper has access to it. |
226 | Thread.current[:page_redirects] ? |
227 | Thread.current[:page_redirects].update({ @revision.page => redirects}) : |
228 | Thread.current[:page_redirects] = { @revision.page => redirects} |
229 | |
230 | categories = rendering_result.find_chunks(Category).map { |cat| cat.list }.flatten |
231 | categories.each do |category| |
232 | references.build :referenced_name => category, :link_type => WikiReference::CATEGORY |
233 | end |
234 | end |
235 | end |