olelo 0.9.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 (282) hide show
  1. data/.gitignore +10 -0
  2. data/.gitmodules +3 -0
  3. data/Gemfile +3 -0
  4. data/README.markdown +104 -0
  5. data/Rakefile +92 -0
  6. data/bin/olelo +5 -0
  7. data/config.ru +86 -0
  8. data/config/aspects.rb +256 -0
  9. data/config/config.yml.default +154 -0
  10. data/config/initializers/00-mime_types.rb +29 -0
  11. data/config/initializers/01-slim.rb +2 -0
  12. data/config/interwiki.yml +11 -0
  13. data/doc/AUTHORS +7 -0
  14. data/doc/LICENSE +22 -0
  15. data/lib/olelo.rb +39 -0
  16. data/lib/olelo/application.rb +294 -0
  17. data/lib/olelo/attributes.rb +285 -0
  18. data/lib/olelo/config.rb +88 -0
  19. data/lib/olelo/extensions.rb +252 -0
  20. data/lib/olelo/helper.rb +290 -0
  21. data/lib/olelo/hooks.rb +142 -0
  22. data/lib/olelo/html_safe.rb +29 -0
  23. data/lib/olelo/initializer.rb +76 -0
  24. data/lib/olelo/locale.rb +63 -0
  25. data/lib/olelo/locale.yml +284 -0
  26. data/lib/olelo/menu.rb +101 -0
  27. data/lib/olelo/middleware/blacklist.rb +25 -0
  28. data/lib/olelo/middleware/degrade_mime_type.rb +19 -0
  29. data/lib/olelo/middleware/flash.rb +97 -0
  30. data/lib/olelo/middleware/force_encoding.rb +41 -0
  31. data/lib/olelo/page.rb +266 -0
  32. data/lib/olelo/patch.rb +311 -0
  33. data/lib/olelo/plugin.rb +188 -0
  34. data/lib/olelo/repository.rb +225 -0
  35. data/lib/olelo/routing.rb +223 -0
  36. data/lib/olelo/templates.rb +30 -0
  37. data/lib/olelo/user.rb +132 -0
  38. data/lib/olelo/util.rb +233 -0
  39. data/lib/olelo/version.rb +3 -0
  40. data/lib/olelo/virtualfs.rb +161 -0
  41. data/lib/rack/olelo_patches.rb +33 -0
  42. data/lib/rack/relative_redirect.rb +44 -0
  43. data/lib/rack/static_cache.rb +93 -0
  44. data/lib/yard/addons.rb +1 -0
  45. data/lib/yard/addons/hook_handler.rb +25 -0
  46. data/lib/yard/addons/override_tag.rb +14 -0
  47. data/lib/yard/addons/route_handler.rb +33 -0
  48. data/lib/yard/addons/sanitize_anchor.rb +16 -0
  49. data/olelo.gemspec +31 -0
  50. data/plugins/aspects/changelog.rb +45 -0
  51. data/plugins/aspects/documentbrowser.rb +57 -0
  52. data/plugins/aspects/download.rb +11 -0
  53. data/plugins/aspects/highlight.rb +8 -0
  54. data/plugins/aspects/image.rb +41 -0
  55. data/plugins/aspects/imageinfo.rb +64 -0
  56. data/plugins/aspects/locale.yml +60 -0
  57. data/plugins/aspects/main.rb +199 -0
  58. data/plugins/aspects/pageinfo.rb +37 -0
  59. data/plugins/aspects/source.rb +6 -0
  60. data/plugins/aspects/subpages.rb +44 -0
  61. data/plugins/aspects/text.rb +9 -0
  62. data/plugins/blog/blog.css +1 -0
  63. data/plugins/blog/blog.scss +37 -0
  64. data/plugins/blog/locale.yml +12 -0
  65. data/plugins/blog/main.rb +85 -0
  66. data/plugins/editor/locale.yml +18 -0
  67. data/plugins/editor/markup/main.rb +3 -0
  68. data/plugins/editor/markup/script.js +10 -0
  69. data/plugins/editor/markup/script/00-jquery.textselection.js +267 -0
  70. data/plugins/editor/markup/script/01-olelo.markupeditor.js +116 -0
  71. data/plugins/editor/markup/script/init.js +10 -0
  72. data/plugins/editor/preview.rb +52 -0
  73. data/plugins/editor/recaptcha.rb +56 -0
  74. data/plugins/filters/creole.rb +37 -0
  75. data/plugins/filters/disposition.rb +9 -0
  76. data/plugins/filters/editsection.rb +67 -0
  77. data/plugins/filters/fix_img_tag.rb +16 -0
  78. data/plugins/filters/html_wrapper.rb +12 -0
  79. data/plugins/filters/interwiki.rb +19 -0
  80. data/plugins/filters/link_classifier.rb +26 -0
  81. data/plugins/filters/locale.yml +15 -0
  82. data/plugins/filters/main.rb +202 -0
  83. data/plugins/filters/markdown_nowiki.rb +15 -0
  84. data/plugins/filters/numbering.xsl +93 -0
  85. data/plugins/filters/orgmode.rb +6 -0
  86. data/plugins/filters/rubypants.rb +6 -0
  87. data/plugins/filters/s5/main.rb +32 -0
  88. data/plugins/filters/s5/s5.xsl +118 -0
  89. data/plugins/filters/tilt.rb +17 -0
  90. data/plugins/filters/toc.rb +50 -0
  91. data/plugins/filters/xhtml2latex.xsl +232 -0
  92. data/plugins/filters/xslt.rb +22 -0
  93. data/plugins/gallery/gallery.css +1 -0
  94. data/plugins/gallery/gallery.scss +28 -0
  95. data/plugins/gallery/main.rb +34 -0
  96. data/plugins/misc/fancybox/images/blank.gif +0 -0
  97. data/plugins/misc/fancybox/images/fancy_close.png +0 -0
  98. data/plugins/misc/fancybox/images/fancy_loading.png +0 -0
  99. data/plugins/misc/fancybox/images/fancy_nav_left.png +0 -0
  100. data/plugins/misc/fancybox/images/fancy_nav_right.png +0 -0
  101. data/plugins/misc/fancybox/images/fancy_shadow_e.png +0 -0
  102. data/plugins/misc/fancybox/images/fancy_shadow_n.png +0 -0
  103. data/plugins/misc/fancybox/images/fancy_shadow_ne.png +0 -0
  104. data/plugins/misc/fancybox/images/fancy_shadow_nw.png +0 -0
  105. data/plugins/misc/fancybox/images/fancy_shadow_s.png +0 -0
  106. data/plugins/misc/fancybox/images/fancy_shadow_se.png +0 -0
  107. data/plugins/misc/fancybox/images/fancy_shadow_sw.png +0 -0
  108. data/plugins/misc/fancybox/images/fancy_shadow_w.png +0 -0
  109. data/plugins/misc/fancybox/images/fancy_title_left.png +0 -0
  110. data/plugins/misc/fancybox/images/fancy_title_main.png +0 -0
  111. data/plugins/misc/fancybox/images/fancy_title_over.png +0 -0
  112. data/plugins/misc/fancybox/images/fancy_title_right.png +0 -0
  113. data/plugins/misc/fancybox/images/fancybox-x.png +0 -0
  114. data/plugins/misc/fancybox/images/fancybox-y.png +0 -0
  115. data/plugins/misc/fancybox/images/fancybox.png +0 -0
  116. data/plugins/misc/fancybox/jquery.fancybox.css +1 -0
  117. data/plugins/misc/fancybox/jquery.fancybox.scss +323 -0
  118. data/plugins/misc/fancybox/main.rb +4 -0
  119. data/plugins/misc/fancybox/script.js +37 -0
  120. data/plugins/misc/fancybox/script/00-jquery.mousewheel.js +84 -0
  121. data/plugins/misc/fancybox/script/01-jquery.easing.js +205 -0
  122. data/plugins/misc/fancybox/script/02-jquery.fancybox.js +1156 -0
  123. data/plugins/misc/fancybox/script/init.js +18 -0
  124. data/plugins/misc/system.rb +192 -0
  125. data/plugins/misc/variables.rb +29 -0
  126. data/plugins/misc/webdav.rb +45 -0
  127. data/plugins/repositories/git_grep.rb +69 -0
  128. data/plugins/repositories/gitrb_repository.rb +204 -0
  129. data/plugins/repositories/locale.yml +12 -0
  130. data/plugins/repositories/rugged_repository.rb +454 -0
  131. data/plugins/security/acl.rb +57 -0
  132. data/plugins/security/basic_auth.rb +21 -0
  133. data/plugins/security/locale.yml +21 -0
  134. data/plugins/security/persistent_login.rb +32 -0
  135. data/plugins/security/portal.rb +28 -0
  136. data/plugins/security/private_wiki.rb +24 -0
  137. data/plugins/security/readonly_wiki.rb +25 -0
  138. data/plugins/security/stack.rb +20 -0
  139. data/plugins/security/yamlfile.rb +66 -0
  140. data/plugins/tags/code.rb +6 -0
  141. data/plugins/tags/footnotes.rb +35 -0
  142. data/plugins/tags/gist-embed.css +123 -0
  143. data/plugins/tags/gist.rb +13 -0
  144. data/plugins/tags/html.rb +57 -0
  145. data/plugins/tags/include.rb +20 -0
  146. data/plugins/tags/main.rb +353 -0
  147. data/plugins/tags/math.rb +117 -0
  148. data/plugins/tags/scripting.rb +64 -0
  149. data/plugins/tags/sort.rb +7 -0
  150. data/plugins/tags/tabs.rb +20 -0
  151. data/plugins/treeview/images/collapsed.png +0 -0
  152. data/plugins/treeview/images/expanded.png +0 -0
  153. data/plugins/treeview/images/menu.png +0 -0
  154. data/plugins/treeview/images/tree.png +0 -0
  155. data/plugins/treeview/images/wait.gif +0 -0
  156. data/plugins/treeview/main.rb +16 -0
  157. data/plugins/treeview/script.js +5 -0
  158. data/plugins/treeview/script/00-jquery.treeview.js +164 -0
  159. data/plugins/treeview/script/init.js +25 -0
  160. data/plugins/treeview/treeview.css +1 -0
  161. data/plugins/treeview/treeview.scss +113 -0
  162. data/plugins/utils/assets.rb +74 -0
  163. data/plugins/utils/cache.rb +53 -0
  164. data/plugins/utils/image_magick.rb +34 -0
  165. data/plugins/utils/pygments.css +1 -0
  166. data/plugins/utils/pygments.rb +50 -0
  167. data/plugins/utils/pygments.scss +83 -0
  168. data/plugins/utils/semaphore.rb +50 -0
  169. data/plugins/utils/shell.rb +45 -0
  170. data/plugins/utils/store.rb +315 -0
  171. data/plugins/utils/worker.rb +36 -0
  172. data/plugins/utils/xml.rb +29 -0
  173. data/static/images/favicon.png +0 -0
  174. data/static/script.js +267 -0
  175. data/static/script/00-json2.js +486 -0
  176. data/static/script/01-jstorage.js +217 -0
  177. data/static/script/02-jquery.js +9440 -0
  178. data/static/script/03-jquery.ui.core.js +337 -0
  179. data/static/script/04-jquery.ui.widget.js +502 -0
  180. data/static/script/05-jquery.ui.position.js +517 -0
  181. data/static/script/06-jquery.ui.menu.js +609 -0
  182. data/static/script/07-jquery.ui.autocomplete.js +601 -0
  183. data/static/script/08-olelo.i18n.js +37 -0
  184. data/static/script/09-olelo.unsaved.js +68 -0
  185. data/static/script/10-olelo.historytable.js +40 -0
  186. data/static/script/11-olelo.pagination.js +18 -0
  187. data/static/script/13-olelo.tabwidget.js +57 -0
  188. data/static/script/14-olelo.timeago.js +70 -0
  189. data/static/script/15-olelo.underliner.js +31 -0
  190. data/static/script/16-olelo.ui.combobox.js +32 -0
  191. data/static/script/init.js +48 -0
  192. data/static/themes/atlantis/constants.scss +15 -0
  193. data/static/themes/atlantis/iehacks.scss +38 -0
  194. data/static/themes/atlantis/images/actions/delete.png +0 -0
  195. data/static/themes/atlantis/images/actions/edit.png +0 -0
  196. data/static/themes/atlantis/images/actions/history.png +0 -0
  197. data/static/themes/atlantis/images/actions/home.png +0 -0
  198. data/static/themes/atlantis/images/actions/move.png +0 -0
  199. data/static/themes/atlantis/images/actions/new.png +0 -0
  200. data/static/themes/atlantis/images/actions/page.png +0 -0
  201. data/static/themes/atlantis/images/bg/button.png +0 -0
  202. data/static/themes/atlantis/images/bg/container.png +0 -0
  203. data/static/themes/atlantis/images/bg/content.png +0 -0
  204. data/static/themes/atlantis/images/bg/footer.png +0 -0
  205. data/static/themes/atlantis/images/bg/header.jpg +0 -0
  206. data/static/themes/atlantis/images/bg/header.orig.jpg +0 -0
  207. data/static/themes/atlantis/images/bg/header_gray.jpg +0 -0
  208. data/static/themes/atlantis/images/bug.png +0 -0
  209. data/static/themes/atlantis/images/filetypes/7z.png +0 -0
  210. data/static/themes/atlantis/images/filetypes/_archive.png +0 -0
  211. data/static/themes/atlantis/images/filetypes/_audio.png +0 -0
  212. data/static/themes/atlantis/images/filetypes/_code.png +0 -0
  213. data/static/themes/atlantis/images/filetypes/_linux.png +0 -0
  214. data/static/themes/atlantis/images/filetypes/_picture.png +0 -0
  215. data/static/themes/atlantis/images/filetypes/_video.png +0 -0
  216. data/static/themes/atlantis/images/filetypes/bz2.png +0 -0
  217. data/static/themes/atlantis/images/filetypes/doc.png +0 -0
  218. data/static/themes/atlantis/images/filetypes/flac.png +0 -0
  219. data/static/themes/atlantis/images/filetypes/gz.png +0 -0
  220. data/static/themes/atlantis/images/filetypes/html.png +0 -0
  221. data/static/themes/atlantis/images/filetypes/java.png +0 -0
  222. data/static/themes/atlantis/images/filetypes/jpg.png +0 -0
  223. data/static/themes/atlantis/images/filetypes/midi.png +0 -0
  224. data/static/themes/atlantis/images/filetypes/mp3.png +0 -0
  225. data/static/themes/atlantis/images/filetypes/ogg.png +0 -0
  226. data/static/themes/atlantis/images/filetypes/pdf.png +0 -0
  227. data/static/themes/atlantis/images/filetypes/php.png +0 -0
  228. data/static/themes/atlantis/images/filetypes/png.png +0 -0
  229. data/static/themes/atlantis/images/filetypes/ppt.png +0 -0
  230. data/static/themes/atlantis/images/filetypes/psd.png +0 -0
  231. data/static/themes/atlantis/images/filetypes/rar.png +0 -0
  232. data/static/themes/atlantis/images/filetypes/rb.png +0 -0
  233. data/static/themes/atlantis/images/filetypes/sh.png +0 -0
  234. data/static/themes/atlantis/images/filetypes/tar.png +0 -0
  235. data/static/themes/atlantis/images/filetypes/txt.png +0 -0
  236. data/static/themes/atlantis/images/filetypes/wma.png +0 -0
  237. data/static/themes/atlantis/images/filetypes/xls.png +0 -0
  238. data/static/themes/atlantis/images/filetypes/zip.png +0 -0
  239. data/static/themes/atlantis/images/folder.png +0 -0
  240. data/static/themes/atlantis/images/folder_open.png +0 -0
  241. data/static/themes/atlantis/images/loading.gif +0 -0
  242. data/static/themes/atlantis/images/loading.xcf +0 -0
  243. data/static/themes/atlantis/images/not_found.png +0 -0
  244. data/static/themes/atlantis/images/page.png +0 -0
  245. data/static/themes/atlantis/images/search.png +0 -0
  246. data/static/themes/atlantis/layout.scss +115 -0
  247. data/static/themes/atlantis/menu.scss +99 -0
  248. data/static/themes/atlantis/print.scss +129 -0
  249. data/static/themes/atlantis/screen.scss +495 -0
  250. data/static/themes/atlantis/style.css +3 -0
  251. data/static/themes/lib/autocomplete.scss +39 -0
  252. data/static/themes/lib/headlines.scss +10 -0
  253. data/static/themes/lib/horizontal-list.scss +31 -0
  254. data/static/themes/lib/patch.scss +88 -0
  255. data/static/themes/lib/reset.scss +114 -0
  256. data/static/themes/lib/rounded.scss +46 -0
  257. data/static/themes/lib/shadow.scss +14 -0
  258. data/test/config_test.rb +28 -0
  259. data/test/factory_test.rb +29 -0
  260. data/test/hash_extensions_test.rb +16 -0
  261. data/test/helper.rb +38 -0
  262. data/test/hooks_test.rb +85 -0
  263. data/test/object_extensions_test.rb +20 -0
  264. data/test/page_test.rb +168 -0
  265. data/test/request_test.rb +166 -0
  266. data/test/string_extensions_test.rb +32 -0
  267. data/test/templates_test.rb +39 -0
  268. data/test/util_test.rb +71 -0
  269. data/views/changes.slim +22 -0
  270. data/views/compare.slim +8 -0
  271. data/views/delete.slim +9 -0
  272. data/views/deleted.slim +2 -0
  273. data/views/edit.slim +65 -0
  274. data/views/error.slim +6 -0
  275. data/views/history.slim +20 -0
  276. data/views/layout.slim +38 -0
  277. data/views/login.slim +37 -0
  278. data/views/move.slim +10 -0
  279. data/views/not_found.slim +6 -0
  280. data/views/profile.slim +26 -0
  281. data/views/show.slim +9 -0
  282. metadata +488 -0
@@ -0,0 +1,64 @@
1
+ description 'Image information aspect'
2
+ dependencies 'utils/image_magick'
3
+
4
+ Aspect.create(:imageinfo, :priority => 1, :layout => true, :cacheable => true, :accepts => %r{^image/}) do
5
+ def call(context, page)
6
+ @page = page
7
+ identify = ImageMagick.identify('-format', '%m %h %w', '-').run(page.content).split(' ')
8
+ @type = identify[0]
9
+ @geometry = "#{identify[1]}x#{identify[2]}"
10
+ begin
11
+ @exif = Shell.exif('-m', '/dev/stdin').run(page.content)
12
+ @exif.force_encoding(Encoding.default_external)
13
+ @exif = @exif.split("\n").map {|line| line.split("\t") }
14
+ @exif = nil if !@exif[0] || !@exif[0][1]
15
+ rescue => ex
16
+ Olelo.logger.warn "Exif data could not be read: #{ex.message}"
17
+ @exif = nil
18
+ end
19
+ render :info
20
+ end
21
+ end
22
+
23
+ __END__
24
+ @@ info.slim
25
+ p
26
+ a href=build_path(@page, :aspect => 'image')
27
+ img src=build_path(@page, :aspect => 'image', :geometry => '640x480>') alt=@page.title
28
+ h3= :information.t
29
+ table
30
+ tbody
31
+ tr
32
+ td= :name.t
33
+ td= @page.name
34
+ tr
35
+ td= :title.t
36
+ td= @page.title
37
+ tr
38
+ td= :description.t
39
+ td= @page.attributes['description']
40
+ tr
41
+ td= :type.t
42
+ td= @type
43
+ tr
44
+ td= :geometry.t
45
+ td= @geometry
46
+ - if @page.version
47
+ tr
48
+ td= :last_modified.t
49
+ td= date @page.version.date
50
+ tr
51
+ td= :version.t
52
+ td.version= @page.version
53
+ - if @exif
54
+ h3= :exif.t
55
+ table
56
+ thead
57
+ tr
58
+ th= :entry.t
59
+ th= :value.t
60
+ tbody
61
+ - @exif.each do |key, value|
62
+ tr
63
+ td= key
64
+ td= value
@@ -0,0 +1,60 @@
1
+ en:
2
+ attribute_aspect: 'Default Aspect'
3
+ download: 'Download'
4
+ aspect_documentbrowser: 'Document Browser'
5
+ aspect_download: 'Raw Download'
6
+ aspect_pageinfo: 'Page Information'
7
+ aspect_highlight: 'Highlighted Source'
8
+ aspect_imageinfo: 'Image Information'
9
+ aspect_image: 'Raw Image'
10
+ aspect_source: 'Page Source'
11
+ aspect_subpages: 'Subpages'
12
+ aspect_text: 'Text Download'
13
+ aspect_gallery: 'Image Gallery'
14
+ aspect_not_available: 'Aspect #{aspect} is not available for page #{page} with type #{type}.'
15
+ entry: 'Entry'
16
+ exif: 'EXIF Information'
17
+ geometry: 'Geometry'
18
+ information: 'Information'
19
+ type: 'Type'
20
+ value: 'Value'
21
+ de:
22
+ attribute_aspect: 'Standard-Ansicht'
23
+ download: 'Herunterladen'
24
+ aspect_documentbrowser: 'Dokumentenbrowser'
25
+ aspect_download: 'Herunterladen'
26
+ aspect_pageinfo: 'Seiten-Information'
27
+ aspect_highlight: 'Quellcode mit Syntaxhighlighting'
28
+ aspect_imageinfo: 'Bild-Information'
29
+ aspect_image: 'Bild'
30
+ aspect_source: 'Quellcode'
31
+ aspect_subpages: 'Unterseiten'
32
+ aspect_text: 'Quellcode herunterladen'
33
+ aspect_gallery: 'Bildergalerie'
34
+ aspect_not_available: 'Aspekt #{aspect} für die Seite #{page} mit dem Typ #{type} ist nicht verfügbar.'
35
+ entry: 'Eintrag'
36
+ exif: 'EXIF-Information'
37
+ geometry: 'Geometrie'
38
+ information: 'Information'
39
+ type: 'Typ'
40
+ value: 'Wert'
41
+ cs_CZ:
42
+ attribute_aspect: 'Přednastavený vzhled'
43
+ download: 'Stáhnout'
44
+ aspect_documentbrowser: 'Document Browser'
45
+ aspect_download: 'Stažení (neupraveno)'
46
+ aspect_pageinfo: 'Informace o stránce'
47
+ aspect_highlight: 'Zvýrazněný zdroj'
48
+ aspect_imageinfo: 'Informace o obrázku'
49
+ aspect_image: 'Stažení obrázku'
50
+ aspect_source: 'Zdroj stránky'
51
+ aspect_subpages: 'Podstránky'
52
+ aspect_text: 'Stažení textu'
53
+ aspect_gallery: 'Galerie'
54
+ aspect_not_available: 'Vzhled #{aspect} není k dispozici pro stránku #{page} s typem #{type}.'
55
+ entry: 'Položka'
56
+ exif: 'Informace EXIF'
57
+ geometry: 'Geometrie'
58
+ information: 'Informace'
59
+ type: 'Typ'
60
+ value: 'Hodnota'
@@ -0,0 +1,199 @@
1
+ description 'Aspect subsystem'
2
+ dependencies 'utils/cache'
3
+
4
+ Page.attributes do
5
+ enum :aspect do
6
+ Aspect.aspects.keys.inject({}) do |hash, name|
7
+ hash[name] = Locale.translate("aspect_#{name}", :fallback => titlecase(name))
8
+ hash
9
+ end
10
+ end
11
+ end
12
+
13
+ # Aspect context
14
+ # A aspect context holds the request parameters and other
15
+ # variables used by the aspects.
16
+ # It is possible for a aspect to run sub-aspects. For this
17
+ # purpose you create a subcontext which inherits the variables.
18
+ class Context
19
+ include Hooks
20
+ has_hooks :initialized
21
+
22
+ attr_reader :page, :private, :params, :request, :header
23
+
24
+ def initialize(options = {})
25
+ @page = options[:page]
26
+ @private = options[:private] || Hash.with_indifferent_access
27
+ @params = Hash.with_indifferent_access.merge(options[:params] || {})
28
+ @request = options[:request]
29
+ @header = options[:header] || Hash.with_indifferent_access
30
+ invoke_hook(:initialized)
31
+ end
32
+
33
+ def [](key)
34
+ private[key]
35
+ end
36
+
37
+ def []=(key, value)
38
+ private[key] = value
39
+ end
40
+
41
+ def subcontext(options = {})
42
+ Context.new(:page => options[:page] || page,
43
+ :private => private.merge(options[:private] || {}),
44
+ :params => params.merge(options[:params] || {}),
45
+ :request => request,
46
+ :header => header)
47
+ end
48
+ end
49
+
50
+ # An Aspect renders pages
51
+ # Aspects get a page as input and create text.
52
+ class Aspect
53
+ include PageHelper
54
+ include Templates
55
+
56
+ @aspects = {}
57
+
58
+ class NotAvailable < NameError
59
+ def initialize(name, page)
60
+ super(:aspect_not_available.t(:aspect => name, :page => page.path,
61
+ :type => "#{page.mime.comment} (#{page.mime})"))
62
+ end
63
+ end
64
+
65
+ # Constructor for aspect
66
+ # Options:
67
+ # * layout: Aspect output should be wrapped in HTML layout (Not used for download/image aspects for example)
68
+ # * priority: Aspect priority. The aspect with the lowest priority will be used for a page.
69
+ # * cacheable: Aspect is cacheable
70
+ def initialize(name, options)
71
+ @name = name.to_s
72
+ @layout = !!options[:layout]
73
+ @hidden = !!options[:hidden]
74
+ @cacheable = !!options[:cacheable]
75
+ @priority = (options[:priority] || 99).to_i
76
+ @accepts = String === options[:accepts] ? /^(?:#{options[:accepts]})$/ : options[:accepts]
77
+ @mime = options[:mime]
78
+ @plugin = options[:plugin] || Plugin.for(self.class)
79
+ @description = options[:description] || @plugin.description
80
+ end
81
+
82
+ attr_reader :name, :priority, :mime, :accepts, :description, :plugin
83
+ attr_reader? :layout, :hidden, :cacheable
84
+
85
+ # Aspects hash
86
+ def self.aspects
87
+ @aspects
88
+ end
89
+
90
+ # Create aspect class. This is sugar to create and
91
+ # register an aspect class in one step.
92
+ def self.create(name, options = {}, &block)
93
+ options[:plugin] ||= Plugin.for(block)
94
+ klass = Class.new(self)
95
+ klass.class_eval(&block)
96
+ register klass.new(name, options)
97
+ end
98
+
99
+ # Register aspect instance
100
+ def self.register(aspect)
101
+ (@aspects[aspect.name] ||= []) << aspect
102
+ end
103
+
104
+ # Find all accepting aspects for a page
105
+ def self.find_all(page)
106
+ @aspects.values.map do |aspects|
107
+ aspects.sort_by(&:priority).find {|a| a.accepts?(page) }
108
+ end.compact
109
+ end
110
+
111
+ # Find appropiate aspect for page. An optional
112
+ # name can be given to claim a specific aspect.
113
+ # If no aspect is found a exception is raised.
114
+ def self.find!(page, options = {})
115
+ options[:name] ||= page.attributes['aspect']
116
+ aspects = options[:name] ? @aspects[options[:name].to_s] : @aspects.values.flatten
117
+ aspect = aspects.to_a.sort_by(&:priority).find {|a| a.accepts?(page) && (!options[:layout] || a.layout?) }
118
+ raise NotAvailable.new(options[:name], page) if !aspect
119
+ aspect.dup
120
+ end
121
+
122
+ # Find appropiate aspect for page. An optional
123
+ # name can be given to claim a specific aspect.
124
+ # If no aspect is found nil is returned.
125
+ def self.find(page, options = {})
126
+ find!(page, options) rescue nil
127
+ end
128
+
129
+ # Acceptor should return true if page would be accepted by this aspect.
130
+ # Reimplement this method.
131
+ def accepts?(page)
132
+ !@accepts || page.mime.to_s =~ @accepts
133
+ end
134
+
135
+ # Render page content.
136
+ # Reimplement this method.
137
+ def call(context, page)
138
+ raise NotImplementedError
139
+ end
140
+ end
141
+
142
+ # Plug-in the aspect subsystem
143
+ module ::Olelo::PageHelper
144
+ def render_page(page)
145
+ Cache.cache("include-#{page.path}-#{page.version.cache_id}", :update => request.no_cache?, :defer => true) do |context|
146
+ begin
147
+ context = Context.new(:page => page, :params => {:included => true})
148
+ Aspect.find!(page, :layout => true).call(context, page)
149
+ rescue Aspect::NotAvailable => ex
150
+ %{<span class="error">#{escape_html ex.message}</span>}
151
+ end
152
+ end
153
+ end
154
+ end
155
+
156
+ # Plug-in the aspect subsystem
157
+ class ::Olelo::Application
158
+ def show_page
159
+ params[:aspect] ||= 'subpages' if params[:path].to_s.ends_with? '/'
160
+ @selected_aspect, layout, header, content =
161
+ Cache.cache("aspect-#{page.path}-#{page.version.cache_id}-#{build_query(params)}",
162
+ :update => request.no_cache?, :defer => true) do |cache|
163
+ aspect = Aspect.find!(page, :name => params[:aspect])
164
+ cache.disable! if !aspect.cacheable?
165
+ context = Context.new(:page => page, :params => params, :request => request)
166
+ result = aspect.call(context, page)
167
+ context.header['Content-Type'] ||= aspect.mime.to_s if aspect.mime
168
+ context.header['Content-Type'] ||= page.mime.to_s if !aspect.layout?
169
+ [aspect.name, aspect.layout?, context.header.to_hash, result]
170
+ end
171
+ self.response.header.merge!(header)
172
+
173
+ @menu_versions = true
174
+ halt(layout ? render(:show, :locals => {:content => content}) : content)
175
+ rescue Aspect::NotAvailable => ex
176
+ cache_control :no_cache => true
177
+ redirect build_path(page.path) if params[:path].to_s.ends_with? '/'
178
+ raise if params[:aspect]
179
+ flash.error ex.message
180
+ redirect build_path(page, :action => :edit)
181
+ end
182
+
183
+ hook :menu do |menu|
184
+ if menu.name == :actions && view_menu = menu[:view]
185
+ Cache.cache("aspect-menu-#{page.path}-#{page.version.cache_id}-#{@selected_aspect}",
186
+ :update => request.no_cache?, :defer => true) do
187
+ aspects = Aspect.find_all(page).select {|a| !a.hidden? || a.name == @selected_aspect || a.name == page.attributes['aspect'] }.map do |a|
188
+ [Locale.translate("aspect_#{a.name}", :fallback => titlecase(a.name)), a]
189
+ end.sort_by(&:first)
190
+ aspects.select {|label, a| a.layout? }.map do |label, a|
191
+ MenuItem.new(a.name, :label => label, :href => build_path(page, :aspect => a.name), :class => a.name == @selected_aspect ? 'selected' : nil)
192
+ end +
193
+ aspects.reject {|label, a| a.layout? }.map do |label, a|
194
+ MenuItem.new(a.name, :label => label, :href => build_path(page, :aspect => a.name), :class => 'download')
195
+ end
196
+ end.each {|item| view_menu << item }
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,37 @@
1
+ description 'Page information aspect'
2
+
3
+ Aspect.create(:pageinfo, :priority => 4, :layout => true, :cacheable => true) do
4
+ def call(context, page)
5
+ @page = page
6
+ render :info
7
+ end
8
+ end
9
+
10
+ __END__
11
+ @@ info.slim
12
+ table
13
+ tbody
14
+ tr
15
+ td= :name.t
16
+ td= @page.name
17
+ tr
18
+ td= :title.t
19
+ td= @page.title
20
+ tr
21
+ td= :description.t
22
+ td= @page.attributes['description']
23
+ - if @page.version
24
+ tr
25
+ td= :last_modified.t
26
+ td= date @page.version.date
27
+ tr
28
+ td= :version.t
29
+ td.version = @page.version
30
+ tr
31
+ td= :type.t
32
+ td= @page.mime.comment.blank? ? @page.mime : "#{@page.mime.comment} (#{@page.mime})"
33
+ - if !@page.content.empty?
34
+ tr
35
+ td= :download.t
36
+ td
37
+ a href=build_path(@page, :aspect => 'download') = :download.t
@@ -0,0 +1,6 @@
1
+ description 'Source aspect'
2
+
3
+ Aspect.create(:source, :priority => 3, :layout => true, :cacheable => true) do
4
+ def accepts?(page); page.mime.text?; end
5
+ def call(context, page); "<pre>#{escape_html page.content}</pre>"; end
6
+ end
@@ -0,0 +1,44 @@
1
+ description 'Subpages aspect'
2
+
3
+ Aspect.create(:subpages, :priority => 2, :layout => true, :cacheable => true) do
4
+ def accepts?(page); !page.children.empty?; end
5
+ def call(context, page)
6
+ @page_nr = [context.params[:page].to_i, 1].max
7
+ per_page = 20
8
+ @page = page
9
+ @page_count = @page.children.size / per_page + 1
10
+ @children = @page.children[((@page_nr - 1) * per_page) ... (@page_nr * per_page)].to_a
11
+ render :subpages
12
+ end
13
+ end
14
+
15
+ __END__
16
+ @@ subpages.slim
17
+ = pagination(@page, @page_count, @page_nr, :aspect => 'subpages')
18
+ table#subpages-table
19
+ thead
20
+ tr
21
+ th= :name.t
22
+ th= :description.t
23
+ th= :last_modified.t
24
+ th= :author.t
25
+ th= :comment.t
26
+ th= :actions.t
27
+ tbody
28
+ - @children.each do |child|
29
+ - classes = child.children.empty? ? 'page' : 'folder'
30
+ - if !child.extension.empty?
31
+ - classes << " file-type-#{child.extension}"
32
+ tr
33
+ td.link
34
+ a href=build_path(child) class=classes = child.name
35
+ td= truncate(child.attributes['description'], 30)
36
+ td= date(child.version.date)
37
+ td= truncate(child.version.author.name, 30)
38
+ td= truncate(child.version.comment, 30)
39
+ td.actions
40
+ a.action-edit href=build_path(child, :action => :edit) title=:edit.t = :edit.t
41
+ a.action-history href=build_path(child, :action => :history) title=:history.t = :history.t
42
+ a.action-move href=build_path(child, :action => :move) title=:move.t = :move.t
43
+ a.action-delete href=build_path(child, :action => :delete) title=:delete.t = :delete.t
44
+ = pagination(@page, @page_count, @page_nr, :aspect => 'subpages')
@@ -0,0 +1,9 @@
1
+ description 'Text aspect'
2
+
3
+ Aspect.create(:text, :mime => 'text/plain; charset=utf-8') do
4
+ def accepts?(page); page.mime.text?; end
5
+ def call(context, page)
6
+ context.header['Content-Length'] = page.content.bytesize.to_s
7
+ page.content
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ .blog>.article{clear:both;width:100%}.blog>.article>h2{margin:0}.blog>.article>.author,.blog>.article>.date{float:left;font-size:90%;color:#555555;margin:0.2em 0.4em 1em 0}.blog>.article>.content{clear:both;width:100%}.blog>.article>.full{clear:both;float:right}.blog-menu{border-collapse:separate;border-spacing:0;vertical-align:middle;margin:0;padding:0;border:0;outline:0;box-shadow:0px 0px 0px transparent;-webkit-box-shadow:0px 0px 0px transparent}.blog-menu tr{background:transparent}.blog-menu tr td{vertical-align:top}
@@ -0,0 +1,37 @@
1
+ @import "lib/reset.scss";
2
+ @import "lib/shadow.scss";
3
+ @import "lib/horizontal-list.scss";
4
+
5
+ .blog > .article {
6
+ clear: both;
7
+ width: 100%;
8
+ > h2 {
9
+ margin: 0;
10
+ }
11
+ > .author, > .date {
12
+ float: left;
13
+ font-size: 90%;
14
+ color: #555555;
15
+ margin: 0.2em 0.4em 1em 0;
16
+ }
17
+ > .content {
18
+ clear: both;
19
+ width: 100%;
20
+ }
21
+ > .full {
22
+ clear: both;
23
+ float: right;
24
+ }
25
+ }
26
+
27
+ .blog-menu {
28
+ @include reset-table;
29
+ @include reset-box-model;
30
+ @include no-box-shadow;
31
+ tr {
32
+ background: transparent;
33
+ td {
34
+ vertical-align: top;
35
+ }
36
+ }
37
+ }