rtfmd 0.10301.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/HISTORY.md +93 -0
  2. data/LICENSE +21 -0
  3. data/README.md +393 -0
  4. data/bin/rtfm +0 -0
  5. data/bin/rtfmd +4 -0
  6. data/config/rtfmd.ru +33 -0
  7. data/docs/sanitization.md +32 -0
  8. data/lib/gollum.rb +39 -0
  9. data/lib/gollum/blob_entry.rb +78 -0
  10. data/lib/gollum/committer.rb +217 -0
  11. data/lib/gollum/file.rb +64 -0
  12. data/lib/gollum/frontend/app.rb +96 -0
  13. data/lib/gollum/frontend/indexapp.rb +17 -0
  14. data/lib/gollum/frontend/public/css/gollum.css +660 -0
  15. data/lib/gollum/frontend/public/css/ie7.css +69 -0
  16. data/lib/gollum/frontend/public/css/ronn.css +40 -0
  17. data/lib/gollum/frontend/public/css/template.css +381 -0
  18. data/lib/gollum/frontend/public/images/icon-sprite.png +0 -0
  19. data/lib/gollum/frontend/public/javascript/gollum.js +137 -0
  20. data/lib/gollum/frontend/public/javascript/gollum.placeholder.js +54 -0
  21. data/lib/gollum/frontend/public/javascript/jquery.color.js +123 -0
  22. data/lib/gollum/frontend/public/javascript/jquery.js +7179 -0
  23. data/lib/gollum/frontend/templates/error.mustache +8 -0
  24. data/lib/gollum/frontend/templates/index.mustache +25 -0
  25. data/lib/gollum/frontend/templates/layout.mustache +28 -0
  26. data/lib/gollum/frontend/templates/page.mustache +34 -0
  27. data/lib/gollum/frontend/templates/pages.mustache +31 -0
  28. data/lib/gollum/frontend/views/error.rb +7 -0
  29. data/lib/gollum/frontend/views/index.rb +21 -0
  30. data/lib/gollum/frontend/views/layout.rb +28 -0
  31. data/lib/gollum/frontend/views/page.rb +53 -0
  32. data/lib/gollum/frontend/views/pages.rb +19 -0
  33. data/lib/gollum/git_access.rb +248 -0
  34. data/lib/gollum/markup.rb +389 -0
  35. data/lib/gollum/page.rb +374 -0
  36. data/lib/gollum/pagination.rb +61 -0
  37. data/lib/gollum/sanitization.rb +161 -0
  38. data/lib/gollum/wiki.rb +378 -0
  39. data/rtfmd.gemspec +47 -0
  40. metadata +291 -0
data/bin/rtfm ADDED
File without changes
data/bin/rtfmd ADDED
@@ -0,0 +1,4 @@
1
+ #!/bin/bash -e
2
+
3
+ bundle check 2>&- || { bundle install --local && bundle check; }
4
+ exec bundle exec thin start -R config/rtfmd.ru
data/config/rtfmd.ru ADDED
@@ -0,0 +1,33 @@
1
+ require 'sinatra/base'
2
+ require 'gollum/frontend/app'
3
+ require 'gollum/frontend/indexapp'
4
+
5
+ rtfm_root = ENV['RTFM_ROOT'] || "/"
6
+
7
+ Precious::App.set(:roots => {})
8
+
9
+ ENV['RTFM_REPOS'].split(/[\s,]+/).each do |repo|
10
+ wiki_path, base_path = repo.split(":")
11
+ unless base_path
12
+ base_path = "/#{File.basename(wiki_path)}"
13
+ end
14
+
15
+ Precious::App.settings.roots[base_path] = {
16
+ :gollum_path => wiki_path,
17
+ :wiki_options => {
18
+ :base_path => "#{rtfm_root}/#{base_path}/".gsub(/\/{2,}/, "/"),
19
+ :rtfm_root => rtfm_root
20
+ }
21
+ }
22
+ end
23
+
24
+ Precious::App.settings.roots.keys.each do |base_path|
25
+ map "#{rtfm_root}/#{base_path}".gsub(/\/{2,}/, "/") do
26
+ run Precious::App
27
+ end
28
+ end
29
+
30
+ map rtfm_root do
31
+ run Precious::IndexApp
32
+ end
33
+
@@ -0,0 +1,32 @@
1
+ Sanitization Rules
2
+ ==================
3
+
4
+ Gollum uses the [Sanitize](http://wonko.com/post/sanitize) gem for HTML
5
+ sanitization.
6
+
7
+ See `lib/gollum.rb` for actual settings.
8
+
9
+ ## ALLOWED TAGS
10
+
11
+ a, abbr, acronym, address, area, b, big, blockquote, br, button, caption,
12
+ center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em,
13
+ fieldset, font, form, h1, h2, h3, h4, h5, h6, hr, i, img, input, ins, kbd,
14
+ label, legend, li, map, menu, ol, optgroup, option, p, pre, q, s, samp,
15
+ select, small, span, strike, strong, sub, sup, table, tbody, td, textarea,
16
+ tfoot, th, thead, tr, tt, u, ul, var
17
+
18
+ ## ALLOWED ATTRIBUTES
19
+
20
+ abbr, accept, accept-charset, accesskey, action, align, alt, axis, border,
21
+ cellpadding, cellspacing, char, charoff, charset, checked, cite, class, clear,
22
+ cols, colspan, color, compact, coords, datetime, dir, disabled, enctype, for,
23
+ frame, headers, height, href, hreflang, hspace, id, ismap, label, lang,
24
+ longdesc, maxlength, media, method, multiple, name, nohref, noshade, nowrap,
25
+ prompt, readonly, rel, rev, rows, rowspan, rules, scope, selected, shape,
26
+ size, span, src, start, summary, tabindex, target, title, type, usemap,
27
+ valign, value, vspace, width
28
+
29
+ ## ALLOWED PROTOCOLS
30
+
31
+ a href: http, https, mailto
32
+ img src: http, https
data/lib/gollum.rb ADDED
@@ -0,0 +1,39 @@
1
+ # stdlib
2
+ require 'digest/md5'
3
+ require 'ostruct'
4
+
5
+ # external
6
+ require 'grit'
7
+ require 'github/markup'
8
+ require 'sanitize'
9
+
10
+ # internal
11
+ require File.expand_path('../gollum/git_access', __FILE__)
12
+ require File.expand_path('../gollum/committer', __FILE__)
13
+ require File.expand_path('../gollum/pagination', __FILE__)
14
+ require File.expand_path('../gollum/blob_entry', __FILE__)
15
+ require File.expand_path('../gollum/wiki', __FILE__)
16
+ require File.expand_path('../gollum/page', __FILE__)
17
+ require File.expand_path('../gollum/file', __FILE__)
18
+ require File.expand_path('../gollum/markup', __FILE__)
19
+ require File.expand_path('../gollum/sanitization', __FILE__)
20
+
21
+ module Gollum
22
+ VERSION = '1.3.1'
23
+
24
+ class Error < StandardError; end
25
+
26
+ class DuplicatePageError < Error
27
+ attr_accessor :dir
28
+ attr_accessor :existing_path
29
+ attr_accessor :attempted_path
30
+
31
+ def initialize(dir, existing, attempted, message = nil)
32
+ @dir = dir
33
+ @existing_path = existing
34
+ @attempted_path = attempted
35
+ super(message || "Cannot write #{@dir}/#{@attempted_path}, found #{@dir}/#{@existing_path}.")
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,78 @@
1
+ module Gollum
2
+ class BlobEntry
3
+ # Gets the String SHA for this blob.
4
+ attr_reader :sha
5
+
6
+ # Gets the full path String for this blob.
7
+ attr_reader :path
8
+
9
+ # Gets the Fixnum size of this blob.
10
+ attr_reader :size
11
+
12
+ def initialize(sha, path, size = nil)
13
+ @sha = sha
14
+ @path = path
15
+ @size = size
16
+ @dir = @name = @blob = nil
17
+ end
18
+
19
+ # Gets the normalized directory path String for this blob.
20
+ def dir
21
+ @dir ||= self.class.normalize_dir(::File.dirname(@path))
22
+ end
23
+
24
+ # Gets the file base name String for this blob.
25
+ def name
26
+ @name ||= ::File.basename(@path)
27
+ end
28
+
29
+ # Gets a Grit::Blob instance for this blob.
30
+ #
31
+ # repo - Grit::Repo instance for the Grit::Blob.
32
+ #
33
+ # Returns an unbaked Grit::Blob instance.
34
+ def blob(repo)
35
+ @blob ||= Grit::Blob.create(repo,
36
+ :id => @sha, :name => name, :size => @size)
37
+ end
38
+
39
+ # Gets a Page instance for this blob.
40
+ #
41
+ # wiki - Gollum::Wiki instance for the Gollum::Page
42
+ #
43
+ # Returns a Gollum::Page instance.
44
+ def page(wiki, commit)
45
+ blob = self.blob(wiki.repo)
46
+ page = wiki.page_class.new(wiki).populate(blob, self.dir)
47
+ page.version = commit
48
+ page
49
+ end
50
+
51
+ def inspect
52
+ %(#<Gollum::BlobEntry #{@sha} #{@path}>)
53
+ end
54
+
55
+ # Normalizes a given directory name for searching through tree paths.
56
+ # Ensures that a directory begins with a slash, or
57
+ #
58
+ # normalize_dir("") # => ""
59
+ # normalize_dir(".") # => ""
60
+ # normalize_dir("foo") # => "/foo"
61
+ # normalize_dir("/foo/") # => "/foo"
62
+ # normalize_dir("/") # => ""
63
+ # normalize_dir("c:/") # => ""
64
+ #
65
+ # dir - String directory name.
66
+ #
67
+ # Returns a normalized String directory name, or nil if no directory
68
+ # is given.
69
+ def self.normalize_dir(dir)
70
+ return '' if dir =~ /^.:\/$/
71
+ if dir
72
+ dir = ::File.expand_path(dir, '/')
73
+ dir = '' if dir == '/'
74
+ end
75
+ dir
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,217 @@
1
+ module Gollum
2
+ # Responsible for handling the commit process for a Wiki. It sets up the
3
+ # Git index, provides methods for modifying the tree, and stores callbacks
4
+ # to be fired after the commit has been made. This is specifically
5
+ # designed to handle multiple updated pages in a single commit.
6
+ class Committer
7
+ # Gets the instance of the Gollum::Wiki that is being updated.
8
+ attr_reader :wiki
9
+
10
+ # Gets a Hash of commit options.
11
+ attr_reader :options
12
+
13
+ # Initializes the Committer.
14
+ #
15
+ # wiki - The Gollum::Wiki instance that is being updated.
16
+ # options - The commit Hash details:
17
+ # :message - The String commit message.
18
+ # :name - The String author full name.
19
+ # :email - The String email address.
20
+ # :parent - Optional Grit::Commit parent to this update.
21
+ # :tree - Optional String SHA of the tree to create the
22
+ # index from.
23
+ # :committer - Optional Gollum::Committer instance. If provided,
24
+ # assume that this operation is part of batch of
25
+ # updates and the commit happens later.
26
+ #
27
+ # Returns the Committer instance.
28
+ def initialize(wiki, options = {})
29
+ @wiki = wiki
30
+ @options = options
31
+ @callbacks = []
32
+ end
33
+
34
+ # Public: References the Git index for this commit.
35
+ #
36
+ # Returns a Grit::Index.
37
+ def index
38
+ @index ||= begin
39
+ idx = @wiki.repo.index
40
+ if tree = options[:tree]
41
+ idx.read_tree(tree)
42
+ elsif parent = parents.first
43
+ idx.read_tree(parent.tree.id)
44
+ end
45
+ idx
46
+ end
47
+ end
48
+
49
+ # Public: The committer for this commit.
50
+ #
51
+ # Returns a Grit::Actor.
52
+ def actor
53
+ @actor ||= begin
54
+ @options[:name] = @wiki.default_committer_name if @options[:name].to_s.empty?
55
+ @options[:email] = @wiki.default_committer_email if @options[:email].to_s.empty?
56
+ Grit::Actor.new(@options[:name], @options[:email])
57
+ end
58
+ end
59
+
60
+ # Public: The parent commits to this pending commit.
61
+ #
62
+ # Returns an array of Grit::Commit instances.
63
+ def parents
64
+ @parents ||= begin
65
+ arr = [@options[:parent] || @wiki.repo.commit(@wiki.ref)]
66
+ arr.flatten!
67
+ arr.compact!
68
+ arr
69
+ end
70
+ end
71
+
72
+ # Adds a page to the given Index.
73
+ #
74
+ # dir - The String subdirectory of the Gollum::Page without any
75
+ # prefix or suffix slashes (e.g. "foo/bar").
76
+ # name - The String Gollum::Page name.
77
+ # format - The Symbol Gollum::Page format.
78
+ # data - The String wiki data to store in the tree map.
79
+ # allow_same_ext - A Boolean determining if the tree map allows the same
80
+ # filename with the same extension.
81
+ #
82
+ # Raises Gollum::DuplicatePageError if a matching filename already exists.
83
+ # This way, pages are not inadvertently overwritten.
84
+ #
85
+ # Returns nothing (modifies the Index in place).
86
+ def add_to_index(dir, name, format, data, allow_same_ext = false)
87
+ path = @wiki.page_file_name(name, format)
88
+
89
+ dir = '/' if dir.strip.empty?
90
+
91
+ fullpath = ::File.join(*[@wiki.page_file_dir, dir, path].compact)
92
+ fullpath = fullpath[1..-1] if fullpath =~ /^\//
93
+
94
+ if index.current_tree && tree = index.current_tree / dir
95
+ downpath = path.downcase.sub(/\.\w+$/, '')
96
+
97
+ tree.blobs.each do |blob|
98
+ next if page_path_scheduled_for_deletion?(index.tree, fullpath)
99
+ file = blob.name.downcase.sub(/\.\w+$/, '')
100
+ file_ext = ::File.extname(blob.name).sub(/^\./, '')
101
+ if downpath == file && !(allow_same_ext && file_ext == ext)
102
+ raise DuplicatePageError.new(dir, blob.name, path)
103
+ end
104
+ end
105
+ end
106
+
107
+ index.add(fullpath, @wiki.normalize(data))
108
+ end
109
+
110
+ # Update the given file in the repository's working directory if there
111
+ # is a working directory present.
112
+ #
113
+ # dir - The String directory in which the file lives.
114
+ # name - The String name of the page (may be in human format).
115
+ # format - The Symbol format of the page.
116
+ #
117
+ # Returns nothing.
118
+ def update_working_dir(dir, name, format)
119
+ unless @wiki.repo.bare
120
+ if @wiki.page_file_dir
121
+ dir = dir.size.zero? ? @wiki.page_file_dir : ::File.join(dir, @wiki.page_file_dir)
122
+ end
123
+
124
+ path =
125
+ if dir == ''
126
+ @wiki.page_file_name(name, format)
127
+ else
128
+ ::File.join(dir, @wiki.page_file_name(name, format))
129
+ end
130
+
131
+ Dir.chdir(::File.join(@wiki.repo.path, '..')) do
132
+ if file_path_scheduled_for_deletion?(index.tree, path)
133
+ @wiki.repo.git.rm({'f' => true}, '--', path)
134
+ else
135
+ @wiki.repo.git.checkout({}, 'HEAD', '--', path)
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ # Writes the commit to Git and runs the after_commit callbacks.
142
+ #
143
+ # Returns the String SHA1 of the new commit.
144
+ def commit
145
+ sha1 = index.commit(@options[:message], parents, actor, nil, @wiki.ref)
146
+ @callbacks.each do |cb|
147
+ cb.call(self, sha1)
148
+ end
149
+ sha1
150
+ end
151
+
152
+ # Adds a callback to be fired after a commit.
153
+ #
154
+ # block - A block that expects this Committer instance and the created
155
+ # commit's SHA1 as the arguments.
156
+ #
157
+ # Returns nothing.
158
+ def after_commit(&block)
159
+ @callbacks << block
160
+ end
161
+
162
+ # Determine if a given page (regardless of format) is scheduled to be
163
+ # deleted in the next commit for the given Index.
164
+ #
165
+ # map - The Hash map:
166
+ # key - The String directory or filename.
167
+ # val - The Hash submap or the String contents of the file.
168
+ # path - The String path of the page file. This may include the format
169
+ # extension in which case it will be ignored.
170
+ #
171
+ # Returns the Boolean response.
172
+ def page_path_scheduled_for_deletion?(map, path)
173
+ parts = path.split('/')
174
+ if parts.size == 1
175
+ deletions = map.keys.select { |k| !map[k] }
176
+ downfile = parts.first.downcase.sub(/\.\w+$/, '')
177
+ deletions.any? { |d| d.downcase.sub(/\.\w+$/, '') == downfile }
178
+ else
179
+ part = parts.shift
180
+ if rest = map[part]
181
+ page_path_scheduled_for_deletion?(rest, parts.join('/'))
182
+ else
183
+ false
184
+ end
185
+ end
186
+ end
187
+
188
+ # Determine if a given file is scheduled to be deleted in the next commit
189
+ # for the given Index.
190
+ #
191
+ # map - The Hash map:
192
+ # key - The String directory or filename.
193
+ # val - The Hash submap or the String contents of the file.
194
+ # path - The String path of the file including extension.
195
+ #
196
+ # Returns the Boolean response.
197
+ def file_path_scheduled_for_deletion?(map, path)
198
+ parts = path.split('/')
199
+ if parts.size == 1
200
+ deletions = map.keys.select { |k| !map[k] }
201
+ deletions.any? { |d| d == parts.first }
202
+ else
203
+ part = parts.shift
204
+ if rest = map[part]
205
+ file_path_scheduled_for_deletion?(rest, parts.join('/'))
206
+ else
207
+ false
208
+ end
209
+ end
210
+ end
211
+
212
+ # Proxies methods t
213
+ def method_missing(name, *args)
214
+ index.send(name, *args)
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,64 @@
1
+ module Gollum
2
+ class File
3
+ Wiki.file_class = self
4
+
5
+ # Public: Initialize a file.
6
+ #
7
+ # wiki - The Gollum::Wiki in question.
8
+ #
9
+ # Returns a newly initialized Gollum::File.
10
+ def initialize(wiki)
11
+ @wiki = wiki
12
+ @blob = nil
13
+ @path = nil
14
+ end
15
+
16
+ # Public: The on-disk filename of the file.
17
+ #
18
+ # Returns the String name.
19
+ def name
20
+ @blob && @blob.name
21
+ end
22
+
23
+ # Public: The raw contents of the page.
24
+ #
25
+ # Returns the String data.
26
+ def raw_data
27
+ @blob && @blob.data
28
+ end
29
+
30
+ # Public: The Grit::Commit version of the file.
31
+ attr_reader :version
32
+
33
+ # Public: The String path of the file.
34
+ attr_reader :path
35
+
36
+ # Public: The String mime type of the file.
37
+ def mime_type
38
+ @blob.mime_type
39
+ end
40
+
41
+ #########################################################################
42
+ #
43
+ # Internal Methods
44
+ #
45
+ #########################################################################
46
+
47
+ # Find a file in the given Gollum repo.
48
+ #
49
+ # name - The full String path.
50
+ # version - The String version ID to find.
51
+ #
52
+ # Returns a Gollum::File or nil if the file could not be found.
53
+ def find(name, version)
54
+ checked = name.downcase
55
+ map = @wiki.tree_map_for(version)
56
+ if entry = map.detect { |entry| entry.path.downcase == checked }
57
+ @path = name
58
+ @blob = entry.blob(@wiki.repo)
59
+ @version = version.is_a?(Grit::Commit) ? version : @wiki.commit_for(version)
60
+ self
61
+ end
62
+ end
63
+ end
64
+ end