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
@@ -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