arrow 1.0.7
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/ChangeLog +1590 -0
- data/LICENSE +28 -0
- data/README +75 -0
- data/Rakefile +366 -0
- data/Rakefile.local +63 -0
- data/data/arrow/applets/TEMPLATE.rb.tpl +53 -0
- data/data/arrow/applets/args.rb +50 -0
- data/data/arrow/applets/config.rb +55 -0
- data/data/arrow/applets/error.rb +63 -0
- data/data/arrow/applets/files.rb +46 -0
- data/data/arrow/applets/inspect.rb +46 -0
- data/data/arrow/applets/nosuchapplet.rb +31 -0
- data/data/arrow/applets/status.rb +92 -0
- data/data/arrow/applets/test.rb +133 -0
- data/data/arrow/applets/tutorial/counter.rb +96 -0
- data/data/arrow/applets/tutorial/dingus.rb +67 -0
- data/data/arrow/applets/tutorial/hello.rb +34 -0
- data/data/arrow/applets/tutorial/hello2.rb +73 -0
- data/data/arrow/applets/tutorial/imgtext.rb +90 -0
- data/data/arrow/applets/tutorial/imgtext2.rb +286 -0
- data/data/arrow/applets/tutorial/index.rb +36 -0
- data/data/arrow/applets/tutorial/logo.rb +98 -0
- data/data/arrow/applets/tutorial/memcache.rb +61 -0
- data/data/arrow/applets/tutorial/missing.rb +37 -0
- data/data/arrow/applets/tutorial/protected.rb +100 -0
- data/data/arrow/applets/tutorial/redirector.rb +52 -0
- data/data/arrow/applets/tutorial/rndimages.rb +159 -0
- data/data/arrow/applets/tutorial/sharenotes.rb +83 -0
- data/data/arrow/applets/tutorial/subclassed-hello.rb +32 -0
- data/data/arrow/applets/tutorial/superhello.rb +72 -0
- data/data/arrow/applets/tutorial/timeclock.rb +78 -0
- data/data/arrow/applets/view-applet.rb +123 -0
- data/data/arrow/applets/view-template.rb +85 -0
- data/data/arrow/applets/wiki.rb +274 -0
- data/data/arrow/templates/TEMPLATE.tmpl.tpl +36 -0
- data/data/arrow/templates/applet-status.tmpl +153 -0
- data/data/arrow/templates/args-display.tmpl +120 -0
- data/data/arrow/templates/config/display-table.tmpl +36 -0
- data/data/arrow/templates/config/display.tmpl +36 -0
- data/data/arrow/templates/counter-deleted.tmpl +33 -0
- data/data/arrow/templates/counter.tmpl +59 -0
- data/data/arrow/templates/dingus.tmpl +55 -0
- data/data/arrow/templates/enumtable.tmpl +8 -0
- data/data/arrow/templates/error-display.tmpl +92 -0
- data/data/arrow/templates/filemap.tmpl +89 -0
- data/data/arrow/templates/hello-world-src.tmpl +34 -0
- data/data/arrow/templates/hello-world.tmpl +60 -0
- data/data/arrow/templates/imgtext/fontlist.tmpl +46 -0
- data/data/arrow/templates/imgtext/form.tmpl +70 -0
- data/data/arrow/templates/imgtext/reload-error.tmpl +40 -0
- data/data/arrow/templates/imgtext/reload.tmpl +55 -0
- data/data/arrow/templates/inspect/display.tmpl +80 -0
- data/data/arrow/templates/loginform.tmpl +64 -0
- data/data/arrow/templates/logout.tmpl +32 -0
- data/data/arrow/templates/memcache/display.tmpl +41 -0
- data/data/arrow/templates/navbar.incl +27 -0
- data/data/arrow/templates/nosuchapplet.tmpl +32 -0
- data/data/arrow/templates/printsource.tmpl +35 -0
- data/data/arrow/templates/protected.tmpl +36 -0
- data/data/arrow/templates/rndimages.tmpl +38 -0
- data/data/arrow/templates/service-response.tmpl +13 -0
- data/data/arrow/templates/sharenotes/display.tmpl +38 -0
- data/data/arrow/templates/status.tmpl +120 -0
- data/data/arrow/templates/templateviewer.tmpl +43 -0
- data/data/arrow/templates/test/harness.tmpl +57 -0
- data/data/arrow/templates/test/list.tmpl +48 -0
- data/data/arrow/templates/test/problem.tmpl +42 -0
- data/data/arrow/templates/tutorial/index.tmpl +37 -0
- data/data/arrow/templates/tutorial/missingapplet.tmpl +29 -0
- data/data/arrow/templates/view-applet-nosuch.tmpl +32 -0
- data/data/arrow/templates/view-applet.tmpl +40 -0
- data/data/arrow/templates/view-template.tmpl +83 -0
- data/data/arrow/templates/wiki/formerror.tmpl +47 -0
- data/data/arrow/templates/wiki/markup_help.incl +6 -0
- data/data/arrow/templates/wiki/new.tmpl +56 -0
- data/data/arrow/templates/wiki/new_system.tmpl +122 -0
- data/data/arrow/templates/wiki/sectionlist.tmpl +43 -0
- data/data/arrow/templates/wiki/show.tmpl +34 -0
- data/docs/manual/layouts/default.page +43 -0
- data/docs/manual/lib/api-filter.rb +81 -0
- data/docs/manual/lib/editorial-filter.rb +64 -0
- data/docs/manual/lib/examples-filter.rb +244 -0
- data/docs/manual/lib/links-filter.rb +117 -0
- data/lib/apache/fakerequest.rb +448 -0
- data/lib/apache/logger.rb +33 -0
- data/lib/arrow.rb +51 -0
- data/lib/arrow/acceptparam.rb +207 -0
- data/lib/arrow/applet.rb +725 -0
- data/lib/arrow/appletmixins.rb +218 -0
- data/lib/arrow/appletregistry.rb +590 -0
- data/lib/arrow/applettestcase.rb +503 -0
- data/lib/arrow/broker.rb +255 -0
- data/lib/arrow/cache.rb +176 -0
- data/lib/arrow/config-loaders/yaml.rb +75 -0
- data/lib/arrow/config.rb +615 -0
- data/lib/arrow/constants.rb +24 -0
- data/lib/arrow/cookie.rb +359 -0
- data/lib/arrow/cookieset.rb +108 -0
- data/lib/arrow/dispatcher.rb +368 -0
- data/lib/arrow/dispatcherloader.rb +50 -0
- data/lib/arrow/exceptions.rb +61 -0
- data/lib/arrow/fallbackhandler.rb +48 -0
- data/lib/arrow/formvalidator.rb +631 -0
- data/lib/arrow/htmltokenizer.rb +343 -0
- data/lib/arrow/logger.rb +488 -0
- data/lib/arrow/logger/apacheoutputter.rb +69 -0
- data/lib/arrow/logger/arrayoutputter.rb +63 -0
- data/lib/arrow/logger/coloroutputter.rb +111 -0
- data/lib/arrow/logger/fileoutputter.rb +96 -0
- data/lib/arrow/logger/htmloutputter.rb +54 -0
- data/lib/arrow/logger/outputter.rb +123 -0
- data/lib/arrow/mixins.rb +425 -0
- data/lib/arrow/monkeypatches.rb +94 -0
- data/lib/arrow/object.rb +117 -0
- data/lib/arrow/path.rb +196 -0
- data/lib/arrow/service.rb +447 -0
- data/lib/arrow/session.rb +289 -0
- data/lib/arrow/session/dbstore.rb +100 -0
- data/lib/arrow/session/filelock.rb +160 -0
- data/lib/arrow/session/filestore.rb +132 -0
- data/lib/arrow/session/id.rb +98 -0
- data/lib/arrow/session/lock.rb +253 -0
- data/lib/arrow/session/md5id.rb +42 -0
- data/lib/arrow/session/nulllock.rb +42 -0
- data/lib/arrow/session/posixlock.rb +166 -0
- data/lib/arrow/session/sha1id.rb +54 -0
- data/lib/arrow/session/store.rb +366 -0
- data/lib/arrow/session/usertrackid.rb +52 -0
- data/lib/arrow/spechelpers.rb +73 -0
- data/lib/arrow/template.rb +713 -0
- data/lib/arrow/template/attr.rb +31 -0
- data/lib/arrow/template/call.rb +31 -0
- data/lib/arrow/template/comment.rb +33 -0
- data/lib/arrow/template/container.rb +118 -0
- data/lib/arrow/template/else.rb +41 -0
- data/lib/arrow/template/elsif.rb +44 -0
- data/lib/arrow/template/escape.rb +53 -0
- data/lib/arrow/template/export.rb +87 -0
- data/lib/arrow/template/for.rb +145 -0
- data/lib/arrow/template/if.rb +78 -0
- data/lib/arrow/template/import.rb +119 -0
- data/lib/arrow/template/include.rb +206 -0
- data/lib/arrow/template/iterator.rb +208 -0
- data/lib/arrow/template/nodes.rb +734 -0
- data/lib/arrow/template/parser.rb +571 -0
- data/lib/arrow/template/prettyprint.rb +53 -0
- data/lib/arrow/template/render.rb +191 -0
- data/lib/arrow/template/selectlist.rb +94 -0
- data/lib/arrow/template/set.rb +87 -0
- data/lib/arrow/template/timedelta.rb +81 -0
- data/lib/arrow/template/unless.rb +78 -0
- data/lib/arrow/template/urlencode.rb +51 -0
- data/lib/arrow/template/yield.rb +139 -0
- data/lib/arrow/templatefactory.rb +125 -0
- data/lib/arrow/testcase.rb +567 -0
- data/lib/arrow/transaction.rb +608 -0
- data/rake/191_compat.rb +26 -0
- data/rake/dependencies.rb +76 -0
- data/rake/documentation.rb +114 -0
- data/rake/helpers.rb +502 -0
- data/rake/hg.rb +282 -0
- data/rake/manual.rb +787 -0
- data/rake/packaging.rb +129 -0
- data/rake/publishing.rb +278 -0
- data/rake/style.rb +62 -0
- data/rake/svn.rb +668 -0
- data/rake/testing.rb +187 -0
- data/rake/verifytask.rb +64 -0
- data/spec/arrow/acceptparam_spec.rb +157 -0
- data/spec/arrow/applet_spec.rb +575 -0
- data/spec/arrow/appletmixins_spec.rb +409 -0
- data/spec/arrow/appletregistry_spec.rb +294 -0
- data/spec/arrow/broker_spec.rb +153 -0
- data/spec/arrow/config_spec.rb +224 -0
- data/spec/arrow/cookieset_spec.rb +164 -0
- data/spec/arrow/dispatcher_spec.rb +137 -0
- data/spec/arrow/dispatcherloader_spec.rb +65 -0
- data/spec/arrow/formvalidator_spec.rb +781 -0
- data/spec/arrow/logger_spec.rb +346 -0
- data/spec/arrow/mixins_spec.rb +120 -0
- data/spec/arrow/service_spec.rb +645 -0
- data/spec/arrow/session_spec.rb +121 -0
- data/spec/arrow/template/iterator_spec.rb +222 -0
- data/spec/arrow/templatefactory_spec.rb +185 -0
- data/spec/arrow/transaction_spec.rb +319 -0
- data/spec/arrow_spec.rb +37 -0
- data/spec/lib/appletmatchers.rb +281 -0
- data/spec/lib/constants.rb +77 -0
- data/spec/lib/helpers.rb +41 -0
- data/spec/lib/matchers.rb +44 -0
- data/tests/cookie.tests.rb +310 -0
- data/tests/path.tests.rb +157 -0
- data/tests/session.tests.rb +111 -0
- data/tests/session_id.tests.rb +82 -0
- data/tests/session_lock.tests.rb +191 -0
- data/tests/session_store.tests.rb +53 -0
- data/tests/template.tests.rb +1360 -0
- metadata +339 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# The SuperHello class, a derivative of Arrow::Applet. A
|
|
4
|
+
# "hello world" applet.
|
|
5
|
+
#
|
|
6
|
+
# == Authors
|
|
7
|
+
#
|
|
8
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require 'arrow/applet'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### A superclass applet for testing inheritance
|
|
15
|
+
class SuperHello < Arrow::Applet
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Applet signature -- since it defines no 'uri' item, it shouldn't be loaded
|
|
19
|
+
# by the appserver directly.
|
|
20
|
+
Signature = {
|
|
21
|
+
:name => "Hello World",
|
|
22
|
+
:description => %{A 'hello world' applet.},
|
|
23
|
+
:maintainer => "ged@FaerieMUD.org",
|
|
24
|
+
:default_action => 'templated',
|
|
25
|
+
:templates => {
|
|
26
|
+
:templated => 'hello-world.tmpl',
|
|
27
|
+
:printsource => 'hello-world-src.tmpl',
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
######
|
|
34
|
+
public
|
|
35
|
+
######
|
|
36
|
+
|
|
37
|
+
def_action :display do |txn, *args|
|
|
38
|
+
self.log.debug "In the 'display' action of the '%s' applet." %
|
|
39
|
+
self.signature.name
|
|
40
|
+
|
|
41
|
+
txn.content_type = "text/plain"
|
|
42
|
+
return "Hello world."
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def_action :templated do |txn, *args|
|
|
47
|
+
self.log.debug "In the 'templated' action of the %s applet." %
|
|
48
|
+
self.signature.name
|
|
49
|
+
|
|
50
|
+
templ = self.load_template( :templated )
|
|
51
|
+
templ.txn = txn
|
|
52
|
+
templ.applet = self
|
|
53
|
+
|
|
54
|
+
return templ
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def_action :printsource do |txn, *args|
|
|
59
|
+
self.log.debug "In the 'printsource' action of the %s applet." %
|
|
60
|
+
self.signature.name
|
|
61
|
+
|
|
62
|
+
src = File.read( __FILE__ ).gsub(/\t/, ' ')
|
|
63
|
+
|
|
64
|
+
templ = self.load_template( :printsource )
|
|
65
|
+
templ.txn = txn
|
|
66
|
+
templ.applet = self
|
|
67
|
+
templ.source = src
|
|
68
|
+
|
|
69
|
+
return templ
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
end # class SuperHello
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# The TimeClock class, a derivative of Arrow::Applet; it
|
|
4
|
+
# implements a web-based timeclock applet.
|
|
5
|
+
#
|
|
6
|
+
# == Authors
|
|
7
|
+
#
|
|
8
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
|
9
|
+
#
|
|
10
|
+
# Please see the file LICENSE in the top-level directory for licensing details.
|
|
11
|
+
#
|
|
12
|
+
|
|
13
|
+
require 'bdb'
|
|
14
|
+
|
|
15
|
+
require 'arrow/applet'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### A timeclock applet
|
|
19
|
+
class TimeClock < Arrow::Applet
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Applet signature
|
|
23
|
+
Signature = {
|
|
24
|
+
:name => "",
|
|
25
|
+
:description => "A web-enabled timeclock for consultants.",
|
|
26
|
+
:uri => "timeclock",
|
|
27
|
+
:maintainer => "ged@FaerieMUD.org",
|
|
28
|
+
:version => SVNRev,
|
|
29
|
+
:config => {
|
|
30
|
+
:datadir => '/www/RubyCrafters.com/private/timeclock',
|
|
31
|
+
},
|
|
32
|
+
:templates => {
|
|
33
|
+
:main => 'timeclock/main.tmpl',
|
|
34
|
+
:home => 'timeclock/home.tmpl',
|
|
35
|
+
},
|
|
36
|
+
:vargs => {
|
|
37
|
+
:_default_ => {
|
|
38
|
+
:optional => [:username],
|
|
39
|
+
:constraints => {
|
|
40
|
+
:username => /^(\w+)$/,
|
|
41
|
+
},
|
|
42
|
+
:untaint_constraint_fields => %w{class inline},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
:monitors => {},
|
|
46
|
+
:default_action => 'home',
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
######
|
|
52
|
+
public
|
|
53
|
+
######
|
|
54
|
+
|
|
55
|
+
### Override #run to do things that need to be done for every action. All
|
|
56
|
+
### actions will be wrapped by this method, which wraps the template objects
|
|
57
|
+
### they return in a container 'main' template.
|
|
58
|
+
def run( txn, *rest )
|
|
59
|
+
super {|meth, txn, *rest|
|
|
60
|
+
template = self.load_template( :main )
|
|
61
|
+
|
|
62
|
+
template.body = meth.call( txn, *rest )
|
|
63
|
+
template.txn = txn
|
|
64
|
+
template.navbar.txn = txn
|
|
65
|
+
|
|
66
|
+
return template
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
### Actions
|
|
72
|
+
|
|
73
|
+
# 'home' is an implicit (template-only) action
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
end # class TimeClock
|
|
77
|
+
|
|
78
|
+
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# The AppletViewer class, a derivative of Arrow::Applet. It is
|
|
4
|
+
# an introspection applet that can be used to view the code for Arrow applets in
|
|
5
|
+
# a running Arrow application.
|
|
6
|
+
#
|
|
7
|
+
# == VCS Id
|
|
8
|
+
#
|
|
9
|
+
# $Id$
|
|
10
|
+
#
|
|
11
|
+
# == Authors
|
|
12
|
+
#
|
|
13
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
|
14
|
+
#
|
|
15
|
+
|
|
16
|
+
require 'arrow/applet'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### An introspection applet that can be used to view the code for Arrow
|
|
20
|
+
### applets in a running Arrow application.
|
|
21
|
+
class AppletViewer < Arrow::Applet
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Width of tabs in prettified code
|
|
25
|
+
DefaultTabWidth = 4
|
|
26
|
+
|
|
27
|
+
# Applet signature
|
|
28
|
+
Signature = {
|
|
29
|
+
:name => "Applet Viewer",
|
|
30
|
+
:description => "An introspection applet that can be used to view the " +
|
|
31
|
+
"code for Arrow applets in a running Arrow application.",
|
|
32
|
+
:maintainer => "ged@FaerieMUD.org",
|
|
33
|
+
:default_action => 'display',
|
|
34
|
+
:templates => {
|
|
35
|
+
:display => 'view-applet.tmpl',
|
|
36
|
+
:nosuch => 'view-applet-nosuch.tmpl',
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### Read configuration values from the 'viewapplet' section of the config
|
|
42
|
+
### (if it exists)
|
|
43
|
+
def initialize( *args )
|
|
44
|
+
super
|
|
45
|
+
|
|
46
|
+
@tabwidth = DefaultTabWidth
|
|
47
|
+
|
|
48
|
+
if @config.respond_to?( :viewapplet )
|
|
49
|
+
@tabwidth = Integer( @config.viewapplet.tabwidth ) rescue DefaultTabWidth
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
self.log.debug "Tab width set to %d" % @tabwidth
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
######
|
|
57
|
+
public
|
|
58
|
+
######
|
|
59
|
+
|
|
60
|
+
### The main applet-code display action
|
|
61
|
+
def display_action( txn, *appleturi )
|
|
62
|
+
self.log.debug "In the 'display' action of the '%s' app." % self.signature.name
|
|
63
|
+
|
|
64
|
+
# Pick out the applet to view from the URI, if present
|
|
65
|
+
templ = nil
|
|
66
|
+
uri = appleturi.join( "/" )
|
|
67
|
+
applet = txn.broker.registry[ uri ]
|
|
68
|
+
|
|
69
|
+
# If the URI matched a loaded applet, display its source
|
|
70
|
+
if applet
|
|
71
|
+
|
|
72
|
+
# The applet's class knows from whence it was loaded.
|
|
73
|
+
fn = applet.class.filename
|
|
74
|
+
fn.untaint
|
|
75
|
+
|
|
76
|
+
# Plug the loaded values into the template
|
|
77
|
+
templ = self.load_template( :display )
|
|
78
|
+
templ.displayed_applet = applet
|
|
79
|
+
templ.filename = fn
|
|
80
|
+
|
|
81
|
+
# Read the code. This will later undergo some sort of greater
|
|
82
|
+
# fancification.
|
|
83
|
+
code = File.read( fn )
|
|
84
|
+
templ.code = self.format_code( code )
|
|
85
|
+
|
|
86
|
+
else
|
|
87
|
+
|
|
88
|
+
# If there wasn't an applet to load, load the generic "oops"
|
|
89
|
+
# template and plug a message in.
|
|
90
|
+
templ = self.load_template( :nosuch )
|
|
91
|
+
templ.message = "Invalid or missing applet URI"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Plug some more values into the template no matter what happened
|
|
95
|
+
templ.txn = txn
|
|
96
|
+
templ.applet = self
|
|
97
|
+
|
|
98
|
+
return templ
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Allow <uri>/<viewuri> as well as <uri>/display/<viewuri>
|
|
102
|
+
alias_method :action_missing_action, :display_action
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
#########
|
|
106
|
+
protected
|
|
107
|
+
#########
|
|
108
|
+
|
|
109
|
+
### Format the code prettily for display as HTML
|
|
110
|
+
def format_code( code )
|
|
111
|
+
newstr = code.split( /\n/ ).collect {|line|
|
|
112
|
+
line.gsub( /(.*?)\t/ ) do
|
|
113
|
+
$1 + ' ' * (@tabwidth - $1.length % @tabwidth)
|
|
114
|
+
end
|
|
115
|
+
}.join("\n")
|
|
116
|
+
|
|
117
|
+
return newstr
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
end # class AppletViewer
|
|
122
|
+
|
|
123
|
+
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# The TemplateViewer class, a derivative of
|
|
4
|
+
# Arrow::Applet. It is an introspection applet that displays information about
|
|
5
|
+
# Arrow templates.
|
|
6
|
+
#
|
|
7
|
+
# == Authors
|
|
8
|
+
#
|
|
9
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
require 'arrow/applet'
|
|
13
|
+
require 'arrow/htmltokenizer'
|
|
14
|
+
|
|
15
|
+
### A template viewer applet
|
|
16
|
+
class TemplateViewer < Arrow::Applet
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Applet signature
|
|
20
|
+
applet_name "Template Viewer"
|
|
21
|
+
applet_description "It is an introspection applet that displays "\
|
|
22
|
+
"Arrow templates with syntax highlighting."
|
|
23
|
+
applet_maintainer "ged@FaerieMUD.org"
|
|
24
|
+
|
|
25
|
+
default_action :default
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
######
|
|
30
|
+
public
|
|
31
|
+
######
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
#################################################################
|
|
35
|
+
### A C T I O N S
|
|
36
|
+
#################################################################
|
|
37
|
+
|
|
38
|
+
def display_action( txn, *args )
|
|
39
|
+
self.log.debug "In the 'display' action of the '%s' applet." %
|
|
40
|
+
self.signature.name
|
|
41
|
+
|
|
42
|
+
templ = self.load_template( :display )
|
|
43
|
+
templ.txn = txn
|
|
44
|
+
templ.applet = self
|
|
45
|
+
|
|
46
|
+
if txn.vargs && txn.vargs[:template]
|
|
47
|
+
args.replace( txn.vargs[:template].split(%r{/}) )
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Eliminate harmful parts of the path and try to load the template
|
|
51
|
+
# specified by it
|
|
52
|
+
self.log.debug "Args: %p" % [ args ]
|
|
53
|
+
args.reject! {|dir|
|
|
54
|
+
dir.nil? || dir.empty? || /^\./ =~ dir || /[^-\w.]/ =~ dir
|
|
55
|
+
}
|
|
56
|
+
tpath = File.join( *args ).untaint
|
|
57
|
+
templ.path = tpath
|
|
58
|
+
|
|
59
|
+
unless tpath.empty?
|
|
60
|
+
begin
|
|
61
|
+
dtempl = self.templateFactory.get_template( tpath )
|
|
62
|
+
rescue Arrow::TemplateError => err
|
|
63
|
+
templ.error = err
|
|
64
|
+
else
|
|
65
|
+
templ.template = dtempl
|
|
66
|
+
templ.tokenizer = Arrow::HTMLTokenizer.new( dtempl._source )
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
return templ
|
|
71
|
+
end
|
|
72
|
+
template :display => 'view-template.tmpl',
|
|
73
|
+
:default => 'templateviewer.tmpl'
|
|
74
|
+
validator :display, {
|
|
75
|
+
:optional => [:template],
|
|
76
|
+
:constraints => {
|
|
77
|
+
:template => %r{^([\w./-]+)$},
|
|
78
|
+
},
|
|
79
|
+
:untaint_constraint_fields => %w{template},
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
end # class TemplateViewer
|
|
84
|
+
|
|
85
|
+
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# The ArrowWiki class, a derivative of Arrow::Applet. It's a
|
|
4
|
+
# simple wiki to demonstrate the Arrow framework.
|
|
5
|
+
#
|
|
6
|
+
# == VCS Id
|
|
7
|
+
#
|
|
8
|
+
# $Id$
|
|
9
|
+
#
|
|
10
|
+
# == Authors
|
|
11
|
+
#
|
|
12
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
|
13
|
+
#
|
|
14
|
+
|
|
15
|
+
require 'arrow/applet'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### It's a simple wiki that can serve as a proof of concept
|
|
19
|
+
class WikiApplet < Arrow::Applet
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Applet signature
|
|
23
|
+
Signature = {
|
|
24
|
+
:name => "Arrow Wiki Toplevel Applet",
|
|
25
|
+
:description => "A wiki for FaerieMUD documentation",
|
|
26
|
+
:maintainer => "ged@FaerieMUD.org",
|
|
27
|
+
:default_action => 'index',
|
|
28
|
+
:templates => {
|
|
29
|
+
:main => 'wiki/main.tmpl',
|
|
30
|
+
:new_system => 'wiki/new_system.tmpl',
|
|
31
|
+
:show => 'wiki/show.tmpl',
|
|
32
|
+
:new => 'wiki/new.tmpl',
|
|
33
|
+
:save => 'wiki/save.tmpl',
|
|
34
|
+
:formerror => 'wiki/formerror.tmpl',
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
:validator_profiles => {
|
|
38
|
+
|
|
39
|
+
# Target for the form in /new_system
|
|
40
|
+
:create_system => {
|
|
41
|
+
:untaint_all_constraints => true,
|
|
42
|
+
:required => [:password, :web_name, :web_address],
|
|
43
|
+
:constraints => {
|
|
44
|
+
:password => /^([\x20-\xff]+)$/,
|
|
45
|
+
:web_name => /^([\x20-\xff]+)$/,
|
|
46
|
+
:web_address => /^([a-z0-9]+)$/i,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
# Target for the forms in /edit and /new
|
|
51
|
+
:save => {
|
|
52
|
+
:untaint_all_constraints => true,
|
|
53
|
+
:required => [:author, :content],
|
|
54
|
+
:constraints => {
|
|
55
|
+
:author => /^([\x20-\xff]+)$/,
|
|
56
|
+
:content => /^([\r\n\t\x20-\xff]+)$/,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Default location for the wiki storage
|
|
63
|
+
DefaultStoragePath = "/tmp/wikiapplet"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
### Create a new wiki controller applet.
|
|
67
|
+
def initialize( *args )
|
|
68
|
+
super
|
|
69
|
+
|
|
70
|
+
# Read configuration values from the config file if it exists
|
|
71
|
+
if @config.respond_to?( :wiki )
|
|
72
|
+
wconfig = @config.wiki
|
|
73
|
+
WikiService.storage_path = wconfig.storage
|
|
74
|
+
else
|
|
75
|
+
WikiService.storage_path = DefaultStoragePath
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
######
|
|
81
|
+
public
|
|
82
|
+
######
|
|
83
|
+
|
|
84
|
+
# Notes:
|
|
85
|
+
#
|
|
86
|
+
# In instiki the action is stuck between the web and the topic like:
|
|
87
|
+
# /<web>/view/<topic>
|
|
88
|
+
# In this implementation the action always precedes the data, like so:
|
|
89
|
+
# /view/<web>/<topic>
|
|
90
|
+
# This might require tinkering a bit with the WikiService's innards, as I
|
|
91
|
+
# don't know if its link-builder is coupled with its internal urispace,
|
|
92
|
+
# but I suspect it is.
|
|
93
|
+
|
|
94
|
+
### Debugging action /inspect
|
|
95
|
+
def inspect_action( txn, *args )
|
|
96
|
+
txn.content_type = "text/plain"
|
|
97
|
+
return "%p: %d" % [ self.wiki, Process.pid ]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
### Default "/" action -- redirects to the system init if the wiki's not yet
|
|
102
|
+
### set up, redirects to the web list if there are more than one web, or
|
|
103
|
+
### redirects to the home page if there's only one web.
|
|
104
|
+
def index_action( txn, web=nil, *args )
|
|
105
|
+
if !self.wiki.setup?
|
|
106
|
+
return txn.redirect( txn.action + "/new_system/" )
|
|
107
|
+
elsif self.wiki.webs.length == 1
|
|
108
|
+
return txn.redirect( txn.action + "/show/" +
|
|
109
|
+
self.wiki.webs.values.first.address )
|
|
110
|
+
else
|
|
111
|
+
return txn.redirect( txn.action + "/web_list/" )
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
### /new_system action -- Initializes a new wiki.
|
|
117
|
+
def new_system_action( txn, *args )
|
|
118
|
+
if self.wiki.setup?
|
|
119
|
+
return txn.redirect( txn.action )
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
tmpl = self.load_template( :new_system )
|
|
123
|
+
tmpl.txn = txn
|
|
124
|
+
tmpl.applet = self
|
|
125
|
+
tmpl.wiki = self.wiki
|
|
126
|
+
|
|
127
|
+
return tmpl
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
### /create_system action -- the target for the /new_system action's
|
|
132
|
+
### form. Untaints the incoming arguments, does the creation of the new wiki
|
|
133
|
+
### if they check out, or displays /new_system's template again with errors
|
|
134
|
+
### if the args were bad.
|
|
135
|
+
def create_system_action( txn, *args )
|
|
136
|
+
if !(txn.vargs.missing.empty? && txn.vargs.invalid.empty?)
|
|
137
|
+
badargs = txn.vargs.missing | txn.vargs.invalid.keys
|
|
138
|
+
self.log.error "Invalid or missing arguments: %p" % badargs
|
|
139
|
+
|
|
140
|
+
tmpl = self.new_system_action( txn )
|
|
141
|
+
badargs.each do |field|
|
|
142
|
+
tmpl.errors << "Invalid or missing field '#{field}'"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
return tmpl
|
|
146
|
+
else
|
|
147
|
+
vargs = txn.vargs
|
|
148
|
+
self.log.info "Creating new web with args: %p" % [ vargs.valid.to_a ]
|
|
149
|
+
|
|
150
|
+
self.wiki.setup(
|
|
151
|
+
vargs[:password],
|
|
152
|
+
vargs[:web_name],
|
|
153
|
+
vargs[:web_address] ) unless
|
|
154
|
+
self.wiki.setup?
|
|
155
|
+
|
|
156
|
+
return txn.redirect( txn.action + "/new/" + vargs[:web_address] +
|
|
157
|
+
"/HomePage" )
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
### /new/+web+/+topic+ -- create a new topic on the specified +web+ called
|
|
163
|
+
### +topic+.
|
|
164
|
+
def new_action( txn, web=nil, topic=nil, *args )
|
|
165
|
+
return txn.redirect( txn.action ) unless web
|
|
166
|
+
return txn.redirect( txn.action + "/show/" + web ) unless topic
|
|
167
|
+
|
|
168
|
+
templ = self.load_template( :new )
|
|
169
|
+
|
|
170
|
+
templ.txn = txn
|
|
171
|
+
templ.topic = topic
|
|
172
|
+
templ.web = web
|
|
173
|
+
templ.author = txn.session[:author] || "AnonymousCoward"
|
|
174
|
+
|
|
175
|
+
return templ
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
### /show/+web+/+topic+ -- Show the specified +topic+ from the given +web+.
|
|
180
|
+
def show_action( txn, web=nil, topic="HomePage", *args )
|
|
181
|
+
return txn.redirect( txn.action ) unless web
|
|
182
|
+
|
|
183
|
+
self.log.debug "Showing '#{web}/#{topic}'"
|
|
184
|
+
|
|
185
|
+
if page = self.wiki.read_page( web, topic )
|
|
186
|
+
templ = self.load_template( :show )
|
|
187
|
+
templ.txn = txn
|
|
188
|
+
templ.web = web
|
|
189
|
+
templ.page = page
|
|
190
|
+
|
|
191
|
+
return templ
|
|
192
|
+
else
|
|
193
|
+
return txn.redirect( txn.applet + "/new/" + topic )
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
### /save/+web+/+topic+ -- Save the values for the specified +topic+ to the
|
|
199
|
+
### given +web+, creating it if it didn't already exist.
|
|
200
|
+
def save_action( txn, web=nil, topic=nil, *args )
|
|
201
|
+
self.log.debug "Save: web: %p, topic: %p" % [web, topic]
|
|
202
|
+
return txn.redirect( txn.applet ) unless web
|
|
203
|
+
return txn.redirect( txn.applet + "/show/" + web ) unless topic
|
|
204
|
+
|
|
205
|
+
templ = nil
|
|
206
|
+
txn.session[:wiki_author] = txn.vargs[:author] if txn.vargs.key?( :author )
|
|
207
|
+
|
|
208
|
+
# If the arguments don't validate, fetch the template from the referring
|
|
209
|
+
# action again and inject explanatory errors into it.
|
|
210
|
+
return self.report_form_errors( txn, web, topic ) if txn.vargs.errors?
|
|
211
|
+
|
|
212
|
+
webobj = self.wiki.webs[ web ] or return txn.redirect( txn.applet )
|
|
213
|
+
author = Author.new( txn.vargs[:author], txn.remote_ip )
|
|
214
|
+
self.log.debug "Save for web: %p, author: %p" % [webobj, author]
|
|
215
|
+
|
|
216
|
+
# If it already exists, add a revision to the page
|
|
217
|
+
if webobj.pages[ topic ]
|
|
218
|
+
self.log.debug "Revising page '#{topic}'"
|
|
219
|
+
page = self.wiki.
|
|
220
|
+
revise_page( web, topic, txn.params[:content], Time.now, author )
|
|
221
|
+
page.unlock
|
|
222
|
+
|
|
223
|
+
# Otherwise it's a new page
|
|
224
|
+
else
|
|
225
|
+
self.log.debug "Creating page '#{topic}'"
|
|
226
|
+
page = self.wiki.
|
|
227
|
+
write_page( web, topic, txn.params[:content], Time.now, author )
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
return txn.redirect( txn.applet + "/" + ["show", web, topic].join("/") )
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
### /web_list -- List the webs that are available
|
|
235
|
+
def web_list_action( txn, *args )
|
|
236
|
+
txn.content_type = "text/plain"
|
|
237
|
+
return "Would be listing the available webs"
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
#########
|
|
242
|
+
protected
|
|
243
|
+
#########
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
### Call the referring action again, but add any validator error messages to
|
|
247
|
+
### the returned template's 'formerrors' field.
|
|
248
|
+
def report_form_errors( txn, web, topic, *args )
|
|
249
|
+
templ = nil
|
|
250
|
+
refaction = txn.referringAction
|
|
251
|
+
|
|
252
|
+
if refaction && self.actions[ refaction ]
|
|
253
|
+
templ = self.subrun( refaction, txn, web, topic, *args )
|
|
254
|
+
else
|
|
255
|
+
templ = self.load_template( :formerror )
|
|
256
|
+
|
|
257
|
+
templ.txn = txn
|
|
258
|
+
templ.applet = self
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
templ.formerrors = txn.vargs.error_messages
|
|
262
|
+
return templ
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
### Return the WikiService instance
|
|
267
|
+
def wiki
|
|
268
|
+
WikiService.instance
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
end # class ArrowWiki
|
|
273
|
+
|
|
274
|
+
|