gollum 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of gollum might be problematic. Click here for more details.

@@ -1,7 +1,7 @@
1
1
  <div class="guide">
2
2
  <div class="main">
3
3
  <div class="actions">
4
- <a href="/{{name}}">&laquo; Back</a>
4
+ <a href="/{{escaped_name}}">&laquo; Back</a>
5
5
  </div>
6
6
  <h1>{{title}}: {{before}} &rarr; {{after}}</h1>
7
7
  <div id="files">
@@ -2,7 +2,7 @@
2
2
  <a href="/">&laquo; Home</a>
3
3
  <h1>{{title}}</h1>
4
4
 
5
- <form class="new_wiki" method="post" action="/create/{{name}}">
5
+ <form class="new_wiki" method="post" action="/create/{{escaped_name}}">
6
6
  <div>
7
7
  <label>
8
8
  Title<br />
@@ -1,8 +1,8 @@
1
1
  <div class="write">
2
- <a href="/{{name}}">&laquo; Back</a>
2
+ <a href="/{{escaped_name}}">&laquo; Back</a>
3
3
  <h1>{{title}}</h1>
4
4
 
5
- <form class="edit_wiki" method="post" action="/edit/{{name}}">
5
+ <form class="edit_wiki" method="post" action="/edit/{{escaped_name}}">
6
6
  <div>
7
7
  <label>
8
8
  Title<br />
@@ -0,0 +1,11 @@
1
+ <div class="guide">
2
+ <div class="main">
3
+ <div class="actions">
4
+ <a href="/">Home</a>
5
+ </div>
6
+ <h1>Error</h1>
7
+ <div class="error">
8
+ {{message}}
9
+ </div>
10
+ </div>
11
+ </div>
@@ -1,10 +1,10 @@
1
1
  <div class="guide">
2
2
  <div class="main">
3
3
  <div class="actions">
4
- <a href="/{{name}}">&laquo; Back</a>
4
+ <a href="/{{escaped_name}}">&laquo; Back</a>
5
5
  </div>
6
6
  <h1>{{title}}</h1>
7
- <form id="history" method="post" action="/compare/{{name}}">
7
+ <form id="history" method="post" action="/compare/{{escaped_name}}">
8
8
  <table class="commits" cellpadding="0" cellspacing="0">
9
9
  <tr>
10
10
  <th colspan="5">
@@ -17,7 +17,7 @@
17
17
  <input name="versions[]" type="checkbox" value="{{id}}" />
18
18
  </td>
19
19
  <td class="sha">
20
- <a href="/{{name}}/{{id}}">{{id7}}</a>
20
+ <a href="/{{escaped_name}}/{{id}}">{{id7}}</a>
21
21
  </td>
22
22
  <td nowrap class="author">
23
23
  <img src="http://www.gravatar.com/avatar/{{gravatar}}?s=16" alt="Gravatar" />
@@ -1,7 +1,13 @@
1
1
  <div class="guide">
2
2
  <div class="main">
3
3
  <div class="actions">
4
- <a href="/">Home</a> | <a href="/edit/{{name}}">Edit</a>
4
+ <form action="/search" method="get">
5
+ <div>
6
+ <a href="/">Home</a> |
7
+ <a href="/edit/{{escaped_name}}">Edit</a> |
8
+ <input type="search" name="q" size="10" /> <input type="submit" value="search" />
9
+ </div>
10
+ </form>
5
11
  </div>
6
12
  <h1>{{title}}</h1>
7
13
  <div class="content wikistyle gollum {{format}}">
@@ -18,10 +24,10 @@
18
24
  <div style="float: left;">
19
25
  <small>Last edited by <b>{{author}}</b>, {{date}}</small>
20
26
  <div class="actions">
21
- <a href="/">Home</a> | <a href="/edit/{{name}}">Edit</a>
27
+ <a href="/">Home</a> | <a href="/edit/{{escaped_name}}">Edit</a>
22
28
  </div>
23
29
  </div>
24
30
  <div style="float: right;">
25
- <a href="/history/{{name}}">View Revision History</a>
31
+ <a href="/history/{{escaped_name}}">View Revision History</a>
26
32
  </div>
27
33
  </div>
@@ -0,0 +1,19 @@
1
+ <div class="guide">
2
+ <div class="main">
3
+ <div class="actions">
4
+ <form action="/search" method="get">
5
+ <a href="/">Home</a> | <input type="search" name="q" size="10" /> <input type="submit" value="search" />
6
+ </form>
7
+ </div>
8
+ <h1>Search for “{{query}}”</h1>
9
+ <div class="content wikistyle gollum">
10
+ {{#has_results}}
11
+ <ul>
12
+ {{#results}}
13
+ <li><a href="/{{name}}">{{name}} ({{count}})</a></li>
14
+ {{/results}}
15
+ </ul>
16
+ {{/has_results}}
17
+ </div>
18
+ </div>
19
+ </div>
@@ -0,0 +1,7 @@
1
+ module Precious
2
+ module Views
3
+ class Error < Layout
4
+ attr_reader :message
5
+ end
6
+ end
7
+ end
@@ -1,3 +1,5 @@
1
+ require 'cgi'
2
+
1
3
  module Precious
2
4
  module Views
3
5
  class Layout < Mustache
@@ -6,6 +8,10 @@ module Precious
6
8
 
7
9
  attr_reader :name
8
10
 
11
+ def escaped_name
12
+ CGI.escape(@name)
13
+ end
14
+
9
15
  def title
10
16
  "Home"
11
17
  end
@@ -0,0 +1,12 @@
1
+ module Precious
2
+ module Views
3
+ class Search < Layout
4
+ attr_reader :content, :page, :footer, :results, :query
5
+
6
+ def has_results
7
+ !@results.empty?
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -1,4 +1,5 @@
1
1
  require 'digest/sha1'
2
+ require 'cgi'
2
3
 
3
4
  module Gollum
4
5
  class Markup
@@ -21,8 +22,14 @@ module Gollum
21
22
  # Render the content with Gollum wiki syntax on top of the file's own
22
23
  # markup language.
23
24
  #
25
+ # no_follow - Boolean that determines if rel="nofollow" is added to all
26
+ # <a> tags.
27
+ #
24
28
  # Returns the formatted String content.
25
- def render
29
+ def render(no_follow = false)
30
+ sanitize_options = no_follow ?
31
+ HISTORY_SANITIZATION_OPTIONS :
32
+ SANITIZATION_OPTIONS
26
33
  data = extract_tex(@data)
27
34
  data = extract_code(data)
28
35
  data = extract_tags(data)
@@ -36,9 +43,9 @@ module Gollum
36
43
  end
37
44
  data = process_tags(data)
38
45
  data = process_code(data)
39
- data = Sanitize.clean(data, SANITIZATION_OPTIONS)
46
+ data = Sanitize.clean(data, sanitize_options)
40
47
  data = process_tex(data)
41
- data = data.gsub(/<p><\/p>/, '')
48
+ data.gsub!(/<p><\/p>/, '')
42
49
  data
43
50
  end
44
51
 
@@ -114,28 +121,33 @@ module Gollum
114
121
  # Process all tags from the tagmap and replace the placeholders with the
115
122
  # final markup.
116
123
  #
117
- # data - The String data (with placeholders).
124
+ # data - The String data (with placeholders).
125
+ # no_follow - Boolean that determines if rel="nofollow" is added to all
126
+ # <a> tags.
118
127
  #
119
128
  # Returns the marked up String data.
120
- def process_tags(data)
129
+ def process_tags(data, no_follow = false)
121
130
  @tagmap.each do |id, tag|
122
- data.gsub!(id, process_tag(tag))
131
+ data.gsub!(id, process_tag(tag, no_follow))
123
132
  end
124
133
  data
125
134
  end
126
135
 
127
136
  # Process a single tag into its final HTML form.
128
137
  #
129
- # tag - The String tag contents (the stuff inside the double brackets).
138
+ # tag - The String tag contents (the stuff inside the double
139
+ # brackets).
140
+ # no_follow - Boolean that determines if rel="nofollow" is added to all
141
+ # <a> tags.
130
142
  #
131
143
  # Returns the String HTML version of the tag.
132
- def process_tag(tag)
144
+ def process_tag(tag, no_follow = false)
133
145
  if html = process_image_tag(tag)
134
- return html
135
- elsif html = process_file_link_tag(tag)
136
- return html
146
+ html
147
+ elsif html = process_file_link_tag(tag, no_follow)
148
+ html
137
149
  else
138
- return process_page_link_tag(tag)
150
+ process_page_link_tag(tag, no_follow)
139
151
  end
140
152
  end
141
153
 
@@ -150,7 +162,7 @@ module Gollum
150
162
  name = parts[0].strip
151
163
  path = if file = find_file(name)
152
164
  ::File.join @wiki.base_path, file.path
153
- elsif name =~ /^https?:\/\/.+(jpg|png|gif|svg|bmp)$/
165
+ elsif name =~ /^https?:\/\/.+(jpg|png|gif|svg|bmp)$/i
154
166
  name
155
167
  end
156
168
 
@@ -200,7 +212,7 @@ module Gollum
200
212
  classes << 'frame' if opts['frame']
201
213
  %{<span class="#{classes.join(' ')}">} +
202
214
  %{<span>} +
203
- %{<img src="/#{file.path}" #{attr_string}/>} +
215
+ %{<img src="#{path}" #{attr_string}/>} +
204
216
  (alt ? %{<span>#{alt}</span>} : '') +
205
217
  %{</span>} +
206
218
  %{</span>}
@@ -228,11 +240,14 @@ module Gollum
228
240
 
229
241
  # Attempt to process the tag as a file link tag.
230
242
  #
231
- # tag - The String tag contents (the stuff inside the double brackets).
243
+ # tag - The String tag contents (the stuff inside the double
244
+ # brackets).
245
+ # no_follow - Boolean that determines if rel="nofollow" is added to all
246
+ # <a> tags.
232
247
  #
233
248
  # Returns the String HTML if the tag is a valid file link tag or nil
234
249
  # if it is not.
235
- def process_file_link_tag(tag)
250
+ def process_file_link_tag(tag, no_follow = false)
236
251
  parts = tag.split('|')
237
252
  name = parts[0].strip
238
253
  path = parts[1] && parts[1].strip
@@ -244,37 +259,49 @@ module Gollum
244
259
  nil
245
260
  end
246
261
 
247
- if name && path && file
262
+ tag = if name && path && file
248
263
  %{<a href="#{::File.join @wiki.base_path, file.path}">#{name}</a>}
249
264
  elsif name && path
250
265
  %{<a href="#{path}">#{name}</a>}
251
266
  else
252
267
  nil
253
268
  end
269
+ if tag && no_follow
270
+ tag.sub! /^<a/, '<a ref="nofollow"'
271
+ end
272
+ tag
254
273
  end
255
274
 
256
275
  # Attempt to process the tag as a page link tag.
257
276
  #
258
- # tag - The String tag contents (the stuff inside the double brackets).
277
+ # tag - The String tag contents (the stuff inside the double
278
+ # brackets).
279
+ # no_follow - Boolean that determines if rel="nofollow" is added to all
280
+ # <a> tags.
259
281
  #
260
282
  # Returns the String HTML if the tag is a valid page link tag or nil
261
283
  # if it is not.
262
- def process_page_link_tag(tag)
284
+ def process_page_link_tag(tag, no_follow = false)
263
285
  parts = tag.split('|')
264
286
  name = parts[0].strip
265
287
  cname = Page.cname((parts[1] || parts[0]).strip)
266
- if name =~ %r{^https?://} && parts[1].nil?
288
+ tag = if name =~ %r{^https?://} && parts[1].nil?
267
289
  %{<a href="#{name}">#{name}</a>}
268
290
  else
269
- if page = @wiki.page(cname)
270
- link = ::File.join(@wiki.base_path, Page.cname(page.name))
271
- presence = "present"
272
- else
273
- link = ::File.join(@wiki.base_path, cname)
274
- presence = "absent"
291
+ presence = "absent"
292
+ link_name = cname
293
+ page, extra = find_page_from_name(cname)
294
+ if page
295
+ link_name = Page.cname(page.name)
296
+ presence = "present"
275
297
  end
276
- %{<a class="internal #{presence}" href="#{link}">#{name}</a>}
298
+ link = ::File.join(@wiki.base_path, CGI.escape(link_name))
299
+ %{<a class="internal #{presence}" href="#{link}#{extra}">#{name}</a>}
277
300
  end
301
+ if tag && no_follow
302
+ tag.sub! /^<a/, '<a ref="nofollow"'
303
+ end
304
+ tag
278
305
  end
279
306
 
280
307
  # Find the given file in the repo.
@@ -291,6 +318,23 @@ module Gollum
291
318
  end
292
319
  end
293
320
 
321
+ # Find a page from a given cname. If the page has an anchor (#) and has
322
+ # no match, strip the anchor and try again.
323
+ #
324
+ # cname - The String canonical page name.
325
+ #
326
+ # Returns a Gollum::Page instance if a page is found, or an Array of
327
+ # [Gollum::Page, String extra] if a page without the extra anchor data
328
+ # is found.
329
+ def find_page_from_name(cname)
330
+ if page = @wiki.page(cname)
331
+ return page
332
+ end
333
+ if pos = cname.index('#')
334
+ [@wiki.page(cname[0...pos]), cname[pos..-1]]
335
+ end
336
+ end
337
+
294
338
  #########################################################################
295
339
  #
296
340
  # Code
@@ -320,7 +364,7 @@ module Gollum
320
364
  @codemap.each do |id, spec|
321
365
  lang = spec[:lang]
322
366
  code = spec[:code]
323
- if code.all? { |line| line =~ /\A\r?\n\Z/ || line =~ /^( |\t)/ }
367
+ if code.lines.all? { |line| line =~ /\A\r?\n\Z/ || line =~ /^( |\t)/ }
324
368
  code.gsub!(/^( |\t)/m, '')
325
369
  end
326
370
  data.gsub!(id, Gollum::Albino.new(code, lang).colorize)
@@ -328,4 +372,4 @@ module Gollum
328
372
  data
329
373
  end
330
374
  end
331
- end
375
+ end
@@ -14,6 +14,11 @@ module Gollum
14
14
  :asciidoc => "AsciiDoc",
15
15
  :pod => "Pod" }
16
16
 
17
+ # Sets a Boolean determing whether this page is a historical version.
18
+ #
19
+ # Returns nothing.
20
+ attr_writer :historical
21
+
17
22
  # Checks if a filename has a valid extension understood by GitHub::Markup.
18
23
  #
19
24
  # filename - String filename, like "Home.md".
@@ -35,6 +40,14 @@ module Gollum
35
40
  filename =~ /^_/ ? false : match
36
41
  end
37
42
 
43
+ # Reusable filter to turn a filename (without path) into a canonical name.
44
+ # Strips extension, converts spaces to dashes.
45
+ #
46
+ # Returns the filtered String.
47
+ def self.canonicalize_filename(filename)
48
+ filename.split('.')[0..-2].join('.').gsub('-', ' ')
49
+ end
50
+
38
51
  # Public: Initialize a page.
39
52
  #
40
53
  # wiki - The Gollum::Wiki in question.
@@ -57,7 +70,7 @@ module Gollum
57
70
  #
58
71
  # Returns the String name.
59
72
  def name
60
- filename.split('.')[0..-2].join('.').gsub('-', ' ')
73
+ self.class.canonicalize_filename(filename)
61
74
  end
62
75
 
63
76
  # Public: If the first element of a formatted page is an <h1> tag it can
@@ -107,7 +120,7 @@ module Gollum
107
120
  #
108
121
  # Returns the String data.
109
122
  def formatted_data
110
- @blob && Gollum::Markup.new(self).render
123
+ @blob && Gollum::Markup.new(self).render(historical?)
111
124
  end
112
125
 
113
126
  # Public: The format of the page.
@@ -174,20 +187,24 @@ module Gollum
174
187
 
175
188
  dirs = self.path.split('/')
176
189
  dirs.pop
190
+ map = @wiki.tree_map_for(self.version.id)
177
191
  while !dirs.empty?
178
- tree = self.version.tree / dirs.join('/')
179
- if page = find_page_in_this_tree(tree, dirs.join('/'), '_Footer')
192
+ if page = find_page_in_tree(map, '_Footer', dirs.join('/'))
180
193
  return page
181
194
  end
182
195
  dirs.pop
183
196
  end
184
197
 
185
- tree = self.version.tree
186
- if page = find_page_in_this_tree(tree, '', '_Footer')
187
- return page
188
- end
198
+ find_page_in_tree(map, '_Footer', '')
199
+ end
189
200
 
190
- return nil
201
+ # Gets a Boolean determining whether this page is a historical version.
202
+ # Historical pages are pulled using exact SHA hashes and format all links
203
+ # with rel="nofollow"
204
+ #
205
+ # Returns true if the page is pulled from a named branch or tag, or false.
206
+ def historical?
207
+ !!@historical
191
208
  end
192
209
 
193
210
  #########################################################################
@@ -207,7 +224,9 @@ module Gollum
207
224
  #
208
225
  # Returns the String canonical name.
209
226
  def self.cname(name)
210
- name.gsub(%r{[ /<>]}, '-')
227
+ name.respond_to?(:gsub) ?
228
+ name.gsub(%r{[ /<>]}, '-') :
229
+ ''
211
230
  end
212
231
 
213
232
  # Convert a format Symbol into an extension String.
@@ -251,61 +270,35 @@ module Gollum
251
270
  #
252
271
  # Returns a Gollum::Page or nil if the page could not be found.
253
272
  def find(name, version)
254
- if commit = @wiki.repo.commit(version)
255
- if page = find_page_in_tree(commit.tree, name)
256
- page.version = commit
257
- page
258
- end
273
+ map = @wiki.tree_map_for(version)
274
+ if page = find_page_in_tree(map, name)
275
+ sha = @wiki.ref_map[version] || version
276
+ page.version = Grit::Commit.create(@wiki.repo, :id => sha)
277
+ page.historical = sha == version
278
+ page
259
279
  end
280
+ rescue Grit::GitRuby::Repository::NoSuchShaFound
260
281
  end
261
282
 
262
283
  # Find a page in a given tree.
263
284
  #
264
- # tree - The Grit::Tree in which to look.
265
- # name - The canonical String page name.
285
+ # map - The Array tree map from Wiki#tree_map.
286
+ # name - The canonical String page name.
287
+ # checked_dir - Optional String of the directory a matching page needs
288
+ # to be in. The string should
266
289
  #
267
290
  # Returns a Gollum::Page or nil if the page could not be found.
268
- def find_page_in_tree(tree, name)
269
- treemap = {}
270
- trees = [tree]
271
-
272
- while !trees.empty?
273
- ptree = trees.shift
274
- ptree.contents.each do |item|
275
- case item
276
- when Grit::Blob
277
- if page_match(name, item.name)
278
- return self.class.new(@wiki).populate(item, tree_path(treemap, ptree))
279
- end
280
- when Grit::Tree
281
- treemap[item] = ptree
282
- trees << item
283
- end
284
- end
291
+ def find_page_in_tree(map, name, checked_dir = nil)
292
+ return nil if name.to_s.empty?
293
+ if checked_dir = BlobEntry.normalize_dir(checked_dir)
294
+ checked_dir.downcase!
285
295
  end
286
296
 
287
- return nil # nothing was found
288
- end
289
-
290
- # Find a page in a given tree without recursing into subtrees.
291
- #
292
- # tree - The Grit::Tree in which to look.
293
- # dir - The String path of the given Grit::Tree.
294
- # name - The canonical String page name.
295
- #
296
- # Returns a Gollum::Page or nil if the page could not be found.
297
- def find_page_in_this_tree(tree, dir, name)
298
- treemap = {}
299
- tree.contents.each do |item|
300
- case item
301
- when Grit::Blob
302
- if page_match(name, item.name)
303
- path = dir == '' ? '' : ::File.join('/', dir)
304
- page = self.class.new(@wiki).populate(item, path)
305
- page.version = self.version
306
- return page
307
- end
308
- end
297
+ map.each do |entry|
298
+ next if entry.name.to_s.empty?
299
+ next unless checked_dir.nil? || entry.dir.downcase == checked_dir
300
+ next unless page_match(name, entry.name)
301
+ return entry.page(@wiki, @version)
309
302
  end
310
303
 
311
304
  return nil # nothing was found