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
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# coding: binary
|
|
2
|
+
# Coding is required for StringIO
|
|
3
|
+
module Olelo
|
|
4
|
+
module Middleware
|
|
5
|
+
class Blacklist
|
|
6
|
+
NULL_IO = StringIO.new('')
|
|
7
|
+
|
|
8
|
+
def initialize(app, options)
|
|
9
|
+
@app = app
|
|
10
|
+
@list = options[:blacklist]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call(env)
|
|
14
|
+
if %w(POST PUT DELETE).include?(env['REQUEST_METHOD']) && @list.include?(Rack::Request.new(env).ip)
|
|
15
|
+
env.delete('rack.request.form_vars')
|
|
16
|
+
env.delete('rack.request.form_hash')
|
|
17
|
+
env.delete('rack.request.form_input')
|
|
18
|
+
env['rack.input'] = NULL_IO
|
|
19
|
+
env['REQUEST_METHOD'] = 'GET'
|
|
20
|
+
end
|
|
21
|
+
@app.call(env)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Olelo
|
|
2
|
+
module Middleware
|
|
3
|
+
class DegradeMimeType
|
|
4
|
+
def initialize(app)
|
|
5
|
+
@app = app
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def call(env)
|
|
9
|
+
status, header, body = @app.call(env)
|
|
10
|
+
if env['HTTP_ACCEPT'].to_s !~ %r{application/xhtml\+xml}
|
|
11
|
+
if header['Content-Type'] =~ %r{\Aapplication/xhtml\+xml(;?.*)\Z}
|
|
12
|
+
header['Content-Type'] = "text/html#{$1}"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
[status, header, body]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
module Olelo
|
|
2
|
+
module Middleware
|
|
3
|
+
class Flash
|
|
4
|
+
# Implements bracket accessors for storing and retrieving flash entries.
|
|
5
|
+
class FlashHash
|
|
6
|
+
def initialize(session)
|
|
7
|
+
@session = session
|
|
8
|
+
raise 'No session available' if !session
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Remove an entry from the session and return its value. Cache result in
|
|
12
|
+
# the instance cache.
|
|
13
|
+
def [](key)
|
|
14
|
+
key = key.to_sym
|
|
15
|
+
cache[key] ||= values.delete(key)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Store the entry in the session, updating the instance cache as well.
|
|
19
|
+
def []=(key,val)
|
|
20
|
+
key = key.to_sym
|
|
21
|
+
cache[key] = values[key] = val
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Store a flash entry for only the current request, swept regardless of
|
|
25
|
+
# whether or not it was actually accessed
|
|
26
|
+
def now
|
|
27
|
+
cache
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Checks for the presence of a flash entry without retrieving or removing
|
|
31
|
+
# it from the cache or store.
|
|
32
|
+
def include?(key)
|
|
33
|
+
key = key.to_sym
|
|
34
|
+
cache.keys.include?(key) || values.keys.include?(key)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Clear the hash
|
|
38
|
+
def clear
|
|
39
|
+
cache.clear
|
|
40
|
+
@session.delete(:olelo_flash)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Hide the underlying olelo.flash session key and only expose values stored
|
|
44
|
+
# in the flash.
|
|
45
|
+
def inspect
|
|
46
|
+
"#<FlashHash @values=#{values.inspect} @cache=#{cache.inspect}>"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Human readable for logging.
|
|
50
|
+
def to_s
|
|
51
|
+
values.inspect
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
# Maintain an instance-level cache of retrieved flash entries. These
|
|
57
|
+
# entries will have been removed from the session, but are still available
|
|
58
|
+
# through the cache.
|
|
59
|
+
def cache
|
|
60
|
+
@cache ||= {}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Helper to access flash entries from olelo.flash session value. This key
|
|
64
|
+
# is used to prevent collisions with other user-defined session values.
|
|
65
|
+
def values
|
|
66
|
+
@session[:olelo_flash] ||= {}
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def initialize(app, options = {})
|
|
71
|
+
@app = app
|
|
72
|
+
@hash = Class.new(FlashHash)
|
|
73
|
+
[*options[:accessors]].compact.each do |key|
|
|
74
|
+
key = key.to_sym
|
|
75
|
+
@hash.class_eval do
|
|
76
|
+
define_method(key) {|*a| a.size > 0 ? (self[key] = a[0]) : self[key] }
|
|
77
|
+
define_method("#{key}=") {|val| self[key] = val }
|
|
78
|
+
define_method("#{key}!") {|val| cache[key] = val }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
[*options[:set_accessors]].compact.each do |key|
|
|
82
|
+
key = key.to_sym
|
|
83
|
+
@hash.class_eval do
|
|
84
|
+
define_method(key) {|*val| val.size > 0 ? (self[key] ||= Set.new).merge(val) : self[key] }
|
|
85
|
+
define_method("#{key}!") {|*val| val.size > 0 ? (cache[key] ||= Set.new).merge(val) : cache[key] }
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def call(env)
|
|
91
|
+
session = env['rack.session']
|
|
92
|
+
env['olelo.flash'] ||= @hash.new(session)
|
|
93
|
+
@app.call(env)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Olelo
|
|
2
|
+
module Middleware
|
|
3
|
+
class ForceEncoding
|
|
4
|
+
def initialize(app, encoding = Encoding.default_external)
|
|
5
|
+
@app, @encoding = app, encoding
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def call(env)
|
|
9
|
+
request = Rack::Request.new(env)
|
|
10
|
+
encode(env)
|
|
11
|
+
encode(request.params)
|
|
12
|
+
@app.call(env)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def encode(x)
|
|
18
|
+
case x
|
|
19
|
+
when Hash
|
|
20
|
+
y = x.frozen? ? {} : x
|
|
21
|
+
x.each { |k,v| y[k] = encode(v) }
|
|
22
|
+
y
|
|
23
|
+
when Array
|
|
24
|
+
y = x.frozen? ? [] : x
|
|
25
|
+
x.each_with_index {|v,i| y[i] = encode(v) }
|
|
26
|
+
y
|
|
27
|
+
when String
|
|
28
|
+
# Try to force encoding and revert to old encoding if this doesn't work
|
|
29
|
+
if x.encoding != @encoding
|
|
30
|
+
x = x.dup if x.frozen?
|
|
31
|
+
x.try_encoding(@encoding)
|
|
32
|
+
else
|
|
33
|
+
x
|
|
34
|
+
end
|
|
35
|
+
else
|
|
36
|
+
x
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/lib/olelo/page.rb
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
module Olelo
|
|
2
|
+
# Wiki page object
|
|
3
|
+
class Page
|
|
4
|
+
include Util
|
|
5
|
+
include Hooks
|
|
6
|
+
include Attributes
|
|
7
|
+
|
|
8
|
+
has_around_hooks :move, :delete, :save
|
|
9
|
+
|
|
10
|
+
attributes do
|
|
11
|
+
string :title
|
|
12
|
+
boolean :no_title
|
|
13
|
+
string :description
|
|
14
|
+
string :mime do
|
|
15
|
+
Config['mime_suggestions'].inject({}) do |hash, mime|
|
|
16
|
+
comment = MimeMagic.new(mime).comment
|
|
17
|
+
hash[mime] = comment.blank? ? mime : "#{comment} (#{mime})"
|
|
18
|
+
hash
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Pattern for valid paths
|
|
24
|
+
# @api public
|
|
25
|
+
PATH_PATTERN = '[^\s](?:.*[^\s]+)?'.freeze
|
|
26
|
+
PATH_REGEXP = /^#{PATH_PATTERN}$/
|
|
27
|
+
|
|
28
|
+
# Mime type for empty page
|
|
29
|
+
# @api public
|
|
30
|
+
EMPTY_MIME = MimeMagic.new('inode/x-empty')
|
|
31
|
+
|
|
32
|
+
# Mime type for directory
|
|
33
|
+
# @api public
|
|
34
|
+
DIRECTORY_MIME = MimeMagic.new('inode/directory')
|
|
35
|
+
|
|
36
|
+
attr_reader :path, :tree_version
|
|
37
|
+
|
|
38
|
+
def initialize(path, tree_version = nil, parent = nil)
|
|
39
|
+
@path, @tree_version, @parent = path.to_s.cleanpath.freeze, tree_version, parent
|
|
40
|
+
Page.check_path(@path)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.transaction(&block)
|
|
44
|
+
raise 'Transaction already running' if Thread.current[:olelo_tx]
|
|
45
|
+
Thread.current[:olelo_tx] = []
|
|
46
|
+
repository.transaction(&block)
|
|
47
|
+
ensure
|
|
48
|
+
Thread.current[:olelo_tx] = nil
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.current_transaction
|
|
52
|
+
Thread.current[:olelo_tx] || raise('No transaction running')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.commit(comment)
|
|
56
|
+
tree_version = repository.commit(comment)
|
|
57
|
+
current_transaction.each {|proc| proc.call(tree_version) }
|
|
58
|
+
current_transaction.clear
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Throws exceptions if access denied, returns nil if not found
|
|
62
|
+
def self.find(path, tree_version = nil)
|
|
63
|
+
path = path.to_s.cleanpath
|
|
64
|
+
check_path(path)
|
|
65
|
+
tree_version = repository.get_version(tree_version) unless Version === tree_version
|
|
66
|
+
Page.new(path, tree_version) if tree_version && repository.path_exists?(path, tree_version)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Throws if not found
|
|
70
|
+
def self.find!(path, tree_version = nil)
|
|
71
|
+
find(path, tree_version) || raise(NotFound, path)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Head version
|
|
75
|
+
def head?
|
|
76
|
+
new? || tree_version.head?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def root?
|
|
80
|
+
path.empty?
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def next_version
|
|
84
|
+
init_versions
|
|
85
|
+
@next_version
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def previous_version
|
|
89
|
+
init_versions
|
|
90
|
+
@previous_version
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def version
|
|
94
|
+
init_versions
|
|
95
|
+
@version
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def history(skip = nil, limit = nil)
|
|
99
|
+
raise 'Page is new' if new?
|
|
100
|
+
repository.get_history(path, skip, limit)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def parent
|
|
104
|
+
@parent ||= Page.find(path/'..', tree_version) || Page.new(path/'..', tree_version) if !root?
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def move(destination)
|
|
108
|
+
raise 'Page is not head' unless head?
|
|
109
|
+
raise 'Page is new' if new?
|
|
110
|
+
destination = destination.to_s.cleanpath
|
|
111
|
+
Page.check_path(destination)
|
|
112
|
+
raise :already_exists.t(:page => destination) if Page.find(destination)
|
|
113
|
+
with_hooks(:move, destination) { repository.move(path, destination) }
|
|
114
|
+
after_commit {|tree_version| update(destination, tree_version) }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def delete
|
|
118
|
+
raise 'Page is not head' unless head?
|
|
119
|
+
raise 'Page is new' if new?
|
|
120
|
+
with_hooks(:delete) { repository.delete(path) }
|
|
121
|
+
after_commit {|tree_version| update(path, nil) }
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def diff(from, to)
|
|
125
|
+
raise 'Page is new' if new?
|
|
126
|
+
repository.diff(path, from, to)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def new?
|
|
130
|
+
!tree_version
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def name
|
|
134
|
+
i = path.rindex('/')
|
|
135
|
+
name = i ? path[i+1..-1] : path
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def title
|
|
139
|
+
attributes['title'] || (root? ? :root.t : name)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def extension
|
|
143
|
+
i = path.index('.')
|
|
144
|
+
i ? path[i+1..-1] : ''
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def attributes
|
|
148
|
+
@attributes ||= deep_copy(saved_attributes)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def saved_attributes
|
|
152
|
+
@saved_attributes ||= new? ? {} : repository.get_attributes(path, tree_version)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def attributes=(a)
|
|
156
|
+
a ||= {}
|
|
157
|
+
if attributes != a
|
|
158
|
+
@attributes = a
|
|
159
|
+
@mime = nil
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def saved_content
|
|
164
|
+
@saved_content ||= new? ? '' : repository.get_content(path, tree_version)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def content
|
|
168
|
+
@content ||= saved_content
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def content=(c)
|
|
172
|
+
if content != c
|
|
173
|
+
@mime = nil
|
|
174
|
+
@content = c
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def modified?
|
|
179
|
+
content != saved_content || attributes != saved_attributes
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def save
|
|
183
|
+
raise 'Page is not head' unless head?
|
|
184
|
+
raise :already_exists.t(:page => path) if new? && Page.find(path)
|
|
185
|
+
with_hooks(:save) do
|
|
186
|
+
repository.set_content(path, content)
|
|
187
|
+
repository.set_attributes(path, attributes)
|
|
188
|
+
end
|
|
189
|
+
after_commit {|tree_version| update(path, tree_version) }
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def mime
|
|
193
|
+
@mime ||= detect_mime
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def children
|
|
197
|
+
@children ||=
|
|
198
|
+
if new?
|
|
199
|
+
[]
|
|
200
|
+
else
|
|
201
|
+
repository.get_children(path, tree_version).sort.map do |name|
|
|
202
|
+
Page.new(path/name, tree_version, self)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def self.default_mime
|
|
208
|
+
mime = Config['mime'].find {|m| m.include? '/'}
|
|
209
|
+
mime ? MimeMagic.new(mime) : nil
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
private
|
|
213
|
+
|
|
214
|
+
def update(path, tree_version)
|
|
215
|
+
@path = path.freeze
|
|
216
|
+
@tree_version = tree_version
|
|
217
|
+
@version = @next_version = @previous_version =
|
|
218
|
+
@parent = @children = @mime =
|
|
219
|
+
@attributes = @saved_attributes =
|
|
220
|
+
@content = @saved_content = nil
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def after_commit(&block)
|
|
224
|
+
Page.current_transaction << block
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def self.check_path(path)
|
|
228
|
+
raise :invalid_path.t if !(path.blank? || path =~ PATH_REGEXP) || !valid_xml_chars?(path)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def detect_mime
|
|
232
|
+
return MimeMagic.new(attributes['mime']) if attributes['mime']
|
|
233
|
+
Config['mime'].each do |mime|
|
|
234
|
+
mime = if mime == 'extension'
|
|
235
|
+
MimeMagic.by_extension(extension)
|
|
236
|
+
elsif %w(content magic).include?(mime)
|
|
237
|
+
if !new?
|
|
238
|
+
if content.blank?
|
|
239
|
+
children.empty? ? EMPTY_MIME : DIRECTORY_MIME
|
|
240
|
+
else
|
|
241
|
+
MimeMagic.by_magic(content)
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
else
|
|
245
|
+
MimeMagic.new(mime)
|
|
246
|
+
end
|
|
247
|
+
return mime if mime
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def init_versions
|
|
252
|
+
if !@version && @tree_version
|
|
253
|
+
raise 'Page is new' if new?
|
|
254
|
+
@previous_version, @version, @next_version = repository.get_path_version(path, tree_version)
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def repository
|
|
259
|
+
Repository.instance
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def self.repository
|
|
263
|
+
Repository.instance
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|