gollum-lib 5.0.a.4-java → 5.0.5-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -1
  3. data/HISTORY.md +4 -0
  4. data/README.md +12 -7
  5. data/Rakefile +8 -7
  6. data/gemspec.rb +17 -10
  7. data/gollum-lib.gemspec +8 -6
  8. data/gollum-lib_java.gemspec +2 -2
  9. data/lib/gollum-lib.rb +9 -9
  10. data/lib/gollum-lib/blob_entry.rb +2 -8
  11. data/lib/gollum-lib/committer.rb +23 -61
  12. data/lib/gollum-lib/file.rb +105 -82
  13. data/lib/gollum-lib/file_view.rb +8 -4
  14. data/lib/gollum-lib/filter.rb +12 -0
  15. data/lib/gollum-lib/filter/code.rb +9 -13
  16. data/lib/gollum-lib/filter/critic_markup.rb +97 -0
  17. data/lib/gollum-lib/filter/emoji.rb +11 -8
  18. data/lib/gollum-lib/filter/macro.rb +5 -2
  19. data/lib/gollum-lib/filter/plantuml.rb +1 -1
  20. data/lib/gollum-lib/filter/remote_code.rb +3 -2
  21. data/lib/gollum-lib/filter/render.rb +25 -2
  22. data/lib/gollum-lib/filter/sanitize.rb +1 -8
  23. data/lib/gollum-lib/filter/tags.rb +57 -46
  24. data/lib/gollum-lib/filter/toc.rb +17 -21
  25. data/lib/gollum-lib/filter/yaml.rb +1 -1
  26. data/lib/gollum-lib/git_access.rb +0 -25
  27. data/lib/gollum-lib/helpers.rb +13 -3
  28. data/lib/gollum-lib/macro.rb +4 -0
  29. data/lib/gollum-lib/macro/audio.rb +9 -0
  30. data/lib/gollum-lib/macro/global_toc.rb +2 -1
  31. data/lib/gollum-lib/macro/navigation.rb +8 -6
  32. data/lib/gollum-lib/macro/note.rb +19 -0
  33. data/lib/gollum-lib/macro/octicon.rb +12 -0
  34. data/lib/gollum-lib/macro/series.rb +2 -2
  35. data/lib/gollum-lib/macro/warn.rb +11 -0
  36. data/lib/gollum-lib/markup.rb +17 -32
  37. data/lib/gollum-lib/markups.rb +13 -8
  38. data/lib/gollum-lib/page.rb +80 -166
  39. data/lib/gollum-lib/pagination.rb +7 -6
  40. data/lib/gollum-lib/redirects.rb +42 -0
  41. data/lib/gollum-lib/sanitization.rb +32 -357
  42. data/lib/gollum-lib/version.rb +1 -1
  43. data/lib/gollum-lib/wiki.rb +216 -404
  44. metadata +65 -27
  45. data/ROADMAP +0 -6
@@ -14,17 +14,11 @@ class Gollum::Filter::Emoji < Gollum::Filter
14
14
  (?!\]{^2})
15
15
  }ix
16
16
 
17
- PROCESS_PATTERN = %r{
18
- =EEMMOOJJII=
19
- (?<name>[\w-]+)
20
- =IIJJOOMMEE=
21
- }ix
22
-
23
17
  def extract(data)
24
18
  data.gsub! EXTRACT_PATTERN do
25
19
  case
26
20
  when $~[:escape] then $&[1..-1]
27
- when emoji_exists?($~[:name]) then "=EEMMOOJJII=#{$~[:name]}=IIJJOOMMEE="
21
+ when emoji_exists?($~[:name]) then "#{open_pattern}#{$~[:name]}#{close_pattern}"
28
22
  else $&
29
23
  end
30
24
  end
@@ -32,12 +26,21 @@ class Gollum::Filter::Emoji < Gollum::Filter
32
26
  end
33
27
 
34
28
  def process(data)
35
- data.gsub! PROCESS_PATTERN, %q(<img src="/emoji/\k<name>" alt="\k<name>" class="emoji">)
29
+ src = ::File.join(@markup.wiki.base_path, '/gollum/emoji/\\k<name>')
30
+ data.gsub! process_pattern, %Q(<img src="#{src}" alt="\\k<name>" class="emoji">)
36
31
  data
37
32
  end
38
33
 
39
34
  private
40
35
 
36
+ def process_pattern
37
+ %r{
38
+ #{open_pattern}
39
+ (?<name>[\w-]+)
40
+ #{close_pattern}
41
+ }ix
42
+ end
43
+
41
44
  def emoji_exists?(name)
42
45
  @index ||= Gemojione::Index.new
43
46
  !!@index.find_by_name(name)
@@ -1,4 +1,5 @@
1
1
  # ~*~ encoding: utf-8 ~*~
2
+ require 'octicons'
2
3
 
3
4
  # Replace specified tokens with dynamically generated content.
4
5
  class Gollum::Filter::Macro < Gollum::Filter
@@ -12,7 +13,7 @@ class Gollum::Filter::Macro < Gollum::Filter
12
13
 
13
14
  data.gsub(/('?)\<\<\s*([A-Z][A-Za-z0-9]*)\s*\(#{arg_list}\)\s*\>\>/) do
14
15
  next CGI.escape_html($&[1..-1]) unless Regexp.last_match[1].empty?
15
- id = Digest::SHA1.hexdigest(Regexp.last_match[2] + Regexp.last_match[3])
16
+ id = "#{open_pattern}#{Digest::SHA1.hexdigest(Regexp.last_match[2] + Regexp.last_match[3])}#{close_pattern}"
16
17
  macro = Regexp.last_match[2]
17
18
  argstr = Regexp.last_match[3]
18
19
  args = []
@@ -47,7 +48,9 @@ class Gollum::Filter::Macro < Gollum::Filter
47
48
  begin
48
49
  Gollum::Macro.instance(macro, @markup.wiki, @markup.page).render(*args)
49
50
  rescue StandardError => e
50
- "!!!Macro Error: #{e.message}!!!"
51
+ icon = Octicons::Octicon.new('zap', {width: 24, height: 24})
52
+ icon.options[:class] << ' mr-2'
53
+ "<div class='flash flash-error'>#{icon.to_svg}Macro Error for #{macro}: #{e.message}</div>"
51
54
  end
52
55
  end
53
56
  end
@@ -70,7 +70,7 @@ class Gollum::Filter::PlantUML < Gollum::Filter
70
70
  # placeholders.
71
71
  def extract(data)
72
72
  data.gsub(/(@startuml\r?\n.+?\r?\n@enduml\r?$)/m) do
73
- id = Digest::SHA1.hexdigest($1)
73
+ id = "#{open_pattern}#{Digest::SHA1.hexdigest($1)}#{close_pattern}"
74
74
  @map[id] = { :code => $1 }
75
75
  id
76
76
  end
@@ -20,7 +20,7 @@ class Gollum::Filter::RemoteCode < Gollum::Filter
20
20
 
21
21
  # Detect local file
22
22
  if protocol.nil?
23
- if (file = @markup.find_file(uri, @markup.wiki.ref))
23
+ if (file = @markup.wiki.file(uri, @markup.wiki.ref))
24
24
  contents = file.raw_data
25
25
  else
26
26
  # How do we communicate a render error?
@@ -45,7 +45,8 @@ class Gollum::Filter::RemoteCode < Gollum::Filter
45
45
  return "Too many redirects or retries" if cut >= 10
46
46
  http = Net::HTTP.new uri.host, uri.port
47
47
  http.use_ssl = true
48
- resp = http.get uri.path, {
48
+ path = uri.path.empty? ? '/' : uri.path
49
+ resp = http.get path, {
49
50
  'Accept' => 'text/plain',
50
51
  'Cache-Control' => 'no-cache',
51
52
  'Connection' => 'keep-alive',
@@ -3,7 +3,11 @@
3
3
  class Gollum::Filter::Render < Gollum::Filter
4
4
  def extract(data)
5
5
  begin
6
- data = GitHub::Markup.render(@markup.name, data)
6
+ working_dir = Pathname.new(@markup.wiki.path).join(@markup.dir)
7
+ working_dir = working_dir.exist? ? working_dir.to_s : '.'
8
+ Dir.chdir(working_dir) do
9
+ data = GitHub::Markup.render_s(@markup.format, data)
10
+ end
7
11
  if data.nil?
8
12
  raise "There was an error converting #{@markup.name} to HTML."
9
13
  end
@@ -15,6 +19,25 @@ class Gollum::Filter::Render < Gollum::Filter
15
19
  end
16
20
 
17
21
  def process(data)
22
+ data = add_editable_header_class(data)
18
23
  data
19
24
  end
20
- end
25
+
26
+ private
27
+
28
+ def add_editable_header_class(data)
29
+ doc = Nokogiri::HTML::DocumentFragment.parse(data)
30
+ doc.css('h1,h2,h3,h4,h5,h6').each_with_index do |header, i|
31
+ next if header.content.empty?
32
+ next if header.inner_html.match(PLACEHOLDER_PATTERN)
33
+ klass = header['class']
34
+ if klass
35
+ header['class'] = klass << ' editable'
36
+ else
37
+ header['class'] = 'editable'
38
+ end
39
+ end
40
+ doc.to_xml(@markup.class.to_xml_opts)
41
+ end
42
+
43
+ end
@@ -6,13 +6,6 @@ class Gollum::Filter::Sanitize < Gollum::Filter
6
6
  end
7
7
 
8
8
  def process(data)
9
- if @markup.sanitize
10
- doc = Nokogiri::HTML::DocumentFragment.parse(data)
11
- doc = @markup.sanitize.clean_node!(doc)
12
-
13
- doc.to_xml(@markup.to_xml_opts).gsub(/<p><\/p>/, '')
14
- else
15
- data
16
- end
9
+ sanitize(data)
17
10
  end
18
11
  end
@@ -34,7 +34,7 @@ class Gollum::Filter::Tags < Gollum::Filter
34
34
  doc.traverse do |node|
35
35
  if node.text? then
36
36
  content = node.content
37
- content.gsub!(/TAG[a-f0-9]+TAG/) do |id|
37
+ content.gsub!(%r{#{open_pattern}[a-f0-9]+#{close_pattern}}) do |id|
38
38
  if (tag = @map[id]) then
39
39
  if is_preformatted?(node) then
40
40
  "[[#{tag}]]"
@@ -56,7 +56,7 @@ class Gollum::Filter::Tags < Gollum::Filter
56
56
  INCLUDE_TAG = 'include:'
57
57
 
58
58
  def register_tag(tag)
59
- id = "TAG#{Digest::SHA1.hexdigest(tag)}TAG"
59
+ id = "#{open_pattern}#{Digest::SHA1.hexdigest(tag)}#{close_pattern}"
60
60
  @map[id] = tag
61
61
  id
62
62
  end
@@ -77,7 +77,6 @@ class Gollum::Filter::Tags < Gollum::Filter
77
77
  return generate_link('', nil, nil, :page_absent) if link_part.nil?
78
78
  img_args = extra ? [extra, link_part] : [link_part]
79
79
  mime = MIME::Types.type_for(::File.extname(img_args.first.to_s)).first
80
-
81
80
  result = if tag =~ /^_TOC_/
82
81
  %{[[#{tag}]]}
83
82
  elsif link_part =~ /^_$/
@@ -122,9 +121,9 @@ class Gollum::Filter::Tags < Gollum::Filter
122
121
  len = INCLUDE_TAG.length
123
122
  return html_error('Cannot process include directive: no page name given') if tag.length <= len
124
123
  page_name = tag[len..-1]
125
- resolved_page_name = ::File.expand_path(page_name, "#{::File::SEPARATOR}#{@markup.dir}")
124
+ resolved_page_name = ::File.join(@markup.dir, page_name)
126
125
  if @markup.include_levels > 0
127
- page = find_page_from_path(resolved_page_name)
126
+ page = find_page_or_file_from_path(resolved_page_name)
128
127
  if page
129
128
  page.formatted_data(@markup.encoding, @markup.include_levels-1)
130
129
  else
@@ -146,8 +145,8 @@ class Gollum::Filter::Tags < Gollum::Filter
146
145
  opts = parse_image_tag_options(options)
147
146
  if path =~ /^https?:\/\/.+$/i
148
147
  generate_image(path, opts)
149
- elsif file = @markup.find_file(path)
150
- generate_image(generate_href_for_path(file.path), opts)
148
+ elsif file = find_page_or_file_from_path(path, :file)
149
+ generate_image(generate_href_for_path(file.url_path), opts)
151
150
  else
152
151
  generate_image('', opts)
153
152
  end
@@ -173,14 +172,8 @@ class Gollum::Filter::Tags < Gollum::Filter
173
172
  # Return the String HTML if the tag is a valid external link tag or
174
173
  # nil if it is not.
175
174
  def process_external_link_tag(url, pretty_name = nil)
176
- accepted_protocols = @markup.wiki.sanitization.protocols['a']['href'].dup
177
- if accepted_protocols.include?(:relative)
178
- accepted_protocols.select!{|protocol| protocol != :relative}
179
- regexp = %r{^((#{accepted_protocols.join("|")}):)?(//)}
180
- else
181
- regexp = %r{^((#{accepted_protocols.join("|")}):)}
182
- end
183
- if url =~ regexp
175
+ @accepted_protocols_regex ||= %r{^((#{::Gollum::Sanitization.accepted_protocols.join('|')}):)?(//)}
176
+ if url =~ @accepted_protocols_regex
184
177
  generate_link(url, pretty_name, nil, :external)
185
178
  else
186
179
  nil
@@ -195,8 +188,9 @@ class Gollum::Filter::Tags < Gollum::Filter
195
188
  # Returns the String HTML if the tag is a valid file link tag or nil
196
189
  # if it is not.
197
190
  def process_file_link_tag(link_part, pretty_name)
198
- if file = @markup.find_file(link_part)
199
- generate_link(file.path, pretty_name, nil, :file)
191
+ return nil if ::Gollum::Page.valid_extension?(link_part)
192
+ if file = find_page_or_file_from_path(link_part, :file)
193
+ generate_link(file.url_path, pretty_name, nil, :file)
200
194
  else
201
195
  nil
202
196
  end
@@ -212,18 +206,32 @@ class Gollum::Filter::Tags < Gollum::Filter
212
206
  def process_page_link_tag(link_part, pretty_name = nil)
213
207
  presence = :page_absent
214
208
  link = link_part
215
- page = find_page_from_path(link)
209
+ page = find_page_or_file_from_path(link)
216
210
 
217
211
  # If no match yet, try finding page with anchor removed
218
- if (page.nil? && pos = link.rindex('#'))
219
- extra = link[pos..-1]
220
- link = link[0...pos]
221
- page = find_page_from_path(link)
212
+ if page.nil?
213
+ if pos = link.rindex('#')
214
+ extra = link[pos..-1]
215
+ link = link[0...pos]
216
+ else
217
+ extra = nil
218
+ end
219
+
220
+ if link.empty? && extra # Internal anchor link, don't search for the page but return immediately
221
+ return generate_link(nil, pretty_name, extra, :internal_anchor)
222
+ end
223
+
224
+ page = find_page_or_file_from_path(link)
225
+ end
226
+ presence = :page_present if page
227
+
228
+ if pretty_name
229
+ name = pretty_name
230
+ else
231
+ name = page ? path_to_link_text(link) : link
222
232
  end
223
- presence = :page_present if page
224
233
 
225
- name = pretty_name ? pretty_name : link
226
- link = page ? page.escaped_url_path : CGI.escape(link)
234
+ link = page ? page.escaped_url_path : ERB::Util.url_encode(link).force_encoding('utf-8')
227
235
  generate_link(link, name, extra, presence)
228
236
  end
229
237
 
@@ -232,15 +240,15 @@ class Gollum::Filter::Tags < Gollum::Filter
232
240
  # path - The String path to search for.
233
241
  #
234
242
  # Returns a Gollum::Page instance if a page is found, or nil otherwise
235
- def find_page_from_path(path)
236
- slash = path.rindex('/')
237
-
238
- unless slash.nil?
239
- name = path[slash+1..-1]
240
- path = path[0..slash]
241
- @markup.wiki.paged(name, path)
243
+ def find_page_or_file_from_path(path, kind = :page)
244
+ if Pathname.new(path).relative?
245
+ result = @markup.wiki.send(kind, ::File.join(@markup.dir, path))
246
+ if result.nil? && @markup.wiki.global_tag_lookup # 4.x link compatibility option. Slow!
247
+ result = @markup.wiki.send(kind, path, nil, true)
248
+ end
249
+ result
242
250
  else
243
- @markup.wiki.page(path)
251
+ @markup.wiki.send(kind, path)
244
252
  end
245
253
  end
246
254
 
@@ -264,7 +272,8 @@ class Gollum::Filter::Tags < Gollum::Filter
264
272
  #
265
273
  # Returns a String href.
266
274
  def generate_href_for_path(path, extra = nil)
267
- "#{trim_leading_slash(::File.join(@markup.wiki.base_path, path))}#{extra}"
275
+ return extra if !path && extra # Internal anchor link
276
+ "#{trim_leading_slashes(::File.join(@markup.wiki.base_path, path))}#{extra}"
268
277
  end
269
278
 
270
279
  # Construct a CSS class and attribute string for different kinds of links: internal Pages (absent or present) and Files, and External links.
@@ -278,6 +287,8 @@ class Gollum::Filter::Tags < Gollum::Filter
278
287
  'class="internal absent"'
279
288
  when :page_present
280
289
  'class="internal present"'
290
+ when :internal_anchor
291
+ 'class="internal anchorlink"'
281
292
  when :file
282
293
  nil
283
294
  when :external
@@ -299,10 +310,10 @@ class Gollum::Filter::Tags < Gollum::Filter
299
310
  attr_string = attrs.map {|key, value| "#{key}=\"#{value}\""}.join(' ')
300
311
 
301
312
  if containered
302
- %{<span class="#{classes.join(' ')}">} +
303
- %{<span>} +
313
+ %{<span class="d-flex #{classes[:container].join(' ')}">} +
314
+ %{<span class="#{classes[:nested].join(' ')}">} +
304
315
  %{<img src="#{path}" #{attr_string}/>} +
305
- (attrs[:alt] ? %{<span>#{attrs[:alt]}</span>} : '') +
316
+ (options[:frame] && attrs[:alt] ? %{<span class="clearfix">#{attrs[:alt]}</span>} : '') +
306
317
  %{</span>} +
307
318
  %{</span>}
308
319
  else
@@ -314,31 +325,31 @@ class Gollum::Filter::Tags < Gollum::Filter
314
325
  #
315
326
  # options - The Hash of parsed image options.
316
327
  #
317
- # Returns an Array of CSS classes, a Hash of CSS attributes, and a Boolean indicating whether or not the image is containered.
328
+ # Returns a Hash containing CSS class Arrays, a Hash of CSS attributes, and a Boolean indicating whether or not the image is containered.
318
329
  def generate_image_attributes(options)
319
330
  containered = false
320
- classes = [] # applied to whatever the outermost container is
331
+ classes = {container: [], nested: []} # applied to the container(s)
321
332
  attrs = {} # applied to the image
322
333
 
323
334
  align = options[:align]
324
335
  if options[:float]
325
336
  containered = true
326
- align ||= 'left'
327
- if %w{left right}.include?(align)
328
- classes << "float-#{align}"
329
- end
337
+ align = 'left' unless align == 'right'
338
+ classes[:container] << "float-#{align} pb-4"
330
339
  elsif %w{top texttop middle absmiddle bottom absbottom baseline}.include?(align)
331
340
  attrs[:align] = align
332
341
  elsif align
333
342
  if %w{left center right}.include?(align)
334
343
  containered = true
335
- classes << "align-#{align}"
344
+ text_align = "text-#{align}"
345
+ align = 'end' if align == 'right'
346
+ classes[:container] << "flex-justify-#{align} #{text_align}"
336
347
  end
337
348
  end
338
349
 
339
- if options[:frame]
350
+ if options[:frame]
340
351
  containered = true
341
- classes << 'frame'
352
+ classes[:nested] << 'border p-4'
342
353
  end
343
354
 
344
355
  attrs[:alt] = options[:alt] if options[:alt]
@@ -21,15 +21,14 @@ class Gollum::Filter::TOC < Gollum::Filter
21
21
  next if (i == 0 && header.name =~ /[Hh]1/) && @markup.wiki && @markup.wiki.h1_title
22
22
 
23
23
  anchor_name = generate_anchor_name(header)
24
-
25
24
  add_anchor_to_header header, anchor_name
26
25
  add_entry_to_toc header, anchor_name
27
26
  end
28
27
  if not @toc_doc.nil?
29
- toc_str = @toc_doc.to_xml(@markup.to_xml_opts)
28
+ toc_str = @toc_doc.to_xml(@markup.class.to_xml_opts)
30
29
  end
31
30
 
32
- data = @doc.to_xml(@markup.to_xml_opts)
31
+ data = @doc.to_xml(@markup.class.to_xml_opts)
33
32
  end
34
33
 
35
34
  @markup.toc = toc_str
@@ -51,7 +50,7 @@ class Gollum::Filter::TOC < Gollum::Filter
51
50
  e.remove
52
51
  end
53
52
  end
54
- toc_clone.to_xml(@markup.to_xml_opts)
53
+ toc_clone.to_xml(@markup.class.to_xml_opts)
55
54
  end
56
55
  end
57
56
 
@@ -60,9 +59,9 @@ class Gollum::Filter::TOC < Gollum::Filter
60
59
 
61
60
  private
62
61
 
63
- # Generates the anchor name from the given header element
62
+ # Generates the anchor name from the given header element
64
63
  # removing all non alphanumeric characters, replacing them
65
- # with single dashes.
64
+ # with single dashes.
66
65
  #
67
66
  # Generates heading ancestry prefixing the headings
68
67
  # ancestor names to the generated name.
@@ -70,31 +69,28 @@ class Gollum::Filter::TOC < Gollum::Filter
70
69
  # Prefixes duplicate anchors with an index
71
70
  def generate_anchor_name(header)
72
71
  name = header.content
73
- level = header.name.gsub(/[hH]/, '').to_i
72
+ level = header.name[1..-1].to_i
74
73
 
75
74
  # normalize the header name
76
- name.gsub!(/[^\d\w\u00C0-\u1FFF\u2C00-\uD7FF]/, "-")
77
- name.gsub!(/-+/, "-")
78
- name.gsub!(/^-/, "")
79
- name.gsub!(/-$/, "")
75
+ name.gsub!(/[^\d\w\u00C0-\u1FFF\u2C00-\uD7FF]/, '-')
76
+ name.gsub!(/-+/, '-')
77
+ name.gsub!(/^-/, '')
78
+ name.gsub!(/-$/, '')
80
79
  name.downcase!
81
80
 
82
- @current_ancestors[level - 1] = name
83
- @current_ancestors = @current_ancestors.take(level)
84
- anchor_name = @current_ancestors.compact.join("_")
85
-
86
81
  # 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
82
+ index = increment_anchor_index(name)
83
+ index.zero? ? name : "#{name}-#{index}"
91
84
  end
92
85
 
93
86
  # Creates an anchor element with the given name and adds it before
94
87
  # the given header element.
95
88
  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
89
+ a = Nokogiri::XML::Node.new('a', @doc)
90
+ a['class'] = 'anchor'
91
+ a['id'] = name
92
+ a['href'] = "##{name}"
93
+ header.children.before(a) # Add anchor element before the header
98
94
  end
99
95
 
100
96
  # Adds an entry to the TOC for the given header. The generated entry
@@ -10,7 +10,7 @@ class Gollum::Filter::YAML < Gollum::Filter
10
10
  data.gsub!(YAML_FRONT_MATTER_REGEXP) do
11
11
  @markup.metadata ||= {}
12
12
  begin
13
- frontmatter = ::YAML.safe_load(@markup.sanitize.clean(Regexp.last_match[1]))
13
+ frontmatter = ::YAML.safe_load(sanitize(Regexp.last_match[1]))
14
14
  @markup.metadata.merge!(frontmatter) if frontmatter.respond_to?(:keys) && frontmatter.respond_to?(:values)
15
15
  rescue ::Psych::SyntaxError, ::Psych::DisallowedClass, ::Psych::BadAlias => error
16
16
  @markup.metadata['errors'] ||= []