gollum-lib 4.0.3-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Gemfile +3 -0
- data/HISTORY.md +11 -0
- data/LICENSE +21 -0
- data/README.md +286 -0
- data/Rakefile +187 -0
- data/docs/sanitization.md +33 -0
- data/gemspec.rb +100 -0
- data/gollum-lib.gemspec +4 -0
- data/gollum-lib_java.gemspec +4 -0
- data/lib/gollum-lib.rb +64 -0
- data/lib/gollum-lib/blob_entry.rb +95 -0
- data/lib/gollum-lib/committer.rb +243 -0
- data/lib/gollum-lib/file.rb +158 -0
- data/lib/gollum-lib/file_view.rb +155 -0
- data/lib/gollum-lib/filter.rb +78 -0
- data/lib/gollum-lib/filter/code.rb +145 -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/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 +320 -0
- data/lib/gollum-lib/filter/toc.rb +109 -0
- data/lib/gollum-lib/filter/wsd.rb +54 -0
- data/lib/gollum-lib/git_access.rb +247 -0
- data/lib/gollum-lib/gitcode.rb +48 -0
- data/lib/gollum-lib/helpers.rb +13 -0
- 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/markup.rb +197 -0
- data/lib/gollum-lib/markups.rb +20 -0
- data/lib/gollum-lib/page.rb +491 -0
- data/lib/gollum-lib/pagination.rb +62 -0
- data/lib/gollum-lib/sanitization.rb +176 -0
- data/lib/gollum-lib/version.rb +5 -0
- data/lib/gollum-lib/wiki.rb +925 -0
- data/licenses/licenses.txt +6 -0
- metadata +410 -0
@@ -0,0 +1,109 @@
|
|
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 = nil
|
11
|
+
@anchor_names = {}
|
12
|
+
@current_ancestors = []
|
13
|
+
|
14
|
+
if @markup.sub_page && @markup.parent_page
|
15
|
+
@toc = @markup.parent_page.toc_data
|
16
|
+
else
|
17
|
+
@doc.css('h1,h2,h3,h4,h5,h6').each_with_index do |header, i|
|
18
|
+
next if header.content.empty?
|
19
|
+
# omit the first H1 (the page title) from the TOC if so configured
|
20
|
+
next if (i == 0 && header.name =~ /[Hh]1/) && @markup.wiki && @markup.wiki.h1_title
|
21
|
+
|
22
|
+
anchor_name = generate_anchor_name(header)
|
23
|
+
|
24
|
+
add_anchor_to_header header, anchor_name
|
25
|
+
add_entry_to_toc header, anchor_name
|
26
|
+
end
|
27
|
+
|
28
|
+
@toc = @toc.to_xml(@markup.to_xml_opts) if @toc != nil
|
29
|
+
data = @doc.to_xml(@markup.to_xml_opts)
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
@markup.toc = @toc
|
34
|
+
data.gsub("[[_TOC_]]") do
|
35
|
+
@toc.nil? ? '' : @toc
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Generates the anchor name from the given header element
|
42
|
+
# removing all non alphanumeric characters, replacing them
|
43
|
+
# with single dashes.
|
44
|
+
#
|
45
|
+
# Generates heading ancestry prefixing the headings
|
46
|
+
# ancestor names to the generated name.
|
47
|
+
#
|
48
|
+
# Prefixes duplicate anchors with an index
|
49
|
+
def generate_anchor_name(header)
|
50
|
+
name = header.content
|
51
|
+
level = header.name.gsub(/[hH]/, '').to_i
|
52
|
+
|
53
|
+
# normalize the header name
|
54
|
+
name.gsub!(/[^\d\w\u00C0-\u1FFF\u2C00-\uD7FF]/, "-")
|
55
|
+
name.gsub!(/-+/, "-")
|
56
|
+
name.gsub!(/^-/, "")
|
57
|
+
name.gsub!(/-$/, "")
|
58
|
+
name.downcase!
|
59
|
+
|
60
|
+
@current_ancestors[level - 1] = name
|
61
|
+
@current_ancestors = @current_ancestors.take(level)
|
62
|
+
anchor_name = @current_ancestors.compact.join("_")
|
63
|
+
|
64
|
+
# Ensure duplicate anchors have a unique prefix or the toc will break
|
65
|
+
index = increment_anchor_index(anchor_name)
|
66
|
+
anchor_name = "#{index}-#{anchor_name}" unless index.zero? # if the index is zero this name is unique
|
67
|
+
|
68
|
+
anchor_name
|
69
|
+
end
|
70
|
+
|
71
|
+
# Creates an anchor element with the given name and adds it before
|
72
|
+
# the given header element.
|
73
|
+
def add_anchor_to_header(header, name)
|
74
|
+
anchor_element = %Q(<a class="anchor" id="#{name}" href="##{name}"><i class="fa fa-link"></i></a>)
|
75
|
+
header.children.before anchor_element # Add anchor element before the header
|
76
|
+
end
|
77
|
+
|
78
|
+
# Adds an entry to the TOC for the given header. The generated entry
|
79
|
+
# is a link to the given anchor name
|
80
|
+
def add_entry_to_toc(header, name)
|
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
|
+
|
85
|
+
level = header.name.gsub(/[hH]/, '').to_i
|
86
|
+
|
87
|
+
while tail_level < level
|
88
|
+
node = Nokogiri::XML::Node.new('ul', @doc)
|
89
|
+
tail = tail.add_child(node)
|
90
|
+
tail_level += 1
|
91
|
+
end
|
92
|
+
|
93
|
+
while tail_level > level
|
94
|
+
tail = tail.parent
|
95
|
+
tail_level -= 1
|
96
|
+
end
|
97
|
+
node = Nokogiri::XML::Node.new('li', @doc)
|
98
|
+
# % -> %25 so anchors work on Firefox. See issue #475
|
99
|
+
node.add_child(%Q{<a href="##{name}">#{header.content}</a>})
|
100
|
+
tail.add_child(node)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Increments the number of anchors with the given name
|
104
|
+
# and returns the current index
|
105
|
+
def increment_anchor_index(name)
|
106
|
+
@anchor_names = {} if @anchor_names.nil?
|
107
|
+
@anchor_names[name].nil? ? @anchor_names[name] = 0 : @anchor_names[name] += 1
|
108
|
+
end
|
109
|
+
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($2)
|
20
|
+
@map[id] = { :style => $1, :code => $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/#{$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
|
@@ -0,0 +1,247 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
module Gollum
|
3
|
+
# Controls all access to the Git objects from Gollum. Extend this class to
|
4
|
+
# add custom caching for special cases.
|
5
|
+
class GitAccess
|
6
|
+
# Initializes the GitAccess instance.
|
7
|
+
#
|
8
|
+
# path - The String path to the Git repository that holds the
|
9
|
+
# Gollum site.
|
10
|
+
# page_file_dir - String the directory in which all page files reside
|
11
|
+
#
|
12
|
+
# Returns this instance.
|
13
|
+
def initialize(path, page_file_dir = nil, bare = nil)
|
14
|
+
@page_file_dir = page_file_dir
|
15
|
+
@path = path
|
16
|
+
@repo = Gollum::Git::Repo.new(path, { :is_bare => bare })
|
17
|
+
clear
|
18
|
+
end
|
19
|
+
|
20
|
+
# Public: Determines whether the Git repository exists on disk.
|
21
|
+
#
|
22
|
+
# Returns true if it exists, or false.
|
23
|
+
def exist?
|
24
|
+
@repo.git.exist?
|
25
|
+
end
|
26
|
+
|
27
|
+
# Public: Converts a given Git reference to a SHA, using the cache if
|
28
|
+
# available.
|
29
|
+
#
|
30
|
+
# ref - a String Git reference (ex: "master")
|
31
|
+
#
|
32
|
+
# Returns a String, or nil if the ref isn't found.
|
33
|
+
def ref_to_sha(ref)
|
34
|
+
ref = ref.to_s
|
35
|
+
return if ref.empty?
|
36
|
+
sha =
|
37
|
+
if sha?(ref)
|
38
|
+
ref
|
39
|
+
else
|
40
|
+
get_cache(:ref, ref) { ref_to_sha!(ref) }
|
41
|
+
end.to_s
|
42
|
+
sha.empty? ? nil : sha
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public: Gets a recursive list of Git blobs for the whole tree at the
|
46
|
+
# given commit.
|
47
|
+
#
|
48
|
+
# ref - A String Git reference or Git SHA to a commit.
|
49
|
+
#
|
50
|
+
# Returns an Array of BlobEntry instances.
|
51
|
+
def tree(ref)
|
52
|
+
if sha = ref_to_sha(ref)
|
53
|
+
get_cache(:tree, sha) { tree!(sha) }
|
54
|
+
else
|
55
|
+
[]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Public: Fetches the contents of the Git blob at the given SHA.
|
60
|
+
#
|
61
|
+
# sha - A String Git SHA.
|
62
|
+
#
|
63
|
+
# Returns the String content of the blob.
|
64
|
+
def blob(sha)
|
65
|
+
cat_file!(sha)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Public: Looks up the Git commit using the given Git SHA or ref.
|
69
|
+
#
|
70
|
+
# ref - A String Git SHA or ref.
|
71
|
+
#
|
72
|
+
# Returns a Gollum::Git::Commit.
|
73
|
+
def commit(ref)
|
74
|
+
if sha?(ref)
|
75
|
+
get_cache(:commit, ref) { commit!(ref) }
|
76
|
+
else
|
77
|
+
if sha = get_cache(:ref, ref)
|
78
|
+
commit(sha)
|
79
|
+
else
|
80
|
+
if cm = commit!(ref)
|
81
|
+
set_cache(:ref, ref, cm.id)
|
82
|
+
set_cache(:commit, cm.id, cm)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Public: Clears all of the cached data that this GitAccess is tracking.
|
89
|
+
#
|
90
|
+
# Returns nothing.
|
91
|
+
def clear
|
92
|
+
@ref_map = {}
|
93
|
+
@tree_map = {}
|
94
|
+
@commit_map = {}
|
95
|
+
end
|
96
|
+
|
97
|
+
# Public: Refreshes just the cached Git reference data. This should
|
98
|
+
# be called after every Gollum update.
|
99
|
+
#
|
100
|
+
# Returns nothing.
|
101
|
+
def refresh
|
102
|
+
@ref_map.clear
|
103
|
+
end
|
104
|
+
|
105
|
+
#########################################################################
|
106
|
+
#
|
107
|
+
# Internal Methods
|
108
|
+
#
|
109
|
+
#########################################################################
|
110
|
+
|
111
|
+
# Gets the String path to the Git repository.
|
112
|
+
attr_reader :path
|
113
|
+
|
114
|
+
# Gets the Gollum::Git::Repo instance for the Git repository.
|
115
|
+
attr_reader :repo
|
116
|
+
|
117
|
+
# Gets a Hash cache of refs to commit SHAs.
|
118
|
+
#
|
119
|
+
# {"master" => "abc123", ...}
|
120
|
+
#
|
121
|
+
attr_reader :ref_map
|
122
|
+
|
123
|
+
# Gets a Hash cache of commit SHAs to a recursive tree of blobs.
|
124
|
+
#
|
125
|
+
# {"abc123" => [<BlobEntry>, <BlobEntry>]}
|
126
|
+
#
|
127
|
+
attr_reader :tree_map
|
128
|
+
|
129
|
+
# Gets a Hash cache of commit SHAs to the Gollum::Git::Commit instance.
|
130
|
+
#
|
131
|
+
# {"abcd123" => <Gollum::Git::Commit>}
|
132
|
+
#
|
133
|
+
attr_reader :commit_map
|
134
|
+
|
135
|
+
# Checks to see if the given String is a 40 character hex SHA.
|
136
|
+
#
|
137
|
+
# str - Possible String SHA.
|
138
|
+
#
|
139
|
+
# Returns true if the String is a SHA, or false.
|
140
|
+
def sha?(str)
|
141
|
+
!!(str =~ /^[0-9a-f]{40}$/)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Looks up the Git SHA for the given Git ref.
|
145
|
+
#
|
146
|
+
# ref - String Git ref.
|
147
|
+
#
|
148
|
+
# Returns a String SHA.
|
149
|
+
def ref_to_sha!(ref)
|
150
|
+
commit = @repo.commit(ref)
|
151
|
+
commit ? commit.id : nil
|
152
|
+
end
|
153
|
+
|
154
|
+
# Looks up the Git blobs for a given commit.
|
155
|
+
#
|
156
|
+
# sha - String commit SHA.
|
157
|
+
#
|
158
|
+
# Returns an Array of BlobEntry instances.
|
159
|
+
def tree!(sha)
|
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
|
166
|
+
end
|
167
|
+
if dir = @page_file_dir
|
168
|
+
regex = /^#{dir}\//
|
169
|
+
items.select { |i| i.path =~ regex }
|
170
|
+
else
|
171
|
+
items
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Reads the content from the Git db at the given SHA.
|
176
|
+
#
|
177
|
+
# sha - The String SHA.
|
178
|
+
#
|
179
|
+
# Returns the String content of the Git object.
|
180
|
+
def cat_file!(sha)
|
181
|
+
@repo.git.cat_file({ :p => true }, sha)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Reads a Git commit.
|
185
|
+
#
|
186
|
+
# sha - The string SHA of the Git commit.
|
187
|
+
#
|
188
|
+
# Returns a Gollum::Git::Commit.
|
189
|
+
def commit!(sha)
|
190
|
+
@repo.commit(sha)
|
191
|
+
end
|
192
|
+
|
193
|
+
# Attempts to get the given data from a cache. If it doesn't exist, it'll
|
194
|
+
# pass the results of the yielded block to the cache for future accesses.
|
195
|
+
#
|
196
|
+
# name - The cache prefix used in building the full cache key.
|
197
|
+
# key - The unique cache key suffix, usually a String Git SHA.
|
198
|
+
#
|
199
|
+
# Yields a block to pass to the cache.
|
200
|
+
# Returns the cached result.
|
201
|
+
def get_cache(name, key)
|
202
|
+
cache = instance_variable_get("@#{name}_map")
|
203
|
+
value = cache[key]
|
204
|
+
if value.nil? && block_given?
|
205
|
+
set_cache(name, key, value = yield)
|
206
|
+
end
|
207
|
+
value == :_nil ? nil : value
|
208
|
+
end
|
209
|
+
|
210
|
+
# Writes some data to the internal cache.
|
211
|
+
#
|
212
|
+
# name - The cache prefix used in building the full cache key.
|
213
|
+
# key - The unique cache key suffix, usually a String Git SHA.
|
214
|
+
# value - The value to write to the cache.
|
215
|
+
#
|
216
|
+
# Returns nothing.
|
217
|
+
def set_cache(name, key, value)
|
218
|
+
cache = instance_variable_get("@#{name}_map")
|
219
|
+
cache[key] = value || :_nil
|
220
|
+
end
|
221
|
+
|
222
|
+
# Parses a line of output from the `ls-tree` command.
|
223
|
+
#
|
224
|
+
# line - A String line of output:
|
225
|
+
# "100644 blob 839c2291b30495b9a882c17d08254d3c90d8fb53 Home.md"
|
226
|
+
#
|
227
|
+
# Returns an Array of BlobEntry instances.
|
228
|
+
def parse_tree_line(line)
|
229
|
+
mode, type, sha, size, *name = line.split(/\s+/)
|
230
|
+
BlobEntry.new(sha, name.join(' '), size.to_i, mode.to_i(8))
|
231
|
+
end
|
232
|
+
|
233
|
+
# Decode octal sequences (\NNN) in tree path names.
|
234
|
+
#
|
235
|
+
# path - String path name.
|
236
|
+
#
|
237
|
+
# Returns a decoded String.
|
238
|
+
def decode_git_path(path)
|
239
|
+
if path[0] == ?" && path[-1] == ?"
|
240
|
+
path = path[1...-1]
|
241
|
+
path.gsub!(/\\\d{3}/) { |m| m[1..-1].to_i(8).chr }
|
242
|
+
end
|
243
|
+
path.gsub!(/\\[rn"\\]/) { |m| eval(%("#{m.to_s}")) }
|
244
|
+
path
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
require 'net/http'
|
3
|
+
require 'net/https' # ruby 1.8.7 fix, remove at upgrade
|
4
|
+
require 'uri'
|
5
|
+
require 'open-uri'
|
6
|
+
|
7
|
+
module Gollum
|
8
|
+
class Gitcode
|
9
|
+
def initialize path
|
10
|
+
raise(ArgumentError, 'path is nil or empty') if path.nil? or path.empty?
|
11
|
+
|
12
|
+
@uri = URI::HTTP.build({
|
13
|
+
:path => self.unchomp(path),
|
14
|
+
:host => 'raw.github.com',
|
15
|
+
:scheme => 'https',
|
16
|
+
:port => 443 })
|
17
|
+
end
|
18
|
+
|
19
|
+
def contents
|
20
|
+
@contents ||= self.req @uri
|
21
|
+
end
|
22
|
+
|
23
|
+
def unchomp p
|
24
|
+
return p if p.nil?
|
25
|
+
p[0] == '/' ? p : ('/' + p)
|
26
|
+
end
|
27
|
+
|
28
|
+
def req uri, cut = 1
|
29
|
+
return "Too many redirects or retries" if cut >= 10
|
30
|
+
http = Net::HTTP.new uri.host, uri.port
|
31
|
+
http.use_ssl = true
|
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
|
+
}
|
39
|
+
code = resp.code.to_i
|
40
|
+
return resp.body if code == 200
|
41
|
+
return "Not Found" if code == 404
|
42
|
+
return "Unhandled Response Code #{code}" unless code == 304 or not resp.header['location'].nil?
|
43
|
+
loc = URI.parse resp.header['location']
|
44
|
+
uri2 = loc.relative?() ? (uri + loc) : loc # overloads (+)
|
45
|
+
return req uri2, (cut + 1)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|