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.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/HISTORY.md +3 -0
- data/LICENSE +21 -0
- data/README.md +601 -0
- data/Rakefile +171 -0
- data/docs/sanitization.md +33 -0
- data/gollum-lib.gemspec +75 -0
- data/lib/gollum-lib.rb +53 -0
- data/lib/gollum-lib/blob_entry.rb +95 -0
- data/lib/gollum-lib/committer.rb +236 -0
- data/lib/gollum-lib/file.rb +101 -0
- data/lib/gollum-lib/file_view.rb +155 -0
- data/lib/gollum-lib/git_access.rb +249 -0
- data/lib/gollum-lib/gitcode.rb +48 -0
- data/lib/gollum-lib/grit_ext.rb +20 -0
- data/lib/gollum-lib/helpers.rb +13 -0
- data/lib/gollum-lib/markup.rb +689 -0
- data/lib/gollum-lib/markups.rb +14 -0
- data/lib/gollum-lib/page.rb +485 -0
- data/lib/gollum-lib/pagination.rb +62 -0
- data/lib/gollum-lib/remote_code.rb +39 -0
- data/lib/gollum-lib/sanitization.rb +176 -0
- data/lib/gollum-lib/web_sequence_diagram.rb +44 -0
- data/lib/gollum-lib/wiki.rb +833 -0
- data/licenses/licenses.txt +6 -0
- metadata +315 -0
@@ -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
|