read-only-gollum 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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,174 @@
1
+ module Gollum
2
+ # Encapsulate sanitization options.
3
+ #
4
+ # This class does not yet support all options of Sanitize library.
5
+ # See http://github.com/rgrove/sanitize/.
6
+ class Sanitization
7
+ # Default whitelisted elements.
8
+ ELEMENTS = [
9
+ 'a', 'abbr', 'acronym', 'address', 'area', 'b', 'big',
10
+ 'blockquote', 'br', 'button', 'caption', 'center', 'cite',
11
+ 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dir',
12
+ 'div', 'dl', 'dt', 'em', 'fieldset', 'font', 'form', 'h1',
13
+ 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'input',
14
+ 'ins', 'kbd', 'label', 'legend', 'li', 'map', 'menu',
15
+ 'ol', 'optgroup', 'option', 'p', 'pre', 'q', 's', 'samp',
16
+ 'select', 'small', 'span', 'strike', 'strong', 'sub',
17
+ 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th',
18
+ 'thead', 'tr', 'tt', 'u', 'ul', 'var'
19
+ ].freeze
20
+
21
+ # Default whitelisted attributes.
22
+ ATTRIBUTES = {
23
+ 'a' => ['href'],
24
+ 'img' => ['src'],
25
+ :all => ['abbr', 'accept', 'accept-charset',
26
+ 'accesskey', 'action', 'align', 'alt', 'axis',
27
+ 'border', 'cellpadding', 'cellspacing', 'char',
28
+ 'charoff', 'class', 'charset', 'checked', 'cite',
29
+ 'clear', 'cols', 'colspan', 'color',
30
+ 'compact', 'coords', 'datetime', 'dir',
31
+ 'disabled', 'enctype', 'for', 'frame',
32
+ 'headers', 'height', 'hreflang',
33
+ 'hspace', 'ismap', 'label', 'lang',
34
+ 'longdesc', 'maxlength', 'media', 'method',
35
+ 'multiple', 'name', 'nohref', 'noshade',
36
+ 'nowrap', 'prompt', 'readonly', 'rel', 'rev',
37
+ 'rows', 'rowspan', 'rules', 'scope',
38
+ 'selected', 'shape', 'size', 'span',
39
+ 'start', 'summary', 'tabindex', 'target',
40
+ 'title', 'type', 'usemap', 'valign', 'value',
41
+ 'vspace', 'width']
42
+ }.freeze
43
+
44
+ # Default whitelisted protocols for URLs.
45
+ PROTOCOLS = {
46
+ 'a' => {'href' => ['http', 'https', 'mailto', 'ftp', 'irc', :relative]},
47
+ 'img' => {'src' => ['http', 'https', :relative]}
48
+ }.freeze
49
+
50
+ ADD_ATTRIBUTES = lambda do |env, node|
51
+ if add = env[:config][:add_attributes][node.name]
52
+ add.each do |key, value|
53
+ node[key] = value
54
+ end
55
+ end
56
+ end
57
+
58
+ # Default elements whose contents will be removed in addition
59
+ # to the elements themselve
60
+ REMOVE_CONTENTS = [
61
+ 'script',
62
+ 'style'
63
+ ].freeze
64
+
65
+ # Default transformers to force @id attributes with 'wiki-' prefix
66
+ TRANSFORMERS = [
67
+ lambda do |env|
68
+ node = env[:node]
69
+ return if env[:is_whitelisted] || !node.element?
70
+ prefix = env[:config][:id_prefix]
71
+ found_attrs = %w(id name).select do |key|
72
+ if value = node[key]
73
+ node[key] = value.gsub(/\A(#{prefix})?/, prefix)
74
+ end
75
+ end
76
+ if found_attrs.size > 0
77
+ ADD_ATTRIBUTES.call(env, node)
78
+ {}
79
+ end
80
+ end,
81
+ lambda do |env|
82
+ node = env[:node]
83
+ return unless value = node['href']
84
+ prefix = env[:config][:id_prefix]
85
+ node['href'] = value.gsub(/\A\#(#{prefix})?/, '#'+prefix)
86
+ ADD_ATTRIBUTES.call(env, node)
87
+ {}
88
+ end
89
+ ].freeze
90
+
91
+ # Gets an Array of whitelisted HTML elements. Default: ELEMENTS.
92
+ attr_reader :elements
93
+
94
+ # Gets a Hash describing which attributes are allowed in which HTML
95
+ # elements. Default: ATTRIBUTES.
96
+ attr_reader :attributes
97
+
98
+ # Gets a Hash describing which URI protocols are allowed in HTML
99
+ # attributes. Default: PROTOCOLS
100
+ attr_reader :protocols
101
+
102
+ # Gets a Hash describing which URI protocols are allowed in HTML
103
+ # attributes. Default: TRANSFORMERS
104
+ attr_reader :transformers
105
+
106
+ # Gets or sets a String prefix which is added to ID attributes.
107
+ # Default: 'wiki-'
108
+ attr_accessor :id_prefix
109
+
110
+ # Gets a Hash describing HTML attributes that Sanitize should add.
111
+ # Default: {}
112
+ attr_reader :add_attributes
113
+
114
+ # Gets an Array of element names whose contents will be removed in addition
115
+ # to the elements themselves. Default: REMOVE_CONTENTS
116
+ attr_reader :remove_contents
117
+
118
+ # Sets a boolean determining whether Sanitize allows HTML comments in the
119
+ # output. Default: false.
120
+ attr_writer :allow_comments
121
+
122
+ def initialize
123
+ @elements = ELEMENTS
124
+ @attributes = ATTRIBUTES
125
+ @protocols = PROTOCOLS
126
+ @transformers = TRANSFORMERS
127
+ @add_attributes = {}
128
+ @remove_contents = REMOVE_CONTENTS
129
+ @allow_comments = false
130
+ @id_prefix = 'wiki-'
131
+ yield self if block_given?
132
+ end
133
+
134
+ # Determines if Sanitize should allow HTML comments.
135
+ #
136
+ # Returns True if comments are allowed, or False.
137
+ def allow_comments?
138
+ !!@allow_comments
139
+ end
140
+
141
+ # Modifies the current Sanitization instance to sanitize older revisions
142
+ # of pages.
143
+ #
144
+ # Returns a Sanitization instance.
145
+ def history_sanitization
146
+ self.class.new do |sanitize|
147
+ sanitize.add_attributes['a'] = {'rel' => 'nofollow'}
148
+ end
149
+ end
150
+
151
+ # Builds a Hash of options suitable for Sanitize.clean.
152
+ #
153
+ # Returns a Hash.
154
+ def to_hash
155
+ { :elements => elements,
156
+ :attributes => attributes,
157
+ :protocols => protocols,
158
+ :add_attributes => add_attributes,
159
+ :remove_contents => remove_contents,
160
+ :allow_comments => allow_comments?,
161
+ :transformers => transformers,
162
+ :id_prefix => id_prefix
163
+ }
164
+ end
165
+
166
+ # Builds a Sanitize instance from the current options.
167
+ #
168
+ # Returns a Sanitize instance.
169
+ def to_sanitize
170
+ Sanitize.new(to_hash)
171
+ end
172
+ end
173
+ end
174
+
@@ -0,0 +1,89 @@
1
+ require 'fileutils'
2
+ require 'shellwords'
3
+ require 'tmpdir'
4
+ require 'posix/spawn'
5
+
6
+ module Gollum
7
+ module Tex
8
+ class Error < StandardError; end
9
+
10
+ extend POSIX::Spawn
11
+
12
+ Template = <<-EOS
13
+ \\documentclass[12pt]{article}
14
+ \\usepackage{color}
15
+ \\usepackage[dvips]{graphicx}
16
+ \\pagestyle{empty}
17
+ \\pagecolor{white}
18
+ \\begin{document}
19
+ {\\color{black}
20
+ \\begin{eqnarray*}
21
+ %s
22
+ \\end{eqnarray*}}
23
+ \\end{document}
24
+ EOS
25
+
26
+ class << self
27
+ attr_accessor :latex_path, :dvips_path, :convert_path
28
+ end
29
+
30
+ self.latex_path = 'latex'
31
+ self.dvips_path = 'dvips'
32
+ self.convert_path = 'convert'
33
+
34
+ def self.check_dependencies!
35
+ return if @dependencies_available
36
+
37
+ if `which latex` == ""
38
+ raise Error, "`latex` command not found"
39
+ end
40
+
41
+ if `which dvips` == ""
42
+ raise Error, "`dvips` command not found"
43
+ end
44
+
45
+ if `which convert` == ""
46
+ raise Error, "`convert` command not found"
47
+ end
48
+
49
+ if `which gs` == ""
50
+ raise Error, "`gs` command not found"
51
+ end
52
+
53
+ @dependencies_available = true
54
+ end
55
+
56
+ def self.render_formula(formula)
57
+ check_dependencies!
58
+
59
+ Dir.mktmpdir('tex') do |path|
60
+ tex_path = ::File.join(path, 'formula.tex')
61
+ dvi_path = ::File.join(path, 'formula.dvi')
62
+ eps_path = ::File.join(path, 'formula.eps')
63
+ png_path = ::File.join(path, 'formula.png')
64
+
65
+ ::File.open(tex_path, 'w') { |f| f.write(Template % formula) }
66
+
67
+ result = sh latex_path, '-interaction=batchmode', 'formula.tex', :chdir => path
68
+ raise Error, "`latex` command failed: #{result}" unless ::File.exist?(dvi_path)
69
+
70
+ result = sh dvips_path, '-o', eps_path, '-E', dvi_path
71
+ raise Error, "`dvips` command failed: #{result}" unless ::File.exist?(eps_path)
72
+ result = sh convert_path, '+adjoin',
73
+ '-antialias',
74
+ '-transparent', 'white',
75
+ '-density', '150x150',
76
+ eps_path, png_path
77
+ raise Error, "`convert` command failed: #{result}" unless ::File.exist?(png_path)
78
+
79
+ ::File.read(png_path)
80
+ end
81
+ end
82
+
83
+ private
84
+ def self.sh(*args)
85
+ pid = spawn *args
86
+ Process::waitpid(pid)
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,43 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+ require 'open-uri'
4
+
5
+ class Gollum::WebSequenceDiagram
6
+ WSD_URL = "http://www.websequencediagrams.com/index.php"
7
+
8
+ # Initialize a new WebSequenceDiagram object.
9
+ #
10
+ # code - The String containing the sequence diagram markup.
11
+ # style - The String containing the rendering style.
12
+ #
13
+ # Returns a new Gollum::WebSequenceDiagram object
14
+ def initialize(code, style)
15
+ @code = code
16
+ @style = style
17
+ @tag = ""
18
+
19
+ render
20
+ end
21
+
22
+ # Render the sequence diagram on the remote server and store the url to
23
+ # the rendered image.
24
+ #
25
+ # Returns nil.
26
+ def render
27
+ response = Net::HTTP.post_form(URI.parse(WSD_URL), 'style' => @style, 'message' => @code)
28
+ if response.body =~ /img: "(.+)"/
29
+ url = "http://www.websequencediagrams.com/#{$1}"
30
+ @tag = "<img src=\"#{url}\" />"
31
+ else
32
+ puts response.body
33
+ @tag ="Sorry, unable to render sequence diagram at this time."
34
+ end
35
+ end
36
+
37
+ # Gets the HTML IMG tag for the sequence diagram.
38
+ #
39
+ # Returns a String containing the IMG tag.
40
+ def to_tag
41
+ @tag
42
+ end
43
+ end
@@ -0,0 +1,636 @@
1
+ module Gollum
2
+ class Wiki
3
+ include Pagination
4
+
5
+ class << self
6
+ # Sets the page class used by all instances of this Wiki.
7
+ attr_writer :page_class
8
+
9
+ # Sets the file class used by all instances of this Wiki.
10
+ attr_writer :file_class
11
+
12
+ # Sets the markup class used by all instances of this Wiki.
13
+ attr_writer :markup_classes
14
+
15
+ # Sets the default ref for the wiki.
16
+ attr_accessor :default_ref
17
+
18
+ # Sets the default name for commits.
19
+ attr_accessor :default_committer_name
20
+
21
+ # Sets the default email for commits.
22
+ attr_accessor :default_committer_email
23
+
24
+ # Array of chars to substitute whitespace for when trying to locate file in git repo.
25
+ attr_accessor :default_ws_subs
26
+
27
+ # Sets sanitization options. Set to false to deactivate
28
+ # sanitization altogether.
29
+ attr_writer :sanitization
30
+
31
+ # Sets sanitization options. Set to false to deactivate
32
+ # sanitization altogether.
33
+ attr_writer :history_sanitization
34
+
35
+ # Gets the page class used by all instances of this Wiki.
36
+ # Default: Gollum::Page.
37
+ def page_class
38
+ @page_class ||
39
+ if superclass.respond_to?(:page_class)
40
+ superclass.page_class
41
+ else
42
+ ::Gollum::Page
43
+ end
44
+ end
45
+
46
+ # Gets the file class used by all instances of this Wiki.
47
+ # Default: Gollum::File.
48
+ def file_class
49
+ @file_class ||
50
+ if superclass.respond_to?(:file_class)
51
+ superclass.file_class
52
+ else
53
+ ::Gollum::File
54
+ end
55
+ end
56
+
57
+ # Gets the markup class used by all instances of this Wiki.
58
+ # Default: Gollum::Markup
59
+ def markup_classes
60
+ @markup_classes ||=
61
+ if superclass.respond_to?(:markup_classes)
62
+ superclass.markup_classes
63
+ else
64
+ Hash.new(::Gollum::Markup)
65
+ end
66
+ end
67
+
68
+ # Gets the default markup class used by all instances of this Wiki.
69
+ # Kept for backwards compatibility until Gollum v2.x
70
+ def markup_class(language=:default)
71
+ markup_classes[language]
72
+ end
73
+
74
+ # Sets the default markup class used by all instances of this Wiki.
75
+ # Kept for backwards compatibility until Gollum v2.x
76
+ def markup_class=(default)
77
+ @markup_classes = Hash.new(default).update(markup_classes)
78
+ default
79
+ end
80
+
81
+ alias_method :default_markup_class, :markup_class
82
+ alias_method :default_markup_class=, :markup_class=
83
+
84
+ # Gets the default sanitization options for current pages used by
85
+ # instances of this Wiki.
86
+ def sanitization
87
+ if @sanitization.nil?
88
+ @sanitization = Sanitization.new
89
+ end
90
+ @sanitization
91
+ end
92
+
93
+ # Gets the default sanitization options for older page revisions used by
94
+ # instances of this Wiki.
95
+ def history_sanitization
96
+ if @history_sanitization.nil?
97
+ @history_sanitization = sanitization ?
98
+ sanitization.history_sanitization :
99
+ false
100
+ end
101
+ @history_sanitization
102
+ end
103
+ end
104
+
105
+ self.default_ref = 'master'
106
+ self.default_committer_name = 'Anonymous'
107
+ self.default_committer_email = 'anon@anon.com'
108
+
109
+ self.default_ws_subs = ['_','-']
110
+
111
+ # The String base path to prefix to internal links. For example, when set
112
+ # to "/wiki", the page "Hobbit" will be linked as "/wiki/Hobbit". Defaults
113
+ # to "/".
114
+ attr_reader :base_path
115
+
116
+ # Gets the sanitization options for current pages used by this Wiki.
117
+ attr_reader :sanitization
118
+
119
+ # Gets the sanitization options for older page revisions used by this Wiki.
120
+ attr_reader :history_sanitization
121
+
122
+ # Gets the String ref in which all page files reside.
123
+ attr_reader :ref
124
+
125
+ # Gets the String directory in which all page files reside.
126
+ attr_reader :page_file_dir
127
+
128
+ # Gets the Array of chars to sub for ws in filenames.
129
+ attr_reader :ws_subs
130
+
131
+ # Public: Initialize a new Gollum Repo.
132
+ #
133
+ # path - The String path to the Git repository that holds the Gollum
134
+ # site.
135
+ # options - Optional Hash:
136
+ # :base_path - String base path for all Wiki links.
137
+ # Default: "/"
138
+ # :page_class - The page Class. Default: Gollum::Page
139
+ # :file_class - The file Class. Default: Gollum::File
140
+ # :markup_classes - A hash containing the markup Classes for each
141
+ # document type. Default: { Gollum::Markup }
142
+ # :sanitization - An instance of Sanitization.
143
+ # :page_file_dir - String the directory in which all page files reside
144
+ # :ref - String the repository ref to retrieve pages from
145
+ # :ws_subs - Array of chars to sub for ws in filenames.
146
+ #
147
+ # Returns a fresh Gollum::Repo.
148
+ def initialize(path, options = {})
149
+ if path.is_a?(GitAccess)
150
+ options[:access] = path
151
+ path = path.path
152
+ end
153
+ @path = path
154
+ @page_file_dir = options[:page_file_dir]
155
+ @access = options[:access] || GitAccess.new(path, @page_file_dir)
156
+ @base_path = options[:base_path] || "/"
157
+ @page_class = options[:page_class] || self.class.page_class
158
+ @file_class = options[:file_class] || self.class.file_class
159
+ @markup_classes = options[:markup_classes] || self.class.markup_classes
160
+ @repo = @access.repo
161
+ @ref = options[:ref] || self.class.default_ref
162
+ @sanitization = options[:sanitization] || self.class.sanitization
163
+ @ws_subs = options[:ws_subs] ||
164
+ self.class.default_ws_subs
165
+ @history_sanitization = options[:history_sanitization] ||
166
+ self.class.history_sanitization
167
+ end
168
+
169
+ # Public: check whether the wiki's git repo exists on the filesystem.
170
+ #
171
+ # Returns true if the repo exists, and false if it does not.
172
+ def exist?
173
+ @access.exist?
174
+ end
175
+
176
+ # Public: Get the formatted page for a given page name.
177
+ #
178
+ # name - The human or canonical String page name of the wiki page.
179
+ # version - The String version ID to find (default: @ref).
180
+ #
181
+ # Returns a Gollum::Page or nil if no matching page was found.
182
+ def page(name, version = @ref)
183
+ @page_class.new(self).find(name, version)
184
+ end
185
+
186
+ # Public: Get the static file for a given name.
187
+ #
188
+ # name - The full String pathname to the file.
189
+ # version - The String version ID to find (default: @ref).
190
+ #
191
+ # Returns a Gollum::File or nil if no matching file was found.
192
+ def file(name, version = @ref)
193
+ @file_class.new(self).find(name, version)
194
+ end
195
+
196
+ # Public: Create an in-memory Page with the given data and format. This
197
+ # is useful for previewing what content will look like before committing
198
+ # it to the repository.
199
+ #
200
+ # name - The String name of the page.
201
+ # format - The Symbol format of the page.
202
+ # data - The new String contents of the page.
203
+ #
204
+ # Returns the in-memory Gollum::Page.
205
+ def preview_page(name, data, format)
206
+ page = @page_class.new(self)
207
+ ext = @page_class.format_to_ext(format.to_sym)
208
+ name = @page_class.cname(name) + '.' + ext
209
+ blob = OpenStruct.new(:name => name, :data => data)
210
+ page.populate(blob)
211
+ page.version = @access.commit('master')
212
+ page
213
+ end
214
+
215
+ # Public: Write a new version of a page to the Gollum repo root.
216
+ #
217
+ # name - The String name of the page.
218
+ # format - The Symbol format of the page.
219
+ # data - The new String contents of the page.
220
+ # commit - The commit Hash details:
221
+ # :message - The String commit message.
222
+ # :name - The String author full name.
223
+ # :email - The String email address.
224
+ # :parent - Optional Grit::Commit parent to this update.
225
+ # :tree - Optional String SHA of the tree to create the
226
+ # index from.
227
+ # :committer - Optional Gollum::Committer instance. If provided,
228
+ # assume that this operation is part of batch of
229
+ # updates and the commit happens later.
230
+ #
231
+ # Returns the String SHA1 of the newly written version, or the
232
+ # Gollum::Committer instance if this is part of a batch update.
233
+ def write_page(name, format, data, commit = {})
234
+ multi_commit = false
235
+
236
+ committer = if obj = commit[:committer]
237
+ multi_commit = true
238
+ obj
239
+ else
240
+ Committer.new(self, commit)
241
+ end
242
+
243
+ filename = Gollum::Page.cname(name)
244
+
245
+ committer.add_to_index('', filename, format, data)
246
+
247
+ committer.after_commit do |index, sha|
248
+ @access.refresh
249
+ index.update_working_dir('', filename, format)
250
+ end
251
+
252
+ multi_commit ? committer : committer.commit
253
+ end
254
+
255
+ # Public: Update an existing page with new content. The location of the
256
+ # page inside the repository will not change. If the given format is
257
+ # different than the current format of the page, the filename will be
258
+ # changed to reflect the new format.
259
+ #
260
+ # page - The Gollum::Page to update.
261
+ # name - The String extension-less name of the page.
262
+ # format - The Symbol format of the page.
263
+ # data - The new String contents of the page.
264
+ # commit - The commit Hash details:
265
+ # :message - The String commit message.
266
+ # :name - The String author full name.
267
+ # :email - The String email address.
268
+ # :parent - Optional Grit::Commit parent to this update.
269
+ # :tree - Optional String SHA of the tree to create the
270
+ # index from.
271
+ # :committer - Optional Gollum::Committer instance. If provided,
272
+ # assume that this operation is part of batch of
273
+ # updates and the commit happens later.
274
+ #
275
+ # Returns the String SHA1 of the newly written version, or the
276
+ # Gollum::Committer instance if this is part of a batch update.
277
+ def update_page(page, name, format, data, commit = {})
278
+ name ||= page.name
279
+ format ||= page.format
280
+ dir = ::File.dirname(page.path)
281
+ dir = '' if dir == '.'
282
+ filename = (rename = page.name != name) ?
283
+ Gollum::Page.cname(name) : page.filename_stripped
284
+
285
+ multi_commit = false
286
+
287
+ committer = if obj = commit[:committer]
288
+ multi_commit = true
289
+ obj
290
+ else
291
+ Committer.new(self, commit)
292
+ end
293
+
294
+ if !rename && page.format == format
295
+ committer.add(page.path, normalize(data))
296
+ else
297
+ committer.delete(page.path)
298
+ committer.add_to_index(dir, filename, format, data, :allow_same_ext)
299
+ end
300
+
301
+ committer.after_commit do |index, sha|
302
+ @access.refresh
303
+ index.update_working_dir(dir, page.filename_stripped, page.format)
304
+ index.update_working_dir(dir, filename, format)
305
+ end
306
+
307
+ multi_commit ? committer : committer.commit
308
+ end
309
+
310
+ # Public: Delete a page.
311
+ #
312
+ # page - The Gollum::Page to delete.
313
+ # commit - The commit Hash details:
314
+ # :message - The String commit message.
315
+ # :name - The String author full name.
316
+ # :email - The String email address.
317
+ # :parent - Optional Grit::Commit parent to this update.
318
+ # :tree - Optional String SHA of the tree to create the
319
+ # index from.
320
+ # :committer - Optional Gollum::Committer instance. If provided,
321
+ # assume that this operation is part of batch of
322
+ # updates and the commit happens later.
323
+ #
324
+ # Returns the String SHA1 of the newly written version, or the
325
+ # Gollum::Committer instance if this is part of a batch update.
326
+ def delete_page(page, commit)
327
+ multi_commit = false
328
+
329
+ committer = if obj = commit[:committer]
330
+ multi_commit = true
331
+ obj
332
+ else
333
+ Committer.new(self, commit)
334
+ end
335
+
336
+ committer.delete(page.path)
337
+
338
+ committer.after_commit do |index, sha|
339
+ dir = ::File.dirname(page.path)
340
+ dir = '' if dir == '.'
341
+
342
+ @access.refresh
343
+ index.update_working_dir(dir, page.filename_stripped, page.format)
344
+ end
345
+
346
+ multi_commit ? committer : committer.commit
347
+ end
348
+
349
+ # Public: Applies a reverse diff for a given page. If only 1 SHA is given,
350
+ # the reverse diff will be taken from its parent (^SHA...SHA). If two SHAs
351
+ # are given, the reverse diff is taken from SHA1...SHA2.
352
+ #
353
+ # page - The Gollum::Page to delete.
354
+ # sha1 - String SHA1 of the earlier parent if two SHAs are given,
355
+ # or the child.
356
+ # sha2 - Optional String SHA1 of the child.
357
+ # commit - The commit Hash details:
358
+ # :message - The String commit message.
359
+ # :name - The String author full name.
360
+ # :email - The String email address.
361
+ # :parent - Optional Grit::Commit parent to this update.
362
+ #
363
+ # Returns a String SHA1 of the new commit, or nil if the reverse diff does
364
+ # not apply.
365
+ def revert_page(page, sha1, sha2 = nil, commit = {})
366
+ if sha2.is_a?(Hash)
367
+ commit = sha2
368
+ sha2 = nil
369
+ end
370
+
371
+ patch = full_reverse_diff_for(page, sha1, sha2)
372
+ committer = Committer.new(self, commit)
373
+ parent = committer.parents[0]
374
+ committer.options[:tree] = @repo.git.apply_patch(parent.sha, patch)
375
+ return false unless committer.options[:tree]
376
+ committer.after_commit do |index, sha|
377
+ @access.refresh
378
+
379
+ files = []
380
+ if page
381
+ files << [page.path, page.filename_stripped, page.format]
382
+ else
383
+ # Grit::Diff can't parse reverse diffs.... yet
384
+ patch.each_line do |line|
385
+ if line =~ %r{^diff --git b/.+? a/(.+)$}
386
+ path = $1
387
+ ext = ::File.extname(path)
388
+ name = ::File.basename(path, ext)
389
+ if format = ::Gollum::Page.format_for(ext)
390
+ files << [path, name, format]
391
+ end
392
+ end
393
+ end
394
+ end
395
+
396
+ files.each do |(path, name, format)|
397
+ dir = ::File.dirname(path)
398
+ dir = '' if dir == '.'
399
+ index.update_working_dir(dir, name, format)
400
+ end
401
+ end
402
+
403
+ committer.commit
404
+ end
405
+
406
+ # Public: Applies a reverse diff to the repo. If only 1 SHA is given,
407
+ # the reverse diff will be taken from its parent (^SHA...SHA). If two SHAs
408
+ # are given, the reverse diff is taken from SHA1...SHA2.
409
+ #
410
+ # sha1 - String SHA1 of the earlier parent if two SHAs are given,
411
+ # or the child.
412
+ # sha2 - Optional String SHA1 of the child.
413
+ # commit - The commit Hash details:
414
+ # :message - The String commit message.
415
+ # :name - The String author full name.
416
+ # :email - The String email address.
417
+ #
418
+ # Returns a String SHA1 of the new commit, or nil if the reverse diff does
419
+ # not apply.
420
+ def revert_commit(sha1, sha2 = nil, commit = {})
421
+ revert_page(nil, sha1, sha2, commit)
422
+ end
423
+
424
+ # Public: Lists all pages for this wiki.
425
+ #
426
+ # treeish - The String commit ID or ref to find (default: @ref)
427
+ #
428
+ # Returns an Array of Gollum::Page instances.
429
+ def pages(treeish = nil)
430
+ tree_list(treeish || @ref)
431
+ end
432
+
433
+ # Public: Returns the number of pages accessible from a commit
434
+ #
435
+ # ref - A String ref that is either a commit SHA or references one.
436
+ #
437
+ # Returns a Fixnum
438
+ def size(ref = nil)
439
+ tree_map_for(ref || @ref).inject(0) do |num, entry|
440
+ num + (@page_class.valid_page_name?(entry.name) ? 1 : 0)
441
+ end
442
+ rescue Grit::GitRuby::Repository::NoSuchShaFound
443
+ 0
444
+ end
445
+
446
+ # Public: Search all pages for this wiki.
447
+ #
448
+ # query - The string to search for
449
+ #
450
+ # Returns an Array with Objects of page name and count of matches
451
+ def search(query)
452
+ args = [{}, '-i', '-c', query, @ref, '--']
453
+ args << '--' << @page_file_dir if @page_file_dir
454
+
455
+ @repo.git.grep(*args).split("\n").map! do |line|
456
+ result = line.split(':')
457
+ file_name = Gollum::Page.canonicalize_filename(::File.basename(result[1]))
458
+
459
+ {
460
+ :count => result[2].to_i,
461
+ :name => file_name
462
+ }
463
+ end
464
+ end
465
+
466
+ # Public: All of the versions that have touched the Page.
467
+ #
468
+ # options - The options Hash:
469
+ # :page - The Integer page number (default: 1).
470
+ # :per_page - The Integer max count of items to return.
471
+ #
472
+ # Returns an Array of Grit::Commit.
473
+ def log(options = {})
474
+ @repo.log(@ref, nil, log_pagination_options(options))
475
+ end
476
+
477
+ # Public: Refreshes just the cached Git reference data. This should
478
+ # be called after every Gollum update.
479
+ #
480
+ # Returns nothing.
481
+ def clear_cache
482
+ @access.refresh
483
+ end
484
+
485
+ # Public: Creates a Sanitize instance using the Wiki's sanitization
486
+ # options.
487
+ #
488
+ # Returns a Sanitize instance.
489
+ def sanitizer
490
+ if options = sanitization
491
+ @sanitizer ||= options.to_sanitize
492
+ end
493
+ end
494
+
495
+ # Public: Creates a Sanitize instance using the Wiki's history sanitization
496
+ # options.
497
+ #
498
+ # Returns a Sanitize instance.
499
+ def history_sanitizer
500
+ if options = history_sanitization
501
+ @history_sanitizer ||= options.to_sanitize
502
+ end
503
+ end
504
+
505
+ #########################################################################
506
+ #
507
+ # Internal Methods
508
+ #
509
+ #########################################################################
510
+
511
+ # The Grit::Repo associated with the wiki.
512
+ #
513
+ # Returns the Grit::Repo.
514
+ attr_reader :repo
515
+
516
+ # The String path to the Git repository that holds the Gollum site.
517
+ #
518
+ # Returns the String path.
519
+ attr_reader :path
520
+
521
+ # Gets the page class used by all instances of this Wiki.
522
+ attr_reader :page_class
523
+
524
+ # Gets the file class used by all instances of this Wiki.
525
+ attr_reader :file_class
526
+
527
+ # Gets the markup class used by all instances of this Wiki.
528
+ attr_reader :markup_classes
529
+
530
+ # Normalize the data.
531
+ #
532
+ # data - The String data to be normalized.
533
+ #
534
+ # Returns the normalized data String.
535
+ def normalize(data)
536
+ data.gsub(/\r/, '')
537
+ end
538
+
539
+ # Assemble a Page's filename from its name and format.
540
+ #
541
+ # name - The String name of the page (should be pre-canonicalized).
542
+ # format - The Symbol format of the page.
543
+ #
544
+ # Returns the String filename.
545
+ def page_file_name(name, format)
546
+ name + '.' + @page_class.format_to_ext(format)
547
+ end
548
+
549
+ # Fill an array with a list of pages.
550
+ #
551
+ # ref - A String ref that is either a commit SHA or references one.
552
+ #
553
+ # Returns a flat Array of Gollum::Page instances.
554
+ def tree_list(ref)
555
+ if sha = @access.ref_to_sha(ref)
556
+ commit = @access.commit(sha)
557
+ tree_map_for(sha).inject([]) do |list, entry|
558
+ next list unless @page_class.valid_page_name?(entry.name)
559
+ list << entry.page(self, commit)
560
+ end
561
+ else
562
+ []
563
+ end
564
+ end
565
+
566
+ # Creates a reverse diff for the given SHAs on the given Gollum::Page.
567
+ #
568
+ # page - The Gollum::Page to scope the patch to, or a String Path.
569
+ # sha1 - String SHA1 of the earlier parent if two SHAs are given,
570
+ # or the child.
571
+ # sha2 - Optional String SHA1 of the child.
572
+ #
573
+ # Returns a String of the reverse Diff to apply.
574
+ def full_reverse_diff_for(page, sha1, sha2 = nil)
575
+ sha1, sha2 = "#{sha1}^", sha1 if sha2.nil?
576
+ args = [{:R => true}, sha1, sha2]
577
+ if page
578
+ args << '--' << (page.respond_to?(:path) ? page.path : page.to_s)
579
+ end
580
+ repo.git.native(:diff, *args)
581
+ end
582
+
583
+ # Creates a reverse diff for the given SHAs.
584
+ #
585
+ # sha1 - String SHA1 of the earlier parent if two SHAs are given,
586
+ # or the child.
587
+ # sha2 - Optional String SHA1 of the child.
588
+ #
589
+ # Returns a String of the reverse Diff to apply.
590
+ def full_reverse_diff(sha1, sha2 = nil)
591
+ full_reverse_diff_for(nil, sha1, sha2)
592
+ end
593
+
594
+ # Gets the default name for commits.
595
+ #
596
+ # Returns the String name.
597
+ def default_committer_name
598
+ @default_committer_name ||= \
599
+ @repo.config['user.name'] || self.class.default_committer_name
600
+ end
601
+
602
+ # Gets the default email for commits.
603
+ #
604
+ # Returns the String email address.
605
+ def default_committer_email
606
+ @default_committer_email ||= \
607
+ @repo.config['user.email'] || self.class.default_committer_email
608
+ end
609
+
610
+ # Gets the commit object for the given ref or sha.
611
+ #
612
+ # ref - A string ref or SHA pointing to a valid commit.
613
+ #
614
+ # Returns a Grit::Commit instance.
615
+ def commit_for(ref)
616
+ @access.commit(ref)
617
+ rescue Grit::GitRuby::Repository::NoSuchShaFound
618
+ end
619
+
620
+ # Finds a full listing of files and their blob SHA for a given ref. Each
621
+ # listing is cached based on its actual commit SHA.
622
+ #
623
+ # ref - A String ref that is either a commit SHA or references one.
624
+ #
625
+ # Returns an Array of BlobEntry instances.
626
+ def tree_map_for(ref)
627
+ @access.tree(ref)
628
+ rescue Grit::GitRuby::Repository::NoSuchShaFound
629
+ []
630
+ end
631
+
632
+ def inspect
633
+ %(#<#{self.class.name}:#{object_id} #{@repo.path}>)
634
+ end
635
+ end
636
+ end