gollum-lib 4.0.3-java

Sign up to get free protection for your applications and to get access to all the features.
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