gitlab-gollum-lib 1.1.0 → 4.2.7
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 +4 -4
- data/Gemfile +1 -3
- data/HISTORY.md +25 -0
- data/LICENSE +1 -1
- data/README.md +24 -312
- data/Rakefile +32 -16
- data/gemspec.rb +110 -0
- data/gollum-lib.gemspec +8 -75
- data/gollum-lib_java.gemspec +4 -0
- data/lib/gollum-lib.rb +18 -6
- data/lib/gollum-lib/blob_entry.rb +10 -9
- data/lib/gollum-lib/committer.rb +37 -30
- data/lib/gollum-lib/file.rb +71 -15
- data/lib/gollum-lib/file_view.rb +53 -48
- data/lib/gollum-lib/filter.rb +78 -0
- data/lib/gollum-lib/filter/code.rb +145 -0
- data/lib/gollum-lib/filter/emoji.rb +39 -0
- data/lib/gollum-lib/filter/macro.rb +57 -0
- data/lib/gollum-lib/filter/metadata.rb +29 -0
- data/lib/gollum-lib/filter/plain_text.rb +16 -0
- data/lib/gollum-lib/filter/plantuml.rb +176 -0
- data/lib/gollum-lib/filter/remote_code.rb +63 -0
- data/lib/gollum-lib/filter/render.rb +20 -0
- data/lib/gollum-lib/filter/sanitize.rb +18 -0
- data/lib/gollum-lib/filter/tags.rb +327 -0
- data/lib/gollum-lib/filter/toc.rb +134 -0
- data/lib/gollum-lib/filter/wsd.rb +54 -0
- data/lib/gollum-lib/git_access.rb +30 -32
- data/lib/gollum-lib/gitcode.rb +16 -16
- data/lib/gollum-lib/helpers.rb +3 -3
- data/lib/gollum-lib/hook.rb +35 -0
- data/lib/gollum-lib/macro.rb +43 -0
- data/lib/gollum-lib/macro/all_pages.rb +11 -0
- data/lib/gollum-lib/macro/global_toc.rb +12 -0
- data/lib/gollum-lib/macro/navigation.rb +20 -0
- data/lib/gollum-lib/macro/series.rb +48 -0
- data/lib/gollum-lib/markup.rb +95 -572
- data/lib/gollum-lib/markups.rb +9 -3
- data/lib/gollum-lib/page.rb +109 -80
- data/lib/gollum-lib/pagination.rb +1 -1
- data/lib/gollum-lib/sanitization.rb +75 -75
- data/lib/gollum-lib/version.rb +5 -0
- data/lib/gollum-lib/wiki.rb +287 -129
- metadata +237 -92
- data/CHANGELOG +0 -2
- data/VERSION +0 -1
- data/lib/gollum-lib/grit_ext.rb +0 -20
- data/lib/gollum-lib/remote_code.rb +0 -39
- data/lib/gollum-lib/web_sequence_diagram.rb +0 -44
@@ -0,0 +1,134 @@
|
|
1
|
+
# Inserts header anchors and creates TOC
|
2
|
+
class Gollum::Filter::TOC < Gollum::Filter
|
3
|
+
def extract(data)
|
4
|
+
data
|
5
|
+
end
|
6
|
+
|
7
|
+
def process(data)
|
8
|
+
|
9
|
+
@doc = Nokogiri::HTML::DocumentFragment.parse(data)
|
10
|
+
@toc_doc = nil
|
11
|
+
@anchor_names = {}
|
12
|
+
@current_ancestors = []
|
13
|
+
|
14
|
+
toc_str = ''
|
15
|
+
if @markup.sub_page && @markup.parent_page
|
16
|
+
toc_str = @markup.parent_page.toc_data
|
17
|
+
else
|
18
|
+
@doc.css('h1,h2,h3,h4,h5,h6').each_with_index do |header, i|
|
19
|
+
next if header.content.empty?
|
20
|
+
# omit the first H1 (the page title) from the TOC if so configured
|
21
|
+
next if (i == 0 && header.name =~ /[Hh]1/) && @markup.wiki && @markup.wiki.h1_title
|
22
|
+
|
23
|
+
anchor_name = generate_anchor_name(header)
|
24
|
+
|
25
|
+
add_anchor_to_header header, anchor_name
|
26
|
+
add_entry_to_toc header, anchor_name
|
27
|
+
end
|
28
|
+
if not @toc_doc.nil?
|
29
|
+
toc_str = @toc_doc.to_xml(@markup.to_xml_opts)
|
30
|
+
end
|
31
|
+
|
32
|
+
data = @doc.to_xml(@markup.to_xml_opts)
|
33
|
+
end
|
34
|
+
|
35
|
+
@markup.toc = toc_str
|
36
|
+
|
37
|
+
data.gsub!(/\[\[_TOC_(.*?)\]\]/) do
|
38
|
+
levels = nil
|
39
|
+
levels_match = Regexp.last_match[1].match /\|\s*levels\s*=\s*(\d+)/
|
40
|
+
if levels_match
|
41
|
+
levels = levels_match[1].to_i
|
42
|
+
end
|
43
|
+
|
44
|
+
if levels.nil? || toc_str.empty?
|
45
|
+
toc_str
|
46
|
+
else
|
47
|
+
@toc_doc ||= Nokogiri::HTML::DocumentFragment.parse(toc_str)
|
48
|
+
toc_clone = @toc_doc.clone
|
49
|
+
toc_clone.traverse do |e|
|
50
|
+
if e.name == 'ul' and e.ancestors('ul').length > levels - 1
|
51
|
+
e.remove
|
52
|
+
end
|
53
|
+
end
|
54
|
+
toc_clone.to_xml(@markup.to_xml_opts)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
data
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Generates the anchor name from the given header element
|
64
|
+
# removing all non alphanumeric characters, replacing them
|
65
|
+
# with single dashes.
|
66
|
+
#
|
67
|
+
# Generates heading ancestry prefixing the headings
|
68
|
+
# ancestor names to the generated name.
|
69
|
+
#
|
70
|
+
# Prefixes duplicate anchors with an index
|
71
|
+
def generate_anchor_name(header)
|
72
|
+
name = header.content
|
73
|
+
level = header.name.gsub(/[hH]/, '').to_i
|
74
|
+
|
75
|
+
# normalize the header name
|
76
|
+
name.gsub!(/[^\d\w\u00C0-\u1FFF\u2C00-\uD7FF]/, "-")
|
77
|
+
name.gsub!(/-+/, "-")
|
78
|
+
name.gsub!(/^-/, "")
|
79
|
+
name.gsub!(/-$/, "")
|
80
|
+
name.downcase!
|
81
|
+
|
82
|
+
@current_ancestors[level - 1] = name
|
83
|
+
@current_ancestors = @current_ancestors.take(level)
|
84
|
+
anchor_name = @current_ancestors.compact.join("_")
|
85
|
+
|
86
|
+
# Ensure duplicate anchors have a unique prefix or the toc will break
|
87
|
+
index = increment_anchor_index(anchor_name)
|
88
|
+
anchor_name = "#{index}-#{anchor_name}" unless index.zero? # if the index is zero this name is unique
|
89
|
+
|
90
|
+
anchor_name
|
91
|
+
end
|
92
|
+
|
93
|
+
# Creates an anchor element with the given name and adds it before
|
94
|
+
# the given header element.
|
95
|
+
def add_anchor_to_header(header, name)
|
96
|
+
anchor_element = %Q(<a class="anchor" id="#{name}" href="##{name}"><i class="fa fa-link"></i></a>)
|
97
|
+
header.children.before anchor_element # Add anchor element before the header
|
98
|
+
end
|
99
|
+
|
100
|
+
# Adds an entry to the TOC for the given header. The generated entry
|
101
|
+
# is a link to the given anchor name
|
102
|
+
def add_entry_to_toc(header, name)
|
103
|
+
@toc_doc ||= Nokogiri::XML::DocumentFragment.parse('<div class="toc"><div class="toc-title">Table of Contents</div></div>')
|
104
|
+
@tail ||= @toc_doc.child
|
105
|
+
@tail_level ||= 0
|
106
|
+
|
107
|
+
level = header.name.gsub(/[hH]/, '').to_i
|
108
|
+
|
109
|
+
if @tail_level < level
|
110
|
+
while @tail_level < level
|
111
|
+
list = Nokogiri::XML::Node.new('ul', @doc)
|
112
|
+
@tail.add_child(list)
|
113
|
+
@tail = list.add_child(Nokogiri::XML::Node.new('li', @doc))
|
114
|
+
@tail_level += 1
|
115
|
+
end
|
116
|
+
else
|
117
|
+
while @tail_level > level
|
118
|
+
@tail = @tail.parent.parent
|
119
|
+
@tail_level -= 1
|
120
|
+
end
|
121
|
+
@tail = @tail.parent.add_child(Nokogiri::XML::Node.new('li', @doc))
|
122
|
+
end
|
123
|
+
|
124
|
+
# % -> %25 so anchors work on Firefox. See issue #475
|
125
|
+
@tail.add_child(%Q{<a href="##{name}">#{header.content}</a>})
|
126
|
+
end
|
127
|
+
|
128
|
+
# Increments the number of anchors with the given name
|
129
|
+
# and returns the current index
|
130
|
+
def increment_anchor_index(name)
|
131
|
+
@anchor_names = {} if @anchor_names.nil?
|
132
|
+
@anchor_names[name].nil? ? @anchor_names[name] = 0 : @anchor_names[name] += 1
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
require 'net/http'
|
3
|
+
require 'uri'
|
4
|
+
require 'open-uri'
|
5
|
+
|
6
|
+
# Web Sequence Diagrams
|
7
|
+
#
|
8
|
+
# Render an inline web sequence diagram by sending the WSD code through the
|
9
|
+
# online renderer available from www.websequencediagrams.com.
|
10
|
+
#
|
11
|
+
class Gollum::Filter::WSD < Gollum::Filter
|
12
|
+
WSD_URL = "http://www.websequencediagrams.com/index.php"
|
13
|
+
|
14
|
+
# Extract all sequence diagram blocks into the map and replace with
|
15
|
+
# placeholders.
|
16
|
+
def extract(data)
|
17
|
+
return data if @markup.format == :txt
|
18
|
+
data.gsub(/^\{\{\{\{\{\{ ?(.+?)\r?\n(.+?)\r?\n\}\}\}\}\}\}\r?$/m) do
|
19
|
+
id = Digest::SHA1.hexdigest(Regexp.last_match[2])
|
20
|
+
@map[id] = { :style => Regexp.last_match[1], :code => Regexp.last_match[2] }
|
21
|
+
id
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Process all diagrams from the map and replace the placeholders with
|
26
|
+
# the final HTML.
|
27
|
+
#
|
28
|
+
# data - The String data (with placeholders).
|
29
|
+
#
|
30
|
+
# Returns the marked up String data.
|
31
|
+
def process(data)
|
32
|
+
@map.each do |id, spec|
|
33
|
+
data.gsub!(id) do
|
34
|
+
render_wsd(spec[:code], spec[:style])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
data
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
# Render the sequence diagram on the remote server.
|
42
|
+
#
|
43
|
+
# Returns an <img> tag to the rendered image, or an HTML error.
|
44
|
+
def render_wsd(code, style)
|
45
|
+
response = Net::HTTP.post_form(URI.parse(WSD_URL), 'style' => style, 'message' => code)
|
46
|
+
if response.body =~ /img: "(.+)"/
|
47
|
+
url = "//www.websequencediagrams.com/#{Regexp.last_match[1]}"
|
48
|
+
"<img src=\"#{url}\" />"
|
49
|
+
else
|
50
|
+
puts response.body
|
51
|
+
html_error("Sorry, unable to render sequence diagram at this time.")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -10,10 +10,10 @@ module Gollum
|
|
10
10
|
# page_file_dir - String the directory in which all page files reside
|
11
11
|
#
|
12
12
|
# Returns this instance.
|
13
|
-
def initialize(path, page_file_dir = nil, bare =
|
13
|
+
def initialize(path, page_file_dir = nil, bare = nil)
|
14
14
|
@page_file_dir = page_file_dir
|
15
|
-
@path
|
16
|
-
@repo =
|
15
|
+
@path = path
|
16
|
+
@repo = Gollum::Git::Repo.new(path, { :is_bare => bare })
|
17
17
|
clear
|
18
18
|
end
|
19
19
|
|
@@ -34,11 +34,11 @@ module Gollum
|
|
34
34
|
ref = ref.to_s
|
35
35
|
return if ref.empty?
|
36
36
|
sha =
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
if sha?(ref)
|
38
|
+
ref
|
39
|
+
else
|
40
|
+
get_cache(:ref, ref) { ref_to_sha!(ref) }
|
41
|
+
end.to_s
|
42
42
|
sha.empty? ? nil : sha
|
43
43
|
end
|
44
44
|
|
@@ -49,7 +49,7 @@ module Gollum
|
|
49
49
|
#
|
50
50
|
# Returns an Array of BlobEntry instances.
|
51
51
|
def tree(ref)
|
52
|
-
if sha = ref_to_sha(ref)
|
52
|
+
if (sha = ref_to_sha(ref))
|
53
53
|
get_cache(:tree, sha) { tree!(sha) }
|
54
54
|
else
|
55
55
|
[]
|
@@ -69,16 +69,16 @@ module Gollum
|
|
69
69
|
#
|
70
70
|
# ref - A String Git SHA or ref.
|
71
71
|
#
|
72
|
-
# Returns a
|
72
|
+
# Returns a Gollum::Git::Commit.
|
73
73
|
def commit(ref)
|
74
74
|
if sha?(ref)
|
75
75
|
get_cache(:commit, ref) { commit!(ref) }
|
76
76
|
else
|
77
|
-
if sha = get_cache(:ref, ref)
|
77
|
+
if (sha = get_cache(:ref, ref))
|
78
78
|
commit(sha)
|
79
79
|
else
|
80
|
-
if cm = commit!(ref)
|
81
|
-
set_cache(:ref,
|
80
|
+
if (cm = commit!(ref))
|
81
|
+
set_cache(:ref, ref, cm.id)
|
82
82
|
set_cache(:commit, cm.id, cm)
|
83
83
|
end
|
84
84
|
end
|
@@ -111,7 +111,7 @@ module Gollum
|
|
111
111
|
# Gets the String path to the Git repository.
|
112
112
|
attr_reader :path
|
113
113
|
|
114
|
-
# Gets the
|
114
|
+
# Gets the Gollum::Git::Repo instance for the Git repository.
|
115
115
|
attr_reader :repo
|
116
116
|
|
117
117
|
# Gets a Hash cache of refs to commit SHAs.
|
@@ -126,9 +126,9 @@ module Gollum
|
|
126
126
|
#
|
127
127
|
attr_reader :tree_map
|
128
128
|
|
129
|
-
# Gets a Hash cache of commit SHAs to the
|
129
|
+
# Gets a Hash cache of commit SHAs to the Gollum::Git::Commit instance.
|
130
130
|
#
|
131
|
-
# {"abcd123" => <
|
131
|
+
# {"abcd123" => <Gollum::Git::Commit>}
|
132
132
|
#
|
133
133
|
attr_reader :commit_map
|
134
134
|
|
@@ -147,8 +147,8 @@ module Gollum
|
|
147
147
|
#
|
148
148
|
# Returns a String SHA.
|
149
149
|
def ref_to_sha!(ref)
|
150
|
-
@repo.
|
151
|
-
|
150
|
+
commit = @repo.commit(ref)
|
151
|
+
commit ? commit.id : nil
|
152
152
|
end
|
153
153
|
|
154
154
|
# Looks up the Git blobs for a given commit.
|
@@ -157,16 +157,14 @@ module Gollum
|
|
157
157
|
#
|
158
158
|
# Returns an Array of BlobEntry instances.
|
159
159
|
def tree!(sha)
|
160
|
-
tree = @repo.
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
memo << parse_tree_line(line)
|
160
|
+
tree = @repo.lstree(sha, { :recursive => true })
|
161
|
+
items = []
|
162
|
+
tree.each do |entry|
|
163
|
+
if entry[:type] == 'blob'
|
164
|
+
items << BlobEntry.new(entry[:sha], entry[:path], entry[:size], entry[:mode].to_i(8))
|
165
|
+
end
|
167
166
|
end
|
168
|
-
|
169
|
-
if dir = @page_file_dir
|
167
|
+
if (dir = @page_file_dir)
|
170
168
|
regex = /^#{dir}\//
|
171
169
|
items.select { |i| i.path =~ regex }
|
172
170
|
else
|
@@ -180,14 +178,14 @@ module Gollum
|
|
180
178
|
#
|
181
179
|
# Returns the String content of the Git object.
|
182
180
|
def cat_file!(sha)
|
183
|
-
@repo.git.cat_file({:p => true}, sha)
|
181
|
+
@repo.git.cat_file({ :p => true }, sha)
|
184
182
|
end
|
185
183
|
|
186
184
|
# Reads a Git commit.
|
187
185
|
#
|
188
186
|
# sha - The string SHA of the Git commit.
|
189
187
|
#
|
190
|
-
# Returns a
|
188
|
+
# Returns a Gollum::Git::Commit.
|
191
189
|
def commit!(sha)
|
192
190
|
@repo.commit(sha)
|
193
191
|
end
|
@@ -228,7 +226,7 @@ module Gollum
|
|
228
226
|
#
|
229
227
|
# Returns an Array of BlobEntry instances.
|
230
228
|
def parse_tree_line(line)
|
231
|
-
mode,
|
229
|
+
mode, _type, sha, size, *name = line.split(/\s+/)
|
232
230
|
BlobEntry.new(sha, name.join(' '), size.to_i, mode.to_i(8))
|
233
231
|
end
|
234
232
|
|
@@ -240,9 +238,9 @@ module Gollum
|
|
240
238
|
def decode_git_path(path)
|
241
239
|
if path[0] == ?" && path[-1] == ?"
|
242
240
|
path = path[1...-1]
|
243
|
-
path.gsub!(/\\\d{3}/)
|
241
|
+
path.gsub!(/\\\d{3}/) { |m| m[1..-1].to_i(8).chr }
|
244
242
|
end
|
245
|
-
path.gsub!(/\\[rn"\\]/) { |m| eval(%("#{m
|
243
|
+
path.gsub!(/\\[rn"\\]/) { |m| eval(%("#{m}")) }
|
246
244
|
path
|
247
245
|
end
|
248
246
|
end
|
data/lib/gollum-lib/gitcode.rb
CHANGED
@@ -6,41 +6,41 @@ require 'open-uri'
|
|
6
6
|
|
7
7
|
module Gollum
|
8
8
|
class Gitcode
|
9
|
-
def initialize
|
9
|
+
def initialize(path)
|
10
10
|
raise(ArgumentError, 'path is nil or empty') if path.nil? or path.empty?
|
11
11
|
|
12
12
|
@uri = URI::HTTP.build({
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
:path => self.unchomp(path),
|
14
|
+
:host => 'raw.github.com',
|
15
|
+
:scheme => 'https',
|
16
|
+
:port => 443 })
|
17
17
|
end
|
18
18
|
|
19
19
|
def contents
|
20
20
|
@contents ||= self.req @uri
|
21
21
|
end
|
22
22
|
|
23
|
-
def unchomp
|
23
|
+
def unchomp(p)
|
24
24
|
return p if p.nil?
|
25
25
|
p[0] == '/' ? p : ('/' + p)
|
26
26
|
end
|
27
27
|
|
28
|
-
def req
|
28
|
+
def req(uri, cut = 1)
|
29
29
|
return "Too many redirects or retries" if cut >= 10
|
30
|
-
http
|
30
|
+
http = Net::HTTP.new uri.host, uri.port
|
31
31
|
http.use_ssl = true
|
32
|
-
resp
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
32
|
+
resp = http.get uri.path, {
|
33
|
+
'Accept' => 'text/plain',
|
34
|
+
'Cache-Control' => 'no-cache',
|
35
|
+
'Connection' => 'keep-alive',
|
36
|
+
'Host' => uri.host,
|
37
|
+
'User-Agent' => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0'
|
38
38
|
}
|
39
|
-
code
|
39
|
+
code = resp.code.to_i
|
40
40
|
return resp.body if code == 200
|
41
41
|
return "Not Found" if code == 404
|
42
42
|
return "Unhandled Response Code #{code}" unless code == 304 or not resp.header['location'].nil?
|
43
|
-
loc
|
43
|
+
loc = URI.parse resp.header['location']
|
44
44
|
uri2 = loc.relative?() ? (uri + loc) : loc # overloads (+)
|
45
45
|
return req uri2, (cut + 1)
|
46
46
|
end
|
data/lib/gollum-lib/helpers.rb
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
module Gollum
|
3
3
|
module Helpers
|
4
4
|
|
5
|
-
def trim_leading_slash
|
5
|
+
def trim_leading_slash(url)
|
6
6
|
return url if url.nil?
|
7
|
-
url.gsub!('%2F','/')
|
8
|
-
return '/' + url.gsub(/^\/+/,'') if url[0,1] == '/'
|
7
|
+
url.gsub!('%2F', '/')
|
8
|
+
return '/' + url.gsub(/^\/+/, '') if url[0, 1] == '/'
|
9
9
|
url
|
10
10
|
end
|
11
11
|
|
@@ -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
|