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,28 @@
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: hostip.readonly,v 1.1 2004/11/22 04:53:41 austin Exp $
10
+ #
11
+ # This file contains the list of User Agent strings which will be given
12
+ # readonly versions of the wiki instance.
13
+ #
14
+ # This file is in "extended" regular expression format, one optional
15
+ # expression to a line. Spaces are not significant and comments are
16
+ # allowed. If you want to recognise a space in your regular expression, do
17
+ # so either with a character class ([ ]) or the whitespace meta-character
18
+ # (\s). Hash marks must be escaped (\#) or they will be treated as comment
19
+ # markers. Blank or comment-only lines are ignored. All other lines will
20
+ # be joined together:
21
+ #
22
+ # 192\.168\.0\..*
23
+ # 127\.0\.0\..*
24
+ #
25
+ # becomes:
26
+ #
27
+ # %r{192\.168\.0\..*|127\.0\.0\..*}
28
+ #++
@@ -0,0 +1,622 @@
1
+ #--
2
+ # Ruwiki
3
+ # Copyright � 2002 - 2004, Digikata and HaloStatue
4
+ # Alan Chen (alan@digikata.com)
5
+ # Austin Ziegler (austin@halostatue.ca)
6
+ #
7
+ # Licensed under the same terms as Ruby.
8
+ #
9
+ # $Id: ruwiki.rb,v 1.45 2004/11/28 23:28:15 austin Exp $
10
+ #++
11
+
12
+ class Ruwiki
13
+ VERSION = '0.9.0'
14
+ CONTENT_VERSION = 2
15
+ end
16
+
17
+ require 'cgi'
18
+ require 'ruwiki/handler'
19
+ require 'ruwiki/auth'
20
+ require 'ruwiki/template'
21
+ require 'ruwiki/lang/en' # Default to the English language.
22
+ require 'ruwiki/config'
23
+ require 'ruwiki/backend'
24
+ require 'ruwiki/wiki'
25
+ require 'ruwiki/page'
26
+
27
+ # = Ruwiki
28
+ # Ruwiki is a simple, extensible Wiki written in Ruby. It supports both
29
+ # CGI and WEBrick interfaces, templates, and CSS formatting. Additionally,
30
+ # it supports project namespaces, so that two pages can be named the same
31
+ # for differing projects without colliding or having to resort to odd
32
+ # naming conventions. Please see the ::Ruwiki project in the running Wiki
33
+ # for more information. Ruwiki 0.9.0 has German and Spanish translations
34
+ # available.
35
+ #
36
+ # == Quick Start (CGI)
37
+ # 1. Place the Ruwiki directory in a place that your webserver can execute
38
+ # CGI programs and ensure that ruwiki.cgi is executable on your webserver.
39
+ # 2. Point your web browser to the appropriate URL.
40
+ #
41
+ # == Quick Start (WEBrick)
42
+ # 1. Run ruwiki_servlet (ruwiki_servlet.bat under Windows).
43
+ # 2. Point your web browser to <http://localhost:8808/>.
44
+ #
45
+ # == Configuration
46
+ # There are extensive configuration options available. The Ruwiki WEBrick
47
+ # servlet offers command-line options that simplify the configuration of
48
+ # Ruwiki without editing the servlet; use ruwiki_servlet --help for more
49
+ # information.
50
+ #
51
+ # == Copyright
52
+ # Copyright:: Copyright � 2002 - 2004, Digikata and HaloStatue, Ltd.
53
+ # Authors:: Alan Chen (alan@digikata.com)
54
+ # Austin Ziegler (ruwiki@halostatue.ca)
55
+ # Licence:: Ruby's
56
+ class Ruwiki
57
+ ALLOWED_ACTIONS = %w(edit create)
58
+ EDIT_ACTIONS = %w(save cancel)
59
+ EDIT_VARS = %w(newpage version edcomment q)
60
+ RESERVED = ['save', 'preview', 'cancel', EDIT_VARS].flatten
61
+
62
+ # Returns the current configuration object.
63
+ attr_reader :config
64
+ # Returns the current Response object.
65
+ attr_reader :response
66
+ # Returns the current Request object.
67
+ attr_reader :request
68
+ # Returns the current Markup object.
69
+ attr_reader :markup
70
+ # Returns the current Backend object.
71
+ attr_reader :backend
72
+ # Sets the configuration object to a new configuration object.
73
+ def config=(cc)
74
+ raise self.message[:config_not_ruwiki_config] unless cc.kind_of?(Ruwiki::Config)
75
+ @config = cc
76
+ self.config!
77
+ end
78
+
79
+ def config!
80
+ @markup.default_project = @config.default_project
81
+ @markup.message = self.message
82
+ end
83
+
84
+ def load_config(filename)
85
+ @config = Ruwiki::Config.read(filename)
86
+ self.config!
87
+ end
88
+
89
+ # The message hash.
90
+ def message
91
+ @config.message
92
+ end
93
+
94
+ # Initializes Ruwiki.
95
+ def initialize(handler)
96
+ @request = handler.request
97
+ @response = handler.response
98
+
99
+ @config = Ruwiki::Config.new
100
+
101
+ @path_info = @request.determine_request_path || ''
102
+
103
+ @type = nil
104
+ @error = {}
105
+
106
+ @markup = Ruwiki::Wiki.new(@config.default_project,
107
+ @request.script_url,
108
+ @config.title)
109
+ end
110
+
111
+ # Initializes the backend for Ruwiki.
112
+ def set_backend
113
+ @backend = BackendDelegator.new(self, @config.storage_type)
114
+ @markup.backend = @backend
115
+ # Load the blacklists here because I don't as of yet know where else
116
+ # to put them. :(
117
+ @banned_agents = load_blacklist('agents.banned')
118
+ @banned_hostip = load_blacklist('hostip.banned')
119
+ @readonly_agents = load_blacklist('agents.readonly')
120
+ @readonly_hostip = load_blacklist('hostip.readonly')
121
+ end
122
+
123
+ def load_blacklist(filename)
124
+ data = []
125
+ filename = File.join(@config.storage_options[@config.storage_type]['data-path'], filename)
126
+ ii = '^'
127
+ jj = /^#{ii}/o
128
+ File.open(filename, 'rb') do |f|
129
+ f.each do |line|
130
+ line.gsub!(%r{^\s*#.*$}, '')
131
+ line.strip!
132
+ if line.empty?
133
+ data << nil
134
+ else
135
+ if line =~ jj
136
+ data << "(?:#{line}\n)"
137
+ else
138
+ data << line
139
+ end
140
+ end
141
+ end
142
+ end
143
+ data.compact!
144
+ if data.empty?
145
+ nil
146
+ else
147
+ Regexp.new(data.join("|"), Regexp::EXTENDED)
148
+ end
149
+ end
150
+
151
+ def check_useragent
152
+ addr = @request.environment['REMOTE_ADDR']
153
+ user = @request.environment['HTTP_USER_AGENT']
154
+
155
+ if user.nil? or user.empty?
156
+ :forbidden
157
+ elsif @banned_hostip and addr and addr =~ @banned_hostip
158
+ :forbidden
159
+ elsif @banned_agents and user =~ @banned_agents
160
+ :forbidden
161
+ elsif @readonly_hostip and addr and addr =~ @readonly_hostip
162
+ :read_only
163
+ elsif @readonly_agents and user =~ @readonly_agents
164
+ :read_only
165
+ else
166
+ :clean
167
+ end
168
+ end
169
+
170
+ # Runs the steps to process the wiki.
171
+ def run
172
+ @config.verify
173
+ set_backend
174
+ set_page
175
+ process_page
176
+ render
177
+ rescue Exception => ee
178
+ render(:error, ee)
179
+ ensure
180
+ output
181
+ end
182
+
183
+ # Initializes current page for Ruwiki.
184
+ def set_page
185
+ path_info = @path_info.split(%r{/}, -1).map { |ee| ee.empty? ? nil : ee }
186
+
187
+ if path_info.size == 1 or (path_info.size > 1 and path_info[0])
188
+ raise self.message[:invalid_path_info_value] % [@path_info] unless path_info[0].nil?
189
+ end
190
+
191
+ # path_info[0] will ALWAYS be nil.
192
+ path_info.shift
193
+
194
+ case path_info.size
195
+ when 0 # Safety check.
196
+ nil
197
+ when 1 # /PageTopic OR /_edit
198
+ set_page_name_or_action(path_info[0])
199
+ when 2 # /Project/ OR /Project/PageTopic OR /Project/_edit OR /Project/create
200
+ @project = path_info.shift
201
+ set_page_name_or_action(path_info[0])
202
+ else # /Project/PageTopic/_edit OR /Project/diff/3,4 OR something else.
203
+ @project = path_info.shift
204
+ item = path_info.shift
205
+ action = RE_ACTION.match(item)
206
+ if action
207
+ @action = action.captures[0]
208
+ @params = path_info
209
+ else
210
+ @topic = item
211
+ item = path_info.shift
212
+ action = RE_ACTION.match(item)
213
+ if action
214
+ @action = action.captures[0]
215
+ @params = path_info
216
+ end
217
+ end
218
+ end
219
+
220
+ # @request.each_parameter { |key, val| puts "#{key} :: #{val.class}" }
221
+
222
+ @project ||= @config.default_project
223
+ @topic ||= @config.default_page
224
+ end
225
+
226
+ PROJECT_LIST_ITEM = %[%1$s (a href='\\%2$s/%1$s/_topics' class='rw_minilink')%3$s\\</a\\>]
227
+
228
+ # Processes the page through the necessary steps. This is where the edit,
229
+ # save, cancel, and display actions are present.
230
+ def process_page
231
+ content = nil
232
+ formatted = false
233
+
234
+ @page = Ruwiki::Page.new(@backend.retrieve(@topic, @project))
235
+ @type = :content
236
+
237
+ agent_ok = check_useragent
238
+ case agent_ok
239
+ when :read_only
240
+ @page.editable = false
241
+ case @action
242
+ when 'edit', 'save', 'preview', 'cancel', 'search'
243
+ @page.indexable = false
244
+ end
245
+ when :forbidden
246
+ forbidden
247
+ return
248
+ else
249
+ unless @config.auth_mechanism.nil?
250
+ @auth_token = Ruwiki::Auth[@config.auth_mechanism].authenticate(@request, @response, @config.auth_options)
251
+ @page.editable = @auth_token.permissions['edit']
252
+ end
253
+ end
254
+
255
+ # TODO Detect if @action has already been set.
256
+ @action ||= @request.parameters['action'].downcase if @request.parameters['action']
257
+ @action ||= 'save' if @request.parameters['save']
258
+ @action ||= 'cancel' if @request.parameters['cancel']
259
+ @action ||= 'preview' if @request.parameters['preview']
260
+
261
+ unless @page.editable
262
+ case @action
263
+ when 'edit', 'save', 'preview', 'cancel'
264
+ @action = 'show'
265
+ end
266
+ end
267
+
268
+ case @action
269
+ when 'search'
270
+ # get, validate, and cleanse the search string
271
+ # TODO: add empty string rejection.
272
+ srchstr = validate_search_string(@request.parameters['q'])
273
+ if not srchstr.nil?
274
+ srchall = @request.parameters['a']
275
+
276
+ @page.editable = false
277
+ @page.indexable = false
278
+
279
+ @page.content = self.message[:search_results_for] % [srchstr]
280
+ @page.topic = srchstr || ""
281
+
282
+ unless srchall.nil?
283
+ hits = @backend.search_all_projects(srchstr)
284
+ else
285
+ hits = @backend.search_project(@page.project, srchstr)
286
+ end
287
+
288
+ # turn hit hash into content
289
+ hitarr = []
290
+ # organize by number of hits
291
+ hits.each { |key, val| (hitarr[val] ||= []) << key }
292
+
293
+ rhitarr = hitarr.reverse
294
+ maxhits = hitarr.size
295
+ rhitarr.each_with_index do |tarray, rnhits|
296
+ next if tarray.nil? or tarray.empty?
297
+ nhits = maxhits - rnhits - 1
298
+
299
+ if nhits > 0
300
+ @page.content << "\n== #{self.message[:number_of_hits] % [nhits]}\n* "
301
+ @page.content << tarray.join("\n* ")
302
+ end
303
+ end
304
+
305
+ @type = :search
306
+ else
307
+ @sysmessage = self.message[:no_empty_search_string] % [ @page.project, @page.topic ]
308
+ @type = :content
309
+ end
310
+ when 'topics'
311
+ if @backend.project_exists?(@page.project)
312
+ topic_list = @backend.list_topics(@page.project)
313
+ else
314
+ topic_list = []
315
+ end
316
+
317
+ @page.editable = false
318
+
319
+ # todo: make this localized
320
+ if topic_list.empty?
321
+ @page.content = self.message[:no_topics] % [@page.project]
322
+ else
323
+ topic_list.map! do |tt|
324
+ uu = CGI.unescape(tt)
325
+ if (uu != tt) or (tt =~ /^[A-Z][a-z]+$/)
326
+ "[[#{CGI.unescape(tt)}]]"
327
+ else
328
+ tt
329
+ end
330
+ end
331
+ @page.content = <<EPAGE
332
+ = #{self.message[:topics_for_project] % [@page.project]}
333
+ * #{topic_list.join("\n* ")}
334
+ EPAGE
335
+ end
336
+
337
+ @type = :content
338
+ when 'projects'
339
+ proj_list = @backend.list_projects
340
+
341
+ @page.editable = false
342
+
343
+ if proj_list.empty?
344
+ @page.content = self.message[:no_projects]
345
+ else
346
+ # TODO make this localized
347
+ proj_list.map! { |proj| PROJECT_LIST_ITEM % [ proj, @request.script_url, self.message[:project_topics_link] ] }
348
+ @page.content = <<EPAGE
349
+ = #{self.message[:wiki_projects] % [@config.title]}
350
+ * ::#{proj_list.join("\n* ::")}
351
+ EPAGE
352
+ end
353
+
354
+ content = @page.to_html(@markup)
355
+ content.gsub!(%r{\(a href='([^']+)/_topics' class='rw_minilink'\)}, '<a href="\1/_topics" class="rw_minilink">') #'
356
+ content.gsub!(%r{\\&lt;}, '<')
357
+ content.gsub!(%r{\\&gt;}, '>')
358
+ formatted = true
359
+ @type = :content
360
+ when 'edit', 'create'
361
+ # Automatically create the project if it doesn't exist or if the
362
+ # action is 'create'.
363
+ @backend.create_project(@page.project) if @action == 'create'
364
+ @backend.create_project(@page.project) unless @backend.project_exists?(@page.project)
365
+ @page.creator = @auth_token.name if @action == 'create' and @auth_token
366
+ @page.indexable = false
367
+ @lock = @backend.obtain_lock(@page, @request.environment['REMOTE_ADDR']) rescue nil
368
+
369
+ if @lock.nil?
370
+ @type = :content
371
+ @sysmessage = self.message[:page_is_locked]
372
+ else
373
+ content = nil
374
+ formatted = true
375
+ @type = :edit
376
+ end
377
+ when 'save', 'preview'
378
+ np = @request.parameters['newpage'].gsub(/\r/, '').chomp
379
+ @page.topic = @request.parameters['topic']
380
+ @page.project = @request.parameters['project']
381
+ @page.editor_ip = @request.environment['REMOTE_ADDR']
382
+ @page.indexable = false
383
+
384
+ save_ver = @backend.retrieve(@page.topic, @page.project)['properties']['version'].to_i
385
+ sent_ver = @request.parameters['version'].to_i
386
+
387
+ if sent_ver < save_ver
388
+ @type = :edit
389
+
390
+ np = np.split($/)
391
+ content_diff = Diff::LCS.sdiff(np, @page.content.split($/), Diff::LCS::ContextDiffCallbacks)
392
+ content_diff.reverse_each do |hunk|
393
+ case hunk
394
+ when Array
395
+ hunk.reverse_each do |diff|
396
+ case diff.action
397
+ when '+'
398
+ # np.insert(diff.old_position, "+#{diff.new_element}")
399
+ np.insert(diff.old_position, "#{diff.new_element}")
400
+ when '-'
401
+ np.delete_at(diff.old_position)
402
+ # np[diff.old_position] = "-#{diff.old_element}"
403
+ when '!'
404
+ np[diff.old_position] = "-#{diff.old_element}"
405
+ np.insert(diff.old_position + 1, "+#{diff.new_element}")
406
+ end
407
+ end
408
+ when Diff::LCS::ContextChange
409
+ case hunk.action
410
+ when '+'
411
+ np.insert(hunk.old_position, "#{hunk.new_element}")
412
+ # np.insert(hunk.old_position, "+#{hunk.new_element}")
413
+ when '-'
414
+ np.delete_at(hunk.old_position)
415
+ # np[diff.old_position] = "-#{hunk.old_element}"
416
+ when '!'
417
+ np[hunk.old_position] = "-#{hunk.old_element}"
418
+ np.insert(hunk.old_position + 1, "+#{hunk.new_element}")
419
+ end
420
+ end
421
+ end
422
+ @page.content = np.join("\n")
423
+
424
+ edc = @request.parameters['edcomment']
425
+ unless (edc.nil? or edc.empty? or edc == "*")
426
+ @page.edit_comment = edc
427
+ end
428
+
429
+ @sysmessage = self.message[:not_editing_current_version] % [ @page.project, @page.topic ]
430
+ else
431
+ if @action == 'save'
432
+ @page.editor = @auth_token.name if @auth_token
433
+ op = @page.content
434
+ else
435
+ op = nil
436
+ end
437
+
438
+ if (np == op) and (@action == 'save')
439
+ @type = :content
440
+ else
441
+ @page.content = np
442
+ edc = @request.parameters['edcomment']
443
+ unless (edc.nil? or edc.empty? or edc == "*")
444
+ @page.edit_comment = edc
445
+ end
446
+
447
+ if @action == 'save'
448
+ @type = :save
449
+ @page.version = @request.parameters['version'].to_i + 1
450
+ @backend.store(@page)
451
+
452
+ # hack to ensure that Recent Changes are updated correctly
453
+ if @page.topic == 'RecentChanges'
454
+ recent = Ruwiki::Page.new(@backend.retrieve(@page.topic, @page.project))
455
+ @page.content = recent.content
456
+ end
457
+
458
+ @backend.release_lock(@page, @request.environment['REMOTE_ADDR'])
459
+ else
460
+ @type = :preview
461
+ @lock = @backend.obtain_lock(@page, @request.environment['REMOTE_ADDR'])
462
+ content = nil
463
+ formatted = true
464
+ end
465
+ end
466
+ end
467
+ when 'cancel'
468
+ # @page.topic = @request.parameters['topic']
469
+ # @page.project = @request.parameters['project']
470
+ # @page.version = @request.parameters['version'].to_i
471
+
472
+ @backend.release_lock(@page, @request.environment['REMOTE_ADDR'])
473
+ @type = :content
474
+ else
475
+ # TODO AZ: This should probably return a 501 Not Implemented or some
476
+ # other error unless @action.nil?
477
+ nil
478
+ end
479
+ content = @page.to_html(@markup) if not formatted
480
+ rescue Exception => ee # rescue for def process_page
481
+ @type = :error
482
+ if ee.kind_of?(Ruwiki::Backend::BackendError)
483
+ name = "#{self.message[:error]}: #{ee.to_s}"
484
+ else
485
+ name = "#{self.message[:complete_utter_failure]}: #{ee.to_s}"
486
+ end
487
+ @error[:name] = CGI.escapeHTML(name)
488
+ @error[:backtrace] = ee.backtrace.map { |el| CGI.escapeHTML(el) }.join("<br />\n")
489
+ content = nil
490
+ ensure
491
+ @content = content
492
+ end # def process_page
493
+
494
+ # Renders the page.
495
+ def render(*args)
496
+ if args.empty?
497
+ type = @type
498
+ error = @error
499
+ else
500
+ raise ArgumentError, self.message[:render_arguments] unless args.size == 2
501
+ type = args[0]
502
+ error = {
503
+ :name => Ruwiki.clean_entities(args[1].inspect),
504
+ :backtrace => args[1].backtrace.join("<br />\n")
505
+ }
506
+ @page = Ruwiki::Page.new(Ruwiki::Page::NULL_PAGE)
507
+ end
508
+
509
+ @rendered_page = ""
510
+ values = {
511
+ "css_link" => @config.css_link,
512
+ "home_link" => %Q(<a href="#{@request.script_url}">#{@config.title}</a>),
513
+ "cgi_url" => @request.script_url,
514
+ "content" => @content,
515
+ }
516
+
517
+ if @page.nil?
518
+ values["page_project"] = ""
519
+ values["page_raw_topic"] = ""
520
+ values["page_topic"] = ""
521
+ values["editable"] = false
522
+ values["indexable"] = false
523
+ else
524
+ values["page_project"] = @page.project
525
+ values["page_raw_topic"] = @page.topic
526
+ values["page_topic"] = CGI.unescape(@page.topic)
527
+ values["editable"] = @page.editable
528
+ values["indexable"] = @page.indexable
529
+ end
530
+
531
+ values["url_project"] = %Q(#{values["cgi_url"]}/#{values["page_project"]})
532
+ values["url_topic_search"] = %Q(#{values["url_project"]}/_search?q=#{values["page_topic"]})
533
+ values["link_topic_search"] = %Q(<a href='#{values["url_topic_search"]}'><strong>#{values["page_topic"]}</strong></a>)
534
+ values["message"] = @sysmessage unless @sysmessage.nil?
535
+
536
+ case type
537
+ when :content, :save, :search
538
+ values["wiki_title"] = "#{self.message[:error]} - #{@config.title}" if @page.nil?
539
+ values["wiki_title"] ||= "#{@page.project}::#{CGI.unescape(@page.topic)} - #{@config.title}"
540
+ values["label_topic_or_search"] = self.message[:label_topic]
541
+ values["page_topic_name"] = values["page_topic"]
542
+ if type == :content or type == :search
543
+ template = TemplatePage.new(@config.template(:body), @config.template(:content), @config.template(:controls), @config.template(:footer))
544
+ if type == :search
545
+ values["label_topic_or_search"] = self.message[:label_search]
546
+ else
547
+ values["page_topic"] = values["link_topic_search"]
548
+ end
549
+ else
550
+ # action type was save
551
+ values["page_topic"] = values["link_topic_search"]
552
+ template = TemplatePage.new(@config.template(:body), @config.template(:save), @config.template(:controls), @config.template(:footer))
553
+ end
554
+ when :edit, :preview
555
+ template = TemplatePage.new(@config.template(:body), @config.template(:edit))
556
+ values["wiki_title"] = "#{self.message[:editing]}: #{@page.project}::#{CGI.unescape(@page.topic)} - #{@config.title}"
557
+ values["page_content"] = @page.content
558
+ values["page_version"] = @page.version.to_s
559
+ values["unedited_page_content"] = @page.to_html(@markup)
560
+ values["pre_page_content"] = CGI.escapeHTML(@page.content)
561
+ if @request.parameters["edcomment"].nil? or @request.parameters["edcomment"].empty?
562
+ values["edit_comment"] = "*"
563
+ else
564
+ values["edit_comment"] = @request.parameters["edcomment"]
565
+ end
566
+ when :error
567
+ template = TemplatePage.new(@config.template(:body), @config.template(:error))
568
+ values["wiki_title"] = "#{self.message[:error]} - #{@config.title}"
569
+ values["name"] = error[:name]
570
+ values["backtrace"] = error[:backtrace]
571
+ values["backtrace_email"] = error[:backtrace].gsub(/<br \/>/, '')
572
+ values["webmaster"] = @config.webmaster
573
+ end
574
+
575
+ template.to_html(values, :messages => @config.message,
576
+ :output => @rendered_page)
577
+ end
578
+
579
+ # Outputs the page.
580
+ def output
581
+ return if @response.written?
582
+ # if @request.environment["HTTP_ACCEPT"] =~ %r{application/xhtml\+xml}
583
+ # @response.add_header("Content-type", "application/xhtml+xml")
584
+ # else
585
+ @response.add_header("Content-type", "text/html")
586
+ # end
587
+ @response.add_header("Cache-Control", "max_age=0")
588
+ @response.write_headers
589
+ @response << @rendered_page
590
+ end
591
+
592
+ def forbidden
593
+ protocol = @request.environment["SERVER_PROTOCOL"] || "HTTP/1.0"
594
+ @response.write_status "#{protocol} 403 FORBIDDEN\nDate: #{CGI::rfc1123_date(Time.now)}\n\n"
595
+ end
596
+
597
+ # nil if string is invalid
598
+ def validate_search_string(instr)
599
+ return nil if instr.empty?
600
+
601
+ modstr = instr.dup
602
+
603
+ #TODO: add validation of modstr
604
+ return modstr
605
+ end
606
+
607
+ def self.clean_entities(data)
608
+ data.gsub(/&/, '&amp;').gsub(/</, '&lt;').gsub(/>/, '&gt;')
609
+ end
610
+
611
+ private
612
+ RE_ACTION = %r{^_([[:lower:]]+)$}
613
+
614
+ def set_page_name_or_action(item)
615
+ action = RE_ACTION.match(item)
616
+ if action
617
+ @action = action.captures[0]
618
+ else
619
+ @topic = item
620
+ end
621
+ end
622
+ end