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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -3
  3. data/HISTORY.md +25 -0
  4. data/LICENSE +1 -1
  5. data/README.md +24 -312
  6. data/Rakefile +32 -16
  7. data/gemspec.rb +110 -0
  8. data/gollum-lib.gemspec +8 -75
  9. data/gollum-lib_java.gemspec +4 -0
  10. data/lib/gollum-lib.rb +18 -6
  11. data/lib/gollum-lib/blob_entry.rb +10 -9
  12. data/lib/gollum-lib/committer.rb +37 -30
  13. data/lib/gollum-lib/file.rb +71 -15
  14. data/lib/gollum-lib/file_view.rb +53 -48
  15. data/lib/gollum-lib/filter.rb +78 -0
  16. data/lib/gollum-lib/filter/code.rb +145 -0
  17. data/lib/gollum-lib/filter/emoji.rb +39 -0
  18. data/lib/gollum-lib/filter/macro.rb +57 -0
  19. data/lib/gollum-lib/filter/metadata.rb +29 -0
  20. data/lib/gollum-lib/filter/plain_text.rb +16 -0
  21. data/lib/gollum-lib/filter/plantuml.rb +176 -0
  22. data/lib/gollum-lib/filter/remote_code.rb +63 -0
  23. data/lib/gollum-lib/filter/render.rb +20 -0
  24. data/lib/gollum-lib/filter/sanitize.rb +18 -0
  25. data/lib/gollum-lib/filter/tags.rb +327 -0
  26. data/lib/gollum-lib/filter/toc.rb +134 -0
  27. data/lib/gollum-lib/filter/wsd.rb +54 -0
  28. data/lib/gollum-lib/git_access.rb +30 -32
  29. data/lib/gollum-lib/gitcode.rb +16 -16
  30. data/lib/gollum-lib/helpers.rb +3 -3
  31. data/lib/gollum-lib/hook.rb +35 -0
  32. data/lib/gollum-lib/macro.rb +43 -0
  33. data/lib/gollum-lib/macro/all_pages.rb +11 -0
  34. data/lib/gollum-lib/macro/global_toc.rb +12 -0
  35. data/lib/gollum-lib/macro/navigation.rb +20 -0
  36. data/lib/gollum-lib/macro/series.rb +48 -0
  37. data/lib/gollum-lib/markup.rb +95 -572
  38. data/lib/gollum-lib/markups.rb +9 -3
  39. data/lib/gollum-lib/page.rb +109 -80
  40. data/lib/gollum-lib/pagination.rb +1 -1
  41. data/lib/gollum-lib/sanitization.rb +75 -75
  42. data/lib/gollum-lib/version.rb +5 -0
  43. data/lib/gollum-lib/wiki.rb +287 -129
  44. metadata +237 -92
  45. data/CHANGELOG +0 -2
  46. data/VERSION +0 -1
  47. data/lib/gollum-lib/grit_ext.rb +0 -20
  48. data/lib/gollum-lib/remote_code.rb +0 -39
  49. 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 = false)
13
+ def initialize(path, page_file_dir = nil, bare = nil)
14
14
  @page_file_dir = page_file_dir
15
- @path = path
16
- @repo = Grit::Repo.new(path, { :is_bare => bare })
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
- if sha?(ref)
38
- ref
39
- else
40
- get_cache(:ref, ref) { ref_to_sha!(ref) }
41
- end.to_s
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 Grit::Commit.
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, ref, cm.id)
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 Grit::Repo instance for the Git repository.
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 Grit::Commit instance.
129
+ # Gets a Hash cache of commit SHAs to the Gollum::Git::Commit instance.
130
130
  #
131
- # {"abcd123" => <Grit::Commit>}
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.git.rev_list({:max_count=>1}, ref)
151
- rescue Grit::GitRuby::Repository::NoSuchShaFound
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.git.native(:ls_tree,
161
- {:r => true, :l => true, :z => true}, sha)
162
- if tree.respond_to?(:force_encoding)
163
- tree.force_encoding("UTF-8")
164
- end
165
- items = tree.split("\0").inject([]) do |memo, line|
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 Grit::Commit.
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, type, sha, size, *name = line.split(/\s+/)
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}/) { |m| m[1..-1].to_i(8).chr }
241
+ path.gsub!(/\\\d{3}/) { |m| m[1..-1].to_i(8).chr }
244
242
  end
245
- path.gsub!(/\\[rn"\\]/) { |m| eval(%("#{m.to_s}")) }
243
+ path.gsub!(/\\[rn"\\]/) { |m| eval(%("#{m}")) }
246
244
  path
247
245
  end
248
246
  end
@@ -6,41 +6,41 @@ require 'open-uri'
6
6
 
7
7
  module Gollum
8
8
  class Gitcode
9
- def initialize path
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
- :path => self.unchomp(path),
14
- :host => 'raw.github.com',
15
- :scheme => 'https',
16
- :port => 443 })
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 p
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 uri, cut = 1
28
+ def req(uri, cut = 1)
29
29
  return "Too many redirects or retries" if cut >= 10
30
- http = Net::HTTP.new uri.host, uri.port
30
+ http = Net::HTTP.new uri.host, uri.port
31
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'
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 = resp.code.to_i
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 = URI.parse resp.header['location']
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
@@ -2,10 +2,10 @@
2
2
  module Gollum
3
3
  module Helpers
4
4
 
5
- def trim_leading_slash url
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