read-only-gollum 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/HISTORY.md +102 -0
- data/Home.md +3 -0
- data/LICENSE +21 -0
- data/README.md +477 -0
- data/Rakefile +142 -0
- data/bin/read-only-gollum +126 -0
- data/docs/sanitization.md +32 -0
- data/lib/gollum.rb +41 -0
- data/lib/gollum/blob_entry.rb +78 -0
- data/lib/gollum/committer.rb +218 -0
- data/lib/gollum/file.rb +64 -0
- data/lib/gollum/frontend/app.rb +225 -0
- data/lib/gollum/frontend/public/css/dialog.css +141 -0
- data/lib/gollum/frontend/public/css/editor.css +537 -0
- data/lib/gollum/frontend/public/css/gollum.css +660 -0
- data/lib/gollum/frontend/public/css/ie7.css +69 -0
- data/lib/gollum/frontend/public/css/template.css +381 -0
- data/lib/gollum/frontend/public/images/icon-sprite.png +0 -0
- data/lib/gollum/frontend/public/javascript/editor/gollum.editor.js +1096 -0
- data/lib/gollum/frontend/public/javascript/editor/langs/asciidoc.js +167 -0
- data/lib/gollum/frontend/public/javascript/editor/langs/creole.js +104 -0
- data/lib/gollum/frontend/public/javascript/editor/langs/markdown.js +211 -0
- data/lib/gollum/frontend/public/javascript/editor/langs/org.js +173 -0
- data/lib/gollum/frontend/public/javascript/editor/langs/pod.js +111 -0
- data/lib/gollum/frontend/public/javascript/editor/langs/rdoc.js +74 -0
- data/lib/gollum/frontend/public/javascript/editor/langs/textile.js +175 -0
- data/lib/gollum/frontend/public/javascript/gollum.dialog.js +263 -0
- data/lib/gollum/frontend/public/javascript/gollum.js +161 -0
- data/lib/gollum/frontend/public/javascript/gollum.placeholder.js +54 -0
- data/lib/gollum/frontend/public/javascript/jquery.color.js +123 -0
- data/lib/gollum/frontend/public/javascript/jquery.js +7179 -0
- data/lib/gollum/frontend/templates/compare.mustache +38 -0
- data/lib/gollum/frontend/templates/create.mustache +17 -0
- data/lib/gollum/frontend/templates/edit.mustache +17 -0
- data/lib/gollum/frontend/templates/editor.mustache +116 -0
- data/lib/gollum/frontend/templates/error.mustache +8 -0
- data/lib/gollum/frontend/templates/history.mustache +58 -0
- data/lib/gollum/frontend/templates/layout.mustache +28 -0
- data/lib/gollum/frontend/templates/page.mustache +37 -0
- data/lib/gollum/frontend/templates/pages.mustache +35 -0
- data/lib/gollum/frontend/templates/search.mustache +36 -0
- data/lib/gollum/frontend/templates/searchbar.mustache +10 -0
- data/lib/gollum/frontend/views/compare.rb +94 -0
- data/lib/gollum/frontend/views/create.rb +48 -0
- data/lib/gollum/frontend/views/edit.rb +52 -0
- data/lib/gollum/frontend/views/editable.rb +13 -0
- data/lib/gollum/frontend/views/error.rb +7 -0
- data/lib/gollum/frontend/views/history.rb +44 -0
- data/lib/gollum/frontend/views/layout.rb +20 -0
- data/lib/gollum/frontend/views/page.rb +57 -0
- data/lib/gollum/frontend/views/pages.rb +19 -0
- data/lib/gollum/frontend/views/search.rb +20 -0
- data/lib/gollum/git_access.rb +248 -0
- data/lib/gollum/markup.rb +489 -0
- data/lib/gollum/page.rb +430 -0
- data/lib/gollum/pagination.rb +61 -0
- data/lib/gollum/sanitization.rb +174 -0
- data/lib/gollum/tex.rb +89 -0
- data/lib/gollum/web_sequence_diagram.rb +43 -0
- data/lib/gollum/wiki.rb +636 -0
- data/read-only-gollum.gemspec +224 -0
- data/templates/formatting.html +92 -0
- data/test/examples/empty.git/HEAD +1 -0
- data/test/examples/empty.git/config +5 -0
- data/test/examples/empty.git/description +1 -0
- data/test/examples/empty.git/hooks/applypatch-msg.sample +15 -0
- data/test/examples/empty.git/hooks/commit-msg.sample +24 -0
- data/test/examples/empty.git/hooks/post-commit.sample +8 -0
- data/test/examples/empty.git/hooks/post-receive.sample +15 -0
- data/test/examples/empty.git/hooks/post-update.sample +8 -0
- data/test/examples/empty.git/hooks/pre-applypatch.sample +14 -0
- data/test/examples/empty.git/hooks/pre-commit.sample +46 -0
- data/test/examples/empty.git/hooks/pre-rebase.sample +169 -0
- data/test/examples/empty.git/hooks/prepare-commit-msg.sample +36 -0
- data/test/examples/empty.git/hooks/update.sample +128 -0
- data/test/examples/empty.git/info/exclude +6 -0
- data/test/examples/empty.git/objects/info/.gitkeep +0 -0
- data/test/examples/empty.git/objects/pack/.gitkeep +0 -0
- data/test/examples/empty.git/refs/heads/.gitkeep +0 -0
- data/test/examples/lotr.git/COMMIT_EDITMSG +1 -0
- data/test/examples/lotr.git/HEAD +1 -0
- data/test/examples/lotr.git/ORIG_HEAD +1 -0
- data/test/examples/lotr.git/config +12 -0
- data/test/examples/lotr.git/description +1 -0
- data/test/examples/lotr.git/index +0 -0
- data/test/examples/lotr.git/info/exclude +6 -0
- data/test/examples/lotr.git/logs/HEAD +3 -0
- data/test/examples/lotr.git/logs/refs/heads/master +3 -0
- data/test/examples/lotr.git/objects/06/131480411710c92a82fe2d1e76932c70feb2e5 +0 -0
- data/test/examples/lotr.git/objects/0a/de1e2916346d4c1f2fb63b863fd3c16808fe44 +0 -0
- data/test/examples/lotr.git/objects/0e/d8cbe0a25235bd867e65193c7d837c66b328ef +3 -0
- data/test/examples/lotr.git/objects/12/629d666c5e3178f82f533f543d61b53dc78c0b +0 -0
- data/test/examples/lotr.git/objects/1d/b89ebba7e2c14d93b94ff98cfa3708a4f0d4e3 +2 -0
- data/test/examples/lotr.git/objects/24/49c2681badfd3c189e8ed658dacffe8ba48fe5 +0 -0
- data/test/examples/lotr.git/objects/25/4bdc1ba27d8b8a794538a8522d9a2b56ec2dd9 +0 -0
- data/test/examples/lotr.git/objects/2c/b9156ad383914561a8502fc70f5a1d887e48ad +4 -0
- data/test/examples/lotr.git/objects/5d/cac289a8603188d2c5caf481dcba2985126aaa +0 -0
- data/test/examples/lotr.git/objects/60/f12f4254f58801b9ee7db7bca5fa8aeefaa56b +0 -0
- data/test/examples/lotr.git/objects/71/4323c104239440a5c66ab12a67ed07a83c404f +0 -0
- data/test/examples/lotr.git/objects/84/0ec5b1ba1320e8ec443f28f99566f615d5af10 +0 -0
- data/test/examples/lotr.git/objects/93/6b83ee0dd8837adb82511e40d5e4ebe59bb675 +0 -0
- data/test/examples/lotr.git/objects/94/523d7ae48aeba575099dd12926420d8fd0425d +2 -0
- data/test/examples/lotr.git/objects/96/97dc65e095658bbd1b8e8678e08881e86d32f1 +0 -0
- data/test/examples/lotr.git/objects/a3/1ca2a7c352c92531a8b99815d15843b259e814 +0 -0
- data/test/examples/lotr.git/objects/a6/59b3763b822dd97544621fd0beef162ea37b14 +4 -0
- data/test/examples/lotr.git/objects/a8/ad3c09dd842a3517085bfadd37718856dee813 +0 -0
- data/test/examples/lotr.git/objects/aa/b61fe89d56f8614c0a8151da34f939dcedfa68 +0 -0
- data/test/examples/lotr.git/objects/bc/4b5fc0ce2c2ba3acef6647e4f67256ee45ab60 +0 -0
- data/test/examples/lotr.git/objects/c3/b43e9f08966b088e7a0192e436b7a884542e05 +0 -0
- data/test/examples/lotr.git/objects/dc/596d6b2dd89ab05c66f4abd7d5eb706bc17f19 +0 -0
- data/test/examples/lotr.git/objects/ec/da3205bee14520aab5a7bb307392064b938e83 +0 -0
- data/test/examples/lotr.git/objects/f4/84ebb1f40f8eb20d1bcd8d1d71934d2b8ae961 +0 -0
- data/test/examples/lotr.git/objects/fa/e7ef5344202bba4129abdc13060d9297d99465 +3 -0
- data/test/examples/lotr.git/objects/info/packs +2 -0
- data/test/examples/lotr.git/objects/pack/pack-dcbeaf3f6ff6c5eb08ea2b0a2d83626e8763546b.idx +0 -0
- data/test/examples/lotr.git/objects/pack/pack-dcbeaf3f6ff6c5eb08ea2b0a2d83626e8763546b.pack +0 -0
- data/test/examples/lotr.git/packed-refs +2 -0
- data/test/examples/lotr.git/refs/heads/master +1 -0
- data/test/examples/lotr.git/refs/remotes/origin/HEAD +1 -0
- data/test/examples/page_file_dir.git/COMMIT_EDITMSG +1 -0
- data/test/examples/page_file_dir.git/HEAD +1 -0
- data/test/examples/page_file_dir.git/config +6 -0
- data/test/examples/page_file_dir.git/description +1 -0
- data/test/examples/page_file_dir.git/index +0 -0
- data/test/examples/page_file_dir.git/info/exclude +6 -0
- data/test/examples/page_file_dir.git/logs/HEAD +1 -0
- data/test/examples/page_file_dir.git/logs/refs/heads/master +1 -0
- data/test/examples/page_file_dir.git/objects/0c/7d27db1f575263efdcab3dc650f4502a2dbcbf +0 -0
- data/test/examples/page_file_dir.git/objects/22/b404803c966dd92865614d86ff22ca12e50c1e +0 -0
- data/test/examples/page_file_dir.git/objects/25/7cc5642cb1a054f08cc83f2d943e56fd3ebe99 +0 -0
- data/test/examples/page_file_dir.git/objects/57/16ca5987cbf97d6bb54920bea6adde242d87e6 +0 -0
- data/test/examples/page_file_dir.git/objects/5b/43e14e0a15fb6f08feab1773d1c0991e9f71e2 +0 -0
- data/test/examples/page_file_dir.git/refs/heads/master +1 -0
- data/test/examples/revert.git/COMMIT_EDITMSG +1 -0
- data/test/examples/revert.git/HEAD +1 -0
- data/test/examples/revert.git/config +12 -0
- data/test/examples/revert.git/description +1 -0
- data/test/examples/revert.git/index +0 -0
- data/test/examples/revert.git/info/exclude +6 -0
- data/test/examples/revert.git/logs/HEAD +2 -0
- data/test/examples/revert.git/logs/refs/heads/master +2 -0
- data/test/examples/revert.git/objects/20/2ced67cea93c7b6bd2928aa1daef8d1d55a20d +0 -0
- data/test/examples/revert.git/objects/41/76394bfa11222363c66ce7e84b5f154095b6d9 +0 -0
- data/test/examples/revert.git/objects/6a/69f92020f5df77af6e8813ff1232493383b708 +0 -0
- data/test/examples/revert.git/objects/b4/785957bc986dc39c629de9fac9df46972c00fc +0 -0
- data/test/examples/revert.git/objects/f4/03b791119f8232b7cb0ba455c624ac6435f433 +0 -0
- data/test/examples/revert.git/objects/info/packs +2 -0
- data/test/examples/revert.git/objects/pack/pack-a561f8437234f74d0bacb9e0eebe52d207f5770d.idx +0 -0
- data/test/examples/revert.git/objects/pack/pack-a561f8437234f74d0bacb9e0eebe52d207f5770d.pack +0 -0
- data/test/examples/revert.git/packed-refs +2 -0
- data/test/examples/revert.git/refs/heads/master +1 -0
- data/test/examples/revert.git/refs/remotes/origin/HEAD +1 -0
- data/test/examples/yubiwa.git/HEAD +1 -0
- data/test/examples/yubiwa.git/config +5 -0
- data/test/examples/yubiwa.git/description +1 -0
- data/test/examples/yubiwa.git/info/exclude +6 -0
- data/test/examples/yubiwa.git/objects/10/fa2ddc4e3b4009d8a453aace10bd6148c1ad00 +0 -0
- data/test/examples/yubiwa.git/objects/52/4b82874327ea7cbf730389964ba7cb3de966de +0 -0
- data/test/examples/yubiwa.git/objects/58/3fc201cb457fb3f1480f3e1e5999b119633835 +0 -0
- data/test/examples/yubiwa.git/objects/87/bc1dd46ab3d3874d4e898d45dd512cc20a7cc8 +1 -0
- data/test/examples/yubiwa.git/objects/89/64ed1b4e21aa90e831763bbce9034bfda81b70 +0 -0
- data/test/examples/yubiwa.git/objects/9f/f6dd0660da5fba2d3374adb2b84fa653bb538b +0 -0
- data/test/examples/yubiwa.git/objects/ac/e97abf2b177815a1972d7db22f229f58c83309 +0 -0
- data/test/examples/yubiwa.git/objects/b1/f443863a4816628807fbf86141ebef055dda34 +0 -0
- data/test/examples/yubiwa.git/refs/heads/master +1 -0
- data/test/helper.rb +66 -0
- data/test/test_app.rb +169 -0
- data/test/test_committer.rb +64 -0
- data/test/test_file.rb +27 -0
- data/test/test_git_access.rb +52 -0
- data/test/test_markup.rb +628 -0
- data/test/test_page.rb +166 -0
- data/test/test_page_revert.rb +45 -0
- data/test/test_wiki.rb +462 -0
- metadata +470 -0
data/Rakefile
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
#############################################################################
|
6
|
+
#
|
7
|
+
# Helper functions
|
8
|
+
#
|
9
|
+
#############################################################################
|
10
|
+
|
11
|
+
def name
|
12
|
+
@name ||= Dir['*.gemspec'].first.split('.').first
|
13
|
+
end
|
14
|
+
|
15
|
+
def version
|
16
|
+
line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
|
17
|
+
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
18
|
+
end
|
19
|
+
|
20
|
+
def date
|
21
|
+
Date.today.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def rubyforge_project
|
25
|
+
name
|
26
|
+
end
|
27
|
+
|
28
|
+
def gemspec_file
|
29
|
+
"#{name}.gemspec"
|
30
|
+
end
|
31
|
+
|
32
|
+
def gem_file
|
33
|
+
"#{name}-#{version}.gem"
|
34
|
+
end
|
35
|
+
|
36
|
+
def replace_header(head, header_name)
|
37
|
+
head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
|
38
|
+
end
|
39
|
+
|
40
|
+
#############################################################################
|
41
|
+
#
|
42
|
+
# Standard tasks
|
43
|
+
#
|
44
|
+
#############################################################################
|
45
|
+
|
46
|
+
task :default => :test
|
47
|
+
|
48
|
+
require 'rake/testtask'
|
49
|
+
Rake::TestTask.new(:test) do |test|
|
50
|
+
test.libs << 'lib' << 'test' << '.'
|
51
|
+
test.pattern = 'test/**/test_*.rb'
|
52
|
+
test.verbose = true
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "Generate RCov test coverage and open in your browser"
|
56
|
+
task :coverage do
|
57
|
+
require 'rcov'
|
58
|
+
sh "rm -fr coverage"
|
59
|
+
sh "rcov test/test_*.rb"
|
60
|
+
sh "open coverage/index.html"
|
61
|
+
end
|
62
|
+
|
63
|
+
desc "Open an irb session preloaded with this library"
|
64
|
+
task :console do
|
65
|
+
sh "irb -rubygems -r ./lib/#{name}.rb"
|
66
|
+
end
|
67
|
+
|
68
|
+
#############################################################################
|
69
|
+
#
|
70
|
+
# Custom tasks (add your own tasks here)
|
71
|
+
#
|
72
|
+
#############################################################################
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
#############################################################################
|
77
|
+
#
|
78
|
+
# Packaging tasks
|
79
|
+
#
|
80
|
+
#############################################################################
|
81
|
+
|
82
|
+
desc 'Create a release build'
|
83
|
+
task :release => :build do
|
84
|
+
unless `git branch` =~ /^\* master$/
|
85
|
+
puts "You must be on the master branch to release!"
|
86
|
+
exit!
|
87
|
+
end
|
88
|
+
sh "git commit --allow-empty -a -m 'Release #{version}'"
|
89
|
+
sh "git tag v#{version}"
|
90
|
+
sh "git push origin master"
|
91
|
+
sh "git push origin v#{version}"
|
92
|
+
sh "gem push pkg/#{name}-#{version}.gem"
|
93
|
+
end
|
94
|
+
|
95
|
+
desc 'Build gem'
|
96
|
+
task :build => :gemspec do
|
97
|
+
sh "mkdir -p pkg"
|
98
|
+
sh "gem build #{gemspec_file}"
|
99
|
+
sh "mv #{gem_file} pkg"
|
100
|
+
end
|
101
|
+
|
102
|
+
desc 'Validate gemspec'
|
103
|
+
task :gemspec => :validate do
|
104
|
+
# read spec file and split out manifest section
|
105
|
+
spec = File.read(gemspec_file)
|
106
|
+
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
107
|
+
|
108
|
+
# replace name version and date
|
109
|
+
replace_header(head, :name)
|
110
|
+
replace_header(head, :version)
|
111
|
+
replace_header(head, :date)
|
112
|
+
#comment this out if your rubyforge_project has a different name
|
113
|
+
replace_header(head, :rubyforge_project)
|
114
|
+
|
115
|
+
# determine file list from git ls-files
|
116
|
+
files = `git ls-files`.
|
117
|
+
split("\n").
|
118
|
+
sort.
|
119
|
+
reject { |file| file =~ /^\./ }.
|
120
|
+
reject { |file| file =~ /^(rdoc|pkg)/ }.
|
121
|
+
map { |file| " #{file}" }.
|
122
|
+
join("\n")
|
123
|
+
|
124
|
+
# piece file back together and write
|
125
|
+
manifest = " s.files = %w[\n#{files}\n ]\n"
|
126
|
+
spec = [head, manifest, tail].join(" # = MANIFEST =\n")
|
127
|
+
File.open(gemspec_file, 'w') { |io| io.write(spec) }
|
128
|
+
puts "Updated #{gemspec_file}"
|
129
|
+
end
|
130
|
+
|
131
|
+
desc 'Validate lib files and version file'
|
132
|
+
task :validate do
|
133
|
+
libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
|
134
|
+
unless libfiles.empty?
|
135
|
+
puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
|
136
|
+
exit!
|
137
|
+
end
|
138
|
+
unless Dir['VERSION*'].empty?
|
139
|
+
puts "A `VERSION` file at root level violates Gem best practices."
|
140
|
+
exit!
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
4
|
+
|
5
|
+
help = <<HELP
|
6
|
+
Gollum is a multi-format Wiki Engine/API/Frontend.
|
7
|
+
|
8
|
+
Basic Command Line Usage:
|
9
|
+
gollum [OPTIONS] [PATH]
|
10
|
+
|
11
|
+
PATH The path to the Gollum repository (default .).
|
12
|
+
|
13
|
+
Options:
|
14
|
+
HELP
|
15
|
+
|
16
|
+
require 'optparse'
|
17
|
+
require 'rubygems'
|
18
|
+
require 'gollum'
|
19
|
+
|
20
|
+
exec = {}
|
21
|
+
options = { 'port' => 4567, 'bind' => '0.0.0.0' }
|
22
|
+
wiki_options = {}
|
23
|
+
|
24
|
+
opts = OptionParser.new do |opts|
|
25
|
+
opts.banner = help
|
26
|
+
|
27
|
+
opts.on("--port [PORT]", "Bind port (default 4567).") do |port|
|
28
|
+
options['port'] = port.to_i
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on("--host [HOST]", "Hostname or IP address to listen on (default 0.0.0.0).") do |host|
|
32
|
+
options['bind'] = host
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on("--version", "Display current version.") do
|
36
|
+
puts "Gollum " + Gollum::VERSION
|
37
|
+
exit 0
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on("--config [CONFIG]", "Path to additional configuration file") do |config|
|
41
|
+
options['config'] = config
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on("--irb", "Start an irb process with gollum loaded for the current wiki.") do
|
45
|
+
options['irb'] = true
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.on("--page-file-dir [PATH]", "Specify the sub directory for all page files (default: repository root).") do |path|
|
49
|
+
wiki_options[:page_file_dir] = path
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on("--ref [REF]", "Specify the repository ref to use (default: master).") do |ref|
|
53
|
+
wiki_options[:ref] = ref
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Read command line options into `options` hash
|
58
|
+
begin
|
59
|
+
opts.parse!
|
60
|
+
rescue OptionParser::InvalidOption
|
61
|
+
puts "gollum: #{$!.message}"
|
62
|
+
puts "gollum: try 'gollum --help' for more information"
|
63
|
+
exit
|
64
|
+
end
|
65
|
+
|
66
|
+
gollum_path = ARGV[0] || Dir.pwd
|
67
|
+
|
68
|
+
if options['irb']
|
69
|
+
require 'irb'
|
70
|
+
# http://jameskilton.com/2009/04/02/embedding-irb-into-your-ruby-application/
|
71
|
+
module IRB # :nodoc:
|
72
|
+
def self.start_session(binding)
|
73
|
+
unless @__initialized
|
74
|
+
args = ARGV
|
75
|
+
ARGV.replace(ARGV.dup)
|
76
|
+
IRB.setup(nil)
|
77
|
+
ARGV.replace(args)
|
78
|
+
@__initialized = true
|
79
|
+
end
|
80
|
+
|
81
|
+
ws = WorkSpace.new(binding)
|
82
|
+
irb = Irb.new(ws)
|
83
|
+
|
84
|
+
@CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
|
85
|
+
@CONF[:MAIN_CONTEXT] = irb.context
|
86
|
+
|
87
|
+
catch(:IRB_EXIT) do
|
88
|
+
irb.eval_input
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
begin
|
94
|
+
wiki = Gollum::Wiki.new(gollum_path, wiki_options)
|
95
|
+
if !wiki.exist? then raise Grit::InvalidGitRepositoryError end
|
96
|
+
puts "Loaded Gollum wiki at #{File.expand_path(gollum_path).inspect}."
|
97
|
+
puts
|
98
|
+
puts %( page = wiki.page('page-name'))
|
99
|
+
puts %( # => <Gollum::Page>)
|
100
|
+
puts
|
101
|
+
puts %( page.raw_data)
|
102
|
+
puts %( # => "# My wiki page")
|
103
|
+
puts
|
104
|
+
puts %( page.formatted_data)
|
105
|
+
puts %( # => "<h1>My wiki page</h1>")
|
106
|
+
puts
|
107
|
+
puts "Check out the Gollum README for more."
|
108
|
+
IRB.start_session(binding)
|
109
|
+
rescue Grit::InvalidGitRepositoryError, Grit::NoSuchPathError
|
110
|
+
puts "Invalid Gollum wiki at #{File.expand_path(gollum_path).inspect}"
|
111
|
+
exit 0
|
112
|
+
end
|
113
|
+
else
|
114
|
+
require 'gollum/frontend/app'
|
115
|
+
Precious::App.set(:gollum_path, gollum_path)
|
116
|
+
Precious::App.set(:wiki_options, wiki_options)
|
117
|
+
|
118
|
+
if cfg = options['config']
|
119
|
+
# If the path begins with a '/' it will be considered an absolute path,
|
120
|
+
# otherwise it will be relative to the CWD
|
121
|
+
cfg = File.join(Dir.getwd, cfg) unless cfg.slice(0) == File::SEPARATOR
|
122
|
+
require cfg
|
123
|
+
end
|
124
|
+
|
125
|
+
Precious::App.run!(options)
|
126
|
+
end
|
@@ -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,41 @@
|
|
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
|
+
require File.expand_path('../gollum/tex', __FILE__)
|
21
|
+
require File.expand_path('../gollum/web_sequence_diagram', __FILE__)
|
22
|
+
|
23
|
+
module Gollum
|
24
|
+
VERSION = '1.4.0'
|
25
|
+
|
26
|
+
class Error < StandardError; end
|
27
|
+
|
28
|
+
class DuplicatePageError < Error
|
29
|
+
attr_accessor :dir
|
30
|
+
attr_accessor :existing_path
|
31
|
+
attr_accessor :attempted_path
|
32
|
+
|
33
|
+
def initialize(dir, existing, attempted, message = nil)
|
34
|
+
@dir = dir
|
35
|
+
@existing_path = existing
|
36
|
+
@attempted_path = attempted
|
37
|
+
super(message || "Cannot write #{@dir}/#{@attempted_path}, found #{@dir}/#{@existing_path}.")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -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,218 @@
|
|
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 filename_stripped.
|
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 or the stripped filename
|
115
|
+
# (should be pre-canonicalized if required).
|
116
|
+
# format - The Symbol format of the page.
|
117
|
+
#
|
118
|
+
# Returns nothing.
|
119
|
+
def update_working_dir(dir, name, format)
|
120
|
+
unless @wiki.repo.bare
|
121
|
+
if @wiki.page_file_dir
|
122
|
+
dir = dir.size.zero? ? @wiki.page_file_dir : ::File.join(dir, @wiki.page_file_dir)
|
123
|
+
end
|
124
|
+
|
125
|
+
path =
|
126
|
+
if dir == ''
|
127
|
+
@wiki.page_file_name(name, format)
|
128
|
+
else
|
129
|
+
::File.join(dir, @wiki.page_file_name(name, format))
|
130
|
+
end
|
131
|
+
|
132
|
+
Dir.chdir(::File.join(@wiki.repo.path, '..')) do
|
133
|
+
if file_path_scheduled_for_deletion?(index.tree, path)
|
134
|
+
@wiki.repo.git.rm({'f' => true}, '--', path)
|
135
|
+
else
|
136
|
+
@wiki.repo.git.checkout({}, 'HEAD', '--', path)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Writes the commit to Git and runs the after_commit callbacks.
|
143
|
+
#
|
144
|
+
# Returns the String SHA1 of the new commit.
|
145
|
+
def commit
|
146
|
+
sha1 = index.commit(@options[:message], parents, actor, nil, @wiki.ref)
|
147
|
+
@callbacks.each do |cb|
|
148
|
+
cb.call(self, sha1)
|
149
|
+
end
|
150
|
+
sha1
|
151
|
+
end
|
152
|
+
|
153
|
+
# Adds a callback to be fired after a commit.
|
154
|
+
#
|
155
|
+
# block - A block that expects this Committer instance and the created
|
156
|
+
# commit's SHA1 as the arguments.
|
157
|
+
#
|
158
|
+
# Returns nothing.
|
159
|
+
def after_commit(&block)
|
160
|
+
@callbacks << block
|
161
|
+
end
|
162
|
+
|
163
|
+
# Determine if a given page (regardless of format) is scheduled to be
|
164
|
+
# deleted in the next commit for the given Index.
|
165
|
+
#
|
166
|
+
# map - The Hash map:
|
167
|
+
# key - The String directory or filename.
|
168
|
+
# val - The Hash submap or the String contents of the file.
|
169
|
+
# path - The String path of the page file. This may include the format
|
170
|
+
# extension in which case it will be ignored.
|
171
|
+
#
|
172
|
+
# Returns the Boolean response.
|
173
|
+
def page_path_scheduled_for_deletion?(map, path)
|
174
|
+
parts = path.split('/')
|
175
|
+
if parts.size == 1
|
176
|
+
deletions = map.keys.select { |k| !map[k] }
|
177
|
+
downfile = parts.first.downcase.sub(/\.\w+$/, '')
|
178
|
+
deletions.any? { |d| d.downcase.sub(/\.\w+$/, '') == downfile }
|
179
|
+
else
|
180
|
+
part = parts.shift
|
181
|
+
if rest = map[part]
|
182
|
+
page_path_scheduled_for_deletion?(rest, parts.join('/'))
|
183
|
+
else
|
184
|
+
false
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Determine if a given file is scheduled to be deleted in the next commit
|
190
|
+
# for the given Index.
|
191
|
+
#
|
192
|
+
# map - The Hash map:
|
193
|
+
# key - The String directory or filename.
|
194
|
+
# val - The Hash submap or the String contents of the file.
|
195
|
+
# path - The String path of the file including extension.
|
196
|
+
#
|
197
|
+
# Returns the Boolean response.
|
198
|
+
def file_path_scheduled_for_deletion?(map, path)
|
199
|
+
parts = path.split('/')
|
200
|
+
if parts.size == 1
|
201
|
+
deletions = map.keys.select { |k| !map[k] }
|
202
|
+
deletions.any? { |d| d == parts.first }
|
203
|
+
else
|
204
|
+
part = parts.shift
|
205
|
+
if rest = map[part]
|
206
|
+
file_path_scheduled_for_deletion?(rest, parts.join('/'))
|
207
|
+
else
|
208
|
+
false
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Proxies methods t
|
214
|
+
def method_missing(name, *args)
|
215
|
+
index.send(name, *args)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|