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,62 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ module Gollum
3
+ module Pagination
4
+ def self.included(klass)
5
+ klass.extend ClassMethods
6
+ class << klass
7
+ # Default Integer max count of items to return in git commands.
8
+ attr_accessor :per_page
9
+ end
10
+ klass.per_page = 30
11
+ end
12
+
13
+ module ClassMethods
14
+ # Turns a page number into an offset number for the git skip option.
15
+ #
16
+ # page - Integer page number.
17
+ #
18
+ # Returns an Integer.
19
+ def page_to_skip(page)
20
+ ([1, page.to_i].max - 1) * per_page
21
+ end
22
+
23
+ # Fills in git-specific options for the log command using simple
24
+ # pagination options.
25
+ #
26
+ # options - Hash of options:
27
+ # page - Optional Integer page number (default: 1)
28
+ # per_page - Optional Integer max count of items to return.
29
+ # Defaults to #per_class class method.
30
+ #
31
+ # Returns Hash with :max_count and :skip keys.
32
+ def log_pagination_options(options = {})
33
+ skip = page_to_skip(options.delete(:page))
34
+ options[:max_count] = [options.delete(:per_page).to_i, per_page].max
35
+ options[:skip] = skip if skip > 0
36
+ options
37
+ end
38
+ end
39
+
40
+ # Turns a page number into an offset number for the git skip option.
41
+ #
42
+ # page - Integer page number.
43
+ #
44
+ # Returns an Integer.
45
+ def page_to_skip(page)
46
+ self.class.page_to_skip(page)
47
+ end
48
+
49
+ # Fills in git-specific options for the log command using simple
50
+ # pagination options.
51
+ #
52
+ # options - Hash of options:
53
+ # page - Optional Integer page number (default: 1)
54
+ # per_page - Optional Integer max count of items to return.
55
+ # Defaults to #per_class class method.
56
+ #
57
+ # Returns Hash with :max_count and :skip keys.
58
+ def log_pagination_options(options = {})
59
+ self.class.log_pagination_options(options)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,176 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ module Gollum
3
+ # Encapsulate sanitization options.
4
+ #
5
+ # This class does not yet support all options of Sanitize library.
6
+ # See http://github.com/rgrove/sanitize/.
7
+ class Sanitization
8
+ # Default whitelisted elements.
9
+ ELEMENTS = [
10
+ 'a', 'abbr', 'acronym', 'address', 'area', 'b', 'big',
11
+ 'blockquote', 'br', 'button', 'caption', 'center', 'cite',
12
+ 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dir',
13
+ 'div', 'dl', 'dt', 'em', 'fieldset', 'font', 'form', 'h1',
14
+ 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'input',
15
+ 'ins', 'kbd', 'label', 'legend', 'li', 'map', 'menu',
16
+ 'ol', 'optgroup', 'option', 'p', 'pre', 'q', 's', 'samp',
17
+ 'select', 'small', 'span', 'strike', 'strong', 'sub',
18
+ 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th',
19
+ 'thead', 'tr', 'tt', 'u', 'ul', 'var'
20
+ ].freeze
21
+
22
+ # Default whitelisted attributes.
23
+ ATTRIBUTES = {
24
+ 'a' => ['href'],
25
+ 'img' => ['src'],
26
+ :all => ['abbr', 'accept', 'accept-charset',
27
+ 'accesskey', 'action', 'align', 'alt', 'axis',
28
+ 'border', 'cellpadding', 'cellspacing', 'char',
29
+ 'charoff', 'class', 'charset', 'checked', 'cite',
30
+ 'clear', 'cols', 'colspan', 'color',
31
+ 'compact', 'coords', 'datetime', 'dir',
32
+ 'disabled', 'enctype', 'for', 'frame',
33
+ 'headers', 'height', 'hreflang',
34
+ 'hspace', 'id', 'ismap', 'label', 'lang',
35
+ 'longdesc', 'maxlength', 'media', 'method',
36
+ 'multiple', 'name', 'nohref', 'noshade',
37
+ 'nowrap', 'prompt', 'readonly', 'rel', 'rev',
38
+ 'rows', 'rowspan', 'rules', 'scope',
39
+ 'selected', 'shape', 'size', 'span',
40
+ 'start', 'summary', 'tabindex', 'target',
41
+ 'title', 'type', 'usemap', 'valign', 'value',
42
+ 'vspace', 'width']
43
+ }.freeze
44
+
45
+ # Default whitelisted protocols for URLs.
46
+ PROTOCOLS = {
47
+ 'a' => { 'href' => ['http', 'https', 'mailto', 'ftp', 'irc', 'apt', :relative] },
48
+ 'img' => { 'src' => ['http', 'https', :relative] },
49
+ 'form' => { 'action' => ['http', 'https', :relative] }
50
+ }.freeze
51
+
52
+ ADD_ATTRIBUTES = lambda do |env, node|
53
+ if add = env[:config][:add_attributes][node.name]
54
+ add.each do |key, value|
55
+ node[key] = value
56
+ end
57
+ end
58
+ end
59
+
60
+ # Default elements whose contents will be removed in addition
61
+ # to the elements themselve
62
+ REMOVE_CONTENTS = [
63
+ 'script',
64
+ 'style'
65
+ ].freeze
66
+
67
+ # Default transformers to force @id attributes with 'wiki-' prefix
68
+ TRANSFORMERS = [
69
+ lambda do |env|
70
+ node = env[:node]
71
+ return if env[:is_whitelisted] || !node.element?
72
+ prefix = env[:config][:id_prefix]
73
+ found_attrs = %w(id name).select do |key|
74
+ if value = node[key]
75
+ node[key] = value.gsub(/\A(#{prefix})?/, prefix)
76
+ end
77
+ end
78
+ if found_attrs.size > 0
79
+ ADD_ATTRIBUTES.call(env, node)
80
+ {}
81
+ end
82
+ end,
83
+ lambda do |env|
84
+ node = env[:node]
85
+ return unless value = node['href']
86
+ prefix = env[:config][:id_prefix]
87
+ node['href'] = value.gsub(/\A\#(#{prefix})?/, '#'+prefix)
88
+ ADD_ATTRIBUTES.call(env, node)
89
+ {}
90
+ end
91
+ ].freeze
92
+
93
+ # Gets an Array of whitelisted HTML elements. Default: ELEMENTS.
94
+ attr_reader :elements
95
+
96
+ # Gets a Hash describing which attributes are allowed in which HTML
97
+ # elements. Default: ATTRIBUTES.
98
+ attr_reader :attributes
99
+
100
+ # Gets a Hash describing which URI protocols are allowed in HTML
101
+ # attributes. Default: PROTOCOLS
102
+ attr_reader :protocols
103
+
104
+ # Gets a Hash describing which URI protocols are allowed in HTML
105
+ # attributes. Default: TRANSFORMERS
106
+ attr_reader :transformers
107
+
108
+ # Gets or sets a String prefix which is added to ID attributes.
109
+ # Default: ''
110
+ attr_accessor :id_prefix
111
+
112
+ # Gets a Hash describing HTML attributes that Sanitize should add.
113
+ # Default: {}
114
+ attr_reader :add_attributes
115
+
116
+ # Gets an Array of element names whose contents will be removed in addition
117
+ # to the elements themselves. Default: REMOVE_CONTENTS
118
+ attr_reader :remove_contents
119
+
120
+ # Sets a boolean determining whether Sanitize allows HTML comments in the
121
+ # output. Default: false.
122
+ attr_writer :allow_comments
123
+
124
+ def initialize
125
+ @elements = ELEMENTS.dup
126
+ @attributes = ATTRIBUTES.dup
127
+ @protocols = PROTOCOLS.dup
128
+ @transformers = TRANSFORMERS.dup
129
+ @add_attributes = {}
130
+ @remove_contents = REMOVE_CONTENTS.dup
131
+ @allow_comments = false
132
+ @id_prefix = ''
133
+ yield self if block_given?
134
+ end
135
+
136
+ # Determines if Sanitize should allow HTML comments.
137
+ #
138
+ # Returns True if comments are allowed, or False.
139
+ def allow_comments?
140
+ !!@allow_comments
141
+ end
142
+
143
+ # Modifies the current Sanitization instance to sanitize older revisions
144
+ # of pages.
145
+ #
146
+ # Returns a Sanitization instance.
147
+ def history_sanitization
148
+ self.class.new do |sanitize|
149
+ sanitize.add_attributes['a'] = { 'rel' => 'nofollow' }
150
+ end
151
+ end
152
+
153
+ # Builds a Hash of options suitable for Sanitize.clean.
154
+ #
155
+ # Returns a Hash.
156
+ def to_hash
157
+ { :elements => elements,
158
+ :attributes => attributes,
159
+ :protocols => protocols,
160
+ :add_attributes => add_attributes,
161
+ :remove_contents => remove_contents,
162
+ :allow_comments => allow_comments?,
163
+ :transformers => transformers,
164
+ :id_prefix => id_prefix
165
+ }
166
+ end
167
+
168
+ # Builds a Sanitize instance from the current options.
169
+ #
170
+ # Returns a Sanitize instance.
171
+ def to_sanitize
172
+ Sanitize.new(to_hash)
173
+ end
174
+ end
175
+ end
176
+
@@ -0,0 +1,5 @@
1
+ module Gollum
2
+ module Lib
3
+ VERSION = '4.0.3'
4
+ end
5
+ end
@@ -0,0 +1,925 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ module Gollum
3
+ class Wiki
4
+ include Pagination
5
+
6
+ class << self
7
+ # Sets the page class used by all instances of this Wiki.
8
+ attr_writer :page_class
9
+
10
+ # Sets the file class used by all instances of this Wiki.
11
+ attr_writer :file_class
12
+
13
+ # Sets the markup class used by all instances of this Wiki.
14
+ attr_writer :markup_classes
15
+
16
+ # Sets the default ref for the wiki.
17
+ attr_writer :default_ref
18
+
19
+ # Sets the default name for commits.
20
+ attr_writer :default_committer_name
21
+
22
+ # Sets the default email for commits.
23
+ attr_writer :default_committer_email
24
+
25
+ # Array of chars to substitute whitespace for when trying to locate file in git repo.
26
+ attr_writer :default_ws_subs
27
+
28
+ # Sets sanitization options. Set to false to deactivate
29
+ # sanitization altogether.
30
+ attr_writer :sanitization
31
+
32
+ # Sets sanitization options. Set to false to deactivate
33
+ # sanitization altogether.
34
+ attr_writer :history_sanitization
35
+
36
+ # Hash for setting different default wiki options
37
+ # These defaults can be overridden by options passed directly to initialize()
38
+ attr_writer :default_options
39
+
40
+ # Gets the page class used by all instances of this Wiki.
41
+ # Default: Gollum::Page.
42
+ def page_class
43
+ @page_class ||
44
+ if superclass.respond_to?(:page_class)
45
+ superclass.page_class
46
+ else
47
+ ::Gollum::Page
48
+ end
49
+ end
50
+
51
+ # Gets the file class used by all instances of this Wiki.
52
+ # Default: Gollum::File.
53
+ def file_class
54
+ @file_class ||
55
+ if superclass.respond_to?(:file_class)
56
+ superclass.file_class
57
+ else
58
+ ::Gollum::File
59
+ end
60
+ end
61
+
62
+ # Gets the markup class used by all instances of this Wiki.
63
+ # Default: Gollum::Markup
64
+ def markup_classes
65
+ @markup_classes ||=
66
+ if superclass.respond_to?(:markup_classes)
67
+ superclass.markup_classes
68
+ else
69
+ Hash.new(::Gollum::Markup)
70
+ end
71
+ end
72
+
73
+ # Gets the default markup class used by all instances of this Wiki.
74
+ # Kept for backwards compatibility until Gollum v2.x
75
+ def markup_class(language=:default)
76
+ markup_classes[language]
77
+ end
78
+
79
+ # Sets the default markup class used by all instances of this Wiki.
80
+ # Kept for backwards compatibility until Gollum v2.x
81
+ def markup_class=(default)
82
+ @markup_classes = Hash.new(default).update(markup_classes)
83
+ default
84
+ end
85
+
86
+ alias_method :default_markup_class, :markup_class
87
+ alias_method :default_markup_class=, :markup_class=
88
+
89
+ # Gets the default sanitization options for current pages used by
90
+ # instances of this Wiki.
91
+ def sanitization
92
+ if @sanitization.nil?
93
+ @sanitization = Sanitization.new
94
+ end
95
+ @sanitization
96
+ end
97
+
98
+ # Gets the default sanitization options for older page revisions used by
99
+ # instances of this Wiki.
100
+ def history_sanitization
101
+ if @history_sanitization.nil?
102
+ @history_sanitization = sanitization ?
103
+ sanitization.history_sanitization :
104
+ false
105
+ end
106
+ @history_sanitization
107
+ end
108
+
109
+ def default_ref
110
+ @default_ref || 'master'
111
+ end
112
+
113
+ def default_committer_name
114
+ @default_committer_name || 'Anonymous'
115
+ end
116
+
117
+ def default_committer_email
118
+ @default_committer_email || 'anon@anon.com'
119
+ end
120
+
121
+ def default_ws_subs
122
+ @default_ws_subs || ['_', '-']
123
+ end
124
+
125
+ def default_options
126
+ @default_options || {}
127
+ end
128
+ end
129
+
130
+ # The String base path to prefix to internal links. For example, when set
131
+ # to "/wiki", the page "Hobbit" will be linked as "/wiki/Hobbit". Defaults
132
+ # to "/".
133
+ attr_reader :base_path
134
+
135
+ # Gets the sanitization options for current pages used by this Wiki.
136
+ attr_reader :sanitization
137
+
138
+ # Gets the sanitization options for older page revisions used by this Wiki.
139
+ attr_reader :history_sanitization
140
+
141
+ # Gets the String ref in which all page files reside.
142
+ attr_reader :ref
143
+
144
+ # Gets the String directory in which all page files reside.
145
+ attr_reader :page_file_dir
146
+
147
+ # Gets the Array of chars to sub for ws in filenames.
148
+ attr_reader :ws_subs
149
+
150
+ # Gets the boolean live preview value.
151
+ attr_reader :live_preview
152
+
153
+ # Injects custom css from custom.css in root repo.
154
+ # Defaults to false
155
+ attr_reader :css
156
+
157
+ # Sets page title to value of first h1
158
+ # Defaults to false
159
+ attr_reader :h1_title
160
+
161
+ # Gets the custom index page for / and subdirs (e.g. foo/)
162
+ attr_reader :index_page
163
+
164
+ # Gets side on which the sidebar should be shown
165
+ attr_reader :bar_side
166
+
167
+ # An array of symbols which refer to classes under Gollum::Filter,
168
+ # each of which is an element in the "filtering chain". See
169
+ # the documentation for Gollum::Filter for more on how this chain
170
+ # works, and what filter classes need to implement.
171
+ attr_reader :filter_chain
172
+
173
+ # Public: Initialize a new Gollum Repo.
174
+ #
175
+ # path - The String path to the Git repository that holds the Gollum
176
+ # site.
177
+ # options - Optional Hash:
178
+ # :universal_toc - Table of contents on all pages. Default: false
179
+ # :live_preview - Livepreview editing for markdown files. Default: true
180
+ # :base_path - String base path for all Wiki links.
181
+ # Default: "/"
182
+ # :page_class - The page Class. Default: Gollum::Page
183
+ # :file_class - The file Class. Default: Gollum::File
184
+ # :markup_classes - A hash containing the markup Classes for each
185
+ # document type. Default: { Gollum::Markup }
186
+ # :sanitization - An instance of Sanitization.
187
+ # :page_file_dir - String the directory in which all page files reside
188
+ # :ref - String the repository ref to retrieve pages from
189
+ # :ws_subs - Array of chars to sub for ws in filenames.
190
+ # :mathjax - Set to false to disable mathjax.
191
+ # :user_icons - Enable user icons on the history page. [gravatar, identicon, none].
192
+ # Default: none
193
+ # :show_all - Show all files in file view, not just valid pages.
194
+ # Default: false
195
+ # :collapse_tree - Start with collapsed file view. Default: false
196
+ # :css - Include the custom.css file from the repo.
197
+ # :h1_title - Concatenate all h1's on a page to form the
198
+ # page title.
199
+ # :index_page - The default page to retrieve or create if the
200
+ # a directory is accessed.
201
+ # :bar_side - Where the sidebar should be displayed, may be:
202
+ # - :left
203
+ # - :right
204
+ # :allow_uploads - Set to true to allow file uploads.
205
+ # :per_page_uploads - Whether uploads should be stored in a central
206
+ # 'uploads' directory, or in a directory named for
207
+ # the page they were uploaded to.
208
+ # :filter_chain - Override the default filter chain with your own.
209
+ #
210
+ # Returns a fresh Gollum::Repo.
211
+ def initialize(path, options = {})
212
+ options = self.class.default_options.merge(options)
213
+ if path.is_a?(GitAccess)
214
+ options[:access] = path
215
+ path = path.path
216
+ end
217
+
218
+ # Use .fetch instead of ||
219
+ #
220
+ # o = { :a => false }
221
+ # o[:a] || true # => true
222
+ # o.fetch :a, true # => false
223
+
224
+ @path = path
225
+ @repo_is_bare = options.fetch :repo_is_bare, nil
226
+ @page_file_dir = options.fetch :page_file_dir, nil
227
+ @access = options.fetch :access, GitAccess.new(path, @page_file_dir, @repo_is_bare)
228
+ @base_path = options.fetch :base_path, "/"
229
+ @page_class = options.fetch :page_class, self.class.page_class
230
+ @file_class = options.fetch :file_class, self.class.file_class
231
+ @markup_classes = options.fetch :markup_classes, self.class.markup_classes
232
+ @repo = @access.repo
233
+ @ref = options.fetch :ref, self.class.default_ref
234
+ @sanitization = options.fetch :sanitization, self.class.sanitization
235
+ @ws_subs = options.fetch :ws_subs, self.class.default_ws_subs
236
+ @history_sanitization = options.fetch :history_sanitization, self.class.history_sanitization
237
+ @live_preview = options.fetch :live_preview, true
238
+ @universal_toc = options.fetch :universal_toc, false
239
+ @mathjax = options.fetch :mathjax, false
240
+ @show_all = options.fetch :show_all, false
241
+ @collapse_tree = options.fetch :collapse_tree, false
242
+ @css = options.fetch :css, false
243
+ @h1_title = options.fetch :h1_title, false
244
+ @index_page = options.fetch :index_page, 'Home'
245
+ @bar_side = options.fetch :sidebar, :right
246
+ @user_icons = ['gravatar', 'identicon'].include?(options[:user_icons]) ?
247
+ options[:user_icons] : 'none'
248
+ @allow_uploads = options.fetch :allow_uploads, false
249
+ @per_page_uploads = options.fetch :per_page_uploads, false
250
+ @filter_chain = options.fetch :filter_chain,
251
+ [:Metadata, :PlainText, :TOC, :RemoteCode, :Code, :Macro, :Sanitize, :WSD, :Tags, :Render]
252
+ end
253
+
254
+ # Public: check whether the wiki's git repo exists on the filesystem.
255
+ #
256
+ # Returns true if the repo exists, and false if it does not.
257
+ def exist?
258
+ @access.exist?
259
+ end
260
+
261
+ # Public: Get the formatted page for a given page name, version, and dir.
262
+ #
263
+ # name - The human or canonical String page name of the wiki page.
264
+ # version - The String version ID to find (default: @ref).
265
+ # dir - The directory String relative to the repo.
266
+ #
267
+ # Returns a Gollum::Page or nil if no matching page was found.
268
+ def page(name, version = @ref, dir = nil, exact = false)
269
+ version = @ref if version.nil?
270
+ @page_class.new(self).find(name, version, dir, exact)
271
+ end
272
+
273
+ # Public: Convenience method instead of calling page(name, nil, dir).
274
+ #
275
+ # name - The human or canonical String page name of the wiki page.
276
+ # version - The String version ID to find (default: @ref).
277
+ # dir - The directory String relative to the repo.
278
+ #
279
+ # Returns a Gollum::Page or nil if no matching page was found.
280
+ def paged(name, dir = nil, exact = false, version = @ref)
281
+ page(name, version, dir, exact)
282
+ end
283
+
284
+ # Public: Get the static file for a given name.
285
+ #
286
+ # name - The full String pathname to the file.
287
+ # version - The String version ID to find (default: @ref).
288
+ # try_on_disk - If true, try to return just a reference to a file
289
+ # that exists on the disk.
290
+ #
291
+ # Returns a Gollum::File or nil if no matching file was found. Note
292
+ # that if you specify try_on_disk=true, you may or may not get a file
293
+ # for which on_disk? is actually true.
294
+ def file(name, version = @ref, try_on_disk = false)
295
+ @file_class.new(self).find(name, version, try_on_disk)
296
+ end
297
+
298
+ # Public: Create an in-memory Page with the given data and format. This
299
+ # is useful for previewing what content will look like before committing
300
+ # it to the repository.
301
+ #
302
+ # name - The String name of the page.
303
+ # format - The Symbol format of the page.
304
+ # data - The new String contents of the page.
305
+ #
306
+ # Returns the in-memory Gollum::Page.
307
+ def preview_page(name, data, format)
308
+ page = @page_class.new(self)
309
+ ext = @page_class.format_to_ext(format.to_sym)
310
+ name = @page_class.cname(name) + '.' + ext
311
+ blob = OpenStruct.new(:name => name, :data => data, :is_symlink => false)
312
+ page.populate(blob)
313
+ page.version = @access.commit('master')
314
+ page
315
+ end
316
+
317
+ # Public: Write a new version of a page to the Gollum repo root.
318
+ #
319
+ # name - The String name of the page.
320
+ # format - The Symbol format of the page.
321
+ # data - The new String contents of the page.
322
+ # commit - The commit Hash details:
323
+ # :message - The String commit message.
324
+ # :name - The String author full name.
325
+ # :email - The String email address.
326
+ # :parent - Optional Gollum::Git::Commit parent to this update.
327
+ # :tree - Optional String SHA of the tree to create the
328
+ # index from.
329
+ # :committer - Optional Gollum::Committer instance. If provided,
330
+ # assume that this operation is part of batch of
331
+ # updates and the commit happens later.
332
+ # dir - The String subdirectory of the Gollum::Page without any
333
+ # prefix or suffix slashes (e.g. "foo/bar").
334
+ # Returns the String SHA1 of the newly written version, or the
335
+ # Gollum::Committer instance if this is part of a batch update.
336
+ def write_page(name, format, data, commit = {}, dir = '')
337
+ # spaces must be dashes
338
+ sanitized_name = name.gsub(' ', '-')
339
+ sanitized_dir = dir.gsub(' ', '-')
340
+
341
+ multi_commit = !!commit[:committer]
342
+ committer = multi_commit ? commit[:committer] : Committer.new(self, commit)
343
+
344
+ filename = Gollum::Page.cname(sanitized_name)
345
+
346
+ committer.add_to_index(sanitized_dir, filename, format, data)
347
+
348
+ committer.after_commit do |index, sha|
349
+ @access.refresh
350
+ index.update_working_dir(sanitized_dir, filename, format)
351
+ end
352
+
353
+ multi_commit ? committer : committer.commit
354
+ end
355
+
356
+ # Public: Rename an existing page without altering content.
357
+ #
358
+ # page - The Gollum::Page to update.
359
+ # rename - The String extension-less full path of the page (leading '/' is ignored).
360
+ # commit - The commit Hash details:
361
+ # :message - The String commit message.
362
+ # :name - The String author full name.
363
+ # :email - The String email address.
364
+ # :parent - Optional Gollum::Git::Commit parent to this update.
365
+ # :tree - Optional String SHA of the tree to create the
366
+ # index from.
367
+ # :committer - Optional Gollum::Committer instance. If provided,
368
+ # assume that this operation is part of batch of
369
+ # updates and the commit happens later.
370
+ #
371
+ # Returns the String SHA1 of the newly written version, or the
372
+ # Gollum::Committer instance if this is part of a batch update.
373
+ # Returns false if the operation is a NOOP.
374
+ def rename_page(page, rename, commit = {})
375
+ return false if page.nil?
376
+ return false if rename.nil? or rename.empty?
377
+
378
+ (target_dir, target_name) = ::File.split(rename)
379
+ (source_dir, source_name) = ::File.split(page.path)
380
+ source_name = page.filename_stripped
381
+
382
+ # File.split gives us relative paths with ".", commiter.add_to_index doesn't like that.
383
+ target_dir = '' if target_dir == '.'
384
+ source_dir = '' if source_dir == '.'
385
+ target_dir = target_dir.gsub(/^\//, '')
386
+
387
+ # if the rename is a NOOP, abort
388
+ if source_dir == target_dir and source_name == target_name
389
+ return false
390
+ end
391
+
392
+ multi_commit = !!commit[:committer]
393
+ committer = multi_commit ? commit[:committer] : Committer.new(self, commit)
394
+
395
+ committer.delete(page.path)
396
+ committer.add_to_index(target_dir, target_name, page.format, page.raw_data)
397
+
398
+ committer.after_commit do |index, sha|
399
+ @access.refresh
400
+ index.update_working_dir(source_dir, source_name, page.format)
401
+ index.update_working_dir(target_dir, target_name, page.format)
402
+ end
403
+
404
+ multi_commit ? committer : committer.commit
405
+ end
406
+
407
+ # Public: Update an existing page with new content. The location of the
408
+ # page inside the repository will not change. If the given format is
409
+ # different than the current format of the page, the filename will be
410
+ # changed to reflect the new format.
411
+ #
412
+ # page - The Gollum::Page to update.
413
+ # name - The String extension-less name of the page.
414
+ # format - The Symbol format of the page.
415
+ # data - The new String contents of the page.
416
+ # commit - The commit Hash details:
417
+ # :message - The String commit message.
418
+ # :name - The String author full name.
419
+ # :email - The String email address.
420
+ # :parent - Optional Gollum::Git::Commit parent to this update.
421
+ # :tree - Optional String SHA of the tree to create the
422
+ # index from.
423
+ # :committer - Optional Gollum::Committer instance. If provided,
424
+ # assume that this operation is part of batch of
425
+ # updates and the commit happens later.
426
+ #
427
+ # Returns the String SHA1 of the newly written version, or the
428
+ # Gollum::Committer instance if this is part of a batch update.
429
+ def update_page(page, name, format, data, commit = {})
430
+ name ||= page.name
431
+ format ||= page.format
432
+ dir = ::File.dirname(page.path)
433
+ dir = '' if dir == '.'
434
+ filename = (rename = page.name != name) ?
435
+ Gollum::Page.cname(name) : page.filename_stripped
436
+
437
+ multi_commit = !!commit[:committer]
438
+ committer = multi_commit ? commit[:committer] : Committer.new(self, commit)
439
+
440
+ if !rename && page.format == format
441
+ committer.add(page.path, normalize(data))
442
+ else
443
+ committer.delete(page.path)
444
+ committer.add_to_index(dir, filename, format, data)
445
+ end
446
+
447
+ committer.after_commit do |index, sha|
448
+ @access.refresh
449
+ index.update_working_dir(dir, page.filename_stripped, page.format)
450
+ index.update_working_dir(dir, filename, format)
451
+ end
452
+
453
+ multi_commit ? committer : committer.commit
454
+ end
455
+
456
+ # Public: Delete a page.
457
+ #
458
+ # page - The Gollum::Page to delete.
459
+ # commit - The commit Hash details:
460
+ # :message - The String commit message.
461
+ # :name - The String author full name.
462
+ # :email - The String email address.
463
+ # :parent - Optional Gollum::Git::Commit parent to this update.
464
+ # :tree - Optional String SHA of the tree to create the
465
+ # index from.
466
+ # :committer - Optional Gollum::Committer instance. If provided,
467
+ # assume that this operation is part of batch of
468
+ # updates and the commit happens later.
469
+ #
470
+ # Returns the String SHA1 of the newly written version, or the
471
+ # Gollum::Committer instance if this is part of a batch update.
472
+ def delete_page(page, commit)
473
+
474
+ multi_commit = !!commit[:committer]
475
+ committer = multi_commit ? commit[:committer] : Committer.new(self, commit)
476
+
477
+ committer.delete(page.path)
478
+
479
+ committer.after_commit do |index, sha|
480
+ dir = ::File.dirname(page.path)
481
+ dir = '' if dir == '.'
482
+
483
+ @access.refresh
484
+ index.update_working_dir(dir, page.filename_stripped, page.format)
485
+ end
486
+
487
+ multi_commit ? committer : committer.commit
488
+ end
489
+
490
+ # Public: Applies a reverse diff for a given page. If only 1 SHA is given,
491
+ # the reverse diff will be taken from its parent (^SHA...SHA). If two SHAs
492
+ # are given, the reverse diff is taken from SHA1...SHA2.
493
+ #
494
+ # page - The Gollum::Page to delete.
495
+ # sha1 - String SHA1 of the earlier parent if two SHAs are given,
496
+ # or the child.
497
+ # sha2 - Optional String SHA1 of the child.
498
+ # commit - The commit Hash details:
499
+ # :message - The String commit message.
500
+ # :name - The String author full name.
501
+ # :email - The String email address.
502
+ # :parent - Optional Gollum::Git::Commit parent to this update.
503
+ #
504
+ # Returns a String SHA1 of the new commit, or nil if the reverse diff does
505
+ # not apply.
506
+ def revert_page(page, sha1, sha2 = nil, commit = {})
507
+ if sha2.is_a?(Hash)
508
+ commit = sha2
509
+ sha2 = nil
510
+ end
511
+
512
+ patch = full_reverse_diff_for(page, sha1, sha2)
513
+ committer = Committer.new(self, commit)
514
+ parent = committer.parents[0]
515
+ committer.options[:tree] = @repo.git.apply_patch(parent.sha, patch)
516
+ return false unless committer.options[:tree]
517
+ committer.after_commit do |index, sha|
518
+ @access.refresh
519
+
520
+ files = []
521
+ if page
522
+ files << [page.path, page.filename_stripped, page.format]
523
+ else
524
+ # Grit::Diff can't parse reverse diffs.... yet
525
+ patch.each_line do |line|
526
+ if line =~ %r{^diff --git b/.+? a/(.+)$}
527
+ path = $1
528
+ ext = ::File.extname(path)
529
+ name = ::File.basename(path, ext)
530
+ if format = ::Gollum::Page.format_for(ext)
531
+ files << [path, name, format]
532
+ end
533
+ end
534
+ end
535
+ end
536
+
537
+ files.each do |(path, name, format)|
538
+ dir = ::File.dirname(path)
539
+ dir = '' if dir == '.'
540
+ index.update_working_dir(dir, name, format)
541
+ end
542
+ end
543
+
544
+ committer.commit
545
+ end
546
+
547
+ # Public: Applies a reverse diff to the repo. If only 1 SHA is given,
548
+ # the reverse diff will be taken from its parent (^SHA...SHA). If two SHAs
549
+ # are given, the reverse diff is taken from SHA1...SHA2.
550
+ #
551
+ # sha1 - String SHA1 of the earlier parent if two SHAs are given,
552
+ # or the child.
553
+ # sha2 - Optional String SHA1 of the child.
554
+ # commit - The commit Hash details:
555
+ # :message - The String commit message.
556
+ # :name - The String author full name.
557
+ # :email - The String email address.
558
+ #
559
+ # Returns a String SHA1 of the new commit, or nil if the reverse diff does
560
+ # not apply.
561
+ def revert_commit(sha1, sha2 = nil, commit = {})
562
+ revert_page(nil, sha1, sha2, commit)
563
+ end
564
+
565
+ # Public: Lists all pages for this wiki.
566
+ #
567
+ # treeish - The String commit ID or ref to find (default: @ref)
568
+ #
569
+ # Returns an Array of Gollum::Page instances.
570
+ def pages(treeish = nil)
571
+ tree_list(treeish || @ref)
572
+ end
573
+
574
+ # Public: Lists all non-page files for this wiki.
575
+ #
576
+ # treeish - The String commit ID or ref to find (default: @ref)
577
+ #
578
+ # Returns an Array of Gollum::File instances.
579
+ def files(treeish = nil)
580
+ file_list(treeish || @ref)
581
+ end
582
+
583
+ # Public: Returns the number of pages accessible from a commit
584
+ #
585
+ # ref - A String ref that is either a commit SHA or references one.
586
+ #
587
+ # Returns a Fixnum
588
+ def size(ref = nil)
589
+ tree_map_for(ref || @ref).inject(0) do |num, entry|
590
+ num + (@page_class.valid_page_name?(entry.name) ? 1 : 0)
591
+ end
592
+ rescue Gollum::Git::NoSuchShaFound
593
+ 0
594
+ end
595
+
596
+ # Public: Search all pages for this wiki.
597
+ #
598
+ # query - The string to search for
599
+ #
600
+ # Returns an Array with Objects of page name and count of matches
601
+ def search(query)
602
+ options = {:path => page_file_dir, :ref => ref}
603
+ results = {}
604
+ @repo.git.grep(query, options).each do |hit|
605
+ name = hit[:name]
606
+ count = hit[:count]
607
+ # Remove ext only from known extensions.
608
+ # test.pdf => test.pdf, test.md => test
609
+ file_name = Page::valid_page_name?(name) ? name.chomp(::File.extname(name)) : name
610
+ results[file_name] = count.to_i
611
+ end
612
+
613
+ # Use git ls-files '*query*' to search for file names. Grep only searches file content.
614
+ # Spaces are converted to dashes when saving pages to disk.
615
+ @repo.git.ls_files(query.gsub(' ','-'), options).each do |path|
616
+ # Remove ext only from known extensions.
617
+ file_name = Page::valid_page_name?(path) ? path.chomp(::File.extname(path)) : path
618
+ # If there's not already a result for file_name then
619
+ # the value is nil and nil.to_i is 0.
620
+ results[file_name] = results[file_name].to_i + 1;
621
+ end
622
+
623
+ results.map do |key, val|
624
+ { :count => val, :name => key }
625
+ end
626
+ end
627
+
628
+ # Public: All of the versions that have touched the Page.
629
+ #
630
+ # options - The options Hash:
631
+ # :page - The Integer page number (default: 1).
632
+ # :per_page - The Integer max count of items to return.
633
+ #
634
+ # Returns an Array of Gollum::Git::Commit.
635
+ def log(options = {})
636
+ @repo.log(@ref, nil, log_pagination_options(options))
637
+ end
638
+
639
+ # Returns the latest changes in the wiki (globally)
640
+ #
641
+ # options - The options Hash:
642
+ # :max_count - The Integer number of items to return.
643
+ #
644
+ # Returns an Array of Gollum::Git::Commit.
645
+ def latest_changes(options={})
646
+ max_count = options.fetch(:max_count, 10)
647
+ @repo.log(@ref, nil, options)
648
+ end
649
+
650
+ # Public: Refreshes just the cached Git reference data. This should
651
+ # be called after every Gollum update.
652
+ #
653
+ # Returns nothing.
654
+ def clear_cache
655
+ @access.refresh
656
+ end
657
+
658
+ # Public: Creates a Sanitize instance using the Wiki's sanitization
659
+ # options.
660
+ #
661
+ # Returns a Sanitize instance.
662
+ def sanitizer
663
+ if options = sanitization
664
+ @sanitizer ||= options.to_sanitize
665
+ end
666
+ end
667
+
668
+ # Public: Creates a Sanitize instance using the Wiki's history sanitization
669
+ # options.
670
+ #
671
+ # Returns a Sanitize instance.
672
+ def history_sanitizer
673
+ if options = history_sanitization
674
+ @history_sanitizer ||= options.to_sanitize
675
+ end
676
+ end
677
+
678
+ # Public: Add an additional link to the filter chain.
679
+ #
680
+ # name - A symbol which represents the name of a class under the
681
+ # Gollum::Render namespace to insert into the chain.
682
+ #
683
+ # loc - A "location specifier" -- that is, where to put the new
684
+ # filter in the chain. This can be one of `:first`, `:last`,
685
+ # `:before => :SomeElement`, or `:after => :SomeElement`, where
686
+ # `:SomeElement` (if specified) is a symbol already in the
687
+ # filter chain. A `:before` or `:after` which references a
688
+ # filter that doesn't exist will cause `ArgumentError` to be
689
+ # raised.
690
+ #
691
+ # Returns nothing.
692
+ def add_filter(name, loc)
693
+ unless name.is_a? Symbol
694
+ raise ArgumentError,
695
+ "Invalid filter name #{name.inspect} (must be a symbol)"
696
+ end
697
+
698
+ case loc
699
+ when :first
700
+ @filter_chain.unshift(name)
701
+ when :last
702
+ @filter_chain.push(name)
703
+ when Hash
704
+ if loc.length != 1
705
+ raise ArgumentError,
706
+ "Invalid location specifier"
707
+ end
708
+ if ([:before, :after] && loc.keys).empty?
709
+ raise ArgumentError,
710
+ "Invalid location specifier"
711
+ end
712
+
713
+ next_to = loc.values.first
714
+ relative = loc.keys.first
715
+
716
+ i = @filter_chain.index(next_to)
717
+ if i.nil?
718
+ raise ArgumentError,
719
+ "Unknown filter #{next_to.inspect}"
720
+ end
721
+
722
+ i += 1 if relative == :after
723
+ @filter_chain.insert(i, name)
724
+ else
725
+ raise ArgumentError,
726
+ "Invalid location specifier"
727
+ end
728
+ end
729
+
730
+ # Remove the named filter from the filter chain.
731
+ #
732
+ # Returns nothing. Raises `ArgumentError` if the named filter doesn't
733
+ # exist in the chain.
734
+ def remove_filter(name)
735
+ unless name.is_a? Symbol
736
+ raise ArgumentError,
737
+ "Invalid filter name #{name.inspect} (must be a symbol)"
738
+ end
739
+
740
+ unless @filter_chain.delete(name)
741
+ raise ArgumentError,
742
+ "#{name.inspect} not found in filter chain"
743
+ end
744
+ end
745
+
746
+ #########################################################################
747
+ #
748
+ # Internal Methods
749
+ #
750
+ #########################################################################
751
+
752
+ # The Gollum::Git::Repo associated with the wiki.
753
+ #
754
+ # Returns the Gollum::Git::Repo.
755
+ attr_reader :repo
756
+
757
+ # The String path to the Git repository that holds the Gollum site.
758
+ #
759
+ # Returns the String path.
760
+ attr_reader :path
761
+
762
+ # Gets the page class used by all instances of this Wiki.
763
+ attr_reader :page_class
764
+
765
+ # Gets the file class used by all instances of this Wiki.
766
+ attr_reader :file_class
767
+
768
+ # Gets the markup class used by all instances of this Wiki.
769
+ attr_reader :markup_classes
770
+
771
+ # Toggles display of universal table of contents
772
+ attr_reader :universal_toc
773
+
774
+ # Toggles mathjax.
775
+ attr_reader :mathjax
776
+
777
+ # Toggles user icons. Default: 'none'
778
+ attr_reader :user_icons
779
+
780
+ # Toggles showing all files in files view. Default is false.
781
+ # When false, only valid pages in the git repo are displayed.
782
+ attr_reader :show_all
783
+
784
+ # Start with collapsed file view. Default: false
785
+ attr_reader :collapse_tree
786
+
787
+ # Toggles file upload functionality.
788
+ attr_reader :allow_uploads
789
+
790
+ # Toggles whether uploaded files go into 'uploads', or a directory
791
+ # named after the page they were uploaded to.
792
+ attr_reader :per_page_uploads
793
+
794
+ # Normalize the data.
795
+ #
796
+ # data - The String data to be normalized.
797
+ #
798
+ # Returns the normalized data String.
799
+ def normalize(data)
800
+ data.gsub(/\r/, '')
801
+ end
802
+
803
+ # Assemble a Page's filename from its name and format.
804
+ #
805
+ # name - The String name of the page (should be pre-canonicalized).
806
+ # format - The Symbol format of the page.
807
+ #
808
+ # Returns the String filename.
809
+ def page_file_name(name, format)
810
+ name + '.' + @page_class.format_to_ext(format)
811
+ end
812
+
813
+ # Fill an array with a list of pages.
814
+ #
815
+ # ref - A String ref that is either a commit SHA or references one.
816
+ #
817
+ # Returns a flat Array of Gollum::Page instances.
818
+ def tree_list(ref)
819
+ if sha = @access.ref_to_sha(ref)
820
+ commit = @access.commit(sha)
821
+ tree_map_for(sha).inject([]) do |list, entry|
822
+ next list unless @page_class.valid_page_name?(entry.name)
823
+ list << entry.page(self, commit)
824
+ end
825
+ else
826
+ []
827
+ end
828
+ end
829
+
830
+ # Fill an array with a list of files.
831
+ #
832
+ # ref - A String ref that is either a commit SHA or references one.
833
+ #
834
+ # Returns a flat Array of Gollum::File instances.
835
+ def file_list(ref)
836
+ if sha = @access.ref_to_sha(ref)
837
+ commit = @access.commit(sha)
838
+ tree_map_for(sha).inject([]) do |list, entry|
839
+ next list if entry.name.start_with?('_')
840
+ next list if @page_class.valid_page_name?(entry.name)
841
+ list << entry.file(self, commit)
842
+ end
843
+ else
844
+ []
845
+ end
846
+ end
847
+
848
+ # Creates a reverse diff for the given SHAs on the given Gollum::Page.
849
+ #
850
+ # page - The Gollum::Page to scope the patch to, or a String Path.
851
+ # sha1 - String SHA1 of the earlier parent if two SHAs are given,
852
+ # or the child.
853
+ # sha2 - Optional String SHA1 of the child.
854
+ #
855
+ # Returns a String of the reverse Diff to apply.
856
+ def full_reverse_diff_for(page, sha1, sha2 = nil)
857
+ sha1, sha2 = "#{sha1}^", sha1 if sha2.nil?
858
+ if page
859
+ path = (page.respond_to?(:path) ? page.path : page.to_s)
860
+ return repo.diff(sha2, sha1, path).first.diff
861
+ end
862
+ repo.diff(sha2, sha1).map { |d| d.diff }.join("\n")
863
+ end
864
+
865
+ # Creates a reverse diff for the given SHAs.
866
+ #
867
+ # sha1 - String SHA1 of the earlier parent if two SHAs are given,
868
+ # or the child.
869
+ # sha2 - Optional String SHA1 of the child.
870
+ #
871
+ # Returns a String of the reverse Diff to apply.
872
+ def full_reverse_diff(sha1, sha2 = nil)
873
+ full_reverse_diff_for(nil, sha1, sha2)
874
+ end
875
+
876
+ # Gets the default name for commits.
877
+ #
878
+ # Returns the String name.
879
+ def default_committer_name
880
+ @default_committer_name ||= \
881
+ @repo.config['user.name'] || self.class.default_committer_name
882
+ end
883
+
884
+ # Gets the default email for commits.
885
+ #
886
+ # Returns the String email address.
887
+ def default_committer_email
888
+ email = @repo.config['user.email']
889
+ email = email.delete('<>') if email
890
+ @default_committer_email ||= email || self.class.default_committer_email
891
+ end
892
+
893
+ # Gets the commit object for the given ref or sha.
894
+ #
895
+ # ref - A string ref or SHA pointing to a valid commit.
896
+ #
897
+ # Returns a Gollum::Git::Commit instance.
898
+ def commit_for(ref)
899
+ @access.commit(ref)
900
+ rescue Gollum::Git::NoSuchShaFound
901
+ end
902
+
903
+ # Finds a full listing of files and their blob SHA for a given ref. Each
904
+ # listing is cached based on its actual commit SHA.
905
+ #
906
+ # ref - A String ref that is either a commit SHA or references one.
907
+ # ignore_page_file_dir - Boolean, if true, searches all files within the git repo, regardless of dir/subdir
908
+ #
909
+ # Returns an Array of BlobEntry instances.
910
+ def tree_map_for(ref, ignore_page_file_dir=false)
911
+ if ignore_page_file_dir && !@page_file_dir.nil?
912
+ @root_access ||= GitAccess.new(path, nil, @repo_is_bare)
913
+ @root_access.tree(ref)
914
+ else
915
+ @access.tree(ref)
916
+ end
917
+ rescue Gollum::Git::NoSuchShaFound
918
+ []
919
+ end
920
+
921
+ def inspect
922
+ %(#<#{self.class.name}:#{object_id} #{@repo.path}>)
923
+ end
924
+ end
925
+ end