gitlab-gollum-lib 1.0.0

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.
@@ -0,0 +1,14 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ module Gollum
3
+ class Markup
4
+ register(:markdown, "Markdown", :regexp => /md|mkdn?|mdown|markdown/)
5
+ register(:textile, "Textile")
6
+ register(:rdoc, "RDoc")
7
+ register(:org, "Org-mode")
8
+ register(:creole, "Creole")
9
+ register(:rest, "reStructuredText", :regexp => /re?st(\.txt)?/)
10
+ register(:asciidoc, "AsciiDoc")
11
+ register(:mediawiki, "MediaWiki", :regexp => /(media)?wiki/)
12
+ register(:pod, "Pod")
13
+ end
14
+ end
@@ -0,0 +1,485 @@
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
+ @doc = nil
90
+ @parent_page = nil
91
+ end
92
+
93
+ # Public: The on-disk filename of the page including extension.
94
+ #
95
+ # Returns the String name.
96
+ def filename
97
+ @blob && @blob.name
98
+ end
99
+
100
+ # Public: The on-disk filename of the page with extension stripped.
101
+ #
102
+ # Returns the String name.
103
+ def filename_stripped
104
+ self.class.strip_filename(filename)
105
+ end
106
+
107
+ # Public: The canonical page name without extension, and dashes converted
108
+ # to spaces.
109
+ #
110
+ # Returns the String name.
111
+ def name
112
+ self.class.canonicalize_filename(filename)
113
+ end
114
+
115
+ # Public: The title will be constructed from the
116
+ # filename by stripping the extension and replacing any dashes with
117
+ # spaces.
118
+ #
119
+ # Returns the fully sanitized String title.
120
+ def title
121
+ Sanitize.clean(name).strip
122
+ end
123
+
124
+ # Public: Determines if this is a sub-page
125
+ # Sub-pages have filenames beginning with an underscore
126
+ #
127
+ # Returns true or false.
128
+ def sub_page
129
+ filename =~ /^_/
130
+ end
131
+
132
+ # Public: The path of the page within the repo.
133
+ #
134
+ # Returns the String path.
135
+ attr_reader :path
136
+
137
+ # Public: The url path required to reach this page within the repo.
138
+ #
139
+ # Returns the String url_path
140
+ def url_path
141
+ path = if self.path.include?('/')
142
+ self.path.sub(/\/[^\/]+$/, '/')
143
+ else
144
+ ''
145
+ end
146
+
147
+ path << Page.cname(self.name, '-', '-')
148
+ path
149
+ end
150
+
151
+ # Public: Defines title for page.rb
152
+ #
153
+ # Returns the String title
154
+ def url_path_title
155
+ metadata_title || url_path.gsub("-", " ")
156
+ end
157
+
158
+ # Public: Metadata title
159
+ #
160
+ # Set with <!-- --- title: New Title --> in page content
161
+ #
162
+ # Returns the String title or nil if not defined
163
+ def metadata_title
164
+ if metadata
165
+ title = metadata['title']
166
+ return title unless title.nil?
167
+ end
168
+
169
+ nil
170
+ end
171
+
172
+ # Public: The url_path, but CGI escaped.
173
+ #
174
+ # Returns the String url_path
175
+ def escaped_url_path
176
+ CGI.escape(self.url_path).gsub('%2F','/')
177
+ end
178
+
179
+ # Public: The raw contents of the page.
180
+ #
181
+ # Returns the String data.
182
+ def raw_data
183
+ return nil unless @blob
184
+
185
+ if !@wiki.repo.bare && @blob.is_symlink
186
+ new_path = @blob.symlink_target(::File.join(@wiki.repo.path, '..', self.path))
187
+ return IO.read(new_path) if new_path
188
+ end
189
+
190
+ @blob.data
191
+ end
192
+
193
+ # Public: A text data encoded in specified encoding.
194
+ #
195
+ # encoding - An Encoding or nil
196
+ #
197
+ # Returns a character encoding aware String.
198
+ def text_data(encoding=nil)
199
+ if raw_data.respond_to?(:encoding)
200
+ raw_data.force_encoding(encoding || Encoding::UTF_8)
201
+ else
202
+ raw_data
203
+ end
204
+ end
205
+
206
+ # Public: The formatted contents of the page.
207
+ #
208
+ # encoding - Encoding Constant or String.
209
+ #
210
+ # Returns the String data.
211
+ def formatted_data(encoding = nil, &block)
212
+ @blob && markup_class.render(historical?, encoding) do |doc|
213
+ @doc = doc
214
+ yield doc if block_given?
215
+ end
216
+ end
217
+
218
+ # Public: The table of contents of the page.
219
+ #
220
+ # formatted_data - page already marked up in html.
221
+ #
222
+ # Returns the String data.
223
+ def toc_data()
224
+ return @parent_page.toc_data if @parent_page and @sub_page
225
+ formatted_data if markup_class.toc == nil
226
+ markup_class.toc
227
+ end
228
+
229
+ # Public: Embedded metadata.
230
+ #
231
+ # Returns Hash of metadata.
232
+ def metadata()
233
+ formatted_data if markup_class.metadata == nil
234
+ markup_class.metadata
235
+ end
236
+
237
+ # Public: The format of the page.
238
+ #
239
+ # Returns the Symbol format of the page; one of the registered format types
240
+ def format
241
+ self.class.format_for(@blob.name)
242
+ end
243
+
244
+ # Gets the Gollum::Markup instance that will render this page's content.
245
+ #
246
+ # Returns a Gollum::Markup instance.
247
+ def markup_class
248
+ @markup_class ||= @wiki.markup_classes[format].new(self)
249
+ end
250
+
251
+ # Public: The current version of the page.
252
+ #
253
+ # Returns the Grit::Commit.
254
+ attr_reader :version
255
+
256
+ # Public: All of the versions that have touched the Page.
257
+ #
258
+ # options - The options Hash:
259
+ # :page - The Integer page number (default: 1).
260
+ # :per_page - The Integer max count of items to return.
261
+ # :follow - Follow's a file across renames, but falls back
262
+ # to a slower Grit native call. (default: false)
263
+ #
264
+ # Returns an Array of Grit::Commit.
265
+ def versions(options = {})
266
+ if options[:follow]
267
+ options[:pretty] = 'raw'
268
+ options.delete :max_count
269
+ options.delete :skip
270
+ log = @wiki.repo.git.native "log", options, @wiki.ref, "--", @path
271
+ Grit::Commit.list_from_string(@wiki.repo, log)
272
+ else
273
+ @wiki.repo.log(@wiki.ref, @path, log_pagination_options(options))
274
+ end
275
+ end
276
+
277
+ # Public: The first 7 characters of the current version.
278
+ #
279
+ # Returns the first 7 characters of the current version.
280
+ def version_short
281
+ version.to_s[0,7]
282
+ end
283
+
284
+ # Public: The header Page.
285
+ #
286
+ # Returns the header Page or nil if none exists.
287
+ def header
288
+ @header ||= find_sub_page(:header)
289
+ end
290
+
291
+ # Public: The footer Page.
292
+ #
293
+ # Returns the footer Page or nil if none exists.
294
+ def footer
295
+ @footer ||= find_sub_page(:footer)
296
+ end
297
+
298
+ # Public: The sidebar Page.
299
+ #
300
+ # Returns the sidebar Page or nil if none exists.
301
+ def sidebar
302
+ @sidebar ||= find_sub_page(:sidebar)
303
+ end
304
+
305
+ # Gets a Boolean determining whether this page is a historical version.
306
+ # Historical pages are pulled using exact SHA hashes and format all links
307
+ # with rel="nofollow"
308
+ #
309
+ # Returns true if the page is pulled from a named branch or tag, or false.
310
+ def historical?
311
+ !!@historical
312
+ end
313
+
314
+ #########################################################################
315
+ #
316
+ # Class Methods
317
+ #
318
+ #########################################################################
319
+
320
+ # Convert a human page name into a canonical page name.
321
+ #
322
+ # name - The String human page name.
323
+ # char_white_sub - Substitution for whitespace
324
+ # char_other_sub - Substitution for other special chars
325
+ #
326
+ # Examples
327
+ #
328
+ # Page.cname("Bilbo Baggins")
329
+ # # => 'Bilbo-Baggins'
330
+ #
331
+ # Page.cname("Bilbo Baggins",'_')
332
+ # # => 'Bilbo_Baggins'
333
+ #
334
+ # Returns the String canonical name.
335
+ def self.cname(name, char_white_sub = '-', char_other_sub = '-')
336
+ name.respond_to?(:gsub) ?
337
+ name.gsub(%r{\s},char_white_sub).gsub(%r{[<>+]}, char_other_sub) :
338
+ ''
339
+ end
340
+
341
+ # Convert a format Symbol into an extension String.
342
+ #
343
+ # format - The format Symbol.
344
+ #
345
+ # Returns the String extension (no leading period).
346
+ def self.format_to_ext(format)
347
+ format == :markdown ? "md" : format.to_s
348
+ end
349
+
350
+ #########################################################################
351
+ #
352
+ # Internal Methods
353
+ #
354
+ #########################################################################
355
+
356
+ # The underlying wiki repo.
357
+ #
358
+ # Returns the Gollum::Wiki containing the page.
359
+ attr_reader :wiki
360
+
361
+ # Set the Grit::Commit version of the page.
362
+ #
363
+ # Returns nothing.
364
+ attr_writer :version
365
+
366
+ # Find a page in the given Gollum repo.
367
+ #
368
+ # name - The human or canonical String page name to find.
369
+ # version - The String version ID to find.
370
+ #
371
+ # Returns a Gollum::Page or nil if the page could not be found.
372
+ def find(name, version, dir = nil, exact = false)
373
+ map = @wiki.tree_map_for(version.to_s)
374
+ if page = find_page_in_tree(map, name, dir, exact)
375
+ page.version = version.is_a?(Grit::Commit) ?
376
+ version : @wiki.commit_for(version)
377
+ page.historical = page.version.to_s == version.to_s
378
+ page
379
+ end
380
+ rescue Grit::GitRuby::Repository::NoSuchShaFound
381
+ end
382
+
383
+ # Find a page in a given tree.
384
+ #
385
+ # map - The Array tree map from Wiki#tree_map.
386
+ # name - The canonical String page name.
387
+ # checked_dir - Optional String of the directory a matching page needs
388
+ # to be in. The string should
389
+ #
390
+ # Returns a Gollum::Page or nil if the page could not be found.
391
+ def find_page_in_tree(map, name, checked_dir = nil, exact = false)
392
+ return nil if !map || name.to_s.empty?
393
+ if checked_dir = BlobEntry.normalize_dir(checked_dir)
394
+ checked_dir.downcase!
395
+ end
396
+
397
+ checked_dir = '' if exact && checked_dir.nil?
398
+
399
+ map.each do |entry|
400
+ next if entry.name.to_s.empty?
401
+ next unless checked_dir.nil? || entry.dir.downcase == checked_dir
402
+ next unless page_match(name, entry.name)
403
+ return entry.page(@wiki, @version)
404
+ end
405
+
406
+ return nil # nothing was found
407
+ end
408
+
409
+ # Populate the Page with information from the Blob.
410
+ #
411
+ # blob - The Grit::Blob that contains the info.
412
+ # path - The String directory path of the page file.
413
+ #
414
+ # Returns the populated Gollum::Page.
415
+ def populate(blob, path=nil)
416
+ @blob = blob
417
+ @path = "#{path}/#{blob.name}"[1..-1]
418
+ self
419
+ end
420
+
421
+ # The full directory path for the given tree.
422
+ #
423
+ # treemap - The Hash treemap containing parentage information.
424
+ # tree - The Grit::Tree for which to compute the path.
425
+ #
426
+ # Returns the String path.
427
+ def tree_path(treemap, tree)
428
+ if ptree = treemap[tree]
429
+ tree_path(treemap, ptree) + '/' + tree.name
430
+ else
431
+ ''
432
+ end
433
+ end
434
+
435
+ # Compare the canonicalized versions of the two names.
436
+ #
437
+ # name - The human or canonical String page name.
438
+ # filename - the String filename on disk (including extension).
439
+ #
440
+ # Returns a Boolean.
441
+ def page_match(name, filename)
442
+ if match = self.class.valid_filename?(filename)
443
+ @wiki.ws_subs.each do |sub|
444
+ return true if Page.cname(name).downcase == Page.cname(match, sub).downcase
445
+ end
446
+ end
447
+ false
448
+ end
449
+
450
+ # Loads a sub page. Sub page names (footers, headers, sidebars) are prefixed with
451
+ # an underscore to distinguish them from other Pages. If there is not one within
452
+ # the current directory, starts walking up the directory tree to try and find one
453
+ # within parent directories.
454
+ #
455
+ # name - String page name.
456
+ #
457
+ # Returns the Page or nil if none exists.
458
+ def find_sub_page(name)
459
+ return nil unless self.version
460
+ return nil if self.filename =~ /^_/
461
+ name = "_#{name.to_s.capitalize}"
462
+ return nil if page_match(name, self.filename)
463
+
464
+ dirs = self.path.split('/')
465
+ dirs.pop
466
+ map = @wiki.tree_map_for(@wiki.ref, true)
467
+ while !dirs.empty?
468
+ if page = find_page_in_tree(map, name, dirs.join('/'))
469
+ page.parent_page = self
470
+ return page
471
+ end
472
+ dirs.pop
473
+ end
474
+
475
+ if page = find_page_in_tree(map, name, '')
476
+ page.parent_page = self
477
+ end
478
+ page
479
+ end
480
+
481
+ def inspect
482
+ %(#<#{self.class.name}:#{object_id} #{name} (#{format}) @wiki=#{@wiki.repo.path.inspect}>)
483
+ end
484
+ end
485
+ end