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