gollum-lib 4.0.3-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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