ruwiki 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. data/Readme.rubygems +86 -0
  2. data/Readme.tarfile +65 -0
  3. data/bin/ruwiki +58 -0
  4. data/bin/ruwiki.cgi +87 -0
  5. data/bin/ruwiki_convert +56 -0
  6. data/bin/ruwiki_service.rb +82 -0
  7. data/bin/ruwiki_servlet +53 -0
  8. data/contrib/enscript-token.rb +55 -0
  9. data/contrib/rublog_integrator.rb +68 -0
  10. data/data/Default/ProjectIndex.ruwiki +49 -0
  11. data/data/Ruwiki/Antispam.ruwiki +65 -0
  12. data/data/Ruwiki/BugTracking.ruwiki +33 -0
  13. data/data/Ruwiki/ChangeLog.ruwiki +102 -0
  14. data/data/Ruwiki/Configuring_Ruwiki.ruwiki +151 -0
  15. data/data/Ruwiki/Extending_Ruwiki.ruwiki +317 -0
  16. data/data/Ruwiki/LicenseAndAuthorInfo.ruwiki +30 -0
  17. data/data/Ruwiki/ProjectIndex.ruwiki +84 -0
  18. data/data/Ruwiki/Roadmap.ruwiki +225 -0
  19. data/data/Ruwiki/RuwikiTemplatingLibrary.ruwiki +156 -0
  20. data/data/Ruwiki/RuwikiUtilities.ruwiki +157 -0
  21. data/data/Ruwiki/SandBox.ruwiki +9 -0
  22. data/data/Ruwiki/To_Do.ruwiki +51 -0
  23. data/data/Ruwiki/TroubleShooting.ruwiki +33 -0
  24. data/data/Ruwiki/WikiFeatures.ruwiki +17 -0
  25. data/data/Ruwiki/WikiMarkup.ruwiki +261 -0
  26. data/data/Tutorial/AddingPages.ruwiki +16 -0
  27. data/data/Tutorial/AddingProjects.ruwiki +16 -0
  28. data/data/Tutorial/ProjectIndex.ruwiki +11 -0
  29. data/data/Tutorial/SandBox.ruwiki +9 -0
  30. data/data/agents.banned +60 -0
  31. data/data/agents.readonly +321 -0
  32. data/data/hostip.banned +30 -0
  33. data/data/hostip.readonly +28 -0
  34. data/lib/ruwiki.rb +622 -0
  35. data/lib/ruwiki/auth.rb +56 -0
  36. data/lib/ruwiki/auth/gforge.rb +73 -0
  37. data/lib/ruwiki/backend.rb +318 -0
  38. data/lib/ruwiki/backend/flatfiles.rb +217 -0
  39. data/lib/ruwiki/config.rb +244 -0
  40. data/lib/ruwiki/exportable.rb +192 -0
  41. data/lib/ruwiki/handler.rb +342 -0
  42. data/lib/ruwiki/lang/de.rb +339 -0
  43. data/lib/ruwiki/lang/en.rb +334 -0
  44. data/lib/ruwiki/lang/es.rb +339 -0
  45. data/lib/ruwiki/page.rb +262 -0
  46. data/lib/ruwiki/servlet.rb +38 -0
  47. data/lib/ruwiki/template.rb +553 -0
  48. data/lib/ruwiki/utils.rb +24 -0
  49. data/lib/ruwiki/utils/command.rb +102 -0
  50. data/lib/ruwiki/utils/converter.rb +297 -0
  51. data/lib/ruwiki/utils/manager.rb +639 -0
  52. data/lib/ruwiki/utils/servletrunner.rb +295 -0
  53. data/lib/ruwiki/wiki.rb +147 -0
  54. data/lib/ruwiki/wiki/tokens.rb +136 -0
  55. data/lib/ruwiki/wiki/tokens/00default.rb +211 -0
  56. data/lib/ruwiki/wiki/tokens/01wikilinks.rb +166 -0
  57. data/lib/ruwiki/wiki/tokens/02actions.rb +63 -0
  58. data/lib/ruwiki/wiki/tokens/abbreviations.rb +40 -0
  59. data/lib/ruwiki/wiki/tokens/calendar.rb +147 -0
  60. data/lib/ruwiki/wiki/tokens/headings.rb +43 -0
  61. data/lib/ruwiki/wiki/tokens/lists.rb +112 -0
  62. data/lib/ruwiki/wiki/tokens/rubylists.rb +48 -0
  63. data/ruwiki.conf +22 -0
  64. data/ruwiki.pkg +0 -0
  65. data/templates/default/body.tmpl +19 -0
  66. data/templates/default/content.tmpl +7 -0
  67. data/templates/default/controls.tmpl +23 -0
  68. data/templates/default/edit.tmpl +27 -0
  69. data/templates/default/error.tmpl +14 -0
  70. data/templates/default/footer.tmpl +23 -0
  71. data/templates/default/ruwiki.css +297 -0
  72. data/templates/default/save.tmpl +8 -0
  73. data/templates/sidebar/body.tmpl +19 -0
  74. data/templates/sidebar/content.tmpl +8 -0
  75. data/templates/sidebar/controls.tmpl +8 -0
  76. data/templates/sidebar/edit.tmpl +27 -0
  77. data/templates/sidebar/error.tmpl +13 -0
  78. data/templates/sidebar/footer.tmpl +22 -0
  79. data/templates/sidebar/ruwiki.css +347 -0
  80. data/templates/sidebar/save.tmpl +10 -0
  81. data/templates/simple/body.tmpl +13 -0
  82. data/templates/simple/content.tmpl +7 -0
  83. data/templates/simple/controls.tmpl +8 -0
  84. data/templates/simple/edit.tmpl +25 -0
  85. data/templates/simple/error.tmpl +10 -0
  86. data/templates/simple/footer.tmpl +10 -0
  87. data/templates/simple/ruwiki.css +192 -0
  88. data/templates/simple/save.tmpl +8 -0
  89. data/tests/harness.rb +52 -0
  90. data/tests/tc_backend_flatfile.rb +103 -0
  91. data/tests/tc_bugs.rb +74 -0
  92. data/tests/tc_exportable.rb +64 -0
  93. data/tests/tc_template.rb +145 -0
  94. data/tests/tc_tokens.rb +335 -0
  95. data/tests/testall.rb +20 -0
  96. metadata +182 -0
@@ -0,0 +1,38 @@
1
+ #--
2
+ # Ruwiki
3
+ # Copyright � 2002 - 2004, Digikata and HaloStatue
4
+ # Alan Chen (alan@digikata.com)
5
+ # Austin Ziegler (ruwiki@halostatue.ca)
6
+ #
7
+ # Licensed under the same terms as Ruby.
8
+ #
9
+ # $Id: servlet.rb,v 1.7 2004/09/29 05:36:30 austin Exp $
10
+ #++
11
+ require 'webrick'
12
+
13
+ class Ruwiki::Servlet < WEBrick::HTTPServlet::AbstractServlet
14
+ class << self
15
+ attr_accessor :config
16
+ end
17
+
18
+ def initialize(config)
19
+ @config = config
20
+ end
21
+
22
+ # Converts a POST into a GET.
23
+ def do_POST(req, res)
24
+ do_GET(req, res)
25
+ end
26
+
27
+ def do_GET(req, res)
28
+ # Generate the reponse handlers for Ruwiki from the request and response
29
+ # objects provided.
30
+ wiki = Ruwiki.new(Ruwiki::Handler.from_webrick(req, res))
31
+
32
+ # Configuration defaults to certain values. This overrides the defaults.
33
+ wiki.config = Ruwiki::Servlet.config unless Ruwiki::Servlet.config.nil?
34
+ wiki.config!
35
+ wiki.config.logger = @config.logger
36
+ wiki.run
37
+ end
38
+ end
@@ -0,0 +1,553 @@
1
+ #--
2
+ # Ruwiki
3
+ # Copyright � 2002 - 2004, Digikata and HaloStatue
4
+ # Alan Chen (alan@digikata.com)
5
+ # Austin Ziegler (ruwiki@halostatue.ca)
6
+ #
7
+ # Licensed under the same terms as Ruby.
8
+ #
9
+ # This file is originally from rdoc by Dave Thomas (dave@pragprog.com).
10
+ #
11
+ # $Id: template.rb,v 1.11 2004/11/26 12:18:46 austin Exp $
12
+ #++
13
+ require 'cgi'
14
+
15
+ # Ruwiki templating, based originally on RDoc's "cheap-n-cheerful" HTML
16
+ # page template system, which is a line-oriented, text-based templating
17
+ # system.
18
+ #
19
+ # Templates can contain:
20
+ #
21
+ # * The directive !INCLUDE!, which will include the next template from the
22
+ # provided list. This is processed before any template substitution, so
23
+ # repeating and optional blocks work on the values within the template
24
+ # substitution.
25
+ # * Substitutable variable values between percent signs (<tt>%key%</tt>).
26
+ # Optional variable values can be preceded by a question mark
27
+ # (<tt>%?key%</tt>).
28
+ # * Label values between hash marks (<tt>#key#</tt>). Optional label
29
+ # values can be preceded by a question mark (<tt>#?key#</tt>).
30
+ # * Links (<tt>HREF:ref:name:</tt>).
31
+ # * Repeating substitution values (<tt>[:key| stuff :]</tt>). The value of
32
+ # +key+ may be an integer value or a range (in which case key will be
33
+ # used as an iterator, providing the current value of key on successive
34
+ # values), an array of scalar values (substituting each value), or an
35
+ # array of hashes (in which case it works like repeating blocks, see
36
+ # below). These must NOT be nested. Note that integer value counting is
37
+ # one-based.
38
+ # * Optional substitution values (<tt>[?key| stuff ?]</tt> or <tt>[!key|
39
+ # stuff ?]</tt>. These must NOT be nested.
40
+ # * Repeating blocks:
41
+ # START:key
42
+ # ... stuff
43
+ # END:key
44
+ # * Optional blocks:
45
+ # IF:key
46
+ # ... stuff
47
+ # ENDIF:key
48
+ # or:
49
+ # IFNOT:key
50
+ # ... stuff
51
+ # ENDIF:key
52
+ #
53
+ # When generating the output, a hash of values is provided and an optional
54
+ # hash of labels is provided. Simple variables are resolved directly from
55
+ # the hash; labels are resolved as Symbols from the label hash or are
56
+ # otherwise treated as variables. Labels are always resolved from a single
57
+ # message hash.
58
+ #
59
+ # The +key+ for repeating blocks (one-line or multi-line) must be an array
60
+ # of hashes. The repeating block will be generated once for each entry.
61
+ # Blocks can be nested arbitrarily deeply.
62
+ #
63
+ # Optional blocks will only be generated if the test is true. IF blocks
64
+ # test for the presence of +key+ or that +key+ is non-+nil+; IFNOT blocks
65
+ # look for the absence or +nil+ value of +key+. IFBLANK blocks test for
66
+ # the absence, +nil+ value, or emptiness of +key+; IFNOTBLANK blocks test
67
+ # for the presence of +key+ and that it is neither +nil+ nor empty.
68
+ #
69
+ # Usage: Given a set of templates <tt>T1</tt>, <tt>T2</tt>, etc.
70
+ #
71
+ # values = { "name" => "Dave", "state" => "TX" }
72
+ # fr = { :name => "Nom", :state => "Etat" }
73
+ # en = { :name => "Name", :state => "State" }
74
+ # tt = TemplatePage.new(T1, T2, T3)
75
+ #
76
+ # res = ""
77
+ # tt.process(res, values, fr)
78
+ # tt.process(res, values, en)
79
+ #
80
+ class Ruwiki::TemplatePage
81
+ BLOCK_RE = %r{^\s*(IF|IFNOT|IFBLANK|IFNOTBLANK|ENDIF|START|END):(\w+)?}
82
+ HREF_RE = %r{HREF:(\w+?):(\w+?):}
83
+ LABEL_RE = %r{#(\??)(-?)(\w+?)#}
84
+ VARIABLE_RE = %r{%(\??)(-?)(\w+?)%}
85
+ IFLINE_RE = %r{\[([?!])(\w+?)\|(.*?)\?\]}
86
+ BLOCKLINE_RE = %r{\[:(\w+?)\|(.*?):\]}
87
+ INCLUDE_RE = %r{!INCLUDE!}
88
+
89
+ DDLB_RES = [
90
+ [ :check, %r{%check:(\w+?)%} ],
91
+ [ :date, %r{%date:(\w+?)%} ],
92
+ [ :popup, %r{%popup:(\w+?):(\w+?)%} ],
93
+ [ :ddlb, %r{%ddlb:(\w+?):(\w+?)%} ],
94
+ [ :vsortddlb, %r{%vsortddlb:(\w+?):(\w+?)%} ],
95
+ [ :radio, %r{%radio:(\w+?):(\w+?)%} ],
96
+ [ :radioone, %r{%radioone:(\w+?):(\w+?)%} ],
97
+ [ :input, %r{%input:(\w+?):(\d+?):(\d+?)%} ],
98
+ [ :text, %r{%text:(\w+?):(\d+?):(\d+?)%} ],
99
+ [ :pwinput, %r{%pwinput:(\w+?):(\d+?):(\d+?)%} ],
100
+ [ :pair, %r{%pair(\d)?:([^:]+)(\w+?)%} ]
101
+ ]
102
+
103
+ # Nasty hack to allow folks to insert tags if they really, really want to
104
+ OPEN_TAG = "\001"
105
+ CLOSE_TAG = "\002"
106
+ BR = "#{OPEN_TAG}br#{CLOSE_TAG}"
107
+
108
+ # A Context holds a stack of key/value pairs (like a symbol table). When
109
+ # asked to resolve a key, it first searches the top of the stack, then the
110
+ # next level, and so on until it finds a match (or runs out of entries).
111
+ class Context
112
+ def initialize
113
+ @stack = []
114
+ end
115
+
116
+ def push(hash)
117
+ @stack.push(hash)
118
+ end
119
+
120
+ def pop
121
+ @stack.pop
122
+ end
123
+
124
+ # Find a scalar value, throwing an exception if not found. This method is
125
+ # used when substituting the %xxx% constructs
126
+ def find_scalar_raw(key)
127
+ @stack.reverse_each do |level|
128
+ if level.has_key?(key)
129
+ val = level[key]
130
+ return val unless val.kind_of?(Array)
131
+ end
132
+ end
133
+ raise "Template error: can't find variable '#{key}'."
134
+ end
135
+
136
+ def find_scalar(key)
137
+ find_scalar_raw(key) || ''
138
+ end
139
+
140
+ # Lookup any key in the stack of hashes
141
+ def lookup(key)
142
+ @stack.reverse_each do |level|
143
+ return level[key] if level.has_key?(key)
144
+ end
145
+ nil
146
+ end
147
+ end
148
+
149
+ # Simple class to read lines out of a string
150
+ class LineReader
151
+ attr_reader :lines
152
+ def initialize(lines)
153
+ @lines = lines
154
+ end
155
+
156
+ # read the next line
157
+ def read
158
+ @lines.shift
159
+ end
160
+
161
+ # Return a list of lines up to the line that matches a pattern. That last
162
+ # line is discarded.
163
+ def read_up_to(pattern)
164
+ res = []
165
+ while line = read
166
+ if pattern.match(line)
167
+ return LineReader.new(res)
168
+ else
169
+ res << line
170
+ end
171
+ end
172
+ raise "Missing end tag in template: #{pattern.source}"
173
+ end
174
+
175
+ # Return a copy of ourselves that can be modified without affecting us
176
+ def dup
177
+ LineReader.new(@lines.dup)
178
+ end
179
+ end
180
+
181
+ # +templates+ is an array of strings containing the templates. We start at
182
+ # the first, and substitute in subsequent ones where the string
183
+ # <tt>!INCLUDE!</tt> occurs. For example, we could have the overall page
184
+ # template containing
185
+ #
186
+ # <html><body>
187
+ # <h1>Master</h1>
188
+ # !INCLUDE!
189
+ # </body></html>
190
+ #
191
+ # and substitute subpages in to it by passing [master, sub_page]. This
192
+ # gives us a cheap way of framing pages
193
+ def initialize(*templates)
194
+ result = templates.shift.dup
195
+ templates.each { |content| result.sub!(INCLUDE_RE, content) }
196
+ @lines = LineReader.new(result.split(/\r?\n/))
197
+ end
198
+
199
+ attr_reader :lines
200
+
201
+ def set_options(opts = {})
202
+ @message = opts[:messages] || {}
203
+ @output = opts[:output] || $stdout
204
+ end
205
+
206
+ # Render templates as HTML. Compatibility method for Rublog and
207
+ # Rdoc.
208
+ def write_html_on(op, value_hash, message_hash = {})
209
+ to_html(value_hash, { :output => op, :messages => message_hash })
210
+ end
211
+
212
+ # Render templates as HTML
213
+ def to_html(value_hash, options = {})
214
+ set_options(options)
215
+ esc = proc { |str| CGI.escapeHTML(str) }
216
+ @output << process(value_hash, esc)
217
+ end
218
+
219
+ # Render templates as TeX. Compatibility method for Rublog and
220
+ # Rdoc.
221
+ def write_tex_on(op, value_hash, message_hash = {})
222
+ to_tex(value_hash, { :output => op, :messages => message_hash })
223
+ end
224
+
225
+ # Render templates as TeX
226
+ def to_tex(value_hash, options = {})
227
+ set_options(options)
228
+
229
+ esc = proc do |str|
230
+ str.
231
+ gsub(/&lt;/, '<').
232
+ gsub(/&gt;/, '>').
233
+ gsub(/&amp;/) { '\\&' }.
234
+ gsub(/([$&%\#{}_])/) { "\\#$1" }.
235
+ gsub(/>/, '$>$').
236
+ gsub(/</, '$<$')
237
+ end
238
+ str = ""
239
+
240
+ str << process(value_hash, esc)
241
+ @output << str
242
+ end
243
+
244
+ # Render templates as plain text. Compatibility method for Rublog and
245
+ # Rdoc.
246
+ def write_plain_on(op, value_hash, message_hash = {})
247
+ to_plain(value_hash, { :output => op, :messages => message_hash })
248
+ end
249
+
250
+ # Render templates as plain text.
251
+ def to_plain(value_hash, options = {})
252
+ set_options(options)
253
+ esc = proc {|str| str}
254
+ @output << process(value_hash, esc)
255
+ end
256
+
257
+ # Render the templates. The The <tt>value_hash</tt> contains key/value
258
+ # pairs used to drive the substitution (as described above). The
259
+ # +escaper+ is a proc which will be used to sanitise the contents of the
260
+ # template.
261
+ def process(value_hash, escaper)
262
+ @context = Context.new
263
+ sub(@lines.dup, value_hash, escaper).
264
+ tr("\000", '\\').
265
+ tr(OPEN_TAG, '<').
266
+ tr(CLOSE_TAG, '>')
267
+ end
268
+
269
+ # Substitute a set of key/value pairs into the given template. Keys with
270
+ # scalar values have them substituted directly into the page. Those with
271
+ # array values invoke <tt>substitute_array</tt> (below), which examples a
272
+ # block of the template once for each row in the array.
273
+ #
274
+ # This routine also copes with the <tt>IF:</tt>_key_ directive, removing
275
+ # chunks of the template if the corresponding key does not appear in the
276
+ # hash, and the START: directive, which loops its contents for each value
277
+ # in an array
278
+ def sub(lines, values, escaper)
279
+ @context.push(values)
280
+ skip_to = nil
281
+ result = []
282
+
283
+ while line = lines.read
284
+ mv = line.match(BLOCK_RE)
285
+
286
+ if mv.nil?
287
+ result << expand(line.dup, escaper)
288
+ next
289
+ else
290
+ cmd = mv.captures[0]
291
+ tag = mv.captures[1]
292
+ end
293
+
294
+ case cmd
295
+ when "IF", "IFNOT", "IFNOTBLANK", "IFBLANK"
296
+ raise "#{cmd}: must have a key to test." if tag.nil?
297
+
298
+ val = @context.lookup(tag)
299
+ case cmd # Skip lines if the value is...
300
+ when "IF" # false or +nil+ (not false => true)
301
+ test = (not val)
302
+ when "IFBLANK" # +nil+ or empty
303
+ test = (not (val.nil? or val.empty?))
304
+ when "IFNOT"
305
+ test = val
306
+ when "IFNOTBLANK" #
307
+ test = (val.nil? or val.empty?)
308
+ end
309
+ lines.read_up_to(/^\s*ENDIF:#{tag}/) if test
310
+ when "ENDIF"
311
+ nil
312
+ when "START"
313
+ raise "#{cmd}: must have a key." if tag.nil?
314
+
315
+ body = lines.read_up_to(/^\s*END:#{tag}/)
316
+ inner = @context.lookup(tag)
317
+ raise "unknown tag: #{tag}" unless inner
318
+ raise "not array: #{tag}" unless inner.kind_of?(Array)
319
+ inner.each { |vals| result << sub(body.dup, vals, escaper) }
320
+ result << "" # Append the missing \n
321
+ else
322
+ result << expand(line.dup, escaper)
323
+ end
324
+ end
325
+
326
+ @context.pop
327
+
328
+ result.join("\n")
329
+ end
330
+
331
+ # Given an individual line, we look for %xxx%, %?xxx%, #xxx#, #?xxx#,
332
+ # [:key| xxx :], [?key| stuff ?], [!key| stuff ?] and HREF:ref:name:
333
+ # constructs, substituting as appropriate.
334
+ def expand(line, escaper)
335
+ # Generate a cross-reference if a reference is given. Otherwise, just
336
+ # fill in the name part.
337
+ line = line.gsub(HREF_RE) do
338
+ ref = @context.lookup($1)
339
+ name = @context.find_scalar($2)
340
+
341
+ if ref and not ref.kind_of?(Array)
342
+ %Q(<a href="#{ref}">#{name}</a>)
343
+ else
344
+ name
345
+ end
346
+ end
347
+
348
+ # Look for optional content.
349
+ line = line.gsub(IFLINE_RE) do
350
+ type = $1
351
+ name = $2
352
+ stuff = $3
353
+
354
+ case type
355
+ when '?'
356
+ test = @context.lookup(name)
357
+ when '!'
358
+ test = (not @context.lookup(name))
359
+ end
360
+
361
+ if test
362
+ stuff
363
+ else
364
+ ""
365
+ end
366
+ end
367
+
368
+ # Look for repeating content.
369
+ line = line.gsub(BLOCKLINE_RE) do |match|
370
+ name = $1
371
+ stuff = $2
372
+
373
+ val = @context.lookup(name)
374
+ ss = ""
375
+ case val
376
+ when nil
377
+ nil
378
+ when Fixnum
379
+ val.times { |ii| ss << stuff.sub(/%#{name}%/, "#{ii + 1}") }
380
+ when Range
381
+ val.each { |ii| ss << stuff.sub(/%#{name}%/, "#{ii}") }
382
+ when Array
383
+ if not val.empty? and val[0].kind_of?(Hash)
384
+ val.each do |vv|
385
+ @context.push(vv)
386
+ ss << expand(stuff, escaper)
387
+ @context.pop
388
+ end
389
+ else
390
+ val.each { |ee| ss << stuff.sub(/%#{name}%/, "#{ee}") }
391
+ end
392
+ end
393
+ ss
394
+ end
395
+
396
+ # Substitute in values for #xxx# constructs.
397
+ line = line.gsub(LABEL_RE) do
398
+ mandatory = $1.nil?
399
+ escaped = $2.nil?
400
+ key = $3.intern
401
+ val = @message[key]
402
+
403
+ if val.nil?
404
+ raise "Template error: can't find label '#{key}'." if mandatory
405
+ ""
406
+ else
407
+ val = val.to_s
408
+ val = escaper.call(val) if escaped
409
+ val.tr('\\', "\000")
410
+ end
411
+ end
412
+
413
+ # Substitute in values for %xxx% constructs. This is made complex
414
+ # because the replacement string may contain characters that are
415
+ # meaningful to the regexp (like \1)
416
+ line = line.gsub(VARIABLE_RE) do
417
+ mandatory = $1.nil?
418
+ escaped = $2.nil?
419
+ key = $3
420
+ val = @context.lookup(key)
421
+
422
+ if val.nil?
423
+ raise "Template error: can't find variable '#{key}'." if mandatory
424
+ ""
425
+ else
426
+ val = val.to_s
427
+ val = escaper.call(val) if escaped
428
+ val.tr('\\', "\000")
429
+ end
430
+ end
431
+
432
+ # Substitute DDLB controls:
433
+ DDLB_RES.each do |ddlb|
434
+ line = line.gsub(ddlb[1]) do
435
+ self.send(ddlb[0], Regexp.last_match.captures)
436
+ end
437
+ end
438
+
439
+ line
440
+ rescue Exception => ex
441
+ raise "Error in template: #{ex}\nOriginal line: #{line}\n#{ex.backtrace[0]}"
442
+ end
443
+
444
+ def check(*args)
445
+ value = @context.find_scalar_raw(args[0])
446
+ checked = value ? " checked" : ""
447
+ "<input type=\"checkbox\" name=\"#{name}\"#{checked}>"
448
+ end
449
+
450
+ def vsortddlb(*args)
451
+ ddlb(*(args.dup << true))
452
+ end
453
+
454
+ def ddlb(*args)
455
+ value = @context.find_scalar(args[0]).to_s
456
+ options = @context.lookup(args[1])
457
+ sort_on = args[2] || 0
458
+
459
+ unless options and options.kind_of?(Hash)
460
+ raise "Missing options #{args[1]} for ddlb #{args[0]}."
461
+ end
462
+
463
+ res = %Q(<select name="#{args[0]}">)
464
+
465
+ sorted = options.to_a.sort do |aa, bb|
466
+ if aa[0] == -1
467
+ -1
468
+ elsif bb[0] == -1
469
+ 1
470
+ else
471
+ aa[sort_on] <=> bb[sort_on]
472
+ end
473
+ end
474
+
475
+ sorted.each do |key, val|
476
+ selected = (key.to_s == value) ? " selected" : ""
477
+ res << %Q(<option value="#{key}"#{selected}>#{val}</option>)
478
+ end
479
+ res << "</select>"
480
+ end
481
+
482
+ def date(*args)
483
+ yy = "#{argv[0]}_y"
484
+ mm = "#{argv[0]}_m"
485
+ dd = "#{argv[0]}_d"
486
+ %Q<#{input(yy, 4, 4)}&nbsp;.&nbsp;#{input(mm, 2, 2)}&nbsp;.&nbsp;#{input(dd, 2, 2)}>
487
+ end
488
+
489
+ def radioone(*args)
490
+ radio(*(args.dup << ""))
491
+ end
492
+
493
+ def radio(*args)
494
+ value = @context.find_scalar(argv[0]).to_s
495
+ options = @context.lookup(argv[1])
496
+ br = argv[2] || "<br />"
497
+
498
+ unless options and options.kind_of?(Hash)
499
+ raise "Missing options #{args[1]} for radio #{args[0]}."
500
+ end
501
+
502
+ res = ""
503
+ options.keys.sort.each do |key|
504
+ val = options[key]
505
+ checked = (key.to_s == value) ? " checked" : ""
506
+ res << %Q(<label>
507
+ <input type="radio" name="#{args[0]}"
508
+ value="#{key}"#{checked}">#{val}</label>#{br})
509
+ end
510
+ res
511
+ end
512
+
513
+ def text(*args)
514
+ value = @context.find_scalar(args[0]).to_s
515
+ %Q(<textarea name="#{args[0]}" cols="#{args[1]}" rows="#{args[2]}">
516
+ #{CGI.escapeHTML(value)}
517
+ </textarea>)
518
+ end
519
+
520
+ def pwinput(*args)
521
+ input(*(args.dup << "password"))
522
+ end
523
+
524
+ def input(*args)
525
+ name = args[0]
526
+ value = @context.find_scalar(name).to_s
527
+ width = args[1]
528
+ max = args[2]
529
+ iptype = args[3] || "text"
530
+ %Q(<input type="#{iptype}" name="#{name}" value="#{value}" size="#{width}" maxsize="#{max}">)
531
+ end
532
+
533
+ def popup(*args)
534
+ url = CGI.escapeHTML(@context.find_scalar(args[0]).to_s)
535
+ text = @context.find_scalar(args[1]).to_s
536
+ %Q|<a href="#{url}" target="Popup" class="methodtitle" onClick="popup('#{url}'); return false;">#{text}</a>|
537
+ end
538
+
539
+ def pair(*args)
540
+ label = args[0]
541
+ name = args[1]
542
+ colsp = args[2]
543
+
544
+ value = @context.find_scalar(name).to_s
545
+ value = case value
546
+ when "true" then "Yes"
547
+ when "false" then "No"
548
+ else value
549
+ end
550
+ td = (colsp.nil? or colsp.empty?) ? "<td>" : %Q{<td colspan="#{colsp}">}
551
+ "#{Html.tag(label)}#{td}#{value}</td>"
552
+ end
553
+ end