gitlab-gollum-lib 1.0.0

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