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

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 (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
@@ -2,55 +2,125 @@
2
2
 
3
3
  module Gollum
4
4
  class File
5
- Wiki.file_class = self
5
+
6
+ # Does the filesystem support reading symlinks?
7
+ FS_SUPPORT_SYMLINKS = !Gem.win_platform?
8
+
9
+ class << self
10
+
11
+ # For use with self.find: returns true if the given query corresponds to the in-repo path of the BlobEntry.
12
+ #
13
+ # query - The String path to match.
14
+ # entry - The BlobEntry to check against.
15
+ # global_match - (Not implemented for File, see Page.path_match)
16
+ # hyphened_tags - If true, replace spaces in match_path with hyphens.
17
+ # case_insensitive - If true, compare query and match_path case-insensitively
18
+ def path_match(query, entry, global_match = false, hyphened_tags = false, case_insensitive = false)
19
+ path_compare(query, ::File.join('/', entry.path), hyphened_tags, case_insensitive)
20
+ end
21
+
22
+ # For use with self.path_match: returns true if 'query' and 'match_path' match, strictly or taking account of the following parameters:
23
+ # hyphened_tags - If true, replace spaces in match_path with hyphens.
24
+ # case_insensitive - If true, compare query and match_path case-insensitively
25
+ def path_compare(query, match_path, hyphened_tags, case_insensitive)
26
+ if hyphened_tags
27
+ final_query = query.gsub(' ', '-')
28
+ final_match = match_path.gsub(' ', '-')
29
+ else
30
+ final_query = query
31
+ final_match = match_path
32
+ end
33
+ final_match.send(case_insensitive ? :casecmp? : :==, final_query)
34
+ end
35
+ end
36
+
37
+ # Find a file in the given Gollum wiki.
38
+ #
39
+ # wiki - The wiki.
40
+ # path - The full String path.
41
+ # version - The String version ID to find.
42
+ # try_on_disk - If true, try to return just a reference to a file
43
+ # that exists on the disk.
44
+ # global_match - If true, find a File matching path's filename, but not it's directory (so anywhere in the repo)
45
+ #
46
+ # Returns a Gollum::File or nil if the file could not be found. Note
47
+ # that if you specify try_on_disk=true, you may or may not get a file
48
+ # for which on_disk? is actually true.
49
+ def self.find(wiki, path, version, try_on_disk = false, global_match = false)
50
+ map = wiki.tree_map_for(version.to_s)
51
+
52
+ query_path = Pathname.new(::File.join(['/', wiki.page_file_dir, path].compact)).cleanpath.to_s
53
+ query_path.sub!(/^\/\//, '/') if Gem.win_platform? # On Windows, Pathname#cleanpath will leave double slashes at the start of a path intact, so sub them out.
54
+
55
+ begin
56
+ entry = map.detect do |entry|
57
+ path_match(query_path, entry, global_match, wiki.hyphened_tag_lookup, wiki.case_insensitive_tag_lookup)
58
+ end
59
+ entry ? self.new(wiki, entry.blob(wiki.repo), entry.dir, version, try_on_disk) : nil
60
+ rescue Gollum::Git::NoSuchShaFound
61
+ nil
62
+ end
63
+ end
6
64
 
7
65
  # Public: Initialize a file.
8
66
  #
9
- # wiki - The Gollum::Wiki in question.
67
+ # wiki - The Gollum::Wiki
68
+ # blob - The Gollum::Git::Blob
69
+ # path - The String path
70
+ # version - The String SHA or Gollum::Git::Commit version
71
+ # try_on_disk - If true, try to get an on disk reference for this file.
10
72
  #
11
73
  # Returns a newly initialized Gollum::File.
12
- def initialize(wiki)
74
+ def initialize(wiki, blob, path, version, try_on_disk = false)
13
75
  @wiki = wiki
14
- @blob = nil
15
- @path = nil
16
- @on_disk = false
17
- @on_disk_path = nil
76
+ @blob = blob
77
+ @path = "#{path}/#{blob.name}"[1..-1]
78
+ @version = version.is_a?(Gollum::Git::Commit) ? version : @wiki.commit_for(version)
79
+ get_disk_reference if try_on_disk
18
80
  end
19
81
 
20
- # Public: The url path required to reach this page within the repo.
82
+ # Public: The path of the page within the repo.
21
83
  #
22
- # Returns the String url_path
23
- def url_path
24
- path = self.path
25
- path = path.sub(/\/[^\/]+$/, '/') if path.include?('/')
26
- path
27
- end
84
+ # Returns the String path.
85
+ attr_reader :path
86
+
87
+ # Public: The Gollum::Git::Commit version of the file.
88
+ attr_accessor :version
28
89
 
29
- # Public: The SHA hash identifying this file
90
+ # Public: Whether the file can be read from disk.
91
+ attr_accessor :on_disk
92
+
93
+ # Public: The SHA hash identifying this page
30
94
  #
31
95
  # Returns the String SHA.
32
96
  def sha
33
97
  @blob && @blob.id
34
98
  end
35
99
 
36
- # Public: The url_path, but CGI escaped.
100
+ # Public: The on-disk filename of the page including extension.
37
101
  #
38
- # Returns the String url_path
39
- def escaped_url_path
40
- CGI.escape(self.url_path).gsub('%2F', '/')
102
+ # Returns the String name.
103
+ def filename
104
+ @blob && @blob.name
41
105
  end
106
+ alias :name :filename
42
107
 
43
- # Public: The on-disk filename of the file.
108
+ # Public: The url path required to reach this file within the repo.
44
109
  #
45
- # Returns the String name.
46
- def name
47
- return @path if on_disk?
48
- @blob && @blob.name
110
+ # Returns the String url_path
111
+ def url_path
112
+ # Chop off the page_file_dir and first slash if necessary
113
+ @wiki.page_file_dir ? self.path[@wiki.page_file_dir.length+1..-1] : self.path
49
114
  end
50
115
 
51
- alias filename name
116
+ # Public: The url_path, but URL encoded.
117
+ #
118
+ # Returns the String url_path
119
+ def escaped_url_path
120
+ ERB::Util.url_encode(self.url_path).gsub('%2F', '/').force_encoding('utf-8')
121
+ end
52
122
 
53
- # Public: The raw contents of the page.
123
+ # Public: The raw contents of the file.
54
124
  #
55
125
  # Returns the String data.
56
126
  def raw_data
@@ -69,7 +139,7 @@ module Gollum
69
139
  #
70
140
  # Returns true if this is a pointer to an on-disk file
71
141
  def on_disk?
72
- @on_disk
142
+ !!@on_disk
73
143
  end
74
144
 
75
145
  # Public: The path to this file on disk
@@ -79,48 +149,29 @@ module Gollum
79
149
  @on_disk_path
80
150
  end
81
151
 
82
- # Public: The Gollum::Git::Commit version of the file.
83
- attr_accessor :version
84
-
85
- # Public: The String path of the file.
86
- attr_reader :path
87
-
88
152
  # Public: The String mime type of the file.
89
153
  def mime_type
90
154
  @blob && @blob.mime_type
91
155
  end
92
156
 
93
- # Populate the File with information from the Blob.
94
- #
95
- # blob - The Gollum::Git::Blob that contains the info.
96
- # path - The String directory path of the file.
97
- #
98
- # Returns the populated Gollum::File.
99
- def populate(blob, path = nil)
100
- @blob = blob
101
- @path = "#{path}/#{blob.name}"[1..-1]
102
- @on_disk = false
103
- @on_disk_path = nil
104
- self
157
+ def self.protected_files
158
+ ['custom.css', 'custom.js', '.redirects.gollum']
105
159
  end
106
160
 
107
- #########################################################################
108
- #
109
- # Internal Methods
110
- #
111
- #########################################################################
161
+ private
112
162
 
113
163
  # Return the file path to this file on disk, if available.
114
164
  #
115
165
  # Returns nil if the file isn't available on disk. This can occur if the
116
166
  # repo is bare, if the commit isn't the HEAD, or if there are problems
117
167
  # resolving symbolic links.
118
- def get_disk_reference(name, commit)
168
+ def get_disk_reference
119
169
  return false if @wiki.repo.bare
120
- return false if commit.sha != @wiki.repo.head.commit.sha
170
+ return false if @version.sha != @wiki.repo.head.commit.sha
171
+ return false if @blob.is_symlink && !FS_SUPPORT_SYMLINKS
121
172
 
122
173
  # This will try to resolve symbolic links, as well
123
- pathname = Pathname.new(::File.expand_path(::File.join(@wiki.repo.path, '..', name)))
174
+ pathname = Pathname.new(::File.expand_path(::File.join(@wiki.repo.path, '..', @path)))
124
175
  if pathname.symlink?
125
176
  source = ::File.readlink(pathname.to_path)
126
177
  realpath = ::File.join(::File.dirname(pathname.to_path), source)
@@ -129,36 +180,8 @@ module Gollum
129
180
  else
130
181
  @on_disk_path = pathname.to_path
131
182
  end
132
- true
183
+ @on_disk = true
133
184
  end
134
185
 
135
- # Find a file in the given Gollum repo.
136
- #
137
- # name - The full String path.
138
- # version - The String version ID to find.
139
- # try_on_disk - If true, try to return just a reference to a file
140
- # that exists on the disk.
141
- #
142
- # Returns a Gollum::File or nil if the file could not be found. Note
143
- # that if you specify try_on_disk=true, you may or may not get a file
144
- # for which on_disk? is actually true.
145
- def find(name, version, try_on_disk = false)
146
- checked = name.downcase
147
- map = @wiki.tree_map_for(version)
148
- commit = version.is_a?(Gollum::Git::Commit) ? version : @wiki.commit_for(version)
149
-
150
- if (result = map.detect { |entry| entry.path.downcase == checked })
151
- @path = name
152
- @version = commit
153
-
154
- if try_on_disk && get_disk_reference(name, commit)
155
- @on_disk = true
156
- else
157
- @blob = result.blob(@wiki.repo)
158
- end
159
-
160
- self
161
- end
162
- end
163
186
  end
164
187
  end
@@ -10,12 +10,13 @@ module Gollum
10
10
  # set pages to wiki.pages + wiki.files and show_all to true
11
11
  def initialize(pages, options = {})
12
12
  @pages = pages
13
+ @wiki = @pages.first ? @pages.first.wiki : nil
13
14
  @show_all = options[:show_all] || false
14
15
  @checked = options[:collapse_tree] ? '' : "checked"
15
16
  end
16
17
 
17
18
  def enclose_tree(string)
18
- %Q(<ol class="tree">\n) + string + %Q(</ol>)
19
+ sanitize_html(%Q(<ol class="tree">\n) + string + %Q(</ol>))
19
20
  end
20
21
 
21
22
  def new_page(page)
@@ -96,7 +97,7 @@ module Gollum
96
97
  </li>
97
98
  HTML
98
99
 
99
- return enclose_tree html
100
+ return enclose_tree(html)
100
101
  end
101
102
 
102
103
  sorted_folders = []
@@ -153,8 +154,11 @@ module Gollum
153
154
  changed = false
154
155
  end
155
156
 
156
- # return the completed html
157
- enclose_tree html
157
+ enclose_tree(html)
158
158
  end # end render_files
159
+
160
+ def sanitize_html(data)
161
+ @wiki ? @wiki.sanitizer.clean(data) : data
162
+ end
159
163
  end # end FileView class
160
164
  end # end Gollum module
@@ -47,14 +47,21 @@ module Gollum
47
47
  class Filter
48
48
  include Gollum::Helpers
49
49
 
50
+ PLACEHOLDER_PATTERN = /%(\S+)%.+=\1=/
51
+
50
52
  # Setup the object. Sets `@markup` to be the instance of Gollum::Markup that
51
53
  # is running this filter chain, and sets `@map` to be an empty hash (for use
52
54
  # in your extract/process operations).
55
+
53
56
  def initialize(markup)
54
57
  @markup = markup
55
58
  @map = {}
59
+ @open_pattern = "%#{self.class.to_s.split('::').last}%"
60
+ @close_pattern = "=#{self.class.to_s.split('::').last}="
56
61
  end
57
62
 
63
+ attr_reader :open_pattern, :close_pattern
64
+
58
65
  def extract(data)
59
66
  raise RuntimeError,
60
67
  "#{self.class} has not implemented ##extract!"
@@ -66,6 +73,11 @@ module Gollum
66
73
  end
67
74
 
68
75
  protected
76
+
77
+ def sanitize(data)
78
+ @markup.wiki.sanitizer.clean(data, @markup.historical)
79
+ end
80
+
69
81
  # Render a (presumably) non-fatal error as HTML
70
82
  #
71
83
  def html_error(message)
@@ -14,31 +14,27 @@ class Gollum::Filter::Code < Gollum::Filter
14
14
  org_headers = %r{([ \t]*#\+HEADER[S]?:[^\r\n]*\n)*}
15
15
  org_name = %r{([ \t]*#\+NAME:[^\r\n]*\n)?}
16
16
  org_lang = %r{[ ]*([^\n \r]*)[ ]*[^\r\n]*}
17
- org_begin = %r{[ \t]*#\+BEGIN_SRC#{org_lang}\n}
18
- org_end = %r{\n[ \t]*#\+END_SRC[ \t]*}
17
+ org_begin = %r{([ \t]*)#\+BEGIN_SRC#{org_lang}\r?\n}
18
+ org_end = %r{\r?\n[ \t]*#\+END_SRC[ \t\r]*}
19
19
  data.gsub!(/^#{org_headers}#{org_name}#{org_begin}(.+?)#{org_end}$/mi) do
20
- cache_codeblock(Regexp.last_match[3], Regexp.last_match[4])
20
+ "#{Regexp.last_match[3]}#{cache_codeblock(Regexp.last_match[4], Regexp.last_match[5])}"
21
21
  end
22
22
  when :markdown
23
- data.gsub!(/^([ \t]*)(~~~+) ?([^\r\n]+)?\r?\n(.+?)\r?\n\1(~~~+)[ \t\r]*$/m) do
23
+ data.gsub!(/^([ ]{0,3})(~~~+) ?([^\r\n]+)?\r?\n(.+?)\r?\n[ ]{0,3}(~~~+)[ \t\r]*$/m) do
24
24
  m_indent = Regexp.last_match[1]
25
25
  m_start = Regexp.last_match[2] # ~~~
26
26
  m_lang = Regexp.last_match[3]
27
27
  m_code = Regexp.last_match[4]
28
28
  m_end = Regexp.last_match[5] # ~~~
29
- # start and finish tilde fence must be the same length
30
- next '' if m_start.length != m_end.length
31
- lang = m_lang ? m_lang.strip : nil
32
- if lang
33
- lang = lang.match(/\.([^}\s]+)/)
34
- lang = lang[1] unless lang.nil?
35
- end
29
+ # The closing code fence must be at least as long as the opening fence
30
+ next '' if m_end.length < m_start.length
31
+ lang = m_lang ? m_lang.strip.split.first : nil
36
32
  "#{m_indent}#{cache_codeblock(lang, m_code, m_indent)}"
37
33
  end
38
34
  end
39
35
 
40
36
 
41
- data.gsub!(/^([ \t]*)``` ?([^\r\n]+)?\r?\n(.+?)\r?\n\1```[ \t]*\r?$/m) do
37
+ data.gsub!(/^([ ]{0,3})``` ?([^\r\n]+)?\r?\n(.+?)\r?\n[ ]{0,3}```[ \t]*\r?$/m) do
42
38
  "#{Regexp.last_match[1]}#{cache_codeblock(Regexp.last_match[2].to_s.strip, Regexp.last_match[3], Regexp.last_match[1])}" # print the SHA1 ID with the proper indentation
43
39
  end
44
40
  data
@@ -133,7 +129,7 @@ class Gollum::Filter::Code < Gollum::Filter
133
129
 
134
130
  def cache_codeblock(language, code, indent = "")
135
131
  language = language.to_s.empty? ? nil : language
136
- id = Digest::SHA1.hexdigest("#{language}.#{code}")
132
+ id = "#{open_pattern}#{Digest::SHA1.hexdigest("#{language}.#{code}")}#{close_pattern}"
137
133
  cached = @markup.check_cache(:code, id)
138
134
  @map[id] = cached ?
139
135
  { :output => cached } :
@@ -0,0 +1,97 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+
3
+ # CriticMarkup
4
+ #
5
+ # Render CriticMarkup
6
+
7
+ class Gollum::Filter::CriticMarkup < Gollum::Filter
8
+
9
+ # Patterns inspired by https://github.com/DivineDominion/criticmarkup.tmbundle/blob/master/Syntaxes/criticmarkup.tmLanguage
10
+ # All patterns use multiline matching (m flag)
11
+ # Logic inspired by https://github.com/CriticMarkup/CriticMarkup-toolkit/blob/master/CLI/criticParser_CLI.py
12
+
13
+ ADDITION_PATTERN = %r|{\+\+(?<content>.*?)\+\+[ \t]*(\[(.*?)\])?[ \t]*\}|m
14
+ DELETION_PATTERN = %r|{--(?<content>.*?)--[ \t]*(\[(.*?)\])?[ \t]*\}|m
15
+ SUBSTITUTION_PATTERN = %r|{~~(?<oldcontent>.*?)~>(?<newcontent>.*?)~~}|m
16
+ HIGHLIGHT_PATTERN = %r|{\=\=(?<content>.*?)[ \t]*(\[(.*?)\])?[ \t]*\=\=\}{>>(?<comment>.*?)<<}|m
17
+ COMMENT_PATTERN = %r|{>>(?<content>.*?)<<}|m
18
+
19
+ def extract(data)
20
+ data.gsub! ADDITION_PATTERN do
21
+ content = $~[:content]
22
+ placeholder = generate_placeholder("#{content}#{@map.size}")
23
+ # Is there a new paragraph followed by new text
24
+ if content.start_with?("\n\n") && content != "\n\n"
25
+ html = "\n\n<ins class='critic break'>&nbsp;</ins>\n\n<ins>#{content.gsub('\n', ' ')}</ins>"
26
+ # Is the addition just a single new paragraph
27
+ elsif content == "\n\n"
28
+ html = "\n\n<ins class='critic break'>&nbsp;</ins>\n\n"
29
+ # Is it added text followed by a new paragraph?
30
+ elsif content.end_with?("\n\n") && content != "\n\n"
31
+ html = "<ins>#{content.gsub('\n', ' ')}</ins>\n\n<ins class='critic break'>&nbsp;</ins>\n\n"
32
+ else
33
+ html = "<ins>#{content.gsub('\n', ' ')}</ins>"
34
+ end
35
+ @map[placeholder] = html
36
+ placeholder
37
+ end
38
+
39
+ data.gsub! DELETION_PATTERN do
40
+ content = $~[:content]
41
+ placeholder = generate_placeholder("#{content}#{@map.size}")
42
+ if content == "\n\n"
43
+ html = "<del>&nbsp;</del>"
44
+ else
45
+ html = "<del>#{content.gsub('\n\n', ' ')}</del>"
46
+ end
47
+ @map[placeholder] = html
48
+ placeholder
49
+ end
50
+
51
+ data.gsub! SUBSTITUTION_PATTERN do
52
+ oldcontent = $~[:oldcontent]
53
+ newcontent = $~[:newcontent]
54
+ placeholder = generate_placeholder("#{oldcontent}#{newcontent}#{@map.size}")
55
+ html = "<del>#{oldcontent}</del><ins>#{newcontent}</ins>"
56
+ @map[placeholder] = html
57
+ placeholder
58
+ end
59
+
60
+ data.gsub! HIGHLIGHT_PATTERN do
61
+ content = $~[:content]
62
+ comment = $~[:comment]
63
+ placeholder = generate_placeholder("#{content}#{@map.size}")
64
+ html = "<mark>#{content}</mark><span class='critic comment'>#{comment}</span>"
65
+ @map[placeholder] = html
66
+ placeholder
67
+ end
68
+
69
+ data.gsub! COMMENT_PATTERN do
70
+ content = $~[:content]
71
+ placeholder = generate_placeholder("#{content}#{@map.size}")
72
+ html = "<span class='critic comment'>#{content}</span>"
73
+ @map[placeholder] = html
74
+ placeholder
75
+ end
76
+
77
+ data
78
+ end
79
+
80
+ def process(data)
81
+ data.gsub! process_pattern do
82
+ @map[$~[:placeholder]]
83
+ end
84
+ data
85
+ end
86
+
87
+ private
88
+
89
+ def process_pattern
90
+ /(?<placeholder>#{open_pattern}\h{40}#{close_pattern})/
91
+ end
92
+
93
+ def generate_placeholder(content)
94
+ "#{open_pattern}#{Digest::SHA1.hexdigest(content)}#{close_pattern}"
95
+ end
96
+
97
+ end