read-only-gollum 1.4.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.
Files changed (176) hide show
  1. data/Gemfile +4 -0
  2. data/HISTORY.md +102 -0
  3. data/Home.md +3 -0
  4. data/LICENSE +21 -0
  5. data/README.md +477 -0
  6. data/Rakefile +142 -0
  7. data/bin/read-only-gollum +126 -0
  8. data/docs/sanitization.md +32 -0
  9. data/lib/gollum.rb +41 -0
  10. data/lib/gollum/blob_entry.rb +78 -0
  11. data/lib/gollum/committer.rb +218 -0
  12. data/lib/gollum/file.rb +64 -0
  13. data/lib/gollum/frontend/app.rb +225 -0
  14. data/lib/gollum/frontend/public/css/dialog.css +141 -0
  15. data/lib/gollum/frontend/public/css/editor.css +537 -0
  16. data/lib/gollum/frontend/public/css/gollum.css +660 -0
  17. data/lib/gollum/frontend/public/css/ie7.css +69 -0
  18. data/lib/gollum/frontend/public/css/template.css +381 -0
  19. data/lib/gollum/frontend/public/images/icon-sprite.png +0 -0
  20. data/lib/gollum/frontend/public/javascript/editor/gollum.editor.js +1096 -0
  21. data/lib/gollum/frontend/public/javascript/editor/langs/asciidoc.js +167 -0
  22. data/lib/gollum/frontend/public/javascript/editor/langs/creole.js +104 -0
  23. data/lib/gollum/frontend/public/javascript/editor/langs/markdown.js +211 -0
  24. data/lib/gollum/frontend/public/javascript/editor/langs/org.js +173 -0
  25. data/lib/gollum/frontend/public/javascript/editor/langs/pod.js +111 -0
  26. data/lib/gollum/frontend/public/javascript/editor/langs/rdoc.js +74 -0
  27. data/lib/gollum/frontend/public/javascript/editor/langs/textile.js +175 -0
  28. data/lib/gollum/frontend/public/javascript/gollum.dialog.js +263 -0
  29. data/lib/gollum/frontend/public/javascript/gollum.js +161 -0
  30. data/lib/gollum/frontend/public/javascript/gollum.placeholder.js +54 -0
  31. data/lib/gollum/frontend/public/javascript/jquery.color.js +123 -0
  32. data/lib/gollum/frontend/public/javascript/jquery.js +7179 -0
  33. data/lib/gollum/frontend/templates/compare.mustache +38 -0
  34. data/lib/gollum/frontend/templates/create.mustache +17 -0
  35. data/lib/gollum/frontend/templates/edit.mustache +17 -0
  36. data/lib/gollum/frontend/templates/editor.mustache +116 -0
  37. data/lib/gollum/frontend/templates/error.mustache +8 -0
  38. data/lib/gollum/frontend/templates/history.mustache +58 -0
  39. data/lib/gollum/frontend/templates/layout.mustache +28 -0
  40. data/lib/gollum/frontend/templates/page.mustache +37 -0
  41. data/lib/gollum/frontend/templates/pages.mustache +35 -0
  42. data/lib/gollum/frontend/templates/search.mustache +36 -0
  43. data/lib/gollum/frontend/templates/searchbar.mustache +10 -0
  44. data/lib/gollum/frontend/views/compare.rb +94 -0
  45. data/lib/gollum/frontend/views/create.rb +48 -0
  46. data/lib/gollum/frontend/views/edit.rb +52 -0
  47. data/lib/gollum/frontend/views/editable.rb +13 -0
  48. data/lib/gollum/frontend/views/error.rb +7 -0
  49. data/lib/gollum/frontend/views/history.rb +44 -0
  50. data/lib/gollum/frontend/views/layout.rb +20 -0
  51. data/lib/gollum/frontend/views/page.rb +57 -0
  52. data/lib/gollum/frontend/views/pages.rb +19 -0
  53. data/lib/gollum/frontend/views/search.rb +20 -0
  54. data/lib/gollum/git_access.rb +248 -0
  55. data/lib/gollum/markup.rb +489 -0
  56. data/lib/gollum/page.rb +430 -0
  57. data/lib/gollum/pagination.rb +61 -0
  58. data/lib/gollum/sanitization.rb +174 -0
  59. data/lib/gollum/tex.rb +89 -0
  60. data/lib/gollum/web_sequence_diagram.rb +43 -0
  61. data/lib/gollum/wiki.rb +636 -0
  62. data/read-only-gollum.gemspec +224 -0
  63. data/templates/formatting.html +92 -0
  64. data/test/examples/empty.git/HEAD +1 -0
  65. data/test/examples/empty.git/config +5 -0
  66. data/test/examples/empty.git/description +1 -0
  67. data/test/examples/empty.git/hooks/applypatch-msg.sample +15 -0
  68. data/test/examples/empty.git/hooks/commit-msg.sample +24 -0
  69. data/test/examples/empty.git/hooks/post-commit.sample +8 -0
  70. data/test/examples/empty.git/hooks/post-receive.sample +15 -0
  71. data/test/examples/empty.git/hooks/post-update.sample +8 -0
  72. data/test/examples/empty.git/hooks/pre-applypatch.sample +14 -0
  73. data/test/examples/empty.git/hooks/pre-commit.sample +46 -0
  74. data/test/examples/empty.git/hooks/pre-rebase.sample +169 -0
  75. data/test/examples/empty.git/hooks/prepare-commit-msg.sample +36 -0
  76. data/test/examples/empty.git/hooks/update.sample +128 -0
  77. data/test/examples/empty.git/info/exclude +6 -0
  78. data/test/examples/empty.git/objects/info/.gitkeep +0 -0
  79. data/test/examples/empty.git/objects/pack/.gitkeep +0 -0
  80. data/test/examples/empty.git/refs/heads/.gitkeep +0 -0
  81. data/test/examples/lotr.git/COMMIT_EDITMSG +1 -0
  82. data/test/examples/lotr.git/HEAD +1 -0
  83. data/test/examples/lotr.git/ORIG_HEAD +1 -0
  84. data/test/examples/lotr.git/config +12 -0
  85. data/test/examples/lotr.git/description +1 -0
  86. data/test/examples/lotr.git/index +0 -0
  87. data/test/examples/lotr.git/info/exclude +6 -0
  88. data/test/examples/lotr.git/logs/HEAD +3 -0
  89. data/test/examples/lotr.git/logs/refs/heads/master +3 -0
  90. data/test/examples/lotr.git/objects/06/131480411710c92a82fe2d1e76932c70feb2e5 +0 -0
  91. data/test/examples/lotr.git/objects/0a/de1e2916346d4c1f2fb63b863fd3c16808fe44 +0 -0
  92. data/test/examples/lotr.git/objects/0e/d8cbe0a25235bd867e65193c7d837c66b328ef +3 -0
  93. data/test/examples/lotr.git/objects/12/629d666c5e3178f82f533f543d61b53dc78c0b +0 -0
  94. data/test/examples/lotr.git/objects/1d/b89ebba7e2c14d93b94ff98cfa3708a4f0d4e3 +2 -0
  95. data/test/examples/lotr.git/objects/24/49c2681badfd3c189e8ed658dacffe8ba48fe5 +0 -0
  96. data/test/examples/lotr.git/objects/25/4bdc1ba27d8b8a794538a8522d9a2b56ec2dd9 +0 -0
  97. data/test/examples/lotr.git/objects/2c/b9156ad383914561a8502fc70f5a1d887e48ad +4 -0
  98. data/test/examples/lotr.git/objects/5d/cac289a8603188d2c5caf481dcba2985126aaa +0 -0
  99. data/test/examples/lotr.git/objects/60/f12f4254f58801b9ee7db7bca5fa8aeefaa56b +0 -0
  100. data/test/examples/lotr.git/objects/71/4323c104239440a5c66ab12a67ed07a83c404f +0 -0
  101. data/test/examples/lotr.git/objects/84/0ec5b1ba1320e8ec443f28f99566f615d5af10 +0 -0
  102. data/test/examples/lotr.git/objects/93/6b83ee0dd8837adb82511e40d5e4ebe59bb675 +0 -0
  103. data/test/examples/lotr.git/objects/94/523d7ae48aeba575099dd12926420d8fd0425d +2 -0
  104. data/test/examples/lotr.git/objects/96/97dc65e095658bbd1b8e8678e08881e86d32f1 +0 -0
  105. data/test/examples/lotr.git/objects/a3/1ca2a7c352c92531a8b99815d15843b259e814 +0 -0
  106. data/test/examples/lotr.git/objects/a6/59b3763b822dd97544621fd0beef162ea37b14 +4 -0
  107. data/test/examples/lotr.git/objects/a8/ad3c09dd842a3517085bfadd37718856dee813 +0 -0
  108. data/test/examples/lotr.git/objects/aa/b61fe89d56f8614c0a8151da34f939dcedfa68 +0 -0
  109. data/test/examples/lotr.git/objects/bc/4b5fc0ce2c2ba3acef6647e4f67256ee45ab60 +0 -0
  110. data/test/examples/lotr.git/objects/c3/b43e9f08966b088e7a0192e436b7a884542e05 +0 -0
  111. data/test/examples/lotr.git/objects/dc/596d6b2dd89ab05c66f4abd7d5eb706bc17f19 +0 -0
  112. data/test/examples/lotr.git/objects/ec/da3205bee14520aab5a7bb307392064b938e83 +0 -0
  113. data/test/examples/lotr.git/objects/f4/84ebb1f40f8eb20d1bcd8d1d71934d2b8ae961 +0 -0
  114. data/test/examples/lotr.git/objects/fa/e7ef5344202bba4129abdc13060d9297d99465 +3 -0
  115. data/test/examples/lotr.git/objects/info/packs +2 -0
  116. data/test/examples/lotr.git/objects/pack/pack-dcbeaf3f6ff6c5eb08ea2b0a2d83626e8763546b.idx +0 -0
  117. data/test/examples/lotr.git/objects/pack/pack-dcbeaf3f6ff6c5eb08ea2b0a2d83626e8763546b.pack +0 -0
  118. data/test/examples/lotr.git/packed-refs +2 -0
  119. data/test/examples/lotr.git/refs/heads/master +1 -0
  120. data/test/examples/lotr.git/refs/remotes/origin/HEAD +1 -0
  121. data/test/examples/page_file_dir.git/COMMIT_EDITMSG +1 -0
  122. data/test/examples/page_file_dir.git/HEAD +1 -0
  123. data/test/examples/page_file_dir.git/config +6 -0
  124. data/test/examples/page_file_dir.git/description +1 -0
  125. data/test/examples/page_file_dir.git/index +0 -0
  126. data/test/examples/page_file_dir.git/info/exclude +6 -0
  127. data/test/examples/page_file_dir.git/logs/HEAD +1 -0
  128. data/test/examples/page_file_dir.git/logs/refs/heads/master +1 -0
  129. data/test/examples/page_file_dir.git/objects/0c/7d27db1f575263efdcab3dc650f4502a2dbcbf +0 -0
  130. data/test/examples/page_file_dir.git/objects/22/b404803c966dd92865614d86ff22ca12e50c1e +0 -0
  131. data/test/examples/page_file_dir.git/objects/25/7cc5642cb1a054f08cc83f2d943e56fd3ebe99 +0 -0
  132. data/test/examples/page_file_dir.git/objects/57/16ca5987cbf97d6bb54920bea6adde242d87e6 +0 -0
  133. data/test/examples/page_file_dir.git/objects/5b/43e14e0a15fb6f08feab1773d1c0991e9f71e2 +0 -0
  134. data/test/examples/page_file_dir.git/refs/heads/master +1 -0
  135. data/test/examples/revert.git/COMMIT_EDITMSG +1 -0
  136. data/test/examples/revert.git/HEAD +1 -0
  137. data/test/examples/revert.git/config +12 -0
  138. data/test/examples/revert.git/description +1 -0
  139. data/test/examples/revert.git/index +0 -0
  140. data/test/examples/revert.git/info/exclude +6 -0
  141. data/test/examples/revert.git/logs/HEAD +2 -0
  142. data/test/examples/revert.git/logs/refs/heads/master +2 -0
  143. data/test/examples/revert.git/objects/20/2ced67cea93c7b6bd2928aa1daef8d1d55a20d +0 -0
  144. data/test/examples/revert.git/objects/41/76394bfa11222363c66ce7e84b5f154095b6d9 +0 -0
  145. data/test/examples/revert.git/objects/6a/69f92020f5df77af6e8813ff1232493383b708 +0 -0
  146. data/test/examples/revert.git/objects/b4/785957bc986dc39c629de9fac9df46972c00fc +0 -0
  147. data/test/examples/revert.git/objects/f4/03b791119f8232b7cb0ba455c624ac6435f433 +0 -0
  148. data/test/examples/revert.git/objects/info/packs +2 -0
  149. data/test/examples/revert.git/objects/pack/pack-a561f8437234f74d0bacb9e0eebe52d207f5770d.idx +0 -0
  150. data/test/examples/revert.git/objects/pack/pack-a561f8437234f74d0bacb9e0eebe52d207f5770d.pack +0 -0
  151. data/test/examples/revert.git/packed-refs +2 -0
  152. data/test/examples/revert.git/refs/heads/master +1 -0
  153. data/test/examples/revert.git/refs/remotes/origin/HEAD +1 -0
  154. data/test/examples/yubiwa.git/HEAD +1 -0
  155. data/test/examples/yubiwa.git/config +5 -0
  156. data/test/examples/yubiwa.git/description +1 -0
  157. data/test/examples/yubiwa.git/info/exclude +6 -0
  158. data/test/examples/yubiwa.git/objects/10/fa2ddc4e3b4009d8a453aace10bd6148c1ad00 +0 -0
  159. data/test/examples/yubiwa.git/objects/52/4b82874327ea7cbf730389964ba7cb3de966de +0 -0
  160. data/test/examples/yubiwa.git/objects/58/3fc201cb457fb3f1480f3e1e5999b119633835 +0 -0
  161. data/test/examples/yubiwa.git/objects/87/bc1dd46ab3d3874d4e898d45dd512cc20a7cc8 +1 -0
  162. data/test/examples/yubiwa.git/objects/89/64ed1b4e21aa90e831763bbce9034bfda81b70 +0 -0
  163. data/test/examples/yubiwa.git/objects/9f/f6dd0660da5fba2d3374adb2b84fa653bb538b +0 -0
  164. data/test/examples/yubiwa.git/objects/ac/e97abf2b177815a1972d7db22f229f58c83309 +0 -0
  165. data/test/examples/yubiwa.git/objects/b1/f443863a4816628807fbf86141ebef055dda34 +0 -0
  166. data/test/examples/yubiwa.git/refs/heads/master +1 -0
  167. data/test/helper.rb +66 -0
  168. data/test/test_app.rb +169 -0
  169. data/test/test_committer.rb +64 -0
  170. data/test/test_file.rb +27 -0
  171. data/test/test_git_access.rb +52 -0
  172. data/test/test_markup.rb +628 -0
  173. data/test/test_page.rb +166 -0
  174. data/test/test_page_revert.rb +45 -0
  175. data/test/test_wiki.rb +462 -0
  176. metadata +470 -0
@@ -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
@@ -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