gollum-lib 4.0.3-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile +3 -0
  3. data/HISTORY.md +11 -0
  4. data/LICENSE +21 -0
  5. data/README.md +286 -0
  6. data/Rakefile +187 -0
  7. data/docs/sanitization.md +33 -0
  8. data/gemspec.rb +100 -0
  9. data/gollum-lib.gemspec +4 -0
  10. data/gollum-lib_java.gemspec +4 -0
  11. data/lib/gollum-lib.rb +64 -0
  12. data/lib/gollum-lib/blob_entry.rb +95 -0
  13. data/lib/gollum-lib/committer.rb +243 -0
  14. data/lib/gollum-lib/file.rb +158 -0
  15. data/lib/gollum-lib/file_view.rb +155 -0
  16. data/lib/gollum-lib/filter.rb +78 -0
  17. data/lib/gollum-lib/filter/code.rb +145 -0
  18. data/lib/gollum-lib/filter/macro.rb +57 -0
  19. data/lib/gollum-lib/filter/metadata.rb +29 -0
  20. data/lib/gollum-lib/filter/plain_text.rb +16 -0
  21. data/lib/gollum-lib/filter/remote_code.rb +63 -0
  22. data/lib/gollum-lib/filter/render.rb +20 -0
  23. data/lib/gollum-lib/filter/sanitize.rb +18 -0
  24. data/lib/gollum-lib/filter/tags.rb +320 -0
  25. data/lib/gollum-lib/filter/toc.rb +109 -0
  26. data/lib/gollum-lib/filter/wsd.rb +54 -0
  27. data/lib/gollum-lib/git_access.rb +247 -0
  28. data/lib/gollum-lib/gitcode.rb +48 -0
  29. data/lib/gollum-lib/helpers.rb +13 -0
  30. data/lib/gollum-lib/hook.rb +35 -0
  31. data/lib/gollum-lib/macro.rb +43 -0
  32. data/lib/gollum-lib/macro/all_pages.rb +11 -0
  33. data/lib/gollum-lib/markup.rb +197 -0
  34. data/lib/gollum-lib/markups.rb +20 -0
  35. data/lib/gollum-lib/page.rb +491 -0
  36. data/lib/gollum-lib/pagination.rb +62 -0
  37. data/lib/gollum-lib/sanitization.rb +176 -0
  38. data/lib/gollum-lib/version.rb +5 -0
  39. data/lib/gollum-lib/wiki.rb +925 -0
  40. data/licenses/licenses.txt +6 -0
  41. metadata +410 -0
@@ -0,0 +1,13 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ module Gollum
3
+ module Helpers
4
+
5
+ def trim_leading_slash url
6
+ return url if url.nil?
7
+ url.gsub!('%2F', '/')
8
+ return '/' + url.gsub(/^\/+/, '') if url[0, 1] == '/'
9
+ url
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,35 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ module Gollum
3
+ class Hook
4
+ @hooks = {}
5
+
6
+ class << self
7
+ def register(type, id, &block)
8
+ type_hooks = @hooks[type] ||= {}
9
+ type_hooks[id] = block
10
+ end
11
+
12
+ def unregister(type, id)
13
+ type_hooks = @hooks[type]
14
+ if type_hooks
15
+ type_hooks.delete(id)
16
+ @hooks.delete(type) if type_hooks.empty?
17
+ end
18
+ end
19
+
20
+ def get(type, id)
21
+ @hooks.fetch(type, {})[id]
22
+ end
23
+
24
+ def execute(type, *args)
25
+ type_hooks = @hooks[type]
26
+ if type_hooks
27
+ type_hooks.each_value do |block|
28
+ block.call(*args)
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,43 @@
1
+ module Gollum
2
+ class Macro
3
+ # Find the macro named, create an instance of that, and return it
4
+ def self.instance(macro_name, wiki, page)
5
+ begin
6
+ self.const_get(macro_name).new(wiki, page)
7
+ rescue NameError
8
+ Unknown_Macro.new(macro_name)
9
+ end
10
+ end
11
+
12
+ def initialize(wiki, page)
13
+ @wiki = wiki
14
+ @page = page
15
+ end
16
+
17
+ def render(*_args)
18
+ raise ArgumentError,
19
+ "#{self.class} does not implement #render. "+
20
+ "This is a bug in #{self.class}."
21
+ end
22
+
23
+ protected
24
+ def html_error(s)
25
+ "<p class=\"gollum-error\">#{s}</p>"
26
+ end
27
+
28
+ # The special class we reserve for only the finest of screwups. The
29
+ # underscore is to make sure nobody can define a real, callable macro
30
+ # with the same name, because that would be... exciting.
31
+ class Unknown_Macro < Macro
32
+ def initialize(macro_name)
33
+ @macro_name = macro_name
34
+ end
35
+
36
+ def render(*_args)
37
+ "!!!Unknown macro: #{@macro_name}!!!"
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ Dir[File.expand_path('../macro/*.rb', __FILE__)].each { |f| require f }
@@ -0,0 +1,11 @@
1
+ module Gollum
2
+ class Macro
3
+ class AllPages < Gollum::Macro
4
+ def render
5
+ if @wiki.pages.size > 0
6
+ '<ul id="pages">' + @wiki.pages.map { |p| "<li>#{p.name}</li>" }.join + '</ul>'
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,197 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ require 'digest/sha1'
3
+ require 'cgi'
4
+ require 'rouge'
5
+ require 'base64'
6
+
7
+ require File.expand_path '../helpers', __FILE__
8
+
9
+ # Use pygments if it's installed
10
+ begin
11
+ require 'pygments'
12
+ Pygments.start
13
+ rescue Exception
14
+ end
15
+
16
+ module Gollum
17
+
18
+ class Markup
19
+ include Helpers
20
+
21
+ @formats = {}
22
+
23
+ class << self
24
+
25
+ # Only use the formats that are specified in config.rb
26
+ def formats
27
+ if defined? Gollum::Page::FORMAT_NAMES
28
+ @formats.select { |_, value| Gollum::Page::FORMAT_NAMES.values.include? value[:name] }
29
+ else
30
+ @formats
31
+ end
32
+ end
33
+
34
+ # Register a file extension and associated markup type
35
+ #
36
+ # ext - The file extension
37
+ # name - The name of the markup type
38
+ # options - Hash of options:
39
+ # regexp - Regexp to match against.
40
+ # Defaults to exact match of ext.
41
+ #
42
+ # If given a block, that block will be registered with GitHub::Markup to
43
+ # render any matching pages
44
+ def register(ext, name, options = {}, &block)
45
+ regexp = options[:regexp] || Regexp.new(ext.to_s)
46
+ @formats[ext] = { :name => name, :regexp => regexp }
47
+ end
48
+ end
49
+
50
+ attr_accessor :toc
51
+ attr_accessor :metadata
52
+ attr_reader :encoding
53
+ attr_reader :sanitize
54
+ attr_reader :format
55
+ attr_reader :wiki
56
+ attr_reader :page
57
+ attr_reader :parent_page
58
+ attr_reader :sub_page
59
+ attr_reader :name
60
+ attr_reader :include_levels
61
+ attr_reader :to_xml_opts
62
+ attr_reader :dir
63
+
64
+ # Initialize a new Markup object.
65
+ #
66
+ # page - The Gollum::Page.
67
+ #
68
+ # Returns a new Gollum::Markup object, ready for rendering.
69
+ def initialize(page)
70
+ if page
71
+ @wiki = page.wiki
72
+ @name = page.filename
73
+ @data = page.text_data
74
+ @version = page.version.id if page.version
75
+ @format = page.format
76
+ @sub_page = page.sub_page
77
+ @parent_page = page.parent_page
78
+ @page = page
79
+ @dir = ::File.dirname(page.path)
80
+ end
81
+ @metadata = nil
82
+ @to_xml_opts = { :save_with => Nokogiri::XML::Node::SaveOptions::DEFAULT_XHTML ^ 1, :indent => 0, :encoding => 'UTF-8' }
83
+ end
84
+
85
+ # Render data using default chain in the target format.
86
+ #
87
+ # data - the data to render
88
+ # format - format to use as a symbol
89
+ # name - name using the extension of the format
90
+ #
91
+ # Returns the processed data
92
+ def render_default data, format=:markdown, name='render_default.md'
93
+ # set instance vars so we're able to render data without a wiki or page.
94
+ @format = format
95
+ @name = name
96
+
97
+ chain = [:Metadata, :PlainText, :TOC, :RemoteCode, :Code, :Sanitize, :WSD, :Tags, :Render]
98
+
99
+ filter_chain = chain.map do |r|
100
+ Gollum::Filter.const_get(r).new(self)
101
+ end
102
+
103
+ process_chain data, filter_chain
104
+ end
105
+
106
+ # Process the filter chain
107
+ #
108
+ # data - the data to send through the chain
109
+ # filter_chain - the chain to process
110
+ #
111
+ # Returns the formatted data
112
+ def process_chain(data, filter_chain)
113
+ # First we extract the data through the chain...
114
+ filter_chain.each do |filter|
115
+ data = filter.extract(data)
116
+ end
117
+
118
+ # Then we process the data through the chain *backwards*
119
+ filter_chain.reverse.each do |filter|
120
+ data = filter.process(data)
121
+ end
122
+
123
+ # Finally, a little bit of cleanup, just because
124
+ data.gsub!(/<p><\/p>/) do
125
+ ''
126
+ end
127
+
128
+ data
129
+ end
130
+
131
+ # Render the content with Gollum wiki syntax on top of the file's own
132
+ # markup language.
133
+ #
134
+ # no_follow - Boolean that determines if rel="nofollow" is added to all
135
+ # <a> tags.
136
+ # encoding - Encoding Constant or String.
137
+ #
138
+ # Returns the formatted String content.
139
+ def render(no_follow = false, encoding = nil, include_levels = 10)
140
+ @sanitize = no_follow ?
141
+ @wiki.history_sanitizer :
142
+ @wiki.sanitizer
143
+
144
+ @encoding = encoding
145
+ @include_levels = include_levels
146
+
147
+ data = @data.dup
148
+ filter_chain = @wiki.filter_chain.map do |r|
149
+ Gollum::Filter.const_get(r).new(self)
150
+ end
151
+
152
+ # Since the last 'extract' action in our chain *should* be the markup
153
+ # to HTML converter, we now have HTML which we can parse and yield, for
154
+ # anyone who wants it
155
+ if block_given?
156
+ yield Nokogiri::HTML::DocumentFragment.parse(data)
157
+ end
158
+
159
+ process_chain data, filter_chain
160
+ end
161
+
162
+ # Find the given file in the repo.
163
+ #
164
+ # name - The String absolute or relative path of the file.
165
+ #
166
+ # Returns the Gollum::File or nil if none was found.
167
+ def find_file(name, version=@version)
168
+ if name =~ /^\//
169
+ @wiki.file(name[1..-1], version)
170
+ else
171
+ path = @dir == '.' ? name : ::File.join(@dir, name)
172
+ @wiki.file(path, version)
173
+ end
174
+ end
175
+
176
+ # Hook for getting the formatted value of extracted tag data.
177
+ #
178
+ # type - Symbol value identifying what type of data is being extracted.
179
+ # id - String SHA1 hash of original extracted tag data.
180
+ #
181
+ # Returns the String cached formatted data, or nil.
182
+ def check_cache(type, id)
183
+ end
184
+
185
+ # Hook for caching the formatted value of extracted tag data.
186
+ #
187
+ # type - Symbol value identifying what type of data is being extracted.
188
+ # id - String SHA1 hash of original extracted tag data.
189
+ # data - The String formatted value to be cached.
190
+ #
191
+ # Returns nothing.
192
+ def update_cache(type, id, data)
193
+ end
194
+ end
195
+
196
+ MarkupGFM = Markup
197
+ end
@@ -0,0 +1,20 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ module Gollum
3
+ class Markup
4
+
5
+ GitHub::Markup::Markdown::MARKDOWN_GEMS['kramdown'] = proc { |content|
6
+ Kramdown::Document.new(content, :auto_ids => false, :input => "markdown").to_html
7
+ }
8
+
9
+ register(:markdown, "Markdown", :regexp => /md|mkdn?|mdown|markdown/)
10
+ register(:textile, "Textile")
11
+ register(:rdoc, "RDoc")
12
+ register(:org, "Org-mode")
13
+ register(:creole, "Creole")
14
+ register(:rest, "reStructuredText", :regexp => /re?st(\.txt)?/)
15
+ register(:asciidoc, "AsciiDoc")
16
+ register(:mediawiki, "MediaWiki", :regexp => /(media)?wiki/)
17
+ register(:pod, "Pod")
18
+ register(:txt, "Plain Text")
19
+ end
20
+ end
@@ -0,0 +1,491 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ module Gollum
3
+ class Page
4
+ include Pagination
5
+
6
+ Wiki.page_class = self
7
+
8
+ # Sets a Boolean determing whether this page is a historical version.
9
+ #
10
+ # Returns nothing.
11
+ attr_writer :historical
12
+
13
+ # Parent page if this is a sub page
14
+ #
15
+ # Returns a Page
16
+ attr_accessor :parent_page
17
+
18
+
19
+ # Checks a filename against the registered markup extensions
20
+ #
21
+ # filename - String filename, like "Home.md"
22
+ #
23
+ # Returns e.g. ["Home", :markdown], or [] if the extension is unregistered
24
+ def self.parse_filename(filename)
25
+ return [] unless filename =~ /^(.+)\.([a-zA-Z]\w*)$/i
26
+ pref, ext = $1, $2
27
+
28
+ Gollum::Markup.formats.each_pair do |name, format|
29
+ return [pref, name] if ext =~ format[:regexp]
30
+ end
31
+ []
32
+ end
33
+
34
+ # Checks if a filename has a valid, registered extension
35
+ #
36
+ # filename - String filename, like "Home.md".
37
+ #
38
+ # Returns the matching String basename of the file without the extension.
39
+ def self.valid_filename?(filename)
40
+ self.parse_filename(filename).first
41
+ end
42
+
43
+ # Checks if a filename has a valid extension understood by GitHub::Markup.
44
+ # Also, checks if the filename has no "_" in the front (such as
45
+ # _Footer.md).
46
+ #
47
+ # filename - String filename, like "Home.md".
48
+ #
49
+ # Returns the matching String basename of the file without the extension.
50
+ def self.valid_page_name?(filename)
51
+ match = valid_filename?(filename)
52
+ filename =~ /^_/ ? false : match
53
+ end
54
+
55
+ # Public: The format of a given filename.
56
+ #
57
+ # filename - The String filename.
58
+ #
59
+ # Returns the Symbol format of the page; one of the registered format types
60
+ def self.format_for(filename)
61
+ self.parse_filename(filename).last
62
+ end
63
+
64
+ # Reusable filter to turn a filename (without path) into a canonical name.
65
+ # Strips extension, converts dashes to spaces.
66
+ #
67
+ # Returns the filtered String.
68
+ def self.canonicalize_filename(filename)
69
+ strip_filename(filename).gsub('-', ' ')
70
+ end
71
+
72
+ # Reusable filter to strip extension and path from filename
73
+ #
74
+ # filename - The string path or filename to strip
75
+ #
76
+ # Returns the stripped String.
77
+ def self.strip_filename(filename)
78
+ ::File.basename(filename, ::File.extname(filename))
79
+ end
80
+
81
+ # Public: Initialize a page.
82
+ #
83
+ # wiki - The Gollum::Wiki in question.
84
+ #
85
+ # Returns a newly initialized Gollum::Page.
86
+ def initialize(wiki)
87
+ @wiki = wiki
88
+ @blob = @header = @footer = @sidebar = nil
89
+ @formatted_data = nil
90
+ @doc = nil
91
+ @parent_page = nil
92
+ end
93
+
94
+ # Public: The on-disk filename of the page including extension.
95
+ #
96
+ # Returns the String name.
97
+ def filename
98
+ @blob && @blob.name
99
+ end
100
+
101
+ # Public: The on-disk filename of the page with extension stripped.
102
+ #
103
+ # Returns the String name.
104
+ def filename_stripped
105
+ self.class.strip_filename(filename)
106
+ end
107
+
108
+ # Public: The canonical page name without extension, and dashes converted
109
+ # to spaces.
110
+ #
111
+ # Returns the String name.
112
+ def name
113
+ self.class.canonicalize_filename(filename)
114
+ end
115
+
116
+ # Public: The title will be constructed from the
117
+ # filename by stripping the extension and replacing any dashes with
118
+ # spaces.
119
+ #
120
+ # Returns the fully sanitized String title.
121
+ def title
122
+ Sanitize.clean(name).strip
123
+ end
124
+
125
+ # Public: Determines if this is a sub-page
126
+ # Sub-pages have filenames beginning with an underscore
127
+ #
128
+ # Returns true or false.
129
+ def sub_page
130
+ filename =~ /^_/
131
+ end
132
+
133
+ # Public: The path of the page within the repo.
134
+ #
135
+ # Returns the String path.
136
+ attr_reader :path
137
+
138
+ # Public: The url path required to reach this page within the repo.
139
+ #
140
+ # Returns the String url_path
141
+ def url_path
142
+ path = if self.path.include?('/')
143
+ self.path.sub(/\/[^\/]+$/, '/')
144
+ else
145
+ ''
146
+ end
147
+
148
+ path << Page.cname(self.name, '-', '-')
149
+ path
150
+ end
151
+
152
+ # Public: The display form of the url path required to reach this page within the repo.
153
+ #
154
+ # Returns the String url_path
155
+ def url_path_display
156
+ url_path.gsub("-", " ")
157
+ end
158
+
159
+ # Public: Defines title for page.rb
160
+ #
161
+ # Returns the String title
162
+ def url_path_title
163
+ metadata_title || url_path_display
164
+ end
165
+
166
+ # Public: Metadata title
167
+ #
168
+ # Set with <!-- --- title: New Title --> in page content
169
+ #
170
+ # Returns the String title or nil if not defined
171
+ def metadata_title
172
+ if metadata
173
+ title = metadata['title']
174
+ return title unless title.nil?
175
+ end
176
+
177
+ nil
178
+ end
179
+
180
+ # Public: The url_path, but CGI escaped.
181
+ #
182
+ # Returns the String url_path
183
+ def escaped_url_path
184
+ CGI.escape(self.url_path).gsub('%2F', '/')
185
+ end
186
+
187
+ # Public: The raw contents of the page.
188
+ #
189
+ # Returns the String data.
190
+ def raw_data
191
+ return nil unless @blob
192
+
193
+ if !@wiki.repo.bare && @blob.is_symlink
194
+ new_path = @blob.symlink_target(::File.join(@wiki.repo.path, '..', self.path))
195
+ return IO.read(new_path) if new_path
196
+ end
197
+
198
+ @blob.data
199
+ end
200
+
201
+ # Public: A text data encoded in specified encoding.
202
+ #
203
+ # encoding - An Encoding or nil
204
+ #
205
+ # Returns a character encoding aware String.
206
+ def text_data(encoding=nil)
207
+ if raw_data.respond_to?(:encoding)
208
+ raw_data.force_encoding(encoding || Encoding::UTF_8)
209
+ else
210
+ raw_data
211
+ end
212
+ end
213
+
214
+ # Public: The formatted contents of the page.
215
+ #
216
+ # encoding - Encoding Constant or String.
217
+ #
218
+ # Returns the String data.
219
+ def formatted_data(encoding = nil, include_levels = 10, &block)
220
+ return nil unless @blob
221
+
222
+ if @formatted_data && @doc then
223
+ yield @doc if block_given?
224
+ else
225
+ @formatted_data = markup_class.render(historical?, encoding, include_levels) do |doc|
226
+ @doc = doc
227
+ yield doc if block_given?
228
+ end
229
+ end
230
+
231
+ @formatted_data
232
+ end
233
+
234
+ # Public: The table of contents of the page.
235
+ #
236
+ # formatted_data - page already marked up in html.
237
+ #
238
+ # Returns the String data.
239
+ def toc_data()
240
+ return @parent_page.toc_data if @parent_page and @sub_page
241
+ formatted_data if markup_class.toc == nil
242
+ markup_class.toc
243
+ end
244
+
245
+ # Public: Embedded metadata.
246
+ #
247
+ # Returns Hash of metadata.
248
+ def metadata()
249
+ formatted_data if markup_class.metadata == nil
250
+ markup_class.metadata
251
+ end
252
+
253
+ # Public: The format of the page.
254
+ #
255
+ # Returns the Symbol format of the page; one of the registered format types
256
+ def format
257
+ self.class.format_for(@blob.name)
258
+ end
259
+
260
+ # Gets the Gollum::Markup instance that will render this page's content.
261
+ #
262
+ # Returns a Gollum::Markup instance.
263
+ def markup_class
264
+ @markup_class ||= @wiki.markup_classes[format].new(self)
265
+ end
266
+
267
+ # Public: The current version of the page.
268
+ #
269
+ # Returns the Gollum::Git::Commit.
270
+ attr_reader :version
271
+
272
+ # Public: All of the versions that have touched the Page.
273
+ #
274
+ # options - The options Hash:
275
+ # :page - The Integer page number (default: 1).
276
+ # :per_page - The Integer max count of items to return.
277
+ # :follow - Follow's a file across renames, slower. (default: false)
278
+ #
279
+ # Returns an Array of Gollum::Git::Commit.
280
+ def versions(options = {})
281
+ @wiki.repo.git.versions_for_path(@path, @wiki.ref, log_pagination_options(options))
282
+ end
283
+
284
+ # Public: The first 7 characters of the current version.
285
+ #
286
+ # Returns the first 7 characters of the current version.
287
+ def version_short
288
+ version.to_s[0, 7]
289
+ end
290
+
291
+ # Public: The header Page.
292
+ #
293
+ # Returns the header Page or nil if none exists.
294
+ def header
295
+ @header ||= find_sub_page(:header)
296
+ end
297
+
298
+ # Public: The footer Page.
299
+ #
300
+ # Returns the footer Page or nil if none exists.
301
+ def footer
302
+ @footer ||= find_sub_page(:footer)
303
+ end
304
+
305
+ # Public: The sidebar Page.
306
+ #
307
+ # Returns the sidebar Page or nil if none exists.
308
+ def sidebar
309
+ @sidebar ||= find_sub_page(:sidebar)
310
+ end
311
+
312
+ # Gets a Boolean determining whether this page is a historical version.
313
+ # Historical pages are pulled using exact SHA hashes and format all links
314
+ # with rel="nofollow"
315
+ #
316
+ # Returns true if the page is pulled from a named branch or tag, or false.
317
+ def historical?
318
+ !!@historical
319
+ end
320
+
321
+ #########################################################################
322
+ #
323
+ # Class Methods
324
+ #
325
+ #########################################################################
326
+
327
+ # Convert a human page name into a canonical page name.
328
+ #
329
+ # name - The String human page name.
330
+ # char_white_sub - Substitution for whitespace
331
+ # char_other_sub - Substitution for other special chars
332
+ #
333
+ # Examples
334
+ #
335
+ # Page.cname("Bilbo Baggins")
336
+ # # => 'Bilbo-Baggins'
337
+ #
338
+ # Page.cname("Bilbo Baggins",'_')
339
+ # # => 'Bilbo_Baggins'
340
+ #
341
+ # Returns the String canonical name.
342
+ def self.cname(name, char_white_sub = '-', char_other_sub = '-')
343
+ name.respond_to?(:gsub) ?
344
+ name.gsub(%r{\s}, char_white_sub).gsub(%r{[<>+]}, char_other_sub) :
345
+ ''
346
+ end
347
+
348
+ # Convert a format Symbol into an extension String.
349
+ #
350
+ # format - The format Symbol.
351
+ #
352
+ # Returns the String extension (no leading period).
353
+ def self.format_to_ext(format)
354
+ format == :markdown ? "md" : format.to_s
355
+ end
356
+
357
+ #########################################################################
358
+ #
359
+ # Internal Methods
360
+ #
361
+ #########################################################################
362
+
363
+ # The underlying wiki repo.
364
+ #
365
+ # Returns the Gollum::Wiki containing the page.
366
+ attr_reader :wiki
367
+
368
+ # Set the Gollum::Git::Commit version of the page.
369
+ #
370
+ # Returns nothing.
371
+ attr_writer :version
372
+
373
+ # Find a page in the given Gollum repo.
374
+ #
375
+ # name - The human or canonical String page name to find.
376
+ # version - The String version ID to find.
377
+ #
378
+ # Returns a Gollum::Page or nil if the page could not be found.
379
+ def find(name, version, dir = nil, exact = false)
380
+ map = @wiki.tree_map_for(version.to_s)
381
+ if page = find_page_in_tree(map, name, dir, exact)
382
+ page.version = version.is_a?(Gollum::Git::Commit) ?
383
+ version : @wiki.commit_for(version)
384
+ page.historical = page.version.to_s == version.to_s
385
+ page
386
+ end
387
+ rescue Gollum::Git::NoSuchShaFound
388
+ end
389
+
390
+ # Find a page in a given tree.
391
+ #
392
+ # map - The Array tree map from Wiki#tree_map.
393
+ # name - The canonical String page name.
394
+ # checked_dir - Optional String of the directory a matching page needs
395
+ # to be in. The string should
396
+ #
397
+ # Returns a Gollum::Page or nil if the page could not be found.
398
+ def find_page_in_tree(map, name, checked_dir = nil, exact = false)
399
+ return nil if !map || name.to_s.empty?
400
+
401
+ checked_dir = BlobEntry.normalize_dir(checked_dir)
402
+ checked_dir = '' if exact && checked_dir.nil?
403
+ name = ::File.join(checked_dir, name) if checked_dir
404
+
405
+ map.each do |entry|
406
+ next if entry.name.to_s.empty?
407
+ path = checked_dir ? ::File.join(entry.dir, entry.name) : entry.name
408
+ next unless page_match(name, path)
409
+ return entry.page(@wiki, @version)
410
+ end
411
+
412
+ return nil # nothing was found
413
+ end
414
+
415
+ # Populate the Page with information from the Blob.
416
+ #
417
+ # blob - The Gollum::Git::Blob that contains the info.
418
+ # path - The String directory path of the page file.
419
+ #
420
+ # Returns the populated Gollum::Page.
421
+ def populate(blob, path=nil)
422
+ @blob = blob
423
+ @path = "#{path}/#{blob.name}"[1..-1]
424
+ self
425
+ end
426
+
427
+ # The full directory path for the given tree.
428
+ #
429
+ # treemap - The Hash treemap containing parentage information.
430
+ # tree - The Gollum::Git::Tree for which to compute the path.
431
+ #
432
+ # Returns the String path.
433
+ def tree_path(treemap, tree)
434
+ if ptree = treemap[tree]
435
+ tree_path(treemap, ptree) + '/' + tree.name
436
+ else
437
+ ''
438
+ end
439
+ end
440
+
441
+ # Compare the canonicalized versions of the two names.
442
+ #
443
+ # name - The human or canonical String page name.
444
+ # path - the String path on disk (including file extension).
445
+ #
446
+ # Returns a Boolean.
447
+ def page_match(name, path)
448
+ if match = self.class.valid_filename?(path)
449
+ @wiki.ws_subs.each do |sub|
450
+ return true if Page.cname(name).downcase == Page.cname(match, sub).downcase
451
+ end
452
+ end
453
+ false
454
+ end
455
+
456
+ # Loads a sub page. Sub page names (footers, headers, sidebars) are prefixed with
457
+ # an underscore to distinguish them from other Pages. If there is not one within
458
+ # the current directory, starts walking up the directory tree to try and find one
459
+ # within parent directories.
460
+ #
461
+ # name - String page name.
462
+ #
463
+ # Returns the Page or nil if none exists.
464
+ def find_sub_page(name)
465
+ return nil unless self.version
466
+ return nil if self.filename =~ /^_/
467
+ name = "_#{name.to_s.capitalize}"
468
+ return nil if page_match(name, self.filename)
469
+
470
+ dirs = self.path.split('/')
471
+ dirs.pop
472
+ map = @wiki.tree_map_for(@wiki.ref, true)
473
+ while !dirs.empty?
474
+ if page = find_page_in_tree(map, name, dirs.join('/'))
475
+ page.parent_page = self
476
+ return page
477
+ end
478
+ dirs.pop
479
+ end
480
+
481
+ if page = find_page_in_tree(map, name, '')
482
+ page.parent_page = self
483
+ end
484
+ page
485
+ end
486
+
487
+ def inspect
488
+ %(#<#{self.class.name}:#{object_id} #{name} (#{format}) @wiki=#{@wiki.repo.path.inspect}>)
489
+ end
490
+ end
491
+ end