gollum-lib 4.0.3 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +1 -1
- data/Rakefile +3 -3
- data/gemspec.rb +9 -6
- data/lib/gollum-lib/blob_entry.rb +1 -0
- data/lib/gollum-lib/committer.rb +5 -5
- data/lib/gollum-lib/file.rb +7 -7
- data/lib/gollum-lib/file_view.rb +14 -15
- data/lib/gollum-lib/filter/code.rb +28 -29
- data/lib/gollum-lib/filter/macro.rb +16 -16
- data/lib/gollum-lib/filter/metadata.rb +3 -3
- data/lib/gollum-lib/filter/plantuml.rb +167 -0
- data/lib/gollum-lib/filter/remote_code.rb +8 -8
- data/lib/gollum-lib/filter/render.rb +2 -2
- data/lib/gollum-lib/filter/sanitize.rb +2 -2
- data/lib/gollum-lib/filter/tags.rb +31 -30
- data/lib/gollum-lib/filter/toc.rb +7 -7
- data/lib/gollum-lib/filter/wsd.rb +3 -3
- data/lib/gollum-lib/git_access.rb +6 -6
- data/lib/gollum-lib/gitcode.rb +3 -3
- data/lib/gollum-lib/helpers.rb +1 -1
- data/lib/gollum-lib/macro/global_toc.rb +12 -0
- data/lib/gollum-lib/macro/series.rb +48 -0
- data/lib/gollum-lib/markup.rb +8 -3
- data/lib/gollum-lib/markups.rb +2 -2
- data/lib/gollum-lib/page.rb +9 -9
- data/lib/gollum-lib/sanitization.rb +3 -3
- data/lib/gollum-lib/version.rb +1 -1
- data/lib/gollum-lib/wiki.rb +17 -17
- metadata +7 -4
@@ -0,0 +1,167 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
require 'net/http'
|
3
|
+
require 'uri'
|
4
|
+
require 'open-uri'
|
5
|
+
require 'zlib'
|
6
|
+
|
7
|
+
# PlantUML Diagrams
|
8
|
+
#
|
9
|
+
# This filter replaces PlantUML blocks with HTML img tags. These img tags
|
10
|
+
# point to a PlantUML web service that converts the UML text blocks into nice
|
11
|
+
# diagrams.
|
12
|
+
#
|
13
|
+
# For this to work you must have your own PlantUML server running somewhere.
|
14
|
+
# Just follow the instructions on the github page to run your own server:
|
15
|
+
#
|
16
|
+
# https://github.com/plantuml/plantuml-server
|
17
|
+
#
|
18
|
+
# Once you start you own plantuml server you need to configure this filter to
|
19
|
+
# point to it:
|
20
|
+
#
|
21
|
+
# Gollum::Filter::PlantUML.configure do |config|
|
22
|
+
# config.url = "http://localhost:8080/plantuml/png"
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# Then in your wiki pages simply add PlantUML blocks anywhere you want a
|
26
|
+
# diagram:
|
27
|
+
#
|
28
|
+
# @startuml
|
29
|
+
# Alice -> Bob: Authentication Request
|
30
|
+
# Bob --> Alice: Authentication Response
|
31
|
+
# Alice -> Bob: Another authentication Request
|
32
|
+
# Alice <-- Bob: another authentication Response
|
33
|
+
# @enduml
|
34
|
+
#
|
35
|
+
# To learn more about how to create cool PlantUML diagrams check the examples
|
36
|
+
# at: http://plantuml.sourceforge.net/
|
37
|
+
#
|
38
|
+
class Gollum::Filter::PlantUML < Gollum::Filter
|
39
|
+
|
40
|
+
DEFAULT_URL = "http://localhost:8080/plantuml/png"
|
41
|
+
|
42
|
+
# Configuration class used to change the behaviour of the PlatnUML filter.
|
43
|
+
#
|
44
|
+
# url: PlantUML server URL (e.g. http://localhost:8080)
|
45
|
+
# test: Set to true when running tests to skip the server check.
|
46
|
+
#
|
47
|
+
class Configuration
|
48
|
+
attr_accessor :url, :test
|
49
|
+
|
50
|
+
def initialize
|
51
|
+
@url = DEFAULT_URL
|
52
|
+
@test = false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class << self
|
57
|
+
attr_writer :configuration
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.configuration
|
61
|
+
@configuration ||= Configuration.new
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.configure
|
65
|
+
yield(configuration)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Extract all sequence diagram blocks into the map and replace with
|
69
|
+
# placeholders.
|
70
|
+
def extract(data)
|
71
|
+
return data if @markup.format == :txt
|
72
|
+
data.gsub(/(@startuml\r?\n.+?\r?\n@enduml\r?$)/m) do
|
73
|
+
id = Digest::SHA1.hexdigest($1)
|
74
|
+
@map[id] = { :code => $1 }
|
75
|
+
id
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Process all diagrams from the map and replace the placeholders with
|
80
|
+
# the final HTML.
|
81
|
+
def process(data)
|
82
|
+
@map.each do |id, spec|
|
83
|
+
data.gsub!(id) do
|
84
|
+
render_plantuml(id, spec[:code])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
data
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def server_url
|
93
|
+
PlantUML::configuration.url
|
94
|
+
end
|
95
|
+
|
96
|
+
def test?
|
97
|
+
PlantUML::configuration.test
|
98
|
+
end
|
99
|
+
|
100
|
+
def render_plantuml(id, code)
|
101
|
+
if check_server
|
102
|
+
plantuml_url = gen_url(code)
|
103
|
+
"<img src=\"#{gen_url(code)}\" />"
|
104
|
+
else
|
105
|
+
html_error("Sorry, unable to render PlantUML diagram at this time")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Compression code used to generate PlantUML URLs. Taken directly from the
|
110
|
+
# Transcoder class in the PlantUML java code.
|
111
|
+
def gen_url(text)
|
112
|
+
result = ""
|
113
|
+
compressedData = Zlib::Deflate.deflate(text)
|
114
|
+
compressedData.chars.each_slice(3) do |bytes|
|
115
|
+
#print bytes[0], ' ' , bytes[1] , ' ' , bytes[2]
|
116
|
+
b1 = bytes[0].nil? ? 0 : (bytes[0].ord & 0xFF)
|
117
|
+
b2 = bytes[1].nil? ? 0 : (bytes[1].ord & 0xFF)
|
118
|
+
b3 = bytes[2].nil? ? 0 : (bytes[2].ord & 0xFF)
|
119
|
+
result += append3bytes(b1, b2, b3)
|
120
|
+
end
|
121
|
+
"#{server_url}/#{result}"
|
122
|
+
end
|
123
|
+
|
124
|
+
def encode6bit(b)
|
125
|
+
if b < 10
|
126
|
+
return ('0'.ord + b).chr
|
127
|
+
end
|
128
|
+
b = b - 10
|
129
|
+
if b < 26
|
130
|
+
return ('A'.ord + b).chr
|
131
|
+
end
|
132
|
+
b = b - 26
|
133
|
+
if b < 26
|
134
|
+
return ('a'.ord + b).chr
|
135
|
+
end
|
136
|
+
b = b - 26
|
137
|
+
if b == 0
|
138
|
+
return '-'
|
139
|
+
end
|
140
|
+
if b == 1
|
141
|
+
return '_'
|
142
|
+
end
|
143
|
+
return '?'
|
144
|
+
end
|
145
|
+
|
146
|
+
def append3bytes(b1, b2, b3)
|
147
|
+
c1 = b1 >> 2
|
148
|
+
c2 = ((b1 & 0x3) << 4) | (b2 >> 4)
|
149
|
+
c3 = ((b2 & 0xF) << 2) | (b3 >> 6)
|
150
|
+
c4 = b3 & 0x3F
|
151
|
+
return encode6bit(c1 & 0x3F).chr +
|
152
|
+
encode6bit(c2 & 0x3F).chr +
|
153
|
+
encode6bit(c3 & 0x3F).chr +
|
154
|
+
encode6bit(c4 & 0x3F).chr
|
155
|
+
end
|
156
|
+
|
157
|
+
# Make a call to the PlantUML server with the simplest diagram possible to
|
158
|
+
# check if the server is available or not.
|
159
|
+
def check_server
|
160
|
+
return true if test?
|
161
|
+
check_url = "#{server_url}/SyfFKj2rKt3CoKnELR1Io4ZDoSa70000"
|
162
|
+
response = Net::HTTP.get_response(URI(check_url))
|
163
|
+
return response.is_a?(Net::HTTPSuccess)
|
164
|
+
rescue
|
165
|
+
return false
|
166
|
+
end
|
167
|
+
end
|
@@ -14,14 +14,14 @@ require 'open-uri'
|
|
14
14
|
class Gollum::Filter::RemoteCode < Gollum::Filter
|
15
15
|
def extract(data)
|
16
16
|
return data if @markup.format == :txt
|
17
|
-
data.gsub
|
18
|
-
language =
|
19
|
-
uri =
|
20
|
-
protocol =
|
17
|
+
data.gsub(/^[ \t]*``` ?([^:\n\r]+):((http)?[^`\n\r]+)```/) do
|
18
|
+
language = Regexp.last_match[1]
|
19
|
+
uri = Regexp.last_match[2]
|
20
|
+
protocol = Regexp.last_match[3]
|
21
21
|
|
22
22
|
# Detect local file
|
23
23
|
if protocol.nil?
|
24
|
-
if file = @markup.find_file(uri, @markup.wiki.ref)
|
24
|
+
if (file = @markup.find_file(uri, @markup.wiki.ref))
|
25
25
|
contents = file.raw_data
|
26
26
|
else
|
27
27
|
# How do we communicate a render error?
|
@@ -35,13 +35,13 @@ class Gollum::Filter::RemoteCode < Gollum::Filter
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
def process(
|
39
|
-
|
38
|
+
def process(data)
|
39
|
+
data
|
40
40
|
end
|
41
41
|
|
42
42
|
private
|
43
43
|
|
44
|
-
def req
|
44
|
+
def req(uri, cut = 1)
|
45
45
|
uri = URI(uri)
|
46
46
|
return "Too many redirects or retries" if cut >= 10
|
47
47
|
http = Net::HTTP.new uri.host, uri.port
|
@@ -6,23 +6,23 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
6
6
|
def extract(data)
|
7
7
|
return data if @markup.format == :txt || @markup.format == :asciidoc
|
8
8
|
data.gsub!(/(.?)\[\[(.+?)\]\]([^\[]?)/m) do
|
9
|
-
if
|
10
|
-
"[[#{
|
11
|
-
elsif
|
12
|
-
if
|
13
|
-
pre =
|
14
|
-
post =
|
15
|
-
parts =
|
9
|
+
if Regexp.last_match[1] == "'" && Regexp.last_match[3] != "'"
|
10
|
+
"[[#{Regexp.last_match[2]}]]#{Regexp.last_match[3]}"
|
11
|
+
elsif Regexp.last_match[2].include?('][')
|
12
|
+
if Regexp.last_match[2][0..4] == 'file:'
|
13
|
+
pre = Regexp.last_match[1]
|
14
|
+
post = Regexp.last_match[3]
|
15
|
+
parts = Regexp.last_match[2].split('][')
|
16
16
|
parts[0][0..4] = ""
|
17
17
|
link = "#{parts[1]}|#{parts[0].sub(/\.org/, '')}"
|
18
18
|
id = register_tag(link)
|
19
19
|
"#{pre}#{id}#{post}"
|
20
20
|
else
|
21
|
-
|
21
|
+
Regexp.last_match[0]
|
22
22
|
end
|
23
23
|
else
|
24
|
-
id = register_tag(
|
25
|
-
"#{
|
24
|
+
id = register_tag(Regexp.last_match[2])
|
25
|
+
"#{Regexp.last_match[1]}#{id}#{Regexp.last_match[3]}"
|
26
26
|
end
|
27
27
|
end
|
28
28
|
data
|
@@ -42,7 +42,7 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
42
42
|
if node.text? then
|
43
43
|
content = node.content
|
44
44
|
content.gsub!(/TAG[a-f0-9]+TAG/) do |id|
|
45
|
-
if tag = @map[id] then
|
45
|
+
if (tag = @map[id]) then
|
46
46
|
if is_preformatted?(node) then
|
47
47
|
"[[#{tag}]]"
|
48
48
|
else
|
@@ -77,13 +77,13 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
77
77
|
%{[[#{tag}]]}
|
78
78
|
elsif tag =~ /^_$/
|
79
79
|
%{<div class="clearfloats"></div>}
|
80
|
-
elsif html = process_include_tag(tag)
|
80
|
+
elsif (html = process_include_tag(tag))
|
81
81
|
html
|
82
|
-
elsif html = process_image_tag(tag)
|
82
|
+
elsif (html = process_image_tag(tag))
|
83
83
|
html
|
84
|
-
elsif html = process_external_link_tag(tag)
|
84
|
+
elsif (html = process_external_link_tag(tag))
|
85
85
|
html
|
86
|
-
elsif html = process_file_link_tag(tag)
|
86
|
+
elsif (html = process_file_link_tag(tag))
|
87
87
|
html
|
88
88
|
else
|
89
89
|
process_page_link_tag(tag)
|
@@ -125,11 +125,11 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
125
125
|
return if parts.size.zero?
|
126
126
|
|
127
127
|
name = parts[0].strip
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
128
|
+
if (file = @markup.find_file(name))
|
129
|
+
path = ::File.join @markup.wiki.base_path, file.path
|
130
|
+
elsif name =~ /^https?:\/\/.+(jpg|png|gif|svg|bmp)$/i
|
131
|
+
path = name
|
132
|
+
end
|
133
133
|
|
134
134
|
if path
|
135
135
|
opts = parse_image_tag_options(tag)
|
@@ -155,19 +155,19 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
155
155
|
end
|
156
156
|
end
|
157
157
|
|
158
|
-
if width = opts['width']
|
158
|
+
if (width = opts['width'])
|
159
159
|
if width =~ /^\d+(\.\d+)?(em|px)$/
|
160
160
|
attrs << %{width="#{width}"}
|
161
161
|
end
|
162
162
|
end
|
163
163
|
|
164
|
-
if height = opts['height']
|
164
|
+
if (height = opts['height'])
|
165
165
|
if height =~ /^\d+(\.\d+)?(em|px)$/
|
166
166
|
attrs << %{height="#{height}"}
|
167
167
|
end
|
168
168
|
end
|
169
169
|
|
170
|
-
if alt = opts['alt']
|
170
|
+
if (alt = opts['alt'])
|
171
171
|
attrs << %{alt="#{alt}"}
|
172
172
|
end
|
173
173
|
|
@@ -207,6 +207,7 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
207
207
|
# nil if it is not.
|
208
208
|
def process_external_link_tag(tag)
|
209
209
|
parts = tag.split('|')
|
210
|
+
parts.reverse! if @markup.reverse_links?
|
210
211
|
return if parts.size.zero?
|
211
212
|
if parts.size == 1
|
212
213
|
url = parts[0].strip
|
@@ -245,11 +246,11 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
245
246
|
|
246
247
|
name = parts[0].strip
|
247
248
|
path = parts[1] && parts[1].strip
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
249
|
+
if path && file = @markup.find_file(path)
|
250
|
+
path = ::File.join @markup.wiki.base_path, file.path
|
251
|
+
else
|
252
|
+
path = nil
|
253
|
+
end
|
253
254
|
|
254
255
|
if name && path && file
|
255
256
|
%{<a href="#{::File.join @markup.wiki.base_path, file.path}">#{name}</a>}
|
@@ -269,7 +270,7 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
269
270
|
# if it is not.
|
270
271
|
def process_page_link_tag(tag)
|
271
272
|
parts = tag.split('|')
|
272
|
-
parts.reverse! if @markup.
|
273
|
+
parts.reverse! if @markup.reverse_links?
|
273
274
|
|
274
275
|
name, page_name = *parts.compact.map(&:strip)
|
275
276
|
cname = @markup.wiki.page_class.cname(page_name || name)
|
@@ -313,7 +314,7 @@ class Gollum::Filter::Tags < Gollum::Filter
|
|
313
314
|
if page
|
314
315
|
return page
|
315
316
|
end
|
316
|
-
if pos = cname.index('#')
|
317
|
+
if (pos = cname.index('#'))
|
317
318
|
[@markup.wiki.page(cname[0...pos]), cname[pos..-1]]
|
318
319
|
end
|
319
320
|
end
|
@@ -78,22 +78,22 @@ class Gollum::Filter::TOC < Gollum::Filter
|
|
78
78
|
# Adds an entry to the TOC for the given header. The generated entry
|
79
79
|
# is a link to the given anchor name
|
80
80
|
def add_entry_to_toc(header, name)
|
81
|
-
@toc
|
82
|
-
tail
|
83
|
-
tail_level
|
81
|
+
@toc ||= Nokogiri::XML::DocumentFragment.parse('<div class="toc"><div class="toc-title">Table of Contents</div></div>')
|
82
|
+
tail ||= @toc.child
|
83
|
+
tail_level ||= 0
|
84
84
|
|
85
85
|
level = header.name.gsub(/[hH]/, '').to_i
|
86
86
|
|
87
87
|
while tail_level < level
|
88
|
-
node
|
89
|
-
tail
|
88
|
+
node = Nokogiri::XML::Node.new('ul', @doc)
|
89
|
+
tail = tail.add_child(node)
|
90
90
|
tail_level += 1
|
91
91
|
end
|
92
92
|
|
93
93
|
while tail_level > level
|
94
|
-
tail
|
94
|
+
tail = tail.parent
|
95
95
|
tail_level -= 1
|
96
|
-
|
96
|
+
end
|
97
97
|
node = Nokogiri::XML::Node.new('li', @doc)
|
98
98
|
# % -> %25 so anchors work on Firefox. See issue #475
|
99
99
|
node.add_child(%Q{<a href="##{name}">#{header.content}</a>})
|
@@ -16,8 +16,8 @@ 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 = Digest::SHA1.hexdigest(
|
20
|
-
@map[id] = { :style =>
|
19
|
+
id = Digest::SHA1.hexdigest(Regexp.last_match[2])
|
20
|
+
@map[id] = { :style => Regexp.last_match[1], :code => Regexp.last_match[2] }
|
21
21
|
id
|
22
22
|
end
|
23
23
|
end
|
@@ -44,7 +44,7 @@ class Gollum::Filter::WSD < Gollum::Filter
|
|
44
44
|
def render_wsd(code, style)
|
45
45
|
response = Net::HTTP.post_form(URI.parse(WSD_URL), 'style' => style, 'message' => code)
|
46
46
|
if response.body =~ /img: "(.+)"/
|
47
|
-
url = "//www.websequencediagrams.com/#{
|
47
|
+
url = "//www.websequencediagrams.com/#{Regexp.last_match[1]}"
|
48
48
|
"<img src=\"#{url}\" />"
|
49
49
|
else
|
50
50
|
puts response.body
|
@@ -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
|
[]
|
@@ -74,10 +74,10 @@ module Gollum
|
|
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)
|
80
|
+
if (cm = commit!(ref))
|
81
81
|
set_cache(:ref, ref, cm.id)
|
82
82
|
set_cache(:commit, cm.id, cm)
|
83
83
|
end
|
@@ -164,7 +164,7 @@ module Gollum
|
|
164
164
|
items << BlobEntry.new(entry[:sha], entry[:path], entry[:size], entry[:mode].to_i(8))
|
165
165
|
end
|
166
166
|
end
|
167
|
-
if dir = @page_file_dir
|
167
|
+
if (dir = @page_file_dir)
|
168
168
|
regex = /^#{dir}\//
|
169
169
|
items.select { |i| i.path =~ regex }
|
170
170
|
else
|
@@ -226,7 +226,7 @@ module Gollum
|
|
226
226
|
#
|
227
227
|
# Returns an Array of BlobEntry instances.
|
228
228
|
def parse_tree_line(line)
|
229
|
-
mode,
|
229
|
+
mode, _type, sha, size, *name = line.split(/\s+/)
|
230
230
|
BlobEntry.new(sha, name.join(' '), size.to_i, mode.to_i(8))
|
231
231
|
end
|
232
232
|
|
@@ -240,7 +240,7 @@ module Gollum
|
|
240
240
|
path = path[1...-1]
|
241
241
|
path.gsub!(/\\\d{3}/) { |m| m[1..-1].to_i(8).chr }
|
242
242
|
end
|
243
|
-
path.gsub!(/\\[rn"\\]/) { |m| eval(%("#{m
|
243
|
+
path.gsub!(/\\[rn"\\]/) { |m| eval(%("#{m}")) }
|
244
244
|
path
|
245
245
|
end
|
246
246
|
end
|