gitlab-gollum-lib 1.0.0

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.
@@ -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,39 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ require 'net/http'
3
+ require 'net/https' # ruby 1.8.7 fix, remove at upgrade
4
+ require 'uri'
5
+ require 'open-uri'
6
+
7
+ module Gollum
8
+ class RemoteCode
9
+ def initialize path
10
+ raise(ArgumentError, 'path is nil or empty') if path.nil? or path.empty?
11
+ @uri = URI(path)
12
+ end
13
+
14
+ def contents
15
+ @contents ||= req @uri
16
+ end
17
+
18
+ private
19
+
20
+ def req uri, cut = 1
21
+ return "Too many redirects or retries" if cut >= 10
22
+ http = Net::HTTP.new uri.host, uri.port
23
+ http.use_ssl = true
24
+ resp = http.get uri.path, {
25
+ 'Accept' => 'text/plain',
26
+ 'Cache-Control' => 'no-cache',
27
+ 'Connection' => 'keep-alive',
28
+ 'User-Agent' => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0'
29
+ }
30
+ code = resp.code.to_i
31
+ return resp.body if code == 200
32
+ return "Not Found" if code == 404
33
+ return "Unhandled Response Code #{code}" unless code == 304 or not resp.header['location'].nil?
34
+ loc = URI.parse resp.header['location']
35
+ uri2 = loc.relative? ? (uri + loc) : loc # overloads (+)
36
+ req uri2, (cut + 1)
37
+ end
38
+ end
39
+ 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,44 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'open-uri'
5
+
6
+ class Gollum::WebSequenceDiagram
7
+ WSD_URL = "http://www.websequencediagrams.com/index.php"
8
+
9
+ # Initialize a new WebSequenceDiagram object.
10
+ #
11
+ # code - The String containing the sequence diagram markup.
12
+ # style - The String containing the rendering style.
13
+ #
14
+ # Returns a new Gollum::WebSequenceDiagram object
15
+ def initialize(code, style)
16
+ @code = code
17
+ @style = style
18
+ @tag = ""
19
+
20
+ render
21
+ end
22
+
23
+ # Render the sequence diagram on the remote server and store the url to
24
+ # the rendered image.
25
+ #
26
+ # Returns nil.
27
+ def render
28
+ response = Net::HTTP.post_form(URI.parse(WSD_URL), 'style' => @style, 'message' => @code)
29
+ if response.body =~ /img: "(.+)"/
30
+ url = "http://www.websequencediagrams.com/#{$1}"
31
+ @tag = "<img src=\"#{url}\" />"
32
+ else
33
+ puts response.body
34
+ @tag ="Sorry, unable to render sequence diagram at this time."
35
+ end
36
+ end
37
+
38
+ # Gets the HTML IMG tag for the sequence diagram.
39
+ #
40
+ # Returns a String containing the IMG tag.
41
+ def to_tag
42
+ @tag
43
+ end
44
+ end
@@ -0,0 +1,833 @@
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_accessor :default_ref
18
+
19
+ # Sets the default name for commits.
20
+ attr_accessor :default_committer_name
21
+
22
+ # Sets the default email for commits.
23
+ attr_accessor :default_committer_email
24
+
25
+ # Array of chars to substitute whitespace for when trying to locate file in git repo.
26
+ attr_accessor :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_accessor :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
+ end
109
+
110
+ self.default_ref = 'master'
111
+ self.default_committer_name = 'Anonymous'
112
+ self.default_committer_email = 'anon@anon.com'
113
+
114
+ self.default_ws_subs = ['_','-']
115
+ self.default_options = {}
116
+
117
+ # The String base path to prefix to internal links. For example, when set
118
+ # to "/wiki", the page "Hobbit" will be linked as "/wiki/Hobbit". Defaults
119
+ # to "/".
120
+ attr_reader :base_path
121
+
122
+ # Gets the sanitization options for current pages used by this Wiki.
123
+ attr_reader :sanitization
124
+
125
+ # Gets the sanitization options for older page revisions used by this Wiki.
126
+ attr_reader :history_sanitization
127
+
128
+ # Gets the String ref in which all page files reside.
129
+ attr_reader :ref
130
+
131
+ # Gets the String directory in which all page files reside.
132
+ attr_reader :page_file_dir
133
+
134
+ # Gets the Array of chars to sub for ws in filenames.
135
+ attr_reader :ws_subs
136
+
137
+ # Gets the boolean live preview value.
138
+ attr_reader :live_preview
139
+
140
+ # Injects custom css from custom.css in root repo.
141
+ # Defaults to false
142
+ attr_reader :css
143
+
144
+ # Sets page title to value of first h1
145
+ # Defaults to false
146
+ attr_reader :h1_title
147
+
148
+ # Gets the custom index page for / and subdirs (e.g. foo/)
149
+ attr_reader :index_page
150
+
151
+ # Gets side on which the sidebar should be shown
152
+ attr_reader :bar_side
153
+
154
+ # Public: Initialize a new Gollum Repo.
155
+ #
156
+ # path - The String path to the Git repository that holds the Gollum
157
+ # site.
158
+ # options - Optional Hash:
159
+ # :universal_toc - Table of contents on all pages. Default: false
160
+ # :live_preview - Livepreview editing for markdown files. Default: true
161
+ # :base_path - String base path for all Wiki links.
162
+ # Default: "/"
163
+ # :page_class - The page Class. Default: Gollum::Page
164
+ # :file_class - The file Class. Default: Gollum::File
165
+ # :markup_classes - A hash containing the markup Classes for each
166
+ # document type. Default: { Gollum::Markup }
167
+ # :sanitization - An instance of Sanitization.
168
+ # :page_file_dir - String the directory in which all page files reside
169
+ # :ref - String the repository ref to retrieve pages from
170
+ # :ws_subs - Array of chars to sub for ws in filenames.
171
+ # :mathjax - Set to false to disable mathjax.
172
+ # :user_icons - Enable user icons on the history page. [gravatar, identicon, none].
173
+ # Default: none
174
+ # :show_all - Show all files in file view, not just valid pages.
175
+ # Default: false
176
+ # :collapse_tree - Start with collapsed file view. Default: false
177
+ # :css - Include the custom.css file from the repo.
178
+ # :h1_title - Concatenate all h1's on a page to form the
179
+ # page title.
180
+ # :index_page - The default page to retrieve or create if the
181
+ # a directory is accessed.
182
+ # :bar_side - Where the sidebar should be displayed, may be:
183
+ # - :left
184
+ # - :right
185
+ #
186
+ # Returns a fresh Gollum::Repo.
187
+ def initialize(path, options = {})
188
+ options = self.class.default_options.merge(options)
189
+ if path.is_a?(GitAccess)
190
+ options[:access] = path
191
+ path = path.path
192
+ end
193
+
194
+ # Use .fetch instead of ||
195
+ #
196
+ # o = { :a => false }
197
+ # o[:a] || true # => true
198
+ # o.fetch :a, true # => false
199
+
200
+ @path = path
201
+ @repo_is_bare = options.fetch :repo_is_bare, nil
202
+ @page_file_dir = options.fetch :page_file_dir, nil
203
+ @access = options.fetch :access, GitAccess.new(path, @page_file_dir, @repo_is_bare)
204
+ @base_path = options.fetch :base_path, "/"
205
+ @page_class = options.fetch :page_class, self.class.page_class
206
+ @file_class = options.fetch :file_class, self.class.file_class
207
+ @markup_classes = options.fetch :markup_classes, self.class.markup_classes
208
+ @repo = @access.repo
209
+ @ref = options.fetch :ref, self.class.default_ref
210
+ @sanitization = options.fetch :sanitization, self.class.sanitization
211
+ @ws_subs = options.fetch :ws_subs, self.class.default_ws_subs
212
+ @history_sanitization = options.fetch :history_sanitization, self.class.history_sanitization
213
+ @live_preview = options.fetch :live_preview, true
214
+ @universal_toc = options.fetch :universal_toc, false
215
+ @mathjax = options.fetch :mathjax, false
216
+ @show_all = options.fetch :show_all, false
217
+ @collapse_tree = options.fetch :collapse_tree, false
218
+ @css = options.fetch :css, false
219
+ @h1_title = options.fetch :h1_title, false
220
+ @index_page = options.fetch :index_page, 'Home'
221
+ @bar_side = options.fetch :sidebar, :right
222
+ @user_icons = ['gravatar', 'identicon'].include?( options[:user_icons] ) ?
223
+ options[:user_icons] : 'none'
224
+ end
225
+
226
+ # Public: check whether the wiki's git repo exists on the filesystem.
227
+ #
228
+ # Returns true if the repo exists, and false if it does not.
229
+ def exist?
230
+ @access.exist?
231
+ end
232
+
233
+ # Public: Get the formatted page for a given page name, version, and dir.
234
+ #
235
+ # name - The human or canonical String page name of the wiki page.
236
+ # version - The String version ID to find (default: @ref).
237
+ # dir - The directory String relative to the repo.
238
+ #
239
+ # Returns a Gollum::Page or nil if no matching page was found.
240
+ def page(name, version = @ref, dir = nil, exact = false)
241
+ version = @ref if version.nil?
242
+ @page_class.new(self).find(name, version, dir, exact)
243
+ end
244
+
245
+ # Public: Convenience method instead of calling page(name, nil, dir).
246
+ #
247
+ # name - The human or canonical String page name of the wiki page.
248
+ # version - The String version ID to find (default: @ref).
249
+ # dir - The directory String relative to the repo.
250
+ #
251
+ # Returns a Gollum::Page or nil if no matching page was found.
252
+ def paged(name, dir = nil, exact = false, version = @ref)
253
+ page(name, version, dir, exact)
254
+ end
255
+
256
+ # Public: Get the static file for a given name.
257
+ #
258
+ # name - The full String pathname to the file.
259
+ # version - The String version ID to find (default: @ref).
260
+ #
261
+ # Returns a Gollum::File or nil if no matching file was found.
262
+ def file(name, version = @ref)
263
+ @file_class.new(self).find(name, version)
264
+ end
265
+
266
+ # Public: Create an in-memory Page with the given data and format. This
267
+ # is useful for previewing what content will look like before committing
268
+ # it to the repository.
269
+ #
270
+ # name - The String name of the page.
271
+ # format - The Symbol format of the page.
272
+ # data - The new String contents of the page.
273
+ #
274
+ # Returns the in-memory Gollum::Page.
275
+ def preview_page(name, data, format)
276
+ page = @page_class.new(self)
277
+ ext = @page_class.format_to_ext(format.to_sym)
278
+ name = @page_class.cname(name) + '.' + ext
279
+ blob = OpenStruct.new(:name => name, :data => data, :is_symlink => false)
280
+ page.populate(blob)
281
+ page.version = @access.commit('master')
282
+ page
283
+ end
284
+
285
+ # Public: Write a new version of a page to the Gollum repo root.
286
+ #
287
+ # name - The String name of the page.
288
+ # format - The Symbol format of the page.
289
+ # data - The new String contents of the page.
290
+ # commit - The commit Hash details:
291
+ # :message - The String commit message.
292
+ # :name - The String author full name.
293
+ # :email - The String email address.
294
+ # :parent - Optional Grit::Commit parent to this update.
295
+ # :tree - Optional String SHA of the tree to create the
296
+ # index from.
297
+ # :committer - Optional Gollum::Committer instance. If provided,
298
+ # assume that this operation is part of batch of
299
+ # updates and the commit happens later.
300
+ # dir - The String subdirectory of the Gollum::Page without any
301
+ # prefix or suffix slashes (e.g. "foo/bar").
302
+ # Returns the String SHA1 of the newly written version, or the
303
+ # Gollum::Committer instance if this is part of a batch update.
304
+ def write_page(name, format, data, commit = {}, dir = '')
305
+ # spaces must be dashes
306
+ name.gsub!(' ', '-')
307
+ dir.gsub!(' ', '-')
308
+
309
+ multi_commit = false
310
+
311
+ committer = if obj = commit[:committer]
312
+ multi_commit = true
313
+ obj
314
+ else
315
+ Committer.new(self, commit)
316
+ end
317
+
318
+ filename = Gollum::Page.cname(name)
319
+
320
+ committer.add_to_index(dir, filename, format, data)
321
+
322
+ committer.after_commit do |index, sha|
323
+ @access.refresh
324
+ index.update_working_dir(dir, filename, format)
325
+ end
326
+
327
+ multi_commit ? committer : committer.commit
328
+ end
329
+
330
+ # Public: Rename an existing page without altering content.
331
+ #
332
+ # page - The Gollum::Page to update.
333
+ # rename - The String extension-less full path of the page (leading '/' is ignored).
334
+ # commit - The commit Hash details:
335
+ # :message - The String commit message.
336
+ # :name - The String author full name.
337
+ # :email - The String email address.
338
+ # :parent - Optional Grit::Commit parent to this update.
339
+ # :tree - Optional String SHA of the tree to create the
340
+ # index from.
341
+ # :committer - Optional Gollum::Committer instance. If provided,
342
+ # assume that this operation is part of batch of
343
+ # updates and the commit happens later.
344
+ #
345
+ # Returns the String SHA1 of the newly written version, or the
346
+ # Gollum::Committer instance if this is part of a batch update.
347
+ # Returns false if the operation is a NOOP.
348
+ def rename_page(page, rename, commit = {})
349
+ return false if page.nil?
350
+ return false if rename.nil? or rename.empty?
351
+
352
+ (target_dir, target_name) = ::File.split(rename)
353
+ (source_dir, source_name) = ::File.split(page.path)
354
+ source_name = page.filename_stripped
355
+
356
+ # File.split gives us relative paths with ".", commiter.add_to_index doesn't like that.
357
+ target_dir = '' if target_dir == '.'
358
+ source_dir = '' if source_dir == '.'
359
+ target_dir = target_dir.gsub(/^\//, '')
360
+
361
+ # if the rename is a NOOP, abort
362
+ if source_dir == target_dir and source_name == target_name
363
+ return false
364
+ end
365
+
366
+ multi_commit = false
367
+ committer = if obj = commit[:committer]
368
+ multi_commit = true
369
+ obj
370
+ else
371
+ Committer.new(self, commit)
372
+ end
373
+
374
+ committer.delete(page.path)
375
+ committer.add_to_index(target_dir, target_name, page.format, page.raw_data, :allow_same_ext)
376
+
377
+ committer.after_commit do |index, sha|
378
+ @access.refresh
379
+ index.update_working_dir(source_dir, source_name, page.format)
380
+ index.update_working_dir(target_dir, target_name, page.format)
381
+ end
382
+
383
+ multi_commit ? committer : committer.commit
384
+ end
385
+
386
+ # Public: Update an existing page with new content. The location of the
387
+ # page inside the repository will not change. If the given format is
388
+ # different than the current format of the page, the filename will be
389
+ # changed to reflect the new format.
390
+ #
391
+ # page - The Gollum::Page to update.
392
+ # name - The String extension-less name of the page.
393
+ # format - The Symbol format of the page.
394
+ # data - The new String contents of the page.
395
+ # commit - The commit Hash details:
396
+ # :message - The String commit message.
397
+ # :name - The String author full name.
398
+ # :email - The String email address.
399
+ # :parent - Optional Grit::Commit parent to this update.
400
+ # :tree - Optional String SHA of the tree to create the
401
+ # index from.
402
+ # :committer - Optional Gollum::Committer instance. If provided,
403
+ # assume that this operation is part of batch of
404
+ # updates and the commit happens later.
405
+ #
406
+ # Returns the String SHA1 of the newly written version, or the
407
+ # Gollum::Committer instance if this is part of a batch update.
408
+ def update_page(page, name, format, data, commit = {})
409
+ name ||= page.name
410
+ format ||= page.format
411
+ dir = ::File.dirname(page.path)
412
+ dir = '' if dir == '.'
413
+ filename = (rename = page.name != name) ?
414
+ Gollum::Page.cname(name) : page.filename_stripped
415
+
416
+ multi_commit = false
417
+
418
+ committer = if obj = commit[:committer]
419
+ multi_commit = true
420
+ obj
421
+ else
422
+ Committer.new(self, commit)
423
+ end
424
+
425
+ if !rename && page.format == format
426
+ committer.add(page.path, normalize(data))
427
+ else
428
+ committer.delete(page.path)
429
+ committer.add_to_index(dir, filename, format, data, :allow_same_ext)
430
+ end
431
+
432
+ committer.after_commit do |index, sha|
433
+ @access.refresh
434
+ index.update_working_dir(dir, page.filename_stripped, page.format)
435
+ index.update_working_dir(dir, filename, format)
436
+ end
437
+
438
+ multi_commit ? committer : committer.commit
439
+ end
440
+
441
+ # Public: Delete a page.
442
+ #
443
+ # page - The Gollum::Page to delete.
444
+ # commit - The commit Hash details:
445
+ # :message - The String commit message.
446
+ # :name - The String author full name.
447
+ # :email - The String email address.
448
+ # :parent - Optional Grit::Commit parent to this update.
449
+ # :tree - Optional String SHA of the tree to create the
450
+ # index from.
451
+ # :committer - Optional Gollum::Committer instance. If provided,
452
+ # assume that this operation is part of batch of
453
+ # updates and the commit happens later.
454
+ #
455
+ # Returns the String SHA1 of the newly written version, or the
456
+ # Gollum::Committer instance if this is part of a batch update.
457
+ def delete_page(page, commit)
458
+ multi_commit = false
459
+
460
+ committer = if obj = commit[:committer]
461
+ multi_commit = true
462
+ obj
463
+ else
464
+ Committer.new(self, commit)
465
+ end
466
+
467
+ committer.delete(page.path)
468
+
469
+ committer.after_commit do |index, sha|
470
+ dir = ::File.dirname(page.path)
471
+ dir = '' if dir == '.'
472
+
473
+ @access.refresh
474
+ index.update_working_dir(dir, page.filename_stripped, page.format)
475
+ end
476
+
477
+ multi_commit ? committer : committer.commit
478
+ end
479
+
480
+ # Public: Applies a reverse diff for a given page. If only 1 SHA is given,
481
+ # the reverse diff will be taken from its parent (^SHA...SHA). If two SHAs
482
+ # are given, the reverse diff is taken from SHA1...SHA2.
483
+ #
484
+ # page - The Gollum::Page to delete.
485
+ # sha1 - String SHA1 of the earlier parent if two SHAs are given,
486
+ # or the child.
487
+ # sha2 - Optional String SHA1 of the child.
488
+ # commit - The commit Hash details:
489
+ # :message - The String commit message.
490
+ # :name - The String author full name.
491
+ # :email - The String email address.
492
+ # :parent - Optional Grit::Commit parent to this update.
493
+ #
494
+ # Returns a String SHA1 of the new commit, or nil if the reverse diff does
495
+ # not apply.
496
+ def revert_page(page, sha1, sha2 = nil, commit = {})
497
+ if sha2.is_a?(Hash)
498
+ commit = sha2
499
+ sha2 = nil
500
+ end
501
+
502
+ patch = full_reverse_diff_for(page, sha1, sha2)
503
+ committer = Committer.new(self, commit)
504
+ parent = committer.parents[0]
505
+ committer.options[:tree] = @repo.git.apply_patch(parent.sha, patch)
506
+ return false unless committer.options[:tree]
507
+ committer.after_commit do |index, sha|
508
+ @access.refresh
509
+
510
+ files = []
511
+ if page
512
+ files << [page.path, page.filename_stripped, page.format]
513
+ else
514
+ # Grit::Diff can't parse reverse diffs.... yet
515
+ patch.each_line do |line|
516
+ if line =~ %r{^diff --git b/.+? a/(.+)$}
517
+ path = $1
518
+ ext = ::File.extname(path)
519
+ name = ::File.basename(path, ext)
520
+ if format = ::Gollum::Page.format_for(ext)
521
+ files << [path, name, format]
522
+ end
523
+ end
524
+ end
525
+ end
526
+
527
+ files.each do |(path, name, format)|
528
+ dir = ::File.dirname(path)
529
+ dir = '' if dir == '.'
530
+ index.update_working_dir(dir, name, format)
531
+ end
532
+ end
533
+
534
+ committer.commit
535
+ end
536
+
537
+ # Public: Applies a reverse diff to the repo. If only 1 SHA is given,
538
+ # the reverse diff will be taken from its parent (^SHA...SHA). If two SHAs
539
+ # are given, the reverse diff is taken from SHA1...SHA2.
540
+ #
541
+ # sha1 - String SHA1 of the earlier parent if two SHAs are given,
542
+ # or the child.
543
+ # sha2 - Optional String SHA1 of the child.
544
+ # commit - The commit Hash details:
545
+ # :message - The String commit message.
546
+ # :name - The String author full name.
547
+ # :email - The String email address.
548
+ #
549
+ # Returns a String SHA1 of the new commit, or nil if the reverse diff does
550
+ # not apply.
551
+ def revert_commit(sha1, sha2 = nil, commit = {})
552
+ revert_page(nil, sha1, sha2, commit)
553
+ end
554
+
555
+ # Public: Lists all pages for this wiki.
556
+ #
557
+ # treeish - The String commit ID or ref to find (default: @ref)
558
+ #
559
+ # Returns an Array of Gollum::Page instances.
560
+ def pages(treeish = nil)
561
+ tree_list(treeish || @ref)
562
+ end
563
+
564
+ # Public: Lists all non-page files for this wiki.
565
+ #
566
+ # treeish - The String commit ID or ref to find (default: @ref)
567
+ #
568
+ # Returns an Array of Gollum::File instances.
569
+ def files(treeish = nil)
570
+ file_list(treeish || @ref)
571
+ end
572
+
573
+ # Public: Returns the number of pages accessible from a commit
574
+ #
575
+ # ref - A String ref that is either a commit SHA or references one.
576
+ #
577
+ # Returns a Fixnum
578
+ def size(ref = nil)
579
+ tree_map_for(ref || @ref).inject(0) do |num, entry|
580
+ num + (@page_class.valid_page_name?(entry.name) ? 1 : 0)
581
+ end
582
+ rescue Grit::GitRuby::Repository::NoSuchShaFound
583
+ 0
584
+ end
585
+
586
+ # Public: Search all pages for this wiki.
587
+ #
588
+ # query - The string to search for
589
+ #
590
+ # Returns an Array with Objects of page name and count of matches
591
+ def search(query)
592
+ args = [{}, '-i', '-c', query, @ref, '--']
593
+ args << '--' << @page_file_dir if @page_file_dir
594
+
595
+ results = {}
596
+
597
+ @repo.git.grep(*args).split("\n").each do |line|
598
+ result = line.split(':')
599
+ result_1 = result[1]
600
+ # Remove ext only from known extensions.
601
+ # test.pdf => test.pdf, test.md => test
602
+ file_name = Page::valid_page_name?(result_1) ? result_1.chomp(::File.extname(result_1)) :
603
+ result_1
604
+ results[file_name] = result[2].to_i
605
+ end
606
+
607
+ # Use git ls-files '*query*' to search for file names. Grep only searches file content.
608
+ # Spaces are converted to dashes when saving pages to disk.
609
+ @repo.git.ls_files({}, "*#{ query.gsub(' ', '-') }*").split("\n").each do |line|
610
+ # Remove ext only from known extensions.
611
+ file_name = Page::valid_page_name?(line) ? line.chomp(::File.extname(line)) :
612
+ line
613
+ # If there's not already a result for file_name then
614
+ # the value is nil and nil.to_i is 0.
615
+ results[file_name] = results[file_name].to_i + 1;
616
+ end
617
+
618
+ results.map do |key,val|
619
+ { :count => val, :name => key }
620
+ end
621
+ end
622
+
623
+ # Public: All of the versions that have touched the Page.
624
+ #
625
+ # options - The options Hash:
626
+ # :page - The Integer page number (default: 1).
627
+ # :per_page - The Integer max count of items to return.
628
+ #
629
+ # Returns an Array of Grit::Commit.
630
+ def log(options = {})
631
+ @repo.log(@ref, nil, log_pagination_options(options))
632
+ end
633
+
634
+ # Public: Refreshes just the cached Git reference data. This should
635
+ # be called after every Gollum update.
636
+ #
637
+ # Returns nothing.
638
+ def clear_cache
639
+ @access.refresh
640
+ end
641
+
642
+ # Public: Creates a Sanitize instance using the Wiki's sanitization
643
+ # options.
644
+ #
645
+ # Returns a Sanitize instance.
646
+ def sanitizer
647
+ if options = sanitization
648
+ @sanitizer ||= options.to_sanitize
649
+ end
650
+ end
651
+
652
+ # Public: Creates a Sanitize instance using the Wiki's history sanitization
653
+ # options.
654
+ #
655
+ # Returns a Sanitize instance.
656
+ def history_sanitizer
657
+ if options = history_sanitization
658
+ @history_sanitizer ||= options.to_sanitize
659
+ end
660
+ end
661
+
662
+ #########################################################################
663
+ #
664
+ # Internal Methods
665
+ #
666
+ #########################################################################
667
+
668
+ # The Grit::Repo associated with the wiki.
669
+ #
670
+ # Returns the Grit::Repo.
671
+ attr_reader :repo
672
+
673
+ # The String path to the Git repository that holds the Gollum site.
674
+ #
675
+ # Returns the String path.
676
+ attr_reader :path
677
+
678
+ # Gets the page class used by all instances of this Wiki.
679
+ attr_reader :page_class
680
+
681
+ # Gets the file class used by all instances of this Wiki.
682
+ attr_reader :file_class
683
+
684
+ # Gets the markup class used by all instances of this Wiki.
685
+ attr_reader :markup_classes
686
+
687
+ # Toggles display of universal table of contents
688
+ attr_reader :universal_toc
689
+
690
+ # Toggles mathjax.
691
+ attr_reader :mathjax
692
+
693
+ # Toggles user icons. Default: 'none'
694
+ attr_reader :user_icons
695
+
696
+ # Toggles showing all files in files view. Default is false.
697
+ # When false, only valid pages in the git repo are displayed.
698
+ attr_reader :show_all
699
+
700
+ # Start with collapsed file view. Default: false
701
+ attr_reader :collapse_tree
702
+
703
+ # Normalize the data.
704
+ #
705
+ # data - The String data to be normalized.
706
+ #
707
+ # Returns the normalized data String.
708
+ def normalize(data)
709
+ data.gsub(/\r/, '')
710
+ end
711
+
712
+ # Assemble a Page's filename from its name and format.
713
+ #
714
+ # name - The String name of the page (should be pre-canonicalized).
715
+ # format - The Symbol format of the page.
716
+ #
717
+ # Returns the String filename.
718
+ def page_file_name(name, format)
719
+ name + '.' + @page_class.format_to_ext(format)
720
+ end
721
+
722
+ # Fill an array with a list of pages.
723
+ #
724
+ # ref - A String ref that is either a commit SHA or references one.
725
+ #
726
+ # Returns a flat Array of Gollum::Page instances.
727
+ def tree_list(ref)
728
+ if sha = @access.ref_to_sha(ref)
729
+ commit = @access.commit(sha)
730
+ tree_map_for(sha).inject([]) do |list, entry|
731
+ next list unless @page_class.valid_page_name?(entry.name)
732
+ list << entry.page(self, commit)
733
+ end
734
+ else
735
+ []
736
+ end
737
+ end
738
+
739
+ # Fill an array with a list of files.
740
+ #
741
+ # ref - A String ref that is either a commit SHA or references one.
742
+ #
743
+ # Returns a flat Array of Gollum::File instances.
744
+ def file_list(ref)
745
+ if sha = @access.ref_to_sha(ref)
746
+ commit = @access.commit(sha)
747
+ tree_map_for(sha).inject([]) do |list, entry|
748
+ next list if entry.name.start_with?('_')
749
+ next list if @page_class.valid_page_name?(entry.name)
750
+ list << entry.file(self, commit)
751
+ end
752
+ else
753
+ []
754
+ end
755
+ end
756
+
757
+ # Creates a reverse diff for the given SHAs on the given Gollum::Page.
758
+ #
759
+ # page - The Gollum::Page to scope the patch to, or a String Path.
760
+ # sha1 - String SHA1 of the earlier parent if two SHAs are given,
761
+ # or the child.
762
+ # sha2 - Optional String SHA1 of the child.
763
+ #
764
+ # Returns a String of the reverse Diff to apply.
765
+ def full_reverse_diff_for(page, sha1, sha2 = nil)
766
+ sha1, sha2 = "#{sha1}^", sha1 if sha2.nil?
767
+ args = [{:R => true}, sha1, sha2]
768
+ if page
769
+ args << '--' << (page.respond_to?(:path) ? page.path : page.to_s)
770
+ end
771
+ repo.git.native(:diff, *args)
772
+ end
773
+
774
+ # Creates a reverse diff for the given SHAs.
775
+ #
776
+ # sha1 - String SHA1 of the earlier parent if two SHAs are given,
777
+ # or the child.
778
+ # sha2 - Optional String SHA1 of the child.
779
+ #
780
+ # Returns a String of the reverse Diff to apply.
781
+ def full_reverse_diff(sha1, sha2 = nil)
782
+ full_reverse_diff_for(nil, sha1, sha2)
783
+ end
784
+
785
+ # Gets the default name for commits.
786
+ #
787
+ # Returns the String name.
788
+ def default_committer_name
789
+ @default_committer_name ||= \
790
+ @repo.config['user.name'] || self.class.default_committer_name
791
+ end
792
+
793
+ # Gets the default email for commits.
794
+ #
795
+ # Returns the String email address.
796
+ def default_committer_email
797
+ @default_committer_email ||= \
798
+ @repo.config['user.email'] || self.class.default_committer_email
799
+ end
800
+
801
+ # Gets the commit object for the given ref or sha.
802
+ #
803
+ # ref - A string ref or SHA pointing to a valid commit.
804
+ #
805
+ # Returns a Grit::Commit instance.
806
+ def commit_for(ref)
807
+ @access.commit(ref)
808
+ rescue Grit::GitRuby::Repository::NoSuchShaFound
809
+ end
810
+
811
+ # Finds a full listing of files and their blob SHA for a given ref. Each
812
+ # listing is cached based on its actual commit SHA.
813
+ #
814
+ # ref - A String ref that is either a commit SHA or references one.
815
+ # ignore_page_file_dir - Boolean, if true, searches all files within the git repo, regardless of dir/subdir
816
+ #
817
+ # Returns an Array of BlobEntry instances.
818
+ def tree_map_for(ref, ignore_page_file_dir=false)
819
+ if ignore_page_file_dir && !@page_file_dir.nil?
820
+ @root_access ||= GitAccess.new(path, nil, @repo_is_bare)
821
+ @root_access.tree(ref)
822
+ else
823
+ @access.tree(ref)
824
+ end
825
+ rescue Grit::GitRuby::Repository::NoSuchShaFound
826
+ []
827
+ end
828
+
829
+ def inspect
830
+ %(#<#{self.class.name}:#{object_id} #{@repo.path}>)
831
+ end
832
+ end
833
+ end