gitlab-gollum-lib 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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