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.
- data/.gitignore +10 -0
- data/.gitmodules +3 -0
- data/Gemfile +3 -0
- data/README.markdown +104 -0
- data/Rakefile +92 -0
- data/bin/olelo +5 -0
- data/config.ru +86 -0
- data/config/aspects.rb +256 -0
- data/config/config.yml.default +154 -0
- data/config/initializers/00-mime_types.rb +29 -0
- data/config/initializers/01-slim.rb +2 -0
- data/config/interwiki.yml +11 -0
- data/doc/AUTHORS +7 -0
- data/doc/LICENSE +22 -0
- data/lib/olelo.rb +39 -0
- data/lib/olelo/application.rb +294 -0
- data/lib/olelo/attributes.rb +285 -0
- data/lib/olelo/config.rb +88 -0
- data/lib/olelo/extensions.rb +252 -0
- data/lib/olelo/helper.rb +290 -0
- data/lib/olelo/hooks.rb +142 -0
- data/lib/olelo/html_safe.rb +29 -0
- data/lib/olelo/initializer.rb +76 -0
- data/lib/olelo/locale.rb +63 -0
- data/lib/olelo/locale.yml +284 -0
- data/lib/olelo/menu.rb +101 -0
- data/lib/olelo/middleware/blacklist.rb +25 -0
- data/lib/olelo/middleware/degrade_mime_type.rb +19 -0
- data/lib/olelo/middleware/flash.rb +97 -0
- data/lib/olelo/middleware/force_encoding.rb +41 -0
- data/lib/olelo/page.rb +266 -0
- data/lib/olelo/patch.rb +311 -0
- data/lib/olelo/plugin.rb +188 -0
- data/lib/olelo/repository.rb +225 -0
- data/lib/olelo/routing.rb +223 -0
- data/lib/olelo/templates.rb +30 -0
- data/lib/olelo/user.rb +132 -0
- data/lib/olelo/util.rb +233 -0
- data/lib/olelo/version.rb +3 -0
- data/lib/olelo/virtualfs.rb +161 -0
- data/lib/rack/olelo_patches.rb +33 -0
- data/lib/rack/relative_redirect.rb +44 -0
- data/lib/rack/static_cache.rb +93 -0
- data/lib/yard/addons.rb +1 -0
- data/lib/yard/addons/hook_handler.rb +25 -0
- data/lib/yard/addons/override_tag.rb +14 -0
- data/lib/yard/addons/route_handler.rb +33 -0
- data/lib/yard/addons/sanitize_anchor.rb +16 -0
- data/olelo.gemspec +31 -0
- data/plugins/aspects/changelog.rb +45 -0
- data/plugins/aspects/documentbrowser.rb +57 -0
- data/plugins/aspects/download.rb +11 -0
- data/plugins/aspects/highlight.rb +8 -0
- data/plugins/aspects/image.rb +41 -0
- data/plugins/aspects/imageinfo.rb +64 -0
- data/plugins/aspects/locale.yml +60 -0
- data/plugins/aspects/main.rb +199 -0
- data/plugins/aspects/pageinfo.rb +37 -0
- data/plugins/aspects/source.rb +6 -0
- data/plugins/aspects/subpages.rb +44 -0
- data/plugins/aspects/text.rb +9 -0
- data/plugins/blog/blog.css +1 -0
- data/plugins/blog/blog.scss +37 -0
- data/plugins/blog/locale.yml +12 -0
- data/plugins/blog/main.rb +85 -0
- data/plugins/editor/locale.yml +18 -0
- data/plugins/editor/markup/main.rb +3 -0
- data/plugins/editor/markup/script.js +10 -0
- data/plugins/editor/markup/script/00-jquery.textselection.js +267 -0
- data/plugins/editor/markup/script/01-olelo.markupeditor.js +116 -0
- data/plugins/editor/markup/script/init.js +10 -0
- data/plugins/editor/preview.rb +52 -0
- data/plugins/editor/recaptcha.rb +56 -0
- data/plugins/filters/creole.rb +37 -0
- data/plugins/filters/disposition.rb +9 -0
- data/plugins/filters/editsection.rb +67 -0
- data/plugins/filters/fix_img_tag.rb +16 -0
- data/plugins/filters/html_wrapper.rb +12 -0
- data/plugins/filters/interwiki.rb +19 -0
- data/plugins/filters/link_classifier.rb +26 -0
- data/plugins/filters/locale.yml +15 -0
- data/plugins/filters/main.rb +202 -0
- data/plugins/filters/markdown_nowiki.rb +15 -0
- data/plugins/filters/numbering.xsl +93 -0
- data/plugins/filters/orgmode.rb +6 -0
- data/plugins/filters/rubypants.rb +6 -0
- data/plugins/filters/s5/main.rb +32 -0
- data/plugins/filters/s5/s5.xsl +118 -0
- data/plugins/filters/tilt.rb +17 -0
- data/plugins/filters/toc.rb +50 -0
- data/plugins/filters/xhtml2latex.xsl +232 -0
- data/plugins/filters/xslt.rb +22 -0
- data/plugins/gallery/gallery.css +1 -0
- data/plugins/gallery/gallery.scss +28 -0
- data/plugins/gallery/main.rb +34 -0
- data/plugins/misc/fancybox/images/blank.gif +0 -0
- data/plugins/misc/fancybox/images/fancy_close.png +0 -0
- data/plugins/misc/fancybox/images/fancy_loading.png +0 -0
- data/plugins/misc/fancybox/images/fancy_nav_left.png +0 -0
- data/plugins/misc/fancybox/images/fancy_nav_right.png +0 -0
- data/plugins/misc/fancybox/images/fancy_shadow_e.png +0 -0
- data/plugins/misc/fancybox/images/fancy_shadow_n.png +0 -0
- data/plugins/misc/fancybox/images/fancy_shadow_ne.png +0 -0
- data/plugins/misc/fancybox/images/fancy_shadow_nw.png +0 -0
- data/plugins/misc/fancybox/images/fancy_shadow_s.png +0 -0
- data/plugins/misc/fancybox/images/fancy_shadow_se.png +0 -0
- data/plugins/misc/fancybox/images/fancy_shadow_sw.png +0 -0
- data/plugins/misc/fancybox/images/fancy_shadow_w.png +0 -0
- data/plugins/misc/fancybox/images/fancy_title_left.png +0 -0
- data/plugins/misc/fancybox/images/fancy_title_main.png +0 -0
- data/plugins/misc/fancybox/images/fancy_title_over.png +0 -0
- data/plugins/misc/fancybox/images/fancy_title_right.png +0 -0
- data/plugins/misc/fancybox/images/fancybox-x.png +0 -0
- data/plugins/misc/fancybox/images/fancybox-y.png +0 -0
- data/plugins/misc/fancybox/images/fancybox.png +0 -0
- data/plugins/misc/fancybox/jquery.fancybox.css +1 -0
- data/plugins/misc/fancybox/jquery.fancybox.scss +323 -0
- data/plugins/misc/fancybox/main.rb +4 -0
- data/plugins/misc/fancybox/script.js +37 -0
- data/plugins/misc/fancybox/script/00-jquery.mousewheel.js +84 -0
- data/plugins/misc/fancybox/script/01-jquery.easing.js +205 -0
- data/plugins/misc/fancybox/script/02-jquery.fancybox.js +1156 -0
- data/plugins/misc/fancybox/script/init.js +18 -0
- data/plugins/misc/system.rb +192 -0
- data/plugins/misc/variables.rb +29 -0
- data/plugins/misc/webdav.rb +45 -0
- data/plugins/repositories/git_grep.rb +69 -0
- data/plugins/repositories/gitrb_repository.rb +204 -0
- data/plugins/repositories/locale.yml +12 -0
- data/plugins/repositories/rugged_repository.rb +454 -0
- data/plugins/security/acl.rb +57 -0
- data/plugins/security/basic_auth.rb +21 -0
- data/plugins/security/locale.yml +21 -0
- data/plugins/security/persistent_login.rb +32 -0
- data/plugins/security/portal.rb +28 -0
- data/plugins/security/private_wiki.rb +24 -0
- data/plugins/security/readonly_wiki.rb +25 -0
- data/plugins/security/stack.rb +20 -0
- data/plugins/security/yamlfile.rb +66 -0
- data/plugins/tags/code.rb +6 -0
- data/plugins/tags/footnotes.rb +35 -0
- data/plugins/tags/gist-embed.css +123 -0
- data/plugins/tags/gist.rb +13 -0
- data/plugins/tags/html.rb +57 -0
- data/plugins/tags/include.rb +20 -0
- data/plugins/tags/main.rb +353 -0
- data/plugins/tags/math.rb +117 -0
- data/plugins/tags/scripting.rb +64 -0
- data/plugins/tags/sort.rb +7 -0
- data/plugins/tags/tabs.rb +20 -0
- data/plugins/treeview/images/collapsed.png +0 -0
- data/plugins/treeview/images/expanded.png +0 -0
- data/plugins/treeview/images/menu.png +0 -0
- data/plugins/treeview/images/tree.png +0 -0
- data/plugins/treeview/images/wait.gif +0 -0
- data/plugins/treeview/main.rb +16 -0
- data/plugins/treeview/script.js +5 -0
- data/plugins/treeview/script/00-jquery.treeview.js +164 -0
- data/plugins/treeview/script/init.js +25 -0
- data/plugins/treeview/treeview.css +1 -0
- data/plugins/treeview/treeview.scss +113 -0
- data/plugins/utils/assets.rb +74 -0
- data/plugins/utils/cache.rb +53 -0
- data/plugins/utils/image_magick.rb +34 -0
- data/plugins/utils/pygments.css +1 -0
- data/plugins/utils/pygments.rb +50 -0
- data/plugins/utils/pygments.scss +83 -0
- data/plugins/utils/semaphore.rb +50 -0
- data/plugins/utils/shell.rb +45 -0
- data/plugins/utils/store.rb +315 -0
- data/plugins/utils/worker.rb +36 -0
- data/plugins/utils/xml.rb +29 -0
- data/static/images/favicon.png +0 -0
- data/static/script.js +267 -0
- data/static/script/00-json2.js +486 -0
- data/static/script/01-jstorage.js +217 -0
- data/static/script/02-jquery.js +9440 -0
- data/static/script/03-jquery.ui.core.js +337 -0
- data/static/script/04-jquery.ui.widget.js +502 -0
- data/static/script/05-jquery.ui.position.js +517 -0
- data/static/script/06-jquery.ui.menu.js +609 -0
- data/static/script/07-jquery.ui.autocomplete.js +601 -0
- data/static/script/08-olelo.i18n.js +37 -0
- data/static/script/09-olelo.unsaved.js +68 -0
- data/static/script/10-olelo.historytable.js +40 -0
- data/static/script/11-olelo.pagination.js +18 -0
- data/static/script/13-olelo.tabwidget.js +57 -0
- data/static/script/14-olelo.timeago.js +70 -0
- data/static/script/15-olelo.underliner.js +31 -0
- data/static/script/16-olelo.ui.combobox.js +32 -0
- data/static/script/init.js +48 -0
- data/static/themes/atlantis/constants.scss +15 -0
- data/static/themes/atlantis/iehacks.scss +38 -0
- data/static/themes/atlantis/images/actions/delete.png +0 -0
- data/static/themes/atlantis/images/actions/edit.png +0 -0
- data/static/themes/atlantis/images/actions/history.png +0 -0
- data/static/themes/atlantis/images/actions/home.png +0 -0
- data/static/themes/atlantis/images/actions/move.png +0 -0
- data/static/themes/atlantis/images/actions/new.png +0 -0
- data/static/themes/atlantis/images/actions/page.png +0 -0
- data/static/themes/atlantis/images/bg/button.png +0 -0
- data/static/themes/atlantis/images/bg/container.png +0 -0
- data/static/themes/atlantis/images/bg/content.png +0 -0
- data/static/themes/atlantis/images/bg/footer.png +0 -0
- data/static/themes/atlantis/images/bg/header.jpg +0 -0
- data/static/themes/atlantis/images/bg/header.orig.jpg +0 -0
- data/static/themes/atlantis/images/bg/header_gray.jpg +0 -0
- data/static/themes/atlantis/images/bug.png +0 -0
- data/static/themes/atlantis/images/filetypes/7z.png +0 -0
- data/static/themes/atlantis/images/filetypes/_archive.png +0 -0
- data/static/themes/atlantis/images/filetypes/_audio.png +0 -0
- data/static/themes/atlantis/images/filetypes/_code.png +0 -0
- data/static/themes/atlantis/images/filetypes/_linux.png +0 -0
- data/static/themes/atlantis/images/filetypes/_picture.png +0 -0
- data/static/themes/atlantis/images/filetypes/_video.png +0 -0
- data/static/themes/atlantis/images/filetypes/bz2.png +0 -0
- data/static/themes/atlantis/images/filetypes/doc.png +0 -0
- data/static/themes/atlantis/images/filetypes/flac.png +0 -0
- data/static/themes/atlantis/images/filetypes/gz.png +0 -0
- data/static/themes/atlantis/images/filetypes/html.png +0 -0
- data/static/themes/atlantis/images/filetypes/java.png +0 -0
- data/static/themes/atlantis/images/filetypes/jpg.png +0 -0
- data/static/themes/atlantis/images/filetypes/midi.png +0 -0
- data/static/themes/atlantis/images/filetypes/mp3.png +0 -0
- data/static/themes/atlantis/images/filetypes/ogg.png +0 -0
- data/static/themes/atlantis/images/filetypes/pdf.png +0 -0
- data/static/themes/atlantis/images/filetypes/php.png +0 -0
- data/static/themes/atlantis/images/filetypes/png.png +0 -0
- data/static/themes/atlantis/images/filetypes/ppt.png +0 -0
- data/static/themes/atlantis/images/filetypes/psd.png +0 -0
- data/static/themes/atlantis/images/filetypes/rar.png +0 -0
- data/static/themes/atlantis/images/filetypes/rb.png +0 -0
- data/static/themes/atlantis/images/filetypes/sh.png +0 -0
- data/static/themes/atlantis/images/filetypes/tar.png +0 -0
- data/static/themes/atlantis/images/filetypes/txt.png +0 -0
- data/static/themes/atlantis/images/filetypes/wma.png +0 -0
- data/static/themes/atlantis/images/filetypes/xls.png +0 -0
- data/static/themes/atlantis/images/filetypes/zip.png +0 -0
- data/static/themes/atlantis/images/folder.png +0 -0
- data/static/themes/atlantis/images/folder_open.png +0 -0
- data/static/themes/atlantis/images/loading.gif +0 -0
- data/static/themes/atlantis/images/loading.xcf +0 -0
- data/static/themes/atlantis/images/not_found.png +0 -0
- data/static/themes/atlantis/images/page.png +0 -0
- data/static/themes/atlantis/images/search.png +0 -0
- data/static/themes/atlantis/layout.scss +115 -0
- data/static/themes/atlantis/menu.scss +99 -0
- data/static/themes/atlantis/print.scss +129 -0
- data/static/themes/atlantis/screen.scss +495 -0
- data/static/themes/atlantis/style.css +3 -0
- data/static/themes/lib/autocomplete.scss +39 -0
- data/static/themes/lib/headlines.scss +10 -0
- data/static/themes/lib/horizontal-list.scss +31 -0
- data/static/themes/lib/patch.scss +88 -0
- data/static/themes/lib/reset.scss +114 -0
- data/static/themes/lib/rounded.scss +46 -0
- data/static/themes/lib/shadow.scss +14 -0
- data/test/config_test.rb +28 -0
- data/test/factory_test.rb +29 -0
- data/test/hash_extensions_test.rb +16 -0
- data/test/helper.rb +38 -0
- data/test/hooks_test.rb +85 -0
- data/test/object_extensions_test.rb +20 -0
- data/test/page_test.rb +168 -0
- data/test/request_test.rb +166 -0
- data/test/string_extensions_test.rb +32 -0
- data/test/templates_test.rb +39 -0
- data/test/util_test.rb +71 -0
- data/views/changes.slim +22 -0
- data/views/compare.slim +8 -0
- data/views/delete.slim +9 -0
- data/views/deleted.slim +2 -0
- data/views/edit.slim +65 -0
- data/views/error.slim +6 -0
- data/views/history.slim +20 -0
- data/views/layout.slim +38 -0
- data/views/login.slim +37 -0
- data/views/move.slim +10 -0
- data/views/not_found.slim +6 -0
- data/views/profile.slim +26 -0
- data/views/show.slim +9 -0
- metadata +488 -0
data/lib/olelo/helper.rb
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
module Olelo
|
|
3
|
+
module BlockHelper
|
|
4
|
+
def blocks
|
|
5
|
+
@blocks ||= Hash.with_indifferent_access('')
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def define_block(name, content = nil)
|
|
9
|
+
blocks[name] = block_given? ? yield : escape_html(content)
|
|
10
|
+
''
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def include_block(name)
|
|
14
|
+
with_hooks(name) { blocks[name] }.join.html_safe
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def render_block(name)
|
|
18
|
+
with_hooks(name) { yield }.join.html_safe
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def include_or_define_block(name, content = nil, &block)
|
|
22
|
+
if block_given? || content
|
|
23
|
+
define_block(name, content, &block)
|
|
24
|
+
else
|
|
25
|
+
include_block(name)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
module FlashHelper
|
|
31
|
+
include Util
|
|
32
|
+
|
|
33
|
+
def flash
|
|
34
|
+
env['olelo.flash']
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def flash_messages(action = nil)
|
|
38
|
+
if !action || action?(action)
|
|
39
|
+
li = [:error, :warn, :info].map {|level| flash[level].to_a.map {|msg| %{<li class="#{level}">#{escape_html msg}</li>} } }.flatten
|
|
40
|
+
%{<ul class="flash">#{li.join}</ul>}.html_safe if !li.empty?
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
module PageHelper
|
|
46
|
+
include Util
|
|
47
|
+
|
|
48
|
+
def include_page(path)
|
|
49
|
+
page = Page.find(path) rescue nil
|
|
50
|
+
if page
|
|
51
|
+
render_page(page)
|
|
52
|
+
else
|
|
53
|
+
%{<a href="#{escape_html build_path(path, :action => :new)}">#{escape_html :create_page.t(:page => path)}</a>}
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def render_page(page)
|
|
58
|
+
page.content
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def pagination(path, page_count, page_nr, options = {})
|
|
62
|
+
return if page_count <= 1
|
|
63
|
+
li = []
|
|
64
|
+
li << if page_nr > 1
|
|
65
|
+
%{<a href="#{escape_html build_path(path, options.merge(:page => page_nr - 1))}">◂</a>}
|
|
66
|
+
else
|
|
67
|
+
%{<span class="disabled">◂</span>}
|
|
68
|
+
end
|
|
69
|
+
min = page_nr - 3
|
|
70
|
+
max = page_nr + 3
|
|
71
|
+
if min > 1
|
|
72
|
+
min -= max - page_count if max > page_count
|
|
73
|
+
else
|
|
74
|
+
max -= min if min < 1
|
|
75
|
+
end
|
|
76
|
+
max = max + 2 < page_count ? max : page_count
|
|
77
|
+
min = min > 3 ? min : 1
|
|
78
|
+
if min != 1
|
|
79
|
+
li << %{<a href="#{escape_html build_path(path, options.merge(:page => 1))}">1</a>} << %{<span class="ellipsis"/>}
|
|
80
|
+
end
|
|
81
|
+
(min..max).each do |i|
|
|
82
|
+
li << if i == page_nr
|
|
83
|
+
%{<span class="current">#{i}</span>}
|
|
84
|
+
else
|
|
85
|
+
%{<a href="#{escape_html build_path(path, options.merge(:page => i))}">#{i}</a>}
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
if max != page_count
|
|
89
|
+
li << %{<span class="ellipsis"/>} << %{<a href="#{escape_html build_path(path, options.merge(:page => page_count))}">#{page_count}</a>}
|
|
90
|
+
end
|
|
91
|
+
li << if page_nr < page_count
|
|
92
|
+
%{<a href="#{escape_html build_path(path, options.merge(:page => page_nr + 1))}">▸</a>}
|
|
93
|
+
else
|
|
94
|
+
%{<span class="disabled">▸</span>}
|
|
95
|
+
end
|
|
96
|
+
('<ul class="pagination">' + li.map {|x| "<li>#{x}</li>"}.join + '</ul>').html_safe
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def date(t)
|
|
100
|
+
%{<span class="date" data-epoch="#{t.to_i}">#{t.strftime('%d %h %Y %H:%M')}</span>}.html_safe
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def format_diff(diff)
|
|
104
|
+
summary = PatchSummary.new(:links => true)
|
|
105
|
+
formatter = PatchFormatter.new(:links => true, :header => true)
|
|
106
|
+
PatchParser.parse(diff.patch, summary, formatter)
|
|
107
|
+
(summary.html + formatter.html).html_safe
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def breadcrumbs(page)
|
|
111
|
+
path = page.try(:path) || ''
|
|
112
|
+
li = [%{<li class="first breadcrumb#{path.empty? ? ' last' : ''}">
|
|
113
|
+
<a accesskey="z" href="#{escape_html build_path(nil, :version => page)}">#{escape_html :root.t}</a></li>}]
|
|
114
|
+
path.split('/').inject('') do |parent,elem|
|
|
115
|
+
current = parent/elem
|
|
116
|
+
li << %{<li class="breadcrumb#{current == path ? ' last' : ''}">
|
|
117
|
+
<a href="#{escape_html build_path(current, :version => page)}">#{escape_html elem}</a></li>}
|
|
118
|
+
current
|
|
119
|
+
end
|
|
120
|
+
li.join('<li class="breadcrumb">/</li>').html_safe
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def build_path(page, options = {})
|
|
124
|
+
options = options.dup
|
|
125
|
+
action = options.delete(:action)
|
|
126
|
+
version = options.delete(:version)
|
|
127
|
+
path = (page.try(:path) || page).to_s
|
|
128
|
+
|
|
129
|
+
if action
|
|
130
|
+
raise ArgumentError if version
|
|
131
|
+
path = action.to_s/path
|
|
132
|
+
else
|
|
133
|
+
version ||= page if Page === page
|
|
134
|
+
version = version.tree_version if Page === version
|
|
135
|
+
path = 'version'/version/path if version && (options.delete(:force_version) || !version.head?)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
path += '?' + build_query(options) if !options.empty?
|
|
139
|
+
'/' + (Config['base_path'] / path)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def edit_content(page)
|
|
143
|
+
if params[:content]
|
|
144
|
+
params[:content]
|
|
145
|
+
elsif !(String === page.content) || !valid_xml_chars?(page.content)
|
|
146
|
+
:error_binary.t(:page => page.title, :type => "#{page.mime.comment} (#{page.mime})")
|
|
147
|
+
else
|
|
148
|
+
params[:pos] ? page.content[params[:pos].to_i, params[:len].to_i].to_s : page.content
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
module HttpHelper
|
|
154
|
+
include Util
|
|
155
|
+
|
|
156
|
+
# Cache control for page
|
|
157
|
+
def cache_control(options)
|
|
158
|
+
return if !Config['production']
|
|
159
|
+
|
|
160
|
+
if options[:no_cache]
|
|
161
|
+
response.headers.delete('ETag')
|
|
162
|
+
response.headers.delete('Last-Modified')
|
|
163
|
+
response.headers.delete('Cache-Control')
|
|
164
|
+
return
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
last_modified = options.delete(:last_modified)
|
|
168
|
+
modified_since = env['HTTP_IF_MODIFIED_SINCE']
|
|
169
|
+
last_modified = last_modified.try(:to_time) || last_modified
|
|
170
|
+
last_modified = last_modified.try(:httpdate) || last_modified
|
|
171
|
+
|
|
172
|
+
if options[:version]
|
|
173
|
+
options[:etag] = options[:version].cache_id
|
|
174
|
+
options[:last_modified] = options[:version].date
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
if User.logged_in?
|
|
178
|
+
# Always private mode if user is logged in
|
|
179
|
+
options[:private] = true
|
|
180
|
+
|
|
181
|
+
# Special etag for authenticated user
|
|
182
|
+
options[:etag] = "#{User.current.name}-#{options[:etag]}" if options[:etag]
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
if options[:etag]
|
|
186
|
+
value = '"%s"' % options.delete(:etag)
|
|
187
|
+
response['ETag'] = value.to_s
|
|
188
|
+
response['Last-Modified'] = last_modified if last_modified
|
|
189
|
+
if etags = env['HTTP_IF_NONE_MATCH']
|
|
190
|
+
etags = etags.split(/\s*,\s*/)
|
|
191
|
+
# Etag is matching and modification date matches (HTTP Spec §14.26)
|
|
192
|
+
halt :not_modified if (etags.include?(value) || etags.include?('*')) && (!last_modified || last_modified == modified_since)
|
|
193
|
+
end
|
|
194
|
+
elsif last_modified
|
|
195
|
+
# If-Modified-Since is only processed if no etag supplied.
|
|
196
|
+
# If the etag match failed the If-Modified-Since has to be ignored (HTTP Spec §14.26)
|
|
197
|
+
response['Last-Modified'] = last_modified
|
|
198
|
+
halt :not_modified if last_modified == modified_since
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
options[:public] = !options[:private]
|
|
202
|
+
options[:max_age] ||= 0
|
|
203
|
+
options[:must_revalidate] ||= true if !options.include?(:must_revalidate)
|
|
204
|
+
|
|
205
|
+
response['Cache-Control'] = options.map do |k, v|
|
|
206
|
+
if v == true
|
|
207
|
+
k.to_s.tr('_', '-')
|
|
208
|
+
elsif v
|
|
209
|
+
v = 31536000 if v.to_s == 'static'
|
|
210
|
+
"#{k.to_s.tr('_', '-')}=#{v}"
|
|
211
|
+
end
|
|
212
|
+
end.compact.join(', ')
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
module ApplicationHelper
|
|
217
|
+
include BlockHelper
|
|
218
|
+
include FlashHelper
|
|
219
|
+
include PageHelper
|
|
220
|
+
include HttpHelper
|
|
221
|
+
include Templates
|
|
222
|
+
|
|
223
|
+
def tabs(*actions)
|
|
224
|
+
tabs = actions.map do |action|
|
|
225
|
+
%{<li id="tabhead-#{action}"#{action?(action) ? ' class="selected"' : ''}><a href="#tab-#{action}">#{escape_html action.t}</a></li>}
|
|
226
|
+
end
|
|
227
|
+
%{<ul class="tabs">#{tabs.join}</ul>}.html_safe
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def action?(action)
|
|
231
|
+
if params[:action]
|
|
232
|
+
params[:action].split('-').include?(action.to_s)
|
|
233
|
+
else
|
|
234
|
+
unescape(request.path_info).starts_with?("/#{action}")
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def footer(content = nil, &block)
|
|
239
|
+
include_or_define_block(:footer, content, &block)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def title(content = nil, &block)
|
|
243
|
+
include_or_define_block(:title, content, &block)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def script
|
|
247
|
+
invoke_hook(:script).join.html_safe
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def head
|
|
251
|
+
@@theme_link ||=
|
|
252
|
+
begin
|
|
253
|
+
file = File.join(Config['themes_path'], Config['theme'], 'style.css')
|
|
254
|
+
path = build_path "static/themes/#{Config['theme']}/style.css?#{File.mtime(file).to_i}"
|
|
255
|
+
%{<link rel="stylesheet" href="#{escape_html path}" type="text/css"/>}
|
|
256
|
+
end
|
|
257
|
+
@@script_link ||=
|
|
258
|
+
begin
|
|
259
|
+
path = build_path "static/script.js?#{File.mtime(File.join(Config['app_path'], 'static', 'script.js')).to_i}"
|
|
260
|
+
%{<script src="#{escape_html path}" type="text/javascript"/>}
|
|
261
|
+
end
|
|
262
|
+
base_path = if page && page.root?
|
|
263
|
+
url = request.base_url
|
|
264
|
+
url << '/version'/page.tree_version if !page.head?
|
|
265
|
+
%{<base href="#{escape_html url}/"/>}.html_safe
|
|
266
|
+
end
|
|
267
|
+
[base_path, @@theme_link, @@script_link, *invoke_hook(:head)].join.html_safe
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def session
|
|
271
|
+
env['rack.session'] ||= {}
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def menu(name)
|
|
275
|
+
menu = Menu.new(name)
|
|
276
|
+
invoke_hook :menu, menu
|
|
277
|
+
menu.to_html
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
alias render_partial render
|
|
281
|
+
|
|
282
|
+
def render(name, options = {})
|
|
283
|
+
layout = options.delete(:layout) != false && !params[:no_layout]
|
|
284
|
+
output = Symbol === name ? render_partial(name, options) : name
|
|
285
|
+
output = render_partial(:layout, options) { output } if layout
|
|
286
|
+
invoke_hook :render, name, output, layout
|
|
287
|
+
output
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
end
|
data/lib/olelo/hooks.rb
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
module Olelo
|
|
2
|
+
module ErrorHandler
|
|
3
|
+
def self.included(base)
|
|
4
|
+
base.extend(ClassMethods)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def handle_error(error)
|
|
8
|
+
type = error.class
|
|
9
|
+
while type
|
|
10
|
+
self.class.error_handler[type].to_a.each {|prio,method| method.bind(self).call(error) }
|
|
11
|
+
type = type.superclass
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module ClassMethods
|
|
16
|
+
def error_handler
|
|
17
|
+
@error_handler ||= {}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def error(error, priority = 99, &block)
|
|
21
|
+
handler = (error_handler[error] ||= [])
|
|
22
|
+
method = "ERROR #{error} #{handler.size}"
|
|
23
|
+
define_method(method, &block)
|
|
24
|
+
handler << [priority, instance_method(method)]
|
|
25
|
+
handler.sort_by!(&:first)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Include this module to add hook support to your class.
|
|
31
|
+
# The class will be extended with {ClassMethods} which
|
|
32
|
+
# provides the methods to register hooks.
|
|
33
|
+
module Hooks
|
|
34
|
+
def self.included(base)
|
|
35
|
+
base.extend(ClassMethods)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Execute block surrounded with hooks
|
|
39
|
+
#
|
|
40
|
+
# It calls the hooks that were registered by {ClassMethods#before} and {ClassMethods#after} and
|
|
41
|
+
# returns an array consisting of the before hook results,
|
|
42
|
+
# the block result and the after hook results.
|
|
43
|
+
#
|
|
44
|
+
# @param [Symbol] name of hook to call
|
|
45
|
+
# @param *args Hook arguments
|
|
46
|
+
# @return [Array] [*Before hook results, Block result, *After hook results]
|
|
47
|
+
# @api public
|
|
48
|
+
#
|
|
49
|
+
def with_hooks(name, *args)
|
|
50
|
+
result = []
|
|
51
|
+
result.push(*invoke_hook("BEFORE #{name}", *args))
|
|
52
|
+
result << yield
|
|
53
|
+
ensure
|
|
54
|
+
result.push(*invoke_hook("AFTER #{name}", *args))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Invoke hooks registered for this class
|
|
58
|
+
#
|
|
59
|
+
# The hooks can be registered using {ClassMethods#hook}.
|
|
60
|
+
#
|
|
61
|
+
# @param [Symbol] name of hook to call
|
|
62
|
+
# @param *args Hook arguments
|
|
63
|
+
# @return [Array] [Hook results]
|
|
64
|
+
# @api public
|
|
65
|
+
#
|
|
66
|
+
def invoke_hook(name, *args)
|
|
67
|
+
hooks = self.class.hooks[name.to_sym]
|
|
68
|
+
raise "#{self.class} has no hook '#{name}'" if !hooks
|
|
69
|
+
hooks.map {|prio,method| method.bind(self).(*args) }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Extends class with hook functionality
|
|
73
|
+
module ClassMethods
|
|
74
|
+
# Hash of registered hooks
|
|
75
|
+
# @api private
|
|
76
|
+
# @return [Hash] of hooks
|
|
77
|
+
def hooks
|
|
78
|
+
@hooks ||= {}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def has_around_hooks(*names)
|
|
82
|
+
names.each do |name|
|
|
83
|
+
has_hooks "BEFORE #{name}", "AFTER #{name}"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def has_hooks(*names)
|
|
88
|
+
names.map(&:to_sym).each do |name|
|
|
89
|
+
raise "#{self} already has hook '#{name}'" if hooks.include?(name)
|
|
90
|
+
hooks[name] = []
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Register hook for class
|
|
95
|
+
#
|
|
96
|
+
# The hook will be invoked by {#invoke_hook}. Hooks with lower priority are called first.
|
|
97
|
+
#
|
|
98
|
+
# @param [Symbol, String] name of hook
|
|
99
|
+
# @param [Integer] priority
|
|
100
|
+
# @yield Hook block with arguments matching the hook invocation
|
|
101
|
+
# @return [void]
|
|
102
|
+
# @api public
|
|
103
|
+
#
|
|
104
|
+
def hook(name, priority = 99, &block)
|
|
105
|
+
list = hooks[name.to_sym]
|
|
106
|
+
raise "#{self} has no hook '#{name}'" if !list
|
|
107
|
+
method = "HOOK #{name} #{list.size}"
|
|
108
|
+
define_method(method, &block)
|
|
109
|
+
list << [priority, instance_method(method)]
|
|
110
|
+
list.sort_by!(&:first)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Register before hook
|
|
114
|
+
#
|
|
115
|
+
# The hook will be invoked by {#with_hooks}.
|
|
116
|
+
#
|
|
117
|
+
# @param [Symbol, String] name of hook
|
|
118
|
+
# @param [Integer] priority
|
|
119
|
+
# @yield Hook block with arguments matching the hook invocation
|
|
120
|
+
# @return [void]
|
|
121
|
+
# @api public
|
|
122
|
+
#
|
|
123
|
+
def before(name, priority = 99, &block)
|
|
124
|
+
hook("BEFORE #{name}", priority, &block)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Register before hook
|
|
128
|
+
#
|
|
129
|
+
# The hook will be invoked by {#with_hooks}.
|
|
130
|
+
#
|
|
131
|
+
# @param [Symbol, String] name of hook
|
|
132
|
+
# @param [Integer] priority
|
|
133
|
+
# @yield Hook block with arguments matching the hook invocation
|
|
134
|
+
# @return [void]
|
|
135
|
+
# @api public
|
|
136
|
+
#
|
|
137
|
+
def after(name, priority = 99, &block)
|
|
138
|
+
hook("AFTER #{name}", priority, &block)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
class Object
|
|
2
|
+
def html_safe?
|
|
3
|
+
false
|
|
4
|
+
end
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
class String
|
|
8
|
+
class HtmlString < String
|
|
9
|
+
def html_safe?
|
|
10
|
+
true
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def html_safe
|
|
14
|
+
self
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_s
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def html_safe?
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def html_safe
|
|
27
|
+
HtmlString.new(self)
|
|
28
|
+
end
|
|
29
|
+
end
|