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,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