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,430 @@
1
+ module Gollum
2
+ class Page
3
+ include Pagination
4
+
5
+ Wiki.page_class = self
6
+
7
+ VALID_PAGE_RE = /^(.+)\.(md|mkdn?|mdown|markdown|textile|rdoc|org|creole|re?st(\.txt)?|asciidoc|pod|(media)?wiki)$/i
8
+ FORMAT_NAMES = { :markdown => "Markdown",
9
+ :textile => "Textile",
10
+ :rdoc => "RDoc",
11
+ :org => "Org-mode",
12
+ :creole => "Creole",
13
+ :rest => "reStructuredText",
14
+ :asciidoc => "AsciiDoc",
15
+ :mediawiki => "MediaWiki",
16
+ :pod => "Pod" }
17
+
18
+ # Sets a Boolean determing whether this page is a historical version.
19
+ #
20
+ # Returns nothing.
21
+ attr_writer :historical
22
+
23
+ # Checks if a filename has a valid extension understood by GitHub::Markup.
24
+ #
25
+ # filename - String filename, like "Home.md".
26
+ #
27
+ # Returns the matching String basename of the file without the extension.
28
+ def self.valid_filename?(filename)
29
+ filename && filename.to_s =~ VALID_PAGE_RE && $1
30
+ end
31
+
32
+ # Checks if a filename has a valid extension understood by GitHub::Markup.
33
+ # Also, checks if the filename has no "_" in the front (such as
34
+ # _Footer.md).
35
+ #
36
+ # filename - String filename, like "Home.md".
37
+ #
38
+ # Returns the matching String basename of the file without the extension.
39
+ def self.valid_page_name?(filename)
40
+ match = valid_filename?(filename)
41
+ filename =~ /^_/ ? false : match
42
+ end
43
+
44
+ # Public: The format of a given filename.
45
+ #
46
+ # filename - The String filename.
47
+ #
48
+ # Returns the Symbol format of the page. One of:
49
+ # [ :markdown | :textile | :rdoc | :org | :rest | :asciidoc | :pod |
50
+ # :roff ]
51
+ def self.format_for(filename)
52
+ case filename.to_s
53
+ when /\.(md|mkdn?|mdown|markdown)$/i
54
+ :markdown
55
+ when /\.(textile)$/i
56
+ :textile
57
+ when /\.(rdoc)$/i
58
+ :rdoc
59
+ when /\.(org)$/i
60
+ :org
61
+ when /\.(creole)$/i
62
+ :creole
63
+ when /\.(re?st(\.txt)?)$/i
64
+ :rest
65
+ when /\.(asciidoc)$/i
66
+ :asciidoc
67
+ when /\.(pod)$/i
68
+ :pod
69
+ when /\.(\d)$/i
70
+ :roff
71
+ when /\.(media)?wiki$/i
72
+ :mediawiki
73
+ else
74
+ nil
75
+ end
76
+ end
77
+
78
+ # Reusable filter to turn a filename (without path) into a canonical name.
79
+ # Strips extension, converts dashes to spaces.
80
+ #
81
+ # Returns the filtered String.
82
+ def self.canonicalize_filename(filename)
83
+ strip_filename(filename).gsub('-', ' ')
84
+ end
85
+
86
+ # Reusable filter to strip extension and path from filename
87
+ #
88
+ # filename - The string path or filename to strip
89
+ #
90
+ # Returns the stripped String.
91
+ def self.strip_filename(filename)
92
+ ::File.basename(filename, ::File.extname(filename))
93
+ end
94
+
95
+ # Public: Initialize a page.
96
+ #
97
+ # wiki - The Gollum::Wiki in question.
98
+ #
99
+ # Returns a newly initialized Gollum::Page.
100
+ def initialize(wiki)
101
+ @wiki = wiki
102
+ @blob = @footer = @sidebar = nil
103
+ end
104
+
105
+ # Public: The on-disk filename of the page including extension.
106
+ #
107
+ # Returns the String name.
108
+ def filename
109
+ @blob && @blob.name
110
+ end
111
+
112
+ # Public: The on-disk filename of the page with extension stripped.
113
+ #
114
+ # Returns the String name.
115
+ def filename_stripped
116
+ self.class.strip_filename(filename)
117
+ end
118
+
119
+ # Public: The canonical page name without extension, and dashes converted
120
+ # to spaces.
121
+ #
122
+ # Returns the String name.
123
+ def name
124
+ self.class.canonicalize_filename(filename)
125
+ end
126
+
127
+ # Public: If the first element of a formatted page is an <h1> tag it can
128
+ # be considered the title of the page and used in the display. If the
129
+ # first element is NOT an <h1> tag, the title will be constructed from the
130
+ # filename by stripping the extension and replacing any dashes with
131
+ # spaces.
132
+ #
133
+ # Returns the fully sanitized String title.
134
+ def title
135
+ doc = Nokogiri::HTML(%{<div id="gollum-root">} + self.formatted_data + %{</div>})
136
+
137
+ header =
138
+ case self.format
139
+ when :asciidoc
140
+ doc.css("div#gollum-root > div#header > h1:first-child")
141
+ when :org
142
+ doc.css("div#gollum-root > p.title:first-child")
143
+ when :pod
144
+ doc.css("div#gollum-root > a.dummyTopAnchor:first-child + h1")
145
+ when :rest
146
+ doc.css("div#gollum-root > div > div > h1:first-child")
147
+ else
148
+ doc.css("div#gollum-root > h1:first-child")
149
+ end
150
+
151
+ if !header.empty?
152
+ Sanitize.clean(header.to_html)
153
+ else
154
+ Sanitize.clean(name)
155
+ end.strip
156
+ end
157
+
158
+ # Public: The path of the page within the repo.
159
+ #
160
+ # Returns the String path.
161
+ attr_reader :path
162
+
163
+ # Public: The raw contents of the page.
164
+ #
165
+ # Returns the String data.
166
+ def raw_data
167
+ @blob && @blob.data
168
+ end
169
+
170
+ # Public: A text data encoded in specified encoding.
171
+ #
172
+ # encoding - An Encoding or nil
173
+ #
174
+ # Returns a character encoding aware String.
175
+ def text_data(encoding=nil)
176
+ if raw_data.respond_to?(:encoding)
177
+ raw_data.force_encoding(encoding || Encoding::UTF_8)
178
+ else
179
+ raw_data
180
+ end
181
+ end
182
+
183
+ # Public: The formatted contents of the page.
184
+ #
185
+ # encoding - Encoding Constant or String.
186
+ #
187
+ # Returns the String data.
188
+ def formatted_data(encoding = nil, &block)
189
+ @blob && markup_class.render(historical?, encoding, &block)
190
+ end
191
+
192
+ # Public: The format of the page.
193
+ #
194
+ # Returns the Symbol format of the page. One of:
195
+ # [ :markdown | :textile | :rdoc | :org | :rest | :asciidoc | :pod |
196
+ # :roff ]
197
+ def format
198
+ self.class.format_for(@blob.name)
199
+ end
200
+
201
+ # Gets the Gollum::Markup instance that will render this page's content.
202
+ #
203
+ # Returns a Gollum::Markup instance.
204
+ def markup_class
205
+ @markup_class ||= @wiki.markup_classes[format].new(self)
206
+ end
207
+
208
+ # Public: The current version of the page.
209
+ #
210
+ # Returns the Grit::Commit.
211
+ attr_reader :version
212
+
213
+ # Public: All of the versions that have touched the Page.
214
+ #
215
+ # options - The options Hash:
216
+ # :page - The Integer page number (default: 1).
217
+ # :per_page - The Integer max count of items to return.
218
+ # :follow - Follow's a file across renames, but falls back
219
+ # to a slower Grit native call. (default: false)
220
+ #
221
+ # Returns an Array of Grit::Commit.
222
+ def versions(options = {})
223
+ if options[:follow]
224
+ options[:pretty] = 'raw'
225
+ options.delete :max_count
226
+ options.delete :skip
227
+ log = @wiki.repo.git.native "log", options, @wiki.ref, "--", @path
228
+ Grit::Commit.list_from_string(@wiki.repo, log)
229
+ else
230
+ @wiki.repo.log(@wiki.ref, @path, log_pagination_options(options))
231
+ end
232
+ end
233
+
234
+ # Public: The footer Page.
235
+ #
236
+ # Returns the footer Page or nil if none exists.
237
+ def footer
238
+ @footer ||= find_sub_page(:footer)
239
+ end
240
+
241
+ # Public: The sidebar Page.
242
+ #
243
+ # Returns the sidebar Page or nil if none exists.
244
+ def sidebar
245
+ @sidebar ||= find_sub_page(:sidebar)
246
+ end
247
+
248
+ # Gets a Boolean determining whether this page is a historical version.
249
+ # Historical pages are pulled using exact SHA hashes and format all links
250
+ # with rel="nofollow"
251
+ #
252
+ # Returns true if the page is pulled from a named branch or tag, or false.
253
+ def historical?
254
+ !!@historical
255
+ end
256
+
257
+ #########################################################################
258
+ #
259
+ # Class Methods
260
+ #
261
+ #########################################################################
262
+
263
+ # Convert a human page name into a canonical page name.
264
+ #
265
+ # name - The String human page name.
266
+ # char_white_sub - Substitution for whitespace
267
+ # char_other_sub - Substitution for other special chars
268
+ #
269
+ # Examples
270
+ #
271
+ # Page.cname("Bilbo Baggins")
272
+ # # => 'Bilbo-Baggins'
273
+ #
274
+ # Page.cname("Bilbo Baggins",'_')
275
+ # # => 'Bilbo_Baggins'
276
+ #
277
+ # Returns the String canonical name.
278
+ def self.cname(name, char_white_sub = '-', char_other_sub = '-')
279
+ name.respond_to?(:gsub) ?
280
+ name.gsub(%r{\s},char_white_sub).gsub(%r{[/<>+]}, char_other_sub) :
281
+ ''
282
+ end
283
+
284
+ # Convert a format Symbol into an extension String.
285
+ #
286
+ # format - The format Symbol.
287
+ #
288
+ # Returns the String extension (no leading period).
289
+ def self.format_to_ext(format)
290
+ case format
291
+ when :markdown then 'md'
292
+ when :textile then 'textile'
293
+ when :rdoc then 'rdoc'
294
+ when :org then 'org'
295
+ when :creole then 'creole'
296
+ when :rest then 'rest'
297
+ when :asciidoc then 'asciidoc'
298
+ when :pod then 'pod'
299
+ when :mediawiki then 'mediawiki'
300
+ end
301
+ end
302
+
303
+ #########################################################################
304
+ #
305
+ # Internal Methods
306
+ #
307
+ #########################################################################
308
+
309
+ # The underlying wiki repo.
310
+ #
311
+ # Returns the Gollum::Wiki containing the page.
312
+ attr_reader :wiki
313
+
314
+ # Set the Grit::Commit version of the page.
315
+ #
316
+ # Returns nothing.
317
+ attr_writer :version
318
+
319
+ # Find a page in the given Gollum repo.
320
+ #
321
+ # name - The human or canonical String page name to find.
322
+ # version - The String version ID to find.
323
+ #
324
+ # Returns a Gollum::Page or nil if the page could not be found.
325
+ def find(name, version)
326
+ map = @wiki.tree_map_for(version.to_s)
327
+ if page = find_page_in_tree(map, name)
328
+ page.version = version.is_a?(Grit::Commit) ?
329
+ version : @wiki.commit_for(version)
330
+ page.historical = page.version.to_s == version.to_s
331
+ page
332
+ end
333
+ rescue Grit::GitRuby::Repository::NoSuchShaFound
334
+ end
335
+
336
+ # Find a page in a given tree.
337
+ #
338
+ # map - The Array tree map from Wiki#tree_map.
339
+ # name - The canonical String page name.
340
+ # checked_dir - Optional String of the directory a matching page needs
341
+ # to be in. The string should
342
+ #
343
+ # Returns a Gollum::Page or nil if the page could not be found.
344
+ def find_page_in_tree(map, name, checked_dir = nil)
345
+ return nil if !map || name.to_s.empty?
346
+ if checked_dir = BlobEntry.normalize_dir(checked_dir)
347
+ checked_dir.downcase!
348
+ end
349
+
350
+ map.each do |entry|
351
+ next if entry.name.to_s.empty?
352
+ next unless checked_dir.nil? || entry.dir.downcase == checked_dir
353
+ next unless page_match(name, entry.name)
354
+ return entry.page(@wiki, @version)
355
+ end
356
+
357
+ return nil # nothing was found
358
+ end
359
+
360
+ # Populate the Page with information from the Blob.
361
+ #
362
+ # blob - The Grit::Blob that contains the info.
363
+ # path - The String directory path of the page file.
364
+ #
365
+ # Returns the populated Gollum::Page.
366
+ def populate(blob, path=nil)
367
+ @blob = blob
368
+ @path = "#{path}/#{blob.name}"[1..-1]
369
+ self
370
+ end
371
+
372
+ # The full directory path for the given tree.
373
+ #
374
+ # treemap - The Hash treemap containing parentage information.
375
+ # tree - The Grit::Tree for which to compute the path.
376
+ #
377
+ # Returns the String path.
378
+ def tree_path(treemap, tree)
379
+ if ptree = treemap[tree]
380
+ tree_path(treemap, ptree) + '/' + tree.name
381
+ else
382
+ ''
383
+ end
384
+ end
385
+
386
+ # Compare the canonicalized versions of the two names.
387
+ #
388
+ # name - The human or canonical String page name.
389
+ # filename - the String filename on disk (including extension).
390
+ #
391
+ # Returns a Boolean.
392
+ def page_match(name, filename)
393
+ if match = self.class.valid_filename?(filename)
394
+ @wiki.ws_subs.each do |sub|
395
+ return true if Page.cname(name).downcase == Page.cname(match, sub).downcase
396
+ end
397
+ end
398
+ false
399
+ end
400
+
401
+ # Loads a sub page. Sub page nanes (footers) are prefixed with
402
+ # an underscore to distinguish them from other Pages.
403
+ #
404
+ # name - String page name.
405
+ #
406
+ # Returns the Page or nil if none exists.
407
+ def find_sub_page(name)
408
+ return nil unless self.version
409
+ return nil if self.filename =~ /^_/
410
+ name = "_#{name.to_s.capitalize}"
411
+ return nil if page_match(name, self.filename)
412
+
413
+ dirs = self.path.split('/')
414
+ dirs.pop
415
+ map = @wiki.tree_map_for(self.version.id)
416
+ while !dirs.empty?
417
+ if page = find_page_in_tree(map, name, dirs.join('/'))
418
+ return page
419
+ end
420
+ dirs.pop
421
+ end
422
+
423
+ find_page_in_tree(map, name, '')
424
+ end
425
+
426
+ def inspect
427
+ %(#<#{self.class.name}:#{object_id} #{name} (#{format}) @wiki=#{@wiki.repo.path.inspect}>)
428
+ end
429
+ end
430
+ end
@@ -0,0 +1,61 @@
1
+ module Gollum
2
+ module Pagination
3
+ def self.included(klass)
4
+ klass.extend ClassMethods
5
+ class << klass
6
+ # Default Integer max count of items to return in git commands.
7
+ attr_accessor :per_page
8
+ end
9
+ klass.per_page = 30
10
+ end
11
+
12
+ module ClassMethods
13
+ # Turns a page number into an offset number for the git skip option.
14
+ #
15
+ # page - Integer page number.
16
+ #
17
+ # Returns an Integer.
18
+ def page_to_skip(page)
19
+ ([1, page.to_i].max - 1) * per_page
20
+ end
21
+
22
+ # Fills in git-specific options for the log command using simple
23
+ # pagination options.
24
+ #
25
+ # options - Hash of options:
26
+ # page - Optional Integer page number (default: 1)
27
+ # per_page - Optional Integer max count of items to return.
28
+ # Defaults to #per_class class method.
29
+ #
30
+ # Returns Hash with :max_count and :skip keys.
31
+ def log_pagination_options(options = {})
32
+ skip = page_to_skip(options.delete(:page))
33
+ options[:max_count] = [options.delete(:per_page).to_i, per_page].max
34
+ options[:skip] = skip if skip > 0
35
+ options
36
+ end
37
+ end
38
+
39
+ # Turns a page number into an offset number for the git skip option.
40
+ #
41
+ # page - Integer page number.
42
+ #
43
+ # Returns an Integer.
44
+ def page_to_skip(page)
45
+ self.class.page_to_skip(page)
46
+ end
47
+
48
+ # Fills in git-specific options for the log command using simple
49
+ # pagination options.
50
+ #
51
+ # options - Hash of options:
52
+ # page - Optional Integer page number (default: 1)
53
+ # per_page - Optional Integer max count of items to return.
54
+ # Defaults to #per_class class method.
55
+ #
56
+ # Returns Hash with :max_count and :skip keys.
57
+ def log_pagination_options(options = {})
58
+ self.class.log_pagination_options(options)
59
+ end
60
+ end
61
+ end