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.
Files changed (198) hide show
  1. data/ChangeLog +1590 -0
  2. data/LICENSE +28 -0
  3. data/README +75 -0
  4. data/Rakefile +366 -0
  5. data/Rakefile.local +63 -0
  6. data/data/arrow/applets/TEMPLATE.rb.tpl +53 -0
  7. data/data/arrow/applets/args.rb +50 -0
  8. data/data/arrow/applets/config.rb +55 -0
  9. data/data/arrow/applets/error.rb +63 -0
  10. data/data/arrow/applets/files.rb +46 -0
  11. data/data/arrow/applets/inspect.rb +46 -0
  12. data/data/arrow/applets/nosuchapplet.rb +31 -0
  13. data/data/arrow/applets/status.rb +92 -0
  14. data/data/arrow/applets/test.rb +133 -0
  15. data/data/arrow/applets/tutorial/counter.rb +96 -0
  16. data/data/arrow/applets/tutorial/dingus.rb +67 -0
  17. data/data/arrow/applets/tutorial/hello.rb +34 -0
  18. data/data/arrow/applets/tutorial/hello2.rb +73 -0
  19. data/data/arrow/applets/tutorial/imgtext.rb +90 -0
  20. data/data/arrow/applets/tutorial/imgtext2.rb +286 -0
  21. data/data/arrow/applets/tutorial/index.rb +36 -0
  22. data/data/arrow/applets/tutorial/logo.rb +98 -0
  23. data/data/arrow/applets/tutorial/memcache.rb +61 -0
  24. data/data/arrow/applets/tutorial/missing.rb +37 -0
  25. data/data/arrow/applets/tutorial/protected.rb +100 -0
  26. data/data/arrow/applets/tutorial/redirector.rb +52 -0
  27. data/data/arrow/applets/tutorial/rndimages.rb +159 -0
  28. data/data/arrow/applets/tutorial/sharenotes.rb +83 -0
  29. data/data/arrow/applets/tutorial/subclassed-hello.rb +32 -0
  30. data/data/arrow/applets/tutorial/superhello.rb +72 -0
  31. data/data/arrow/applets/tutorial/timeclock.rb +78 -0
  32. data/data/arrow/applets/view-applet.rb +123 -0
  33. data/data/arrow/applets/view-template.rb +85 -0
  34. data/data/arrow/applets/wiki.rb +274 -0
  35. data/data/arrow/templates/TEMPLATE.tmpl.tpl +36 -0
  36. data/data/arrow/templates/applet-status.tmpl +153 -0
  37. data/data/arrow/templates/args-display.tmpl +120 -0
  38. data/data/arrow/templates/config/display-table.tmpl +36 -0
  39. data/data/arrow/templates/config/display.tmpl +36 -0
  40. data/data/arrow/templates/counter-deleted.tmpl +33 -0
  41. data/data/arrow/templates/counter.tmpl +59 -0
  42. data/data/arrow/templates/dingus.tmpl +55 -0
  43. data/data/arrow/templates/enumtable.tmpl +8 -0
  44. data/data/arrow/templates/error-display.tmpl +92 -0
  45. data/data/arrow/templates/filemap.tmpl +89 -0
  46. data/data/arrow/templates/hello-world-src.tmpl +34 -0
  47. data/data/arrow/templates/hello-world.tmpl +60 -0
  48. data/data/arrow/templates/imgtext/fontlist.tmpl +46 -0
  49. data/data/arrow/templates/imgtext/form.tmpl +70 -0
  50. data/data/arrow/templates/imgtext/reload-error.tmpl +40 -0
  51. data/data/arrow/templates/imgtext/reload.tmpl +55 -0
  52. data/data/arrow/templates/inspect/display.tmpl +80 -0
  53. data/data/arrow/templates/loginform.tmpl +64 -0
  54. data/data/arrow/templates/logout.tmpl +32 -0
  55. data/data/arrow/templates/memcache/display.tmpl +41 -0
  56. data/data/arrow/templates/navbar.incl +27 -0
  57. data/data/arrow/templates/nosuchapplet.tmpl +32 -0
  58. data/data/arrow/templates/printsource.tmpl +35 -0
  59. data/data/arrow/templates/protected.tmpl +36 -0
  60. data/data/arrow/templates/rndimages.tmpl +38 -0
  61. data/data/arrow/templates/service-response.tmpl +13 -0
  62. data/data/arrow/templates/sharenotes/display.tmpl +38 -0
  63. data/data/arrow/templates/status.tmpl +120 -0
  64. data/data/arrow/templates/templateviewer.tmpl +43 -0
  65. data/data/arrow/templates/test/harness.tmpl +57 -0
  66. data/data/arrow/templates/test/list.tmpl +48 -0
  67. data/data/arrow/templates/test/problem.tmpl +42 -0
  68. data/data/arrow/templates/tutorial/index.tmpl +37 -0
  69. data/data/arrow/templates/tutorial/missingapplet.tmpl +29 -0
  70. data/data/arrow/templates/view-applet-nosuch.tmpl +32 -0
  71. data/data/arrow/templates/view-applet.tmpl +40 -0
  72. data/data/arrow/templates/view-template.tmpl +83 -0
  73. data/data/arrow/templates/wiki/formerror.tmpl +47 -0
  74. data/data/arrow/templates/wiki/markup_help.incl +6 -0
  75. data/data/arrow/templates/wiki/new.tmpl +56 -0
  76. data/data/arrow/templates/wiki/new_system.tmpl +122 -0
  77. data/data/arrow/templates/wiki/sectionlist.tmpl +43 -0
  78. data/data/arrow/templates/wiki/show.tmpl +34 -0
  79. data/docs/manual/layouts/default.page +43 -0
  80. data/docs/manual/lib/api-filter.rb +81 -0
  81. data/docs/manual/lib/editorial-filter.rb +64 -0
  82. data/docs/manual/lib/examples-filter.rb +244 -0
  83. data/docs/manual/lib/links-filter.rb +117 -0
  84. data/lib/apache/fakerequest.rb +448 -0
  85. data/lib/apache/logger.rb +33 -0
  86. data/lib/arrow.rb +51 -0
  87. data/lib/arrow/acceptparam.rb +207 -0
  88. data/lib/arrow/applet.rb +725 -0
  89. data/lib/arrow/appletmixins.rb +218 -0
  90. data/lib/arrow/appletregistry.rb +590 -0
  91. data/lib/arrow/applettestcase.rb +503 -0
  92. data/lib/arrow/broker.rb +255 -0
  93. data/lib/arrow/cache.rb +176 -0
  94. data/lib/arrow/config-loaders/yaml.rb +75 -0
  95. data/lib/arrow/config.rb +615 -0
  96. data/lib/arrow/constants.rb +24 -0
  97. data/lib/arrow/cookie.rb +359 -0
  98. data/lib/arrow/cookieset.rb +108 -0
  99. data/lib/arrow/dispatcher.rb +368 -0
  100. data/lib/arrow/dispatcherloader.rb +50 -0
  101. data/lib/arrow/exceptions.rb +61 -0
  102. data/lib/arrow/fallbackhandler.rb +48 -0
  103. data/lib/arrow/formvalidator.rb +631 -0
  104. data/lib/arrow/htmltokenizer.rb +343 -0
  105. data/lib/arrow/logger.rb +488 -0
  106. data/lib/arrow/logger/apacheoutputter.rb +69 -0
  107. data/lib/arrow/logger/arrayoutputter.rb +63 -0
  108. data/lib/arrow/logger/coloroutputter.rb +111 -0
  109. data/lib/arrow/logger/fileoutputter.rb +96 -0
  110. data/lib/arrow/logger/htmloutputter.rb +54 -0
  111. data/lib/arrow/logger/outputter.rb +123 -0
  112. data/lib/arrow/mixins.rb +425 -0
  113. data/lib/arrow/monkeypatches.rb +94 -0
  114. data/lib/arrow/object.rb +117 -0
  115. data/lib/arrow/path.rb +196 -0
  116. data/lib/arrow/service.rb +447 -0
  117. data/lib/arrow/session.rb +289 -0
  118. data/lib/arrow/session/dbstore.rb +100 -0
  119. data/lib/arrow/session/filelock.rb +160 -0
  120. data/lib/arrow/session/filestore.rb +132 -0
  121. data/lib/arrow/session/id.rb +98 -0
  122. data/lib/arrow/session/lock.rb +253 -0
  123. data/lib/arrow/session/md5id.rb +42 -0
  124. data/lib/arrow/session/nulllock.rb +42 -0
  125. data/lib/arrow/session/posixlock.rb +166 -0
  126. data/lib/arrow/session/sha1id.rb +54 -0
  127. data/lib/arrow/session/store.rb +366 -0
  128. data/lib/arrow/session/usertrackid.rb +52 -0
  129. data/lib/arrow/spechelpers.rb +73 -0
  130. data/lib/arrow/template.rb +713 -0
  131. data/lib/arrow/template/attr.rb +31 -0
  132. data/lib/arrow/template/call.rb +31 -0
  133. data/lib/arrow/template/comment.rb +33 -0
  134. data/lib/arrow/template/container.rb +118 -0
  135. data/lib/arrow/template/else.rb +41 -0
  136. data/lib/arrow/template/elsif.rb +44 -0
  137. data/lib/arrow/template/escape.rb +53 -0
  138. data/lib/arrow/template/export.rb +87 -0
  139. data/lib/arrow/template/for.rb +145 -0
  140. data/lib/arrow/template/if.rb +78 -0
  141. data/lib/arrow/template/import.rb +119 -0
  142. data/lib/arrow/template/include.rb +206 -0
  143. data/lib/arrow/template/iterator.rb +208 -0
  144. data/lib/arrow/template/nodes.rb +734 -0
  145. data/lib/arrow/template/parser.rb +571 -0
  146. data/lib/arrow/template/prettyprint.rb +53 -0
  147. data/lib/arrow/template/render.rb +191 -0
  148. data/lib/arrow/template/selectlist.rb +94 -0
  149. data/lib/arrow/template/set.rb +87 -0
  150. data/lib/arrow/template/timedelta.rb +81 -0
  151. data/lib/arrow/template/unless.rb +78 -0
  152. data/lib/arrow/template/urlencode.rb +51 -0
  153. data/lib/arrow/template/yield.rb +139 -0
  154. data/lib/arrow/templatefactory.rb +125 -0
  155. data/lib/arrow/testcase.rb +567 -0
  156. data/lib/arrow/transaction.rb +608 -0
  157. data/rake/191_compat.rb +26 -0
  158. data/rake/dependencies.rb +76 -0
  159. data/rake/documentation.rb +114 -0
  160. data/rake/helpers.rb +502 -0
  161. data/rake/hg.rb +282 -0
  162. data/rake/manual.rb +787 -0
  163. data/rake/packaging.rb +129 -0
  164. data/rake/publishing.rb +278 -0
  165. data/rake/style.rb +62 -0
  166. data/rake/svn.rb +668 -0
  167. data/rake/testing.rb +187 -0
  168. data/rake/verifytask.rb +64 -0
  169. data/spec/arrow/acceptparam_spec.rb +157 -0
  170. data/spec/arrow/applet_spec.rb +575 -0
  171. data/spec/arrow/appletmixins_spec.rb +409 -0
  172. data/spec/arrow/appletregistry_spec.rb +294 -0
  173. data/spec/arrow/broker_spec.rb +153 -0
  174. data/spec/arrow/config_spec.rb +224 -0
  175. data/spec/arrow/cookieset_spec.rb +164 -0
  176. data/spec/arrow/dispatcher_spec.rb +137 -0
  177. data/spec/arrow/dispatcherloader_spec.rb +65 -0
  178. data/spec/arrow/formvalidator_spec.rb +781 -0
  179. data/spec/arrow/logger_spec.rb +346 -0
  180. data/spec/arrow/mixins_spec.rb +120 -0
  181. data/spec/arrow/service_spec.rb +645 -0
  182. data/spec/arrow/session_spec.rb +121 -0
  183. data/spec/arrow/template/iterator_spec.rb +222 -0
  184. data/spec/arrow/templatefactory_spec.rb +185 -0
  185. data/spec/arrow/transaction_spec.rb +319 -0
  186. data/spec/arrow_spec.rb +37 -0
  187. data/spec/lib/appletmatchers.rb +281 -0
  188. data/spec/lib/constants.rb +77 -0
  189. data/spec/lib/helpers.rb +41 -0
  190. data/spec/lib/matchers.rb +44 -0
  191. data/tests/cookie.tests.rb +310 -0
  192. data/tests/path.tests.rb +157 -0
  193. data/tests/session.tests.rb +111 -0
  194. data/tests/session_id.tests.rb +82 -0
  195. data/tests/session_lock.tests.rb +191 -0
  196. data/tests/session_store.tests.rb +53 -0
  197. data/tests/template.tests.rb +1360 -0
  198. 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
+