Changesets can be listed by changeset number.
The Git repository is here.
Changeset 359
Inter-link Textile to Wiki syntax page patching - the "other half" of the
migration script, as well as code which tries to protect against "bad" use
of Textile during ordinary page edits.
- Comitted by: rool
- Date: Saturday March 19 22:00:12 2011 (over 13 years ago)
Affected files:
rool/rails/instiki/trunk/app/models/page.rb:
prev. | current | |
raise Instiki::ValidationError.new( | ||
"You have tried to save page '#{name}' without changing its content") | ||
end | ||
17 | | |
17 | ||
self.name = name | ||
author = Author.new(author.to_s) unless author.is_a?(Author) | ||
21 | # 2011-03-12 (ADH): Assumption of Textile processing; patch inter-page | |
22 | # links expressed as Textile links not Wiki links, so | |
23 | # that Instiki's references mechanism recognises them | |
24 | # and pages are less likely to be erroneously declared | |
25 | # to be orphans. | |
26 | # | |
27 | content = patch_interpage_textile_links( content ) | |
28 | ||
# Try to render content to make sure that markup engine can take it, | ||
renderer.revision = Revision.new( | ||
:page => self, :content => content, :author => author, :revised_at => time) | ||
renderer.display_content(update_references = true) | ||
# A user may change a page, look at it and make some more changes - several times. | ||
27 | | |
35 | # Not to record every such iteration as a new revision, if the previous revision was done | |
# by the same author, not more than 30 minutes ago, then update the last revision instead of | ||
# creating a new one | ||
if (revisions_size > 0) && continous_revision?(time, author) | ||
... | ... | |
end | ||
def previous_revision(revision) | ||
53 | | |
54 | | |
55 | | |
61 | revision_index = revisions.each_with_index do |rev, index| | |
62 | if rev.id == revision.id | |
63 | break index | |
else | ||
nil | ||
end | ||
... | ... | |
def redirects | ||
wiki_references.select { |ref| ref.redirected_page? }.map { |ref| ref.referenced_name } | ||
85 | | |
93 | end | |
def included_from | ||
web.select.pages_that_include(name) | ||
... | ... | |
def lock(time, locked_by) | ||
update_attributes(:locked_at => time, :locked_by => locked_by) | ||
end | ||
101 | | |
109 | ||
def lock_duration(time) | ||
((time - locked_at) / 60).to_i unless locked_at.nil? | ||
end | ||
105 | | |
113 | ||
def unlock | ||
update_attribute(:locked_at, nil) | ||
end | ||
109 | | |
117 | ||
def locked?(comparison_time) | ||
locked_at + LOCKING_PERIOD > comparison_time unless locked_at.nil? | ||
end | ||
... | ... | |
current_revision.send(method_id) | ||
end | ||
end | ||
142 | ||
143 | # 2011-03-12 (ADH): Patch for Textile based wiki code. | |
144 | # | |
145 | # Some people use Textile links to refer to pages within the Wiki, perhaps | |
146 | # because they're unaware of the "[[Page name|Visible text]]" alias syntax. | |
147 | # This means that the Wiki may think a page is an orphan, with no links to | |
148 | # it, because there *are* links but they're in Textile format, not in Wiki | |
149 | # reference format. | |
150 | # | |
151 | # This method takes a string and returns a reasonably robustly processed | |
152 | # equivalent which has any Textile links which look like inter-page | |
153 | # references replaced by Wiki syntax equivalents on the "[[link|alias]]" | |
154 | # form. Typically, you pass the body text for entire revision in here. | |
155 | # | |
156 | # Heavily obfuscated Textile links, e.g. those which might specify an | |
157 | # absolute URL path from the document root or even including a host name | |
158 | # but still ultimately point to another page within the Wiki, will not be | |
159 | # replaced. So if users try hard enough, they can still break things! | |
160 | # | |
161 | unless defined? TEXTILE_LINK_PATTERN | |
162 | ||
163 | # In TEXTILE_ALIAS_PATTERN_START, we want it anchored to the start of a | |
164 | # line but a <textarea> uses "\r\n". The regexp fails if we try to use | |
165 | # "^" to anchor to start-of-line. Instead, match "\r\n" literally. | |
166 | # | |
167 | # In TEXTILE_LINK_PATTERN, after the ":" any non-white space character is | |
168 | # allowed. Trailing punctuation at the end of the link ('"foo":bar. ') is | |
169 | # stripped if necessary later. A '"' is not allowed immediately after the | |
170 | # ':' either, since Textile doesn't allow it and one or two sequences in | |
171 | # the ROOL Wiki can lead to disaster otherwise, such as '"#",":"'. | |
172 | ||
173 | TEXTILE_LINK_PATTERN = /\[?"([^\s].*?)":([^\s\"]+)\]?/ | |
174 | TEXTILE_LINK_TRIM_PATTERN = /[^\w]+$/ | |
175 | TEXTILE_ALIAS_PATTERN_START = /\r\n\[/.source | |
176 | TEXTILE_ALIAS_PATTERN_END = /\]([^\s]+)/.source | |
177 | ||
178 | ROOL_OLD_WIKI_PATH_PREFIX = '/wiki/documentation/pages/' | |
179 | end | |
180 | # | |
181 | def patch_interpage_textile_links( str ) | |
182 | ||
183 | # Changes will be made to "sub_str". The original is used for searches. | |
184 | ||
185 | sub_str = str.dup | |
186 | ||
187 | # Textile links may have the link inline, or refer to a link later in the | |
188 | # text using an alias. We record aliases and strip them out after the main | |
189 | # substitutions are done. We can't strip them out as we do the main | |
190 | # substitutions because several different Textile links may refer to the | |
191 | # same alias, even if other parts of that link are different. | |
192 | ||
193 | alias_matches = [] | |
194 | ||
195 | # Scan the string for Textile links. In each match, we get the visible text | |
196 | # in the first parameter, link or alias in the second parameter and the | |
197 | # whole matched string set in "$&". | |
198 | ||
199 | str.scan( TEXTILE_LINK_PATTERN ) do | visible_text, link_or_alias | | |
200 | ||
201 | whole_link = $& | |
202 | ||
203 | # Patch the search: The link or alias has to be allowed to include all | |
204 | # kinds of characters, because they do! Stars, hyphens, underscores and | |
205 | # various other things. However we find in practice that Textile can | |
206 | # understand when a punctuation character occurs at the very end of a | |
207 | # link (it's followed by white space) and so this isn't considered part | |
208 | # of that link (even for ".", which might otherwise be legitimately | |
209 | # present for e.g. filename extensions). | |
210 | # | |
211 | # Accordingly, trim any non-alphabetic, non-numeric characters off the | |
212 | # end of the whole found piece of text and the link or alias text. | |
213 | ||
214 | whole_link.gsub!( TEXTILE_LINK_TRIM_PATTERN, '' ) | |
215 | link_or_alias.gsub!( TEXTILE_LINK_TRIM_PATTERN, '' ) | |
216 | ||
217 | # Generate a regular expression which matches an alias definition. If | |
218 | # we find it, then this link used an alias by definition; else the URL | |
219 | # was inline. | |
220 | ||
221 | alias_regexp = Regexp.new( "#{ TEXTILE_ALIAS_PATTERN_START }#{ Regexp.escape( link_or_alias ) }#{ TEXTILE_ALIAS_PATTERN_END }" ) | |
222 | alias_match = str.match( alias_regexp ) | |
223 | ||
224 | # If an alias definition is found, index 1 will hold the URI part. | |
225 | ||
226 | if ( alias_match.nil? ) | |
227 | link = link_or_alias | |
228 | else | |
229 | link = alias_match[ 1 ] | |
230 | end | |
231 | ||
232 | # Special case: If we have a known absolute URL prefix on the link, | |
233 | # strip it off. This is in case someone's daft enough (and it's seen on | |
234 | # the ROOL wiki) to encode a Textile format link with a URL path from | |
235 | # the root right back down to a Wiki page - for some reason. | |
236 | # | |
237 | # This is very much ROOL specific since the URL path prefix in question | |
238 | # used here is from the old ROOL I2-based Wiki. | |
239 | ||
240 | if ( link.index( ROOL_OLD_WIKI_PATH_PREFIX ) == 0 ) | |
241 | link = link[ ROOL_OLD_WIKI_PATH_PREFIX.length .. -1 ] | |
242 | end | |
243 | ||
244 | # If there's a "/" in the link, it can't be an in-Wiki reference or | |
245 | # alias. Obfuscated in-Wiki references are possible ("/wiki_root/page", | |
246 | # that kind of thing) but we don't try to catch everything. If the user | |
247 | # tries hard enough they can defeat this code. We'd have to do something | |
248 | # complex with URI canonicalisation and the routing table to see if the | |
249 | # URI could possibly refer to the Wiki. It's really not worth the effort | |
250 | # and the performance impact to try that hard to avoid page references | |
251 | # that may lead to pages being declared as orphans, even though there is | |
252 | # technically an obscure format of reference elsewhere within the Wiki. | |
253 | # | |
254 | # A further complication is anchor references. In the ROOL Wiki, Textile | |
255 | # is sometimes used to link to anchors within pages but in most cases | |
256 | # there's also a higher level link to wider page too. Since that wider | |
257 | # link will be Wiki-fied, the page won't be an orphan; so leave the | |
258 | # anchor-based links in Textile format (there's no equivalent Wiki | |
259 | # syntax for them). | |
260 | ||
261 | next if ( link.include?( '/' ) || link.include?( '#' ) ) | |
262 | ||
263 | # A "+" in a Textile link from I2 translates to a space in reality. After | |
264 | # that, hex sequences of the form "%XY" should be unescaped to arrive at | |
265 | # something Instiki will recognise as a Wiki page title. | |
266 | # | |
267 | # There are more esoteric forms such as "(foo). Text" which assigns class | |
268 | # "foo" to the <a> element, but we don't support those here. | |
269 | ||
270 | link.gsub!( '+', ' ' ) | |
271 | link = CGI::unescape( link ) | |
272 | ||
273 | # Substitute any matching Textile links with a Wiki link equivalent. | |
274 | ||
275 | sub_str.gsub!( whole_link, "[[#{ link }|#{ visible_text }]]" ) | |
276 | ||
277 | # Remember alias definitions for removal later. | |
278 | ||
279 | alias_matches.push( alias_match[ 0 ] ) unless alias_match.nil? | |
280 | ||
281 | end | |
282 | ||
283 | # More slowness... Must now remove any referenced links too. Maintain | |
284 | # the "\r\n" included by the regular expression so that we don't | |
285 | # accidentally end up collapsing non-replaced link references against | |
286 | # textile output above, which "modern" Textile doesn't like. | |
287 | ||
288 | alias_matches.each do | alias_match | | |
289 | sub_str.gsub!( alias_match, "\r\n" ) | |
290 | end | |
291 | ||
292 | return sub_str | |
293 | end | |
end |