gollum-lib 4.0.3-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 (41) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile +3 -0
  3. data/HISTORY.md +11 -0
  4. data/LICENSE +21 -0
  5. data/README.md +286 -0
  6. data/Rakefile +187 -0
  7. data/docs/sanitization.md +33 -0
  8. data/gemspec.rb +100 -0
  9. data/gollum-lib.gemspec +4 -0
  10. data/gollum-lib_java.gemspec +4 -0
  11. data/lib/gollum-lib.rb +64 -0
  12. data/lib/gollum-lib/blob_entry.rb +95 -0
  13. data/lib/gollum-lib/committer.rb +243 -0
  14. data/lib/gollum-lib/file.rb +158 -0
  15. data/lib/gollum-lib/file_view.rb +155 -0
  16. data/lib/gollum-lib/filter.rb +78 -0
  17. data/lib/gollum-lib/filter/code.rb +145 -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/remote_code.rb +63 -0
  22. data/lib/gollum-lib/filter/render.rb +20 -0
  23. data/lib/gollum-lib/filter/sanitize.rb +18 -0
  24. data/lib/gollum-lib/filter/tags.rb +320 -0
  25. data/lib/gollum-lib/filter/toc.rb +109 -0
  26. data/lib/gollum-lib/filter/wsd.rb +54 -0
  27. data/lib/gollum-lib/git_access.rb +247 -0
  28. data/lib/gollum-lib/gitcode.rb +48 -0
  29. data/lib/gollum-lib/helpers.rb +13 -0
  30. data/lib/gollum-lib/hook.rb +35 -0
  31. data/lib/gollum-lib/macro.rb +43 -0
  32. data/lib/gollum-lib/macro/all_pages.rb +11 -0
  33. data/lib/gollum-lib/markup.rb +197 -0
  34. data/lib/gollum-lib/markups.rb +20 -0
  35. data/lib/gollum-lib/page.rb +491 -0
  36. data/lib/gollum-lib/pagination.rb +62 -0
  37. data/lib/gollum-lib/sanitization.rb +176 -0
  38. data/lib/gollum-lib/version.rb +5 -0
  39. data/lib/gollum-lib/wiki.rb +925 -0
  40. data/licenses/licenses.txt +6 -0
  41. metadata +410 -0
@@ -0,0 +1,158 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ require 'pathname'
3
+
4
+ module Gollum
5
+ class File
6
+ Wiki.file_class = self
7
+
8
+ # Public: Initialize a file.
9
+ #
10
+ # wiki - The Gollum::Wiki in question.
11
+ #
12
+ # Returns a newly initialized Gollum::File.
13
+ def initialize(wiki)
14
+ @wiki = wiki
15
+ @blob = nil
16
+ @path = nil
17
+ @on_disk = false
18
+ @on_disk_path = nil
19
+ end
20
+
21
+ # Public: The url path required to reach this page within the repo.
22
+ #
23
+ # Returns the String url_path
24
+ def url_path
25
+ path = self.path
26
+ path = path.sub(/\/[^\/]+$/, '/') if path.include?('/')
27
+ path
28
+ end
29
+
30
+ # Public: The url_path, but CGI escaped.
31
+ #
32
+ # Returns the String url_path
33
+ def escaped_url_path
34
+ CGI.escape(self.url_path).gsub('%2F', '/')
35
+ end
36
+
37
+ # Public: The on-disk filename of the file.
38
+ #
39
+ # Returns the String name.
40
+ def name
41
+ return @path if on_disk?
42
+ @blob && @blob.name
43
+ end
44
+
45
+ alias filename name
46
+
47
+ # Public: The raw contents of the page.
48
+ #
49
+ # Returns the String data.
50
+ def raw_data
51
+ return IO.read(@on_disk_path) if on_disk?
52
+ return nil unless @blob
53
+
54
+ if !@wiki.repo.bare && @blob.is_symlink
55
+ new_path = @blob.symlink_target(::File.join(@wiki.repo.path, '..', self.path))
56
+ return IO.read(new_path) if new_path
57
+ end
58
+
59
+ @blob.data
60
+ end
61
+
62
+ # Public: Is this an on-disk file reference?
63
+ #
64
+ # Returns true if this is a pointer to an on-disk file
65
+ def on_disk?
66
+ return @on_disk
67
+ end
68
+
69
+ # Public: The path to this file on disk
70
+ #
71
+ # Returns nil if on_disk? is false.
72
+ def on_disk_path
73
+ return @on_disk_path
74
+ end
75
+
76
+ # Public: The Gollum::Git::Commit version of the file.
77
+ attr_accessor :version
78
+
79
+ # Public: The String path of the file.
80
+ attr_reader :path
81
+
82
+ # Public: The String mime type of the file.
83
+ def mime_type
84
+ @blob && @blob.mime_type
85
+ end
86
+
87
+ # Populate the File with information from the Blob.
88
+ #
89
+ # blob - The Gollum::Git::Blob that contains the info.
90
+ # path - The String directory path of the file.
91
+ #
92
+ # Returns the populated Gollum::File.
93
+ def populate(blob, path=nil)
94
+ @blob = blob
95
+ @path = "#{path}/#{blob.name}"[1..-1]
96
+ @on_disk = false
97
+ @on_disk_path = nil
98
+ self
99
+ end
100
+
101
+ #########################################################################
102
+ #
103
+ # Internal Methods
104
+ #
105
+ #########################################################################
106
+
107
+ # Return the file path to this file on disk, if available.
108
+ #
109
+ # Returns nil if the file isn't available on disk. This can occur if the
110
+ # repo is bare, if the commit isn't the HEAD, or if there are problems
111
+ # resolving symbolic links.
112
+ def get_disk_reference(name, commit)
113
+ return false if @wiki.repo.bare
114
+ return false if commit.sha != @wiki.repo.head.commit.sha
115
+
116
+ # This will try to resolve symbolic links, as well
117
+ pathname = Pathname.new(::File.expand_path(::File.join(@wiki.repo.path, '..', name)))
118
+ if pathname.symlink?
119
+ source = ::File.readlink(pathname.to_path)
120
+ realpath = ::File.join(::File.dirname(pathname.to_path), source)
121
+ return false unless realpath && ::File.exist?(realpath)
122
+ @on_disk_path = realpath.to_s
123
+ else
124
+ @on_disk_path = pathname.to_path
125
+ end
126
+ return true
127
+ end
128
+
129
+ # Find a file in the given Gollum repo.
130
+ #
131
+ # name - The full String path.
132
+ # version - The String version ID to find.
133
+ # try_on_disk - If true, try to return just a reference to a file
134
+ # that exists on the disk.
135
+ #
136
+ # Returns a Gollum::File or nil if the file could not be found. Note
137
+ # that if you specify try_on_disk=true, you may or may not get a file
138
+ # for which on_disk? is actually true.
139
+ def find(name, version, try_on_disk=false)
140
+ checked = name.downcase
141
+ map = @wiki.tree_map_for(version)
142
+ commit = version.is_a?(Gollum::Git::Commit) ? version : @wiki.commit_for(version)
143
+
144
+ if entry = map.detect { |entry| entry.path.downcase == checked }
145
+ @path = name
146
+ @version = commit
147
+
148
+ if try_on_disk && get_disk_reference(name, commit)
149
+ @on_disk = true
150
+ else
151
+ @blob = entry.blob(@wiki.repo)
152
+ end
153
+
154
+ self
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,155 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ module Gollum
3
+ =begin
4
+ FileView requires that:
5
+ - All files in root dir are processed first
6
+ - Then all the folders are sorted and processed
7
+ =end
8
+ class FileView
9
+ # common use cases:
10
+ # set pages to wiki.pages and show_all to false
11
+ # set pages to wiki.pages + wiki.files and show_all to true
12
+ def initialize pages, options = {}
13
+ @pages = pages
14
+ @show_all = options[:show_all] || false
15
+ @checked = options[:collapse_tree] ? '' : "checked"
16
+ end
17
+
18
+ def enclose_tree string
19
+ %Q(<ol class="tree">\n) + string + %Q(</ol>)
20
+ end
21
+
22
+ def new_page page
23
+ name = page.name
24
+ url = url_for_page page
25
+ %Q( <li class="file"><a href="#{url}"><span class="icon"></span>#{name}</a></li>)
26
+ end
27
+
28
+ def new_folder folder_path
29
+ new_sub_folder folder_path
30
+ end
31
+
32
+ def new_sub_folder path
33
+ <<-HTML
34
+ <li>
35
+ <label>#{path}</label> <input type="checkbox" #{@checked} />
36
+ <ol>
37
+ HTML
38
+ end
39
+
40
+ def end_folder
41
+ "</ol></li>\n"
42
+ end
43
+
44
+ def url_for_page page
45
+ url = ''
46
+ if @show_all
47
+ # Remove ext for valid pages.
48
+ filename = page.filename
49
+ filename = Page::valid_page_name?(filename) ? filename.chomp(::File.extname(filename)) : filename
50
+
51
+ url = ::File.join(::File.dirname(page.path), filename)
52
+ else
53
+ url = ::File.join(::File.dirname(page.path), page.filename_stripped)
54
+ end
55
+ url = url[2..-1] if url[0, 2] == './'
56
+ url
57
+ end
58
+
59
+ def render_files
60
+ html = ''
61
+ count = @pages.size
62
+ folder_start = -1
63
+
64
+ # Process all pages until folders start
65
+ count.times do |index|
66
+ page = @pages[index]
67
+ path = page.path
68
+
69
+ unless path.include? '/'
70
+ # Page processed (not contained in a folder)
71
+ html += new_page page
72
+ else
73
+ # Folders start at the next index
74
+ folder_start = index
75
+ break # Pages finished, move on to folders
76
+ end
77
+ end
78
+
79
+ # If there are no folders, then we're done.
80
+ return enclose_tree(html) if folder_start <= -1
81
+
82
+ # Handle special case of only one folder.
83
+ if (count - folder_start == 1)
84
+ page = @pages[folder_start]
85
+ html += <<-HTML
86
+ <li>
87
+ <label>#{::File.dirname(page.path)}</label> <input type="checkbox" #{@checked} />
88
+ <ol>
89
+ #{new_page page}
90
+ </ol>
91
+ </li>
92
+ HTML
93
+
94
+ return enclose_tree html
95
+ end
96
+
97
+ sorted_folders = []
98
+ (folder_start).upto count - 1 do |index|
99
+ sorted_folders += [[@pages[index].path, index]]
100
+ end
101
+
102
+ # http://stackoverflow.com/questions/3482814/sorting-list-of-string-paths-in-vb-net
103
+ sorted_folders.sort! do |first, second|
104
+ a = first[0]
105
+ b = second[0]
106
+
107
+ # use :: operator because gollum defines its own conflicting File class
108
+ dir_compare = ::File.dirname(a) <=> ::File.dirname(b)
109
+
110
+ # Sort based on directory name unless they're equal (0) in
111
+ # which case sort based on file name.
112
+ if dir_compare == 0
113
+ ::File.basename(a) <=> ::File.basename(b)
114
+ else
115
+ dir_compare
116
+ end
117
+ end
118
+
119
+ # keep track of folder depth, 0 = at root.
120
+ cwd_array = []
121
+ changed = false
122
+
123
+ # process rest of folders
124
+ (0...sorted_folders.size).each do |index|
125
+ page = @pages[sorted_folders[index][1]]
126
+ path = page.path
127
+ folder = ::File.dirname path
128
+
129
+ tmp_array = folder.split '/'
130
+
131
+ (0...tmp_array.size).each do |index|
132
+ if cwd_array[index].nil? || changed
133
+ html += new_sub_folder tmp_array[index]
134
+ next
135
+ end
136
+
137
+ if cwd_array[index] != tmp_array[index]
138
+ changed = true
139
+ (cwd_array.size - index).times do
140
+ html += end_folder
141
+ end
142
+ html += new_sub_folder tmp_array[index]
143
+ end
144
+ end
145
+
146
+ html += new_page page
147
+ cwd_array = tmp_array
148
+ changed = false
149
+ end
150
+
151
+ # return the completed html
152
+ enclose_tree html
153
+ end # end render_files
154
+ end # end FileView class
155
+ end # end Gollum module
@@ -0,0 +1,78 @@
1
+ # A "filter", in Gollum-speak, is an object which extracts tokens from an
2
+ # input stream of an arbitrary markup language, then replaces them with a
3
+ # final form in a rendered form of the same document. Filters are composed
4
+ # into a "filter chain", which forms the order in which filters are applied
5
+ # in both the extraction and processing phases (processing happens in the
6
+ # reverse order to extraction). A single instance of a filter class is used
7
+ # for both the extraction and processing, so you can store internal state
8
+ # from the extraction phase for use in the processing phase.
9
+ #
10
+ # Any class which is to be used as a filter must have an `initialize` method
11
+ # which takes a single mandatory argument (the instance of the `Markup`
12
+ # class we're being called from), and must implement two methods: `extract`
13
+ # and `process`, both of which must take a string as input and return a
14
+ # (possibly modified) form of that string as output.
15
+ #
16
+ # An example rendering session: consider a filter chain with three filters
17
+ # in it, :A, :B, and :C (filter chains are specified as symbols, which are
18
+ # taken to be class names within the Gollum::Filter namespace). An
19
+ # equivalent of the following will take place:
20
+ #
21
+ # a = Gollum::Filter.const_get(:A).new
22
+ # b = Gollum::Filter.const_get(:B).new
23
+ # c = Gollum::Filter.const_get(:C).new
24
+ #
25
+ # data = "<some markup>"
26
+ #
27
+ # data = a.extract(data)
28
+ # data = b.extract(data)
29
+ # data = c.extract(data)
30
+ #
31
+ # data = c.process(data)
32
+ # data = b.process(data)
33
+ # data = a.process(data)
34
+ #
35
+ # # `data` now contains the rendered document, ready for processing
36
+ #
37
+ # Note how the extraction steps go in the order of the filter chain, while
38
+ # the processing steps go in the reverse order. There hasn't (yet) been a
39
+ # case where that is a huge problem.
40
+ #
41
+ # If your particular filter doesn't need to do something with either the
42
+ # original markup or rendered forms, you can simply define the relevant
43
+ # method to be a pass-through (`def extract(d) d; end`), but you *must*
44
+ # define both methods yourself.
45
+ #
46
+ module Gollum
47
+ class Filter
48
+ include Gollum::Helpers
49
+
50
+ # Setup the object. Sets `@markup` to be the instance of Gollum::Markup that
51
+ # is running this filter chain, and sets `@map` to be an empty hash (for use
52
+ # in your extract/process operations).
53
+ def initialize(markup)
54
+ @markup = markup
55
+ @map = {}
56
+ end
57
+
58
+ def extract(_d)
59
+ raise RuntimeError,
60
+ "#{self.class} has not implemented ##extract!"
61
+ end
62
+
63
+ def process(_d)
64
+ raise RuntimeError,
65
+ "#{self.class} has not implemented ##process!"
66
+ end
67
+
68
+ protected
69
+ # Render a (presumably) non-fatal error as HTML
70
+ #
71
+ def html_error(message)
72
+ "<p class=\"gollum-error\">#{message}</p>"
73
+ end
74
+ end
75
+ end
76
+
77
+ # Load all standard filters
78
+ Dir[File.expand_path('../filter/*.rb', __FILE__)].each { |f| require f }
@@ -0,0 +1,145 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+
3
+ # Code
4
+ #
5
+ # Render a block of code using the Rouge syntax-highlighter.
6
+ class Gollum::Filter::Code < Gollum::Filter
7
+ def extract(data)
8
+ case @markup.format
9
+ when :txt
10
+ return data
11
+ when :asciidoc
12
+ data.gsub!(/^(\[source,([^\r\n]*)\]\n)?----\n(.+?)\n----$/m) do
13
+ cache_codeblock($2, $3)
14
+ end
15
+ when :org
16
+ org_headers = %r{([ \t]*#\+HEADER[S]?:[^\r\n]*\n)*}
17
+ org_name = %r{([ \t]*#\+NAME:[^\r\n]*\n)?}
18
+ org_lang = %r{[ ]*([^\n \r]*)[ ]*[^\r\n]*}
19
+ org_begin = %r{[ \t]*#\+BEGIN_SRC#{org_lang}\n}
20
+ org_end = %r{\n[ \t]*#\+END_SRC[ \t]*}
21
+ data.gsub!(/^#{org_headers}#{org_name}#{org_begin}(.+?)#{org_end}$/mi) do
22
+ cache_codeblock($3, $4)
23
+ end
24
+ end
25
+ data.gsub!(/^([ \t]*)(~~~+) ?([^\r\n]+)?\r?\n(.+?)\r?\n\1(~~~+)[ \t\r]*$/m) do
26
+ m_indent = $1
27
+ m_start = $2 # ~~~
28
+ m_lang = $3
29
+ m_code = $4
30
+ m_end = $5 # ~~~
31
+ # start and finish tilde fence must be the same length
32
+ next '' if m_start.length != m_end.length
33
+ lang = m_lang ? m_lang.strip : nil
34
+ if lang
35
+ lang = lang.match(/\.([^}\s]+)/)
36
+ lang = lang[1] unless lang.nil?
37
+ end
38
+ "#{m_indent}#{cache_codeblock(lang, m_code, m_indent)}"
39
+ end
40
+
41
+ data.gsub!(/^([ \t]*)``` ?([^\r\n]+)?\r?\n(.+?)\r?\n\1```[ \t]*\r?$/m) do
42
+ "#{$1}#{cache_codeblock($2.to_s.strip, $3, $1)}" # print the SHA1 ID with the proper indentation
43
+ end
44
+ data
45
+ end
46
+
47
+ # Process all code from the codemap and replace the placeholders with the
48
+ # final HTML.
49
+ #
50
+ # data - The String data (with placeholders).
51
+ # encoding - Encoding Constant or String.
52
+ #
53
+ # Returns the marked up String data.
54
+ def process(data)
55
+ return data if data.nil? || data.size.zero? || @map.size.zero?
56
+
57
+ blocks = []
58
+
59
+ @map.each do |id, spec|
60
+ next if spec[:output] # cached
61
+
62
+ code = spec[:code]
63
+
64
+ remove_leading_space(code, /^#{spec[:indent]}/m)
65
+ remove_leading_space(code, /^( |\t)/m)
66
+
67
+ blocks << [spec[:lang], code]
68
+ end
69
+
70
+ highlighted = []
71
+ blocks.each do |lang, code|
72
+ encoding = @markup.encoding || 'utf-8'
73
+
74
+ if defined? Pygments
75
+ # treat unknown and bash as standard pre tags
76
+ if !lang || lang.downcase == 'bash'
77
+ hl_code = "<pre>#{code}</pre>"
78
+ else
79
+ # must set startinline to true for php to be highlighted without <?
80
+ hl_code = Pygments.highlight(code, :lexer => lang, :options => { :encoding => encoding.to_s, :startinline => true })
81
+ end
82
+ else # Rouge
83
+ begin
84
+ # if `lang` was not defined then assume plaintext
85
+ # if `lang` is defined but cannot be found then wrap it and escape it
86
+ lang ||= 'plaintext'
87
+ if Rouge::Lexer.find(lang).nil?
88
+ lexer = Rouge::Lexers::PlainText.new
89
+ formatter = Rouge::Formatters::HTML.new(:wrap => false)
90
+ hl_code = formatter.format(lexer.lex(code))
91
+ hl_code = "<pre class='highlight'><span class='err'>#{CGI.escapeHTML(hl_code)}</span></pre>"
92
+ else
93
+ hl_code = Rouge.highlight(code, lang, 'html')
94
+ end
95
+ rescue
96
+ hl_code = code
97
+ end
98
+ end
99
+
100
+ highlighted << hl_code
101
+ end
102
+
103
+ @map.each do |id, spec|
104
+ body = spec[:output] || begin
105
+ if (body = highlighted.shift.to_s).size > 0
106
+ @markup.update_cache(:code, id, body)
107
+ body
108
+ else
109
+ "<pre><code>#{CGI.escapeHTML(spec[:code])}</code></pre>"
110
+ end
111
+ end
112
+ # Removes paragraph tags surrounding <pre> blocks, see issue https://github.com/gollum/gollum-lib/issues/97
113
+ data.gsub!(/(<p>#{id}<\/p>|#{id})/) do
114
+ body
115
+ end
116
+ end
117
+
118
+ data
119
+ end
120
+
121
+ private
122
+ # Remove the leading space from a code block. Leading space
123
+ # is only removed if every single line in the block has leading
124
+ # whitespace.
125
+ #
126
+ # code - The code block to remove spaces from
127
+ # regex - A regex to match whitespace
128
+ def remove_leading_space(code, regex)
129
+ if code.lines.all? { |line| line =~ /\A\r?\n\Z/ || line =~ regex }
130
+ code.gsub!(regex) do
131
+ ''
132
+ end
133
+ end
134
+ end
135
+
136
+ def cache_codeblock(language, code, indent = "")
137
+ language = language.to_s.empty? ? nil : language
138
+ id = Digest::SHA1.hexdigest("#{language}.#{code}")
139
+ cached = @markup.check_cache(:code, id)
140
+ @map[id] = cached ?
141
+ { :output => cached } :
142
+ { :lang => language, :code => code, :indent => indent }
143
+ id
144
+ end
145
+ end