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.
- data/Readme.rubygems +86 -0
- data/Readme.tarfile +65 -0
- data/bin/ruwiki +58 -0
- data/bin/ruwiki.cgi +87 -0
- data/bin/ruwiki_convert +56 -0
- data/bin/ruwiki_service.rb +82 -0
- data/bin/ruwiki_servlet +53 -0
- data/contrib/enscript-token.rb +55 -0
- data/contrib/rublog_integrator.rb +68 -0
- data/data/Default/ProjectIndex.ruwiki +49 -0
- data/data/Ruwiki/Antispam.ruwiki +65 -0
- data/data/Ruwiki/BugTracking.ruwiki +33 -0
- data/data/Ruwiki/ChangeLog.ruwiki +102 -0
- data/data/Ruwiki/Configuring_Ruwiki.ruwiki +151 -0
- data/data/Ruwiki/Extending_Ruwiki.ruwiki +317 -0
- data/data/Ruwiki/LicenseAndAuthorInfo.ruwiki +30 -0
- data/data/Ruwiki/ProjectIndex.ruwiki +84 -0
- data/data/Ruwiki/Roadmap.ruwiki +225 -0
- data/data/Ruwiki/RuwikiTemplatingLibrary.ruwiki +156 -0
- data/data/Ruwiki/RuwikiUtilities.ruwiki +157 -0
- data/data/Ruwiki/SandBox.ruwiki +9 -0
- data/data/Ruwiki/To_Do.ruwiki +51 -0
- data/data/Ruwiki/TroubleShooting.ruwiki +33 -0
- data/data/Ruwiki/WikiFeatures.ruwiki +17 -0
- data/data/Ruwiki/WikiMarkup.ruwiki +261 -0
- data/data/Tutorial/AddingPages.ruwiki +16 -0
- data/data/Tutorial/AddingProjects.ruwiki +16 -0
- data/data/Tutorial/ProjectIndex.ruwiki +11 -0
- data/data/Tutorial/SandBox.ruwiki +9 -0
- data/data/agents.banned +60 -0
- data/data/agents.readonly +321 -0
- data/data/hostip.banned +30 -0
- data/data/hostip.readonly +28 -0
- data/lib/ruwiki.rb +622 -0
- data/lib/ruwiki/auth.rb +56 -0
- data/lib/ruwiki/auth/gforge.rb +73 -0
- data/lib/ruwiki/backend.rb +318 -0
- data/lib/ruwiki/backend/flatfiles.rb +217 -0
- data/lib/ruwiki/config.rb +244 -0
- data/lib/ruwiki/exportable.rb +192 -0
- data/lib/ruwiki/handler.rb +342 -0
- data/lib/ruwiki/lang/de.rb +339 -0
- data/lib/ruwiki/lang/en.rb +334 -0
- data/lib/ruwiki/lang/es.rb +339 -0
- data/lib/ruwiki/page.rb +262 -0
- data/lib/ruwiki/servlet.rb +38 -0
- data/lib/ruwiki/template.rb +553 -0
- data/lib/ruwiki/utils.rb +24 -0
- data/lib/ruwiki/utils/command.rb +102 -0
- data/lib/ruwiki/utils/converter.rb +297 -0
- data/lib/ruwiki/utils/manager.rb +639 -0
- data/lib/ruwiki/utils/servletrunner.rb +295 -0
- data/lib/ruwiki/wiki.rb +147 -0
- data/lib/ruwiki/wiki/tokens.rb +136 -0
- data/lib/ruwiki/wiki/tokens/00default.rb +211 -0
- data/lib/ruwiki/wiki/tokens/01wikilinks.rb +166 -0
- data/lib/ruwiki/wiki/tokens/02actions.rb +63 -0
- data/lib/ruwiki/wiki/tokens/abbreviations.rb +40 -0
- data/lib/ruwiki/wiki/tokens/calendar.rb +147 -0
- data/lib/ruwiki/wiki/tokens/headings.rb +43 -0
- data/lib/ruwiki/wiki/tokens/lists.rb +112 -0
- data/lib/ruwiki/wiki/tokens/rubylists.rb +48 -0
- data/ruwiki.conf +22 -0
- data/ruwiki.pkg +0 -0
- data/templates/default/body.tmpl +19 -0
- data/templates/default/content.tmpl +7 -0
- data/templates/default/controls.tmpl +23 -0
- data/templates/default/edit.tmpl +27 -0
- data/templates/default/error.tmpl +14 -0
- data/templates/default/footer.tmpl +23 -0
- data/templates/default/ruwiki.css +297 -0
- data/templates/default/save.tmpl +8 -0
- data/templates/sidebar/body.tmpl +19 -0
- data/templates/sidebar/content.tmpl +8 -0
- data/templates/sidebar/controls.tmpl +8 -0
- data/templates/sidebar/edit.tmpl +27 -0
- data/templates/sidebar/error.tmpl +13 -0
- data/templates/sidebar/footer.tmpl +22 -0
- data/templates/sidebar/ruwiki.css +347 -0
- data/templates/sidebar/save.tmpl +10 -0
- data/templates/simple/body.tmpl +13 -0
- data/templates/simple/content.tmpl +7 -0
- data/templates/simple/controls.tmpl +8 -0
- data/templates/simple/edit.tmpl +25 -0
- data/templates/simple/error.tmpl +10 -0
- data/templates/simple/footer.tmpl +10 -0
- data/templates/simple/ruwiki.css +192 -0
- data/templates/simple/save.tmpl +8 -0
- data/tests/harness.rb +52 -0
- data/tests/tc_backend_flatfile.rb +103 -0
- data/tests/tc_bugs.rb +74 -0
- data/tests/tc_exportable.rb +64 -0
- data/tests/tc_template.rb +145 -0
- data/tests/tc_tokens.rb +335 -0
- data/tests/testall.rb +20 -0
- 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
|
+
#++
|
data/lib/ruwiki.rb
ADDED
|
@@ -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{\\<}, '<')
|
|
357
|
+
content.gsub!(%r{\\>}, '>')
|
|
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(/&/, '&').gsub(/</, '<').gsub(/>/, '>')
|
|
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
|