gollum-lib 2.0.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of gollum-lib might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/Rakefile +4 -1
- data/gollum-lib.gemspec +31 -30
- data/lib/gollum-lib.rb +8 -8
- data/lib/gollum-lib/blob_entry.rb +5 -5
- data/lib/gollum-lib/committer.rb +11 -11
- data/lib/gollum-lib/file.rb +10 -9
- data/lib/gollum-lib/file_view.rb +33 -33
- data/lib/gollum-lib/filter.rb +3 -3
- data/lib/gollum-lib/filter/code.rb +30 -18
- data/lib/gollum-lib/filter/metadata.rb +5 -3
- data/lib/gollum-lib/filter/plain_text.rb +4 -3
- data/lib/gollum-lib/filter/remote_code.rb +20 -18
- data/lib/gollum-lib/filter/render.rb +4 -2
- data/lib/gollum-lib/filter/sanitize.rb +4 -2
- data/lib/gollum-lib/filter/tags.rb +36 -35
- data/lib/gollum-lib/filter/toc.rb +29 -13
- data/lib/gollum-lib/filter/wsd.rb +1 -1
- data/lib/gollum-lib/git_access.rb +11 -11
- data/lib/gollum-lib/gitcode.rb +13 -13
- data/lib/gollum-lib/grit_ext.rb +1 -1
- data/lib/gollum-lib/helpers.rb +2 -2
- data/lib/gollum-lib/markup.rb +81 -42
- data/lib/gollum-lib/markups.rb +1 -1
- data/lib/gollum-lib/page.rb +15 -15
- data/lib/gollum-lib/pagination.rb +1 -1
- data/lib/gollum-lib/sanitization.rb +74 -74
- data/lib/gollum-lib/wiki.rb +54 -54
- metadata +111 -103
@@ -28,20 +28,20 @@ class Gollum::Filter::Code < Gollum::Filter
|
|
28
28
|
lang = lang[1] unless lang.nil?
|
29
29
|
end
|
30
30
|
|
31
|
-
@map[id] = cached
|
32
|
-
|
33
|
-
|
31
|
+
@map[id] = cached ?
|
32
|
+
{ :output => cached } :
|
33
|
+
{ :lang => lang, :code => m_code, :indent => m_indent }
|
34
34
|
|
35
35
|
"#{m_indent}#{id}" # print the SHA1 ID with the proper indentation
|
36
36
|
end
|
37
37
|
|
38
38
|
data.gsub!(/^([ \t]*)``` ?([^\r\n]+)?\r?\n(.+?)\r?\n\1```[ \t]*\r?$/m) do
|
39
|
-
lang
|
40
|
-
id
|
41
|
-
cached
|
42
|
-
@map[id] = cached
|
43
|
-
|
44
|
-
|
39
|
+
lang = $2 ? $2.strip : nil
|
40
|
+
id = Digest::SHA1.hexdigest("#{lang}.#{$3}")
|
41
|
+
cached = @markup.check_cache(:code, id)
|
42
|
+
@map[id] = cached ?
|
43
|
+
{ :output => cached } :
|
44
|
+
{ :lang => lang, :code => $3, :indent => $1 }
|
45
45
|
"#{$1}#{id}" # print the SHA1 ID with the proper indentation
|
46
46
|
end
|
47
47
|
|
@@ -74,18 +74,30 @@ class Gollum::Filter::Code < Gollum::Filter
|
|
74
74
|
highlighted = []
|
75
75
|
blocks.each do |lang, code|
|
76
76
|
encoding = @markup.encoding || 'utf-8'
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
hl_code =
|
82
|
-
hl_code = "<pre class='highlight'><span class='err'>#{CGI.escapeHTML(hl_code)}</span></pre>"
|
77
|
+
|
78
|
+
if defined? Pygments
|
79
|
+
# treat unknown and bash as standard pre tags
|
80
|
+
if !lang || lang.downcase == 'bash'
|
81
|
+
hl_code = "<pre>#{code}</pre>"
|
83
82
|
else
|
84
|
-
|
83
|
+
# must set startinline to true for php to be highlighted without <?
|
84
|
+
hl_code = Pygments.highlight(code, :lexer => lang, :options => { :encoding => encoding.to_s, :startinline => true })
|
85
|
+
end
|
86
|
+
else # Rouge
|
87
|
+
begin
|
88
|
+
if Rouge::Lexer.find(lang).nil?
|
89
|
+
lexer = Rouge::Lexers::PlainText.new
|
90
|
+
formatter = Rouge::Formatters::HTML.new(:wrap => false)
|
91
|
+
hl_code = formatter.format(lexer.lex(code))
|
92
|
+
hl_code = "<pre class='highlight'><span class='err'>#{CGI.escapeHTML(hl_code)}</span></pre>"
|
93
|
+
else
|
94
|
+
hl_code = Rouge.highlight(code, lang, 'html')
|
95
|
+
end
|
96
|
+
rescue
|
97
|
+
hl_code = code
|
85
98
|
end
|
86
|
-
rescue
|
87
|
-
hl_code = code
|
88
99
|
end
|
100
|
+
|
89
101
|
highlighted << hl_code
|
90
102
|
end
|
91
103
|
|
@@ -16,12 +16,14 @@ class Gollum::Filter::Metadata < Gollum::Filter
|
|
16
16
|
# HTML elements before parsing each line.
|
17
17
|
$1.split("\n").each do |line|
|
18
18
|
line.gsub!(/<[^>]*>/, '')
|
19
|
-
k, v
|
19
|
+
k, v = line.split(':', 2)
|
20
20
|
@markup.metadata[k.strip] = (v ? v.strip : '') if k
|
21
21
|
end
|
22
22
|
''
|
23
23
|
end
|
24
24
|
end
|
25
|
-
|
26
|
-
def process(d)
|
25
|
+
|
26
|
+
def process(d)
|
27
|
+
d
|
28
|
+
end
|
27
29
|
end
|
@@ -16,7 +16,7 @@ class Gollum::Filter::RemoteCode < Gollum::Filter
|
|
16
16
|
return data if @markup.format == :txt
|
17
17
|
data.gsub /^[ \t]*``` ?([^:\n\r]+):((http)?[^`\n\r]+)```/ do
|
18
18
|
language = $1
|
19
|
-
uri
|
19
|
+
uri = $2
|
20
20
|
protocol = $3
|
21
21
|
|
22
22
|
# Detect local file
|
@@ -34,28 +34,30 @@ class Gollum::Filter::RemoteCode < Gollum::Filter
|
|
34
34
|
"```#{language}\n#{contents}\n```\n"
|
35
35
|
end
|
36
36
|
end
|
37
|
-
|
38
|
-
def process(d)
|
37
|
+
|
38
|
+
def process(d)
|
39
|
+
d
|
40
|
+
end
|
39
41
|
|
40
42
|
private
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
def req uri, cut = 1
|
45
|
+
uri = URI(uri)
|
46
|
+
return "Too many redirects or retries" if cut >= 10
|
47
|
+
http = Net::HTTP.new uri.host, uri.port
|
48
|
+
http.use_ssl = true
|
49
|
+
resp = http.get uri.path, {
|
48
50
|
'Accept' => 'text/plain',
|
49
51
|
'Cache-Control' => 'no-cache',
|
50
52
|
'Connection' => 'keep-alive',
|
51
53
|
'User-Agent' => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0'
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
54
|
+
}
|
55
|
+
code = resp.code.to_i
|
56
|
+
return resp.body if code == 200
|
57
|
+
return "Not Found" if code == 404
|
58
|
+
return "Unhandled Response Code #{code}" unless code == 304 or not resp.header['location'].nil?
|
59
|
+
loc = URI.parse resp.header['location']
|
60
|
+
uri2 = loc.relative? ? (uri + loc) : loc # overloads (+)
|
61
|
+
req uri2, (cut + 1)
|
62
|
+
end
|
61
63
|
end
|
@@ -10,19 +10,19 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
10
10
|
"[[#{$2}]]#{$3}"
|
11
11
|
elsif $2.include?('][')
|
12
12
|
if $2[0..4] == 'file:'
|
13
|
-
pre
|
14
|
-
post
|
15
|
-
parts
|
13
|
+
pre = $1
|
14
|
+
post = $3
|
15
|
+
parts = $2.split('][')
|
16
16
|
parts[0][0..4] = ""
|
17
|
-
link
|
18
|
-
id
|
19
|
-
@map[id]
|
17
|
+
link = "#{parts[1]}|#{parts[0].sub(/\.org/, '')}"
|
18
|
+
id = Digest::SHA1.hexdigest(link)
|
19
|
+
@map[id] = link
|
20
20
|
"#{pre}#{id}#{post}"
|
21
21
|
else
|
22
22
|
$&
|
23
23
|
end
|
24
24
|
else
|
25
|
-
id
|
25
|
+
id = Digest::SHA1.hexdigest($2)
|
26
26
|
@map[id] = $2
|
27
27
|
"#{$1}#{id}#{$3}"
|
28
28
|
end
|
@@ -56,11 +56,12 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
56
56
|
# data - The String data (with placeholders).
|
57
57
|
# id - The String SHA1 hash.
|
58
58
|
PREFORMATTED_TAGS = %w(code tt)
|
59
|
+
|
59
60
|
def is_preformatted?(data, id)
|
60
|
-
doc
|
61
|
+
doc = Nokogiri::HTML::DocumentFragment.parse(data)
|
61
62
|
node = doc.search("[text()*='#{id}']").first
|
62
63
|
node && (PREFORMATTED_TAGS.include?(node.name) ||
|
63
|
-
|
64
|
+
node.ancestors.any? { |a| PREFORMATTED_TAGS.include?(a.name) })
|
64
65
|
end
|
65
66
|
|
66
67
|
# Process a single tag into its final HTML form.
|
@@ -84,7 +85,7 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
84
85
|
process_page_link_tag(tag)
|
85
86
|
end
|
86
87
|
end
|
87
|
-
|
88
|
+
|
88
89
|
# Attempt to process the tag as an include tag
|
89
90
|
#
|
90
91
|
# tag - The String tag contents (the stuff inside the double brackets).
|
@@ -94,8 +95,8 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
94
95
|
#
|
95
96
|
def process_include_tag(tag)
|
96
97
|
return unless /^include:/.match(tag)
|
97
|
-
page_name
|
98
|
-
resolved_page_name =
|
98
|
+
page_name = tag[8..-1]
|
99
|
+
resolved_page_name = ::File.expand_path(page_name, "/"+@markup.dir)
|
99
100
|
|
100
101
|
if @markup.include_levels > 0
|
101
102
|
page = find_page_from_name(resolved_page_name)
|
@@ -119,12 +120,12 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
119
120
|
parts = tag.split('|')
|
120
121
|
return if parts.size.zero?
|
121
122
|
|
122
|
-
name
|
123
|
-
path
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
123
|
+
name = parts[0].strip
|
124
|
+
path = if file = @markup.find_file(name)
|
125
|
+
::File.join @markup.wiki.base_path, file.path
|
126
|
+
elsif name =~ /^https?:\/\/.+(jpg|png|gif|svg|bmp)$/i
|
127
|
+
name
|
128
|
+
end
|
128
129
|
|
129
130
|
if path
|
130
131
|
opts = parse_image_tag_options(tag)
|
@@ -137,7 +138,7 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
137
138
|
align = opts['align']
|
138
139
|
if opts['float']
|
139
140
|
containered = true
|
140
|
-
align
|
141
|
+
align ||= 'left'
|
141
142
|
if %w{left right}.include?(align)
|
142
143
|
classes << "float-#{align}"
|
143
144
|
end
|
@@ -171,11 +172,11 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
171
172
|
if opts['frame'] || containered
|
172
173
|
classes << 'frame' if opts['frame']
|
173
174
|
%{<span class="#{classes.join(' ')}">} +
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
175
|
+
%{<span>} +
|
176
|
+
%{<img src="#{path}" #{attr_string}/>} +
|
177
|
+
(alt ? %{<span>#{alt}</span>} : '') +
|
178
|
+
%{</span>} +
|
179
|
+
%{</span>}
|
179
180
|
else
|
180
181
|
%{<img src="#{path}" #{attr_string}/>}
|
181
182
|
end
|
@@ -192,7 +193,7 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
192
193
|
# val - The String option value or true if it is a binary option.
|
193
194
|
def parse_image_tag_options(tag)
|
194
195
|
tag.split('|')[1..-1].inject({}) do |memo, attr|
|
195
|
-
parts
|
196
|
+
parts = attr.split('=').map { |x| x.strip }
|
196
197
|
memo[parts[0]] = (parts.size == 1 ? true : parts[1])
|
197
198
|
memo
|
198
199
|
end
|
@@ -209,15 +210,15 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
209
210
|
parts = tag.split('|')
|
210
211
|
return if parts.size.zero?
|
211
212
|
|
212
|
-
name
|
213
|
-
path
|
214
|
-
path
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
213
|
+
name = parts[0].strip
|
214
|
+
path = parts[1] && parts[1].strip
|
215
|
+
path = if path && file = @markup.find_file(path)
|
216
|
+
::File.join @markup.wiki.base_path, file.path
|
217
|
+
elsif path =~ %r{^https?://}
|
218
|
+
path
|
219
|
+
else
|
220
|
+
nil
|
221
|
+
end
|
221
222
|
|
222
223
|
if name && path && file
|
223
224
|
%{<a href="#{::File.join @markup.wiki.base_path, file.path}">#{name}</a>}
|
@@ -240,7 +241,7 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
240
241
|
parts.reverse! if @markup.format == :mediawiki
|
241
242
|
|
242
243
|
name, page_name = *parts.compact.map(&:strip)
|
243
|
-
cname
|
244
|
+
cname = @markup.wiki.page_class.cname(page_name || name)
|
244
245
|
|
245
246
|
if name =~ %r{^https?://} && page_name.nil?
|
246
247
|
%{<a href="#{name}">#{name}</a>}
|
@@ -1,31 +1,47 @@
|
|
1
1
|
# Inserts header anchors and creates TOC
|
2
2
|
class Gollum::Filter::TOC < Gollum::Filter
|
3
|
-
def extract(d)
|
4
|
-
|
3
|
+
def extract(d)
|
4
|
+
d
|
5
|
+
end
|
6
|
+
|
5
7
|
def process(data)
|
6
|
-
doc
|
7
|
-
toc
|
8
|
+
doc = Nokogiri::HTML::DocumentFragment.parse(data)
|
9
|
+
toc = nil
|
10
|
+
anchor_names = {}
|
11
|
+
|
8
12
|
doc.css('h1,h2,h3,h4,h5,h6').each do |h|
|
9
13
|
# must escape "
|
10
|
-
h_name
|
14
|
+
h_name = h.content.gsub(' ', '-').gsub('"', '%22')
|
15
|
+
|
16
|
+
# Ensure repeat anchors have a unique prefix or the
|
17
|
+
# toc will break
|
18
|
+
anchor_names[h_name] = 0 if anchor_names[h_name].nil?
|
19
|
+
anchor_names[h_name] += 1
|
20
|
+
|
21
|
+
anchor_prefix_number = anchor_names[h_name]
|
22
|
+
if anchor_prefix_number > 1
|
23
|
+
h_name = anchor_prefix_number.to_s + '-' + h_name
|
24
|
+
end
|
11
25
|
|
12
|
-
level = h.name.gsub(/[hH]/,'').to_i
|
26
|
+
level = h.name.gsub(/[hH]/, '').to_i
|
13
27
|
|
14
28
|
# Add anchors
|
15
|
-
|
29
|
+
anchor_element = %Q(<a class="anchor" id="#{h_name}" href="##{h_name}"><i class="fa fa-link"></i></a>)
|
30
|
+
# Add anchor element as the first child (before h.content)
|
31
|
+
h.children.before anchor_element
|
16
32
|
|
17
33
|
# Build TOC
|
18
|
-
toc
|
19
|
-
tail
|
34
|
+
toc ||= Nokogiri::XML::DocumentFragment.parse('<div class="toc"><div class="toc-title">Table of Contents</div></div>')
|
35
|
+
tail ||= toc.child
|
20
36
|
tail_level ||= 0
|
21
37
|
|
22
38
|
while tail_level < level
|
23
|
-
node
|
24
|
-
tail
|
39
|
+
node = Nokogiri::XML::Node.new('ul', doc)
|
40
|
+
tail = tail.add_child(node)
|
25
41
|
tail_level += 1
|
26
42
|
end
|
27
43
|
while tail_level > level
|
28
|
-
tail
|
44
|
+
tail = tail.parent
|
29
45
|
tail_level -= 1
|
30
46
|
end
|
31
47
|
node = Nokogiri::XML::Node.new('li', doc)
|
@@ -42,4 +58,4 @@ class Gollum::Filter::TOC < Gollum::Filter
|
|
42
58
|
toc.nil? ? '' : toc
|
43
59
|
end
|
44
60
|
end
|
45
|
-
end
|
61
|
+
end
|
@@ -16,7 +16,7 @@ class Gollum::Filter::WSD < Gollum::Filter
|
|
16
16
|
def extract(data)
|
17
17
|
return data if @markup.format == :txt
|
18
18
|
data.gsub(/^\{\{\{\{\{\{ ?(.+?)\r?\n(.+?)\r?\n\}\}\}\}\}\}\r?$/m) do
|
19
|
-
id
|
19
|
+
id = Digest::SHA1.hexdigest($2)
|
20
20
|
@map[id] = { :style => $1, :code => $2 }
|
21
21
|
id
|
22
22
|
end
|
@@ -12,7 +12,7 @@ module Gollum
|
|
12
12
|
# Returns this instance.
|
13
13
|
def initialize(path, page_file_dir = nil, bare = false)
|
14
14
|
@page_file_dir = page_file_dir
|
15
|
-
@path
|
15
|
+
@path = path
|
16
16
|
begin
|
17
17
|
@repo = Grit::Repo.new(path, { :is_bare => bare })
|
18
18
|
rescue Grit::InvalidGitRepositoryError
|
@@ -40,11 +40,11 @@ module Gollum
|
|
40
40
|
ref = ref.to_s
|
41
41
|
return if ref.empty?
|
42
42
|
sha =
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
43
|
+
if sha?(ref)
|
44
|
+
ref
|
45
|
+
else
|
46
|
+
get_cache(:ref, ref) { ref_to_sha!(ref) }
|
47
|
+
end.to_s
|
48
48
|
sha.empty? ? nil : sha
|
49
49
|
end
|
50
50
|
|
@@ -84,7 +84,7 @@ module Gollum
|
|
84
84
|
commit(sha)
|
85
85
|
else
|
86
86
|
if cm = commit!(ref)
|
87
|
-
set_cache(:ref,
|
87
|
+
set_cache(:ref, ref, cm.id)
|
88
88
|
set_cache(:commit, cm.id, cm)
|
89
89
|
end
|
90
90
|
end
|
@@ -163,11 +163,11 @@ module Gollum
|
|
163
163
|
#
|
164
164
|
# Returns an Array of BlobEntry instances.
|
165
165
|
def tree!(sha)
|
166
|
-
tree
|
166
|
+
tree = @repo.lstree(sha, { :recursive => true })
|
167
167
|
items = []
|
168
168
|
tree.each do |entry|
|
169
169
|
if entry[:type] == 'blob'
|
170
|
-
items << BlobEntry.new(entry[:sha], entry[:path], entry[:size], entry[:mode].to_i(8))
|
170
|
+
items << BlobEntry.new(entry[:sha], entry[:path], entry[:size], entry[:mode].to_i(8))
|
171
171
|
end
|
172
172
|
end
|
173
173
|
if dir = @page_file_dir
|
@@ -184,7 +184,7 @@ module Gollum
|
|
184
184
|
#
|
185
185
|
# Returns the String content of the Git object.
|
186
186
|
def cat_file!(sha)
|
187
|
-
@repo.git.cat_file({:p => true}, sha)
|
187
|
+
@repo.git.cat_file({ :p => true }, sha)
|
188
188
|
end
|
189
189
|
|
190
190
|
# Reads a Git commit.
|
@@ -244,7 +244,7 @@ module Gollum
|
|
244
244
|
def decode_git_path(path)
|
245
245
|
if path[0] == ?" && path[-1] == ?"
|
246
246
|
path = path[1...-1]
|
247
|
-
path.gsub!(/\\\d{3}/)
|
247
|
+
path.gsub!(/\\\d{3}/) { |m| m[1..-1].to_i(8).chr }
|
248
248
|
end
|
249
249
|
path.gsub!(/\\[rn"\\]/) { |m| eval(%("#{m.to_s}")) }
|
250
250
|
path
|