ruwiki 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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