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,295 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#--
|
|
3
|
+
# Ruwiki
|
|
4
|
+
# Copyright � 2002 - 2004, Digikata and HaloStatue
|
|
5
|
+
# Alan Chen (alan@digikata.com)
|
|
6
|
+
# Austin Ziegler (ruwiki@halostatue.ca)
|
|
7
|
+
#
|
|
8
|
+
# Licensed under the same terms as Ruby.
|
|
9
|
+
#
|
|
10
|
+
# This file may be renamed to change the URI for the wiki.
|
|
11
|
+
#
|
|
12
|
+
# $Id: servletrunner.rb,v 1.8 2004/11/26 12:18:47 austin Exp $
|
|
13
|
+
#++
|
|
14
|
+
|
|
15
|
+
# Customize this if you put the RuWiki files in a different location.
|
|
16
|
+
require 'webrick'
|
|
17
|
+
|
|
18
|
+
require 'ruwiki/utils'
|
|
19
|
+
require 'ruwiki/exportable'
|
|
20
|
+
require 'ruwiki/servlet'
|
|
21
|
+
require 'ruwiki/lang/en'
|
|
22
|
+
require 'ruwiki/lang/de'
|
|
23
|
+
require 'ruwiki/lang/es'
|
|
24
|
+
|
|
25
|
+
require 'optparse'
|
|
26
|
+
require 'ostruct'
|
|
27
|
+
|
|
28
|
+
module Ruwiki::Utils::ServletRunner
|
|
29
|
+
COPYRIGHT = <<-"COPYRIGHT"
|
|
30
|
+
Ruwiki #{Ruwiki::VERSION}
|
|
31
|
+
Copyright � 2002 - 2004, Digikata and HaloStatue
|
|
32
|
+
|
|
33
|
+
http://rubyforge.org/projects/ruwiki/
|
|
34
|
+
|
|
35
|
+
Alan Chen (alan@digikata.com)
|
|
36
|
+
Austin Ziegler (ruwiki@halostatue.ca)
|
|
37
|
+
|
|
38
|
+
Licensed under the same terms as Ruby.
|
|
39
|
+
|
|
40
|
+
$Id: servletrunner.rb,v 1.8 2004/11/26 12:18:47 austin Exp $
|
|
41
|
+
COPYRIGHT
|
|
42
|
+
|
|
43
|
+
class WEBrickConfig
|
|
44
|
+
include Ruwiki::Exportable
|
|
45
|
+
|
|
46
|
+
exportable_group 'webrick-config'
|
|
47
|
+
attr_accessor :port
|
|
48
|
+
exportable :port
|
|
49
|
+
attr_accessor :addresses
|
|
50
|
+
exportable :addresses
|
|
51
|
+
attr_accessor :mount
|
|
52
|
+
exportable :mount
|
|
53
|
+
attr_accessor :do_log
|
|
54
|
+
exportable :do_log
|
|
55
|
+
attr_accessor :log_dest
|
|
56
|
+
exportable :log_dest
|
|
57
|
+
attr_accessor :threads
|
|
58
|
+
exportable :threads
|
|
59
|
+
|
|
60
|
+
def export
|
|
61
|
+
hash = super
|
|
62
|
+
sc = hash['webrick-config']
|
|
63
|
+
sc['addresses'] = sc['addresses'].join(";")
|
|
64
|
+
sc['do-log'] = (sc['do-log'] ? 'true' : 'false')
|
|
65
|
+
hash
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Because the servlet can be started from the command-line, provide
|
|
69
|
+
# defaults for all possible configuration options.
|
|
70
|
+
def initialize(exported = {})
|
|
71
|
+
sc = exported['webrick-config'] || {}
|
|
72
|
+
@port = sc['port'] || 8808
|
|
73
|
+
@mount = sc['mount'] || '/'
|
|
74
|
+
@addresses = sc['addresses']
|
|
75
|
+
@do_log = ((sc['do-log'] == 'false') ? false : true)
|
|
76
|
+
@log_dest = sc['log-dest']
|
|
77
|
+
@threads = sc['threads'] || 1
|
|
78
|
+
|
|
79
|
+
if @addresses.nil? or @addresses.empty?
|
|
80
|
+
@addresses = []
|
|
81
|
+
else
|
|
82
|
+
@addresses = @addresses.split(/;/)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
if @log_dest.nil? or @log_dest.empty?
|
|
86
|
+
@log_dest = "<STDERR>"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def self.message=(lang)
|
|
92
|
+
if lang.kind_of?(Hash)
|
|
93
|
+
@message = lang
|
|
94
|
+
elsif "constant" == defined?(lang::Message)
|
|
95
|
+
@message = lang::Message
|
|
96
|
+
else
|
|
97
|
+
raise ArgumentError
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
def self.message(id)
|
|
101
|
+
@message[id]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class << self
|
|
105
|
+
# This is for the WEBrick version of Ruwiki. This has been abstracted to
|
|
106
|
+
# accept a Config global variable to reconfigure Ruwiki after initial
|
|
107
|
+
# creation.
|
|
108
|
+
def read_config(filename)
|
|
109
|
+
ch = {}
|
|
110
|
+
if File.exists?(filename)
|
|
111
|
+
File.open(filename, 'rb') { |ff| ch = Ruwiki::Exportable.load(ff.read) }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
@sc = WEBrickConfig.new(ch)
|
|
115
|
+
@rc = Ruwiki::Config.new(ch)
|
|
116
|
+
|
|
117
|
+
if @rc.webmaster.nil? or @rc.webmaster.empty?
|
|
118
|
+
@rc.webmaster = "webmaster@domain.tld"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def run(argv, input = $stdin, output = $stdout, error = $stderr)
|
|
123
|
+
read_config(Ruwiki::Config::CONFIG_NAME)
|
|
124
|
+
|
|
125
|
+
save_config = nil
|
|
126
|
+
|
|
127
|
+
language = 'en'
|
|
128
|
+
find_lang = argv.grep(%r{^--language})
|
|
129
|
+
find_lang.each do |ee|
|
|
130
|
+
if ee =~ %r{^--language=}
|
|
131
|
+
language = ee.sub(%r{^--language=}, '')
|
|
132
|
+
else
|
|
133
|
+
language = argv[argv.index(ee).succ]
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
require "ruwiki/lang/#{language.downcase}"
|
|
138
|
+
@rc.language = Ruwiki::Lang.const_get(language.upcase)
|
|
139
|
+
self.message = @rc.language
|
|
140
|
+
|
|
141
|
+
argv.options do |oo|
|
|
142
|
+
oo.banner = self.message(:runner_usage) % [ File.basename($0) ]
|
|
143
|
+
oo.separator self.message(:runner_general_options)
|
|
144
|
+
oo.on('--save-config [FILENAME]', *([ self.message(:runner_saveconfig_desc), Ruwiki::Config::CONFIG_NAME ].flatten)) { |fname|
|
|
145
|
+
save_config = fname || Ruwiki::Config::CONFIG_NAME
|
|
146
|
+
}
|
|
147
|
+
oo.on('--config FILENAME', *self.message(:runner_config_desc)) { |fn|
|
|
148
|
+
read_config(fn)
|
|
149
|
+
}
|
|
150
|
+
oo.separator ""
|
|
151
|
+
oo.separator self.message(:runner_webrick_options)
|
|
152
|
+
oo.on('-P', '--port PORT', Numeric, *self.message(:runner_port_desc)) { |port|
|
|
153
|
+
@sc.port = port
|
|
154
|
+
}
|
|
155
|
+
oo.on('-A', '--accept ADDRESS,ADDRESS,ADDRESS', Array, *self.message(:runner_address_desc)) { |address|
|
|
156
|
+
@sc.addresses += address
|
|
157
|
+
}
|
|
158
|
+
oo.on('-L', '--local', *self.message(:runner_local_desc)) {
|
|
159
|
+
@sc.addresses = ["127.0.0.1"]
|
|
160
|
+
}
|
|
161
|
+
oo.on('-M', '--mount MOUNT-POINT', *self.message(:runner_mountpoint_desc)) { |mp|
|
|
162
|
+
@sc.mount = mp
|
|
163
|
+
}
|
|
164
|
+
oo.on('--[no-]log', *self.message(:runner_log_desc)) { |log|
|
|
165
|
+
@sc.do_log = log
|
|
166
|
+
}
|
|
167
|
+
oo.on('--logfile LOGFILE', *self.message(:runner_logfile_desc)) { |lf|
|
|
168
|
+
@sc.log_dest = lf
|
|
169
|
+
}
|
|
170
|
+
oo.on('-T', '--threads THREADS', Integer, *self.message(:runner_threads_desc)) { |tc|
|
|
171
|
+
@sc.threads = tc
|
|
172
|
+
}
|
|
173
|
+
oo.separator ""
|
|
174
|
+
oo.separator self.message(:runner_ruwiki_options)
|
|
175
|
+
oo.on('--language=LANGUAGE', *self.message(:runner_language_desc)) { |lang|
|
|
176
|
+
nil
|
|
177
|
+
}
|
|
178
|
+
oo.on('--webmaster WEBMASTER', *self.message(:runner_webmaster_desc)) { |wm|
|
|
179
|
+
@rc.webmaster = wm
|
|
180
|
+
}
|
|
181
|
+
oo.on('--[no-]debug', *self.message(:runner_debug_desc)) { |dd|
|
|
182
|
+
@rc.debug = dd
|
|
183
|
+
}
|
|
184
|
+
oo.on('--title TITLE', *self.message(:runner_title_desc)) { |tt|
|
|
185
|
+
@rc.title = tt
|
|
186
|
+
}
|
|
187
|
+
oo.on('--default-page PAGENAME', *self.message(:runner_defaultpage_desc)) { |dp|
|
|
188
|
+
@rc.default_page = dp
|
|
189
|
+
}
|
|
190
|
+
oo.on('--default-project PAGENAME', *self.message(:runner_defaultproject_desc)) { |dp|
|
|
191
|
+
@rc.default_project = dp
|
|
192
|
+
}
|
|
193
|
+
oo.on('--template-path TEMPLATE_PATH', *self.message(:runner_templatepath_desc)) { |tp|
|
|
194
|
+
@rc.template_path = tp
|
|
195
|
+
}
|
|
196
|
+
oo.on('--templates TEMPLATES', *self.message(:runner_templatename_desc)) { |tp|
|
|
197
|
+
@rc.template_set = tp
|
|
198
|
+
}
|
|
199
|
+
oo.on('--css CSS_NAME', *self.message(:runner_cssname_desc)) { |css|
|
|
200
|
+
@rc.css = css
|
|
201
|
+
}
|
|
202
|
+
oo.on('--storage-type TYPE', Ruwiki::KNOWN_BACKENDS, *([self.message(:runner_storage_desc), Ruwiki::KNOWN_BACKENDS.join(", ")].flatten)) { |st|
|
|
203
|
+
@rc.storage_type = st
|
|
204
|
+
@rc.storage_options[@rc.storage_type]['data-path'] ||= "./data/"
|
|
205
|
+
@rc.storage_options[@rc.storage_type]['extension'] ||= "ruwiki"
|
|
206
|
+
}
|
|
207
|
+
oo.on('--data-path PATH', *self.message(:runner_datapath_desc)) { |fdp|
|
|
208
|
+
@rc.storage_options['flatfiles']['data-path'] = fdp
|
|
209
|
+
}
|
|
210
|
+
oo.on('--extension EXT', *self.message(:runner_extension_desc)) { |ext|
|
|
211
|
+
@rc.storage_options['flatfiles']['data-path'] = fdp
|
|
212
|
+
}
|
|
213
|
+
if defined?(Gem::Cache)
|
|
214
|
+
oo.separator ""
|
|
215
|
+
oo.on('--central', *self.message(:runner_central_desc)) {
|
|
216
|
+
gempath = Gem::Cache.from_installed_gems.search("ruwiki", "=#{Ruwiki::VERSION}").last.full_gem_path
|
|
217
|
+
@rc.storage_type = 'flatfiles'
|
|
218
|
+
@rc.storage_options['flatfiles']['data-path'] = "#{gempath}/data"
|
|
219
|
+
@rc.storage_options['flatfiles']['extension'] = "ruwiki"
|
|
220
|
+
@rc.storage_options['flatfiles']['format'] = "exportable"
|
|
221
|
+
@rc.template_path = "#{gempath}/templates"
|
|
222
|
+
@rc.template_set = "sidebar"
|
|
223
|
+
}
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# TODO: Add options for time, date, and datetime formats.
|
|
227
|
+
oo.separator ""
|
|
228
|
+
oo.separator self.message(:runner_general_info)
|
|
229
|
+
oo.on_tail('--help', *self.message(:runner_help_desc)) {
|
|
230
|
+
error << oo << "\n"
|
|
231
|
+
return 0
|
|
232
|
+
}
|
|
233
|
+
oo.on_tail('--version', *self.message(:runner_version_desc)) {
|
|
234
|
+
error << COPYRIGHT << "\n"
|
|
235
|
+
return 0
|
|
236
|
+
}
|
|
237
|
+
oo.parse!
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
if save_config
|
|
241
|
+
sc = @sc.export
|
|
242
|
+
rc = @rc.export
|
|
243
|
+
cf = sc.merge(rc)
|
|
244
|
+
|
|
245
|
+
File.open(save_config, 'wb') { |ff| ff.puts Ruwiki::Exportable.dump(cf) }
|
|
246
|
+
return 0
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# If the list of accepted addresses is not empty, provide IP-based
|
|
250
|
+
# restrictions.
|
|
251
|
+
if not @sc.addresses.empty?
|
|
252
|
+
localonly = lambda do |sock|
|
|
253
|
+
if not @sc.addresses.include?(sock.peeraddr[3])
|
|
254
|
+
raise WEBrick::ServerError, self.message(:runner_rejected_address) % [ sock.peeraddr[3], @sc.addresses.join(", ") ]
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
else
|
|
258
|
+
localonly = nil
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
if @sc.do_log
|
|
262
|
+
if "<STDERR>" == @sc.log_dest
|
|
263
|
+
dest = $stderr
|
|
264
|
+
else
|
|
265
|
+
dest = File.open(@sc.log_dest, "wb+")
|
|
266
|
+
end
|
|
267
|
+
logger = WEBrick::Log.new(dest, WEBrick::Log::DEBUG)
|
|
268
|
+
else
|
|
269
|
+
logger = nil
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
banner = self.message(:runner_banner) %
|
|
273
|
+
[ Ruwiki::Utils::ServletRunner::COPYRIGHT, @sc.port,
|
|
274
|
+
@sc.addresses.join(", "), @sc.mount, @sc.do_log, @sc.log_dest,
|
|
275
|
+
@sc.threads, @rc.webmaster, @rc.debug, @rc.title,
|
|
276
|
+
@rc.default_project, @rc.default_page, @rc.template_path,
|
|
277
|
+
@rc.template_set, @rc.css, @rc.storage_type,
|
|
278
|
+
@rc.storage_options[@rc.storage_type]['data-path'],
|
|
279
|
+
@rc.storage_options[@rc.storage_type]['extension'] ]
|
|
280
|
+
|
|
281
|
+
banner.each { |bb| logger.info(bb) } unless logger.nil?
|
|
282
|
+
|
|
283
|
+
server = WEBrick::HTTPServer.new(:Port => @sc.port.to_i,
|
|
284
|
+
:StartThreads => @sc.threads.to_i,
|
|
285
|
+
:AcceptCallback => localonly,
|
|
286
|
+
:Logger => logger)
|
|
287
|
+
@rc.logger = logger
|
|
288
|
+
Ruwiki::Servlet.config = @rc
|
|
289
|
+
|
|
290
|
+
server.mount(@sc.mount, Ruwiki::Servlet)
|
|
291
|
+
trap("INT") { server.shutdown; return }
|
|
292
|
+
server.start
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|
data/lib/ruwiki/wiki.rb
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
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: wiki.rb,v 1.14 2004/11/28 05:27:32 austin Exp $
|
|
10
|
+
#++
|
|
11
|
+
# Ruwiki's Wiki markup class. This will convert the Wiki markup known by
|
|
12
|
+
# Ruwiki (defined by Token classes). The algorithm is as follows:
|
|
13
|
+
#
|
|
14
|
+
# 1. For each known Token class, match each instance of it in the content
|
|
15
|
+
# stream. Replace each instance in the content stream with a Token
|
|
16
|
+
# marker: TOKEN_x or \TOKEN_x, where x is a digit representing the Token.
|
|
17
|
+
# (\TOKEN_x is a special case of token matching. See
|
|
18
|
+
# Ruwiki::Markup::Token for more information.) Store the Token for later
|
|
19
|
+
# processing.
|
|
20
|
+
# 2. Go back through the content, replacing each instance of \TOKEN_x with
|
|
21
|
+
# the Token's defined restore value (which should be the same value as was
|
|
22
|
+
# originally matched).
|
|
23
|
+
# 3. Go through the content, replacing each instance of TOKEN_x with the
|
|
24
|
+
# Token's defined replacement value.
|
|
25
|
+
# 4. Go through the tokens, in reverse, and execute the post replacement
|
|
26
|
+
# routine defined by the Token. (This may be necessary to collapse
|
|
27
|
+
# consecutive HTML structures.)
|
|
28
|
+
# 5. Return the parsed content and the collected metadata.
|
|
29
|
+
#
|
|
30
|
+
# == Tokens
|
|
31
|
+
# Look at Ruwiki::Markup::Token describes how to create Token objects.
|
|
32
|
+
class Ruwiki::Wiki
|
|
33
|
+
def parse(content, project)
|
|
34
|
+
content = clean(content)
|
|
35
|
+
tokens = []
|
|
36
|
+
project ||= @default_project
|
|
37
|
+
|
|
38
|
+
Token.tokenlist.each do |token|
|
|
39
|
+
content.gsub!(token.regexp) do |mm|
|
|
40
|
+
match = Regexp.last_match
|
|
41
|
+
tc = token.new(match, project, @backend, @script, @message, @title)
|
|
42
|
+
tokens << tc
|
|
43
|
+
if mm[0, 1] == '\\'
|
|
44
|
+
"\\TOKEN_#{tokens.size - 1}"
|
|
45
|
+
else
|
|
46
|
+
"TOKEN_#{tokens.size - 1}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
replaced = []
|
|
52
|
+
ss = true
|
|
53
|
+
loop do
|
|
54
|
+
break if replaced.size >= tokens.size
|
|
55
|
+
break if ss.nil?
|
|
56
|
+
ss = content.gsub!(/\\TOKEN_(\d+)/) { |mm|
|
|
57
|
+
match = Regexp.last_match
|
|
58
|
+
itoken = match[1].to_i
|
|
59
|
+
replaced << itoken
|
|
60
|
+
tokens[itoken].restore
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
ss = content.gsub!(/TOKEN_(\d+)/) { |mm|
|
|
64
|
+
match = Regexp.last_match
|
|
65
|
+
itoken = match[1].to_i
|
|
66
|
+
replaced << itoken
|
|
67
|
+
tokens[itoken].replace
|
|
68
|
+
}
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
token_classes = tokens.map { |token| token.class }.sort_by { |token| token.rank }
|
|
72
|
+
token_classes.uniq.each { |tc| tc.post_replace(content) }
|
|
73
|
+
|
|
74
|
+
content
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
attr_accessor :default_project
|
|
78
|
+
attr_accessor :script
|
|
79
|
+
attr_accessor :backend
|
|
80
|
+
attr_accessor :message
|
|
81
|
+
|
|
82
|
+
# Creates the markup class.
|
|
83
|
+
def initialize(default_project, script, title)
|
|
84
|
+
@default_project = default_project
|
|
85
|
+
@script = script
|
|
86
|
+
@title = title
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
# Find HTML tags
|
|
91
|
+
SIMPLE_TAG_RE = %r{<[^<>]+?>} # Ensure that only the tag is grabbed.
|
|
92
|
+
HTML_TAG_RE = %r{\A< # Tag must be at start of match.
|
|
93
|
+
(/)? # Closing tag?
|
|
94
|
+
([\w:]+) # Tag name
|
|
95
|
+
(?:\s+ # Space
|
|
96
|
+
([^>]+) # Attributes
|
|
97
|
+
(/)? # Singleton tag?
|
|
98
|
+
)? # The above three are optional
|
|
99
|
+
>}x
|
|
100
|
+
ATTRIBUTES_RE = %r{([\w:]+)(=(?:\w+|"[^"]+?"|'[^']+?'))?}x
|
|
101
|
+
STYLE_NOVD_RE = %r{(?:\s?(visibility|display):[^'";]+;?)}x
|
|
102
|
+
ALLOWED_ATTR = %w(style title type lang dir class id cite datetime abbr) +
|
|
103
|
+
%w(colspan rowspan compact start media)
|
|
104
|
+
ALLOWED_HTML = %w(abbr acronym address b big blockquote br caption cite) +
|
|
105
|
+
%w(code col colgroup dd del dfn dir div dl dt em h1 h2 h3) +
|
|
106
|
+
%w(h4 h5 h6 hr i ins kbd li menu ol p pre q s samp small) +
|
|
107
|
+
%w(span strike strong style sub sup table tbody td tfoot) +
|
|
108
|
+
%w(th thead tr tt u ul var)
|
|
109
|
+
|
|
110
|
+
# Clean the content of unsupported HTML and attributes. This includes
|
|
111
|
+
# XML namespaced HTML. Sorry, but there's too much possibility for
|
|
112
|
+
# abuse.
|
|
113
|
+
def clean(content)
|
|
114
|
+
content = content.gsub(SIMPLE_TAG_RE) do |tag|
|
|
115
|
+
tagset = HTML_TAG_RE.match(tag)
|
|
116
|
+
|
|
117
|
+
if tagset.nil?
|
|
118
|
+
tag = Ruwiki.clean_entities(tag)
|
|
119
|
+
else
|
|
120
|
+
closer, name, attributes, single = tagset.captures
|
|
121
|
+
|
|
122
|
+
if ALLOWED_HTML.include?(name.downcase)
|
|
123
|
+
unless closer or attributes.nil?
|
|
124
|
+
attributes = attributes.scan(ATTRIBUTES_RE).map do |set|
|
|
125
|
+
if ALLOWED_ATTR.include?(set[0].downcase)
|
|
126
|
+
if set[0] == 'style'
|
|
127
|
+
set[1].gsub!(STYLE_NOVD_RE, '')
|
|
128
|
+
end
|
|
129
|
+
set.join
|
|
130
|
+
else
|
|
131
|
+
nil
|
|
132
|
+
end
|
|
133
|
+
end.compact.join(" ")
|
|
134
|
+
tag = "<#{closer}#{name} #{attributes}#{single}>"
|
|
135
|
+
else
|
|
136
|
+
tag = "<#{closer}#{name}>"
|
|
137
|
+
end
|
|
138
|
+
else
|
|
139
|
+
tag = Ruwiki.clean_entities(tag)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
tag.gsub(%r{((?:href|src)=["'])(#{Ruwiki::Wiki::RE_URI_SCHEME})}) { "#{$1}\\#{$2}" }
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
require 'ruwiki/wiki/tokens'
|
|
@@ -0,0 +1,136 @@
|
|
|
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: tokens.rb,v 1.8 2004/11/26 12:18:47 austin Exp $
|
|
10
|
+
#++
|
|
11
|
+
class Ruwiki
|
|
12
|
+
class Wiki
|
|
13
|
+
# The base Token class. All Token classes must inherit from Token and
|
|
14
|
+
# *must* implement the following methods:
|
|
15
|
+
#
|
|
16
|
+
# [self.regexp] The regular expression that the Token will be
|
|
17
|
+
# replacing.
|
|
18
|
+
# [replace] The mechanism for replacing the Token with the desired
|
|
19
|
+
# results.
|
|
20
|
+
#
|
|
21
|
+
# Token classes <i>should</i> implement the following method:
|
|
22
|
+
# [self.rank] Default: <tt>5000</tt>. Affects the sort order.
|
|
23
|
+
# Must return an integer.
|
|
24
|
+
#
|
|
25
|
+
# Token classes <i>may</i> implement the following methods:
|
|
26
|
+
# [restore] Restores the token without replacement.
|
|
27
|
+
# Implements the results of the escape character.
|
|
28
|
+
# NOTE: each Token class is responsible for its own
|
|
29
|
+
# restore. Tokens that are anchored to the
|
|
30
|
+
# beginning of a line are the most likely to need
|
|
31
|
+
# to reimplement this.
|
|
32
|
+
# [self.post_replace] Performs any necessary massaging of the data. See
|
|
33
|
+
# the implementation of Ruwiki::Wiki::Lists for
|
|
34
|
+
# more information.
|
|
35
|
+
class Token
|
|
36
|
+
@@tokenlist = []
|
|
37
|
+
@@sorted = false
|
|
38
|
+
|
|
39
|
+
class << self
|
|
40
|
+
# Tokens should define rank if they must be first or last in
|
|
41
|
+
# processing. Otherwise, they are sorted in the order defined.
|
|
42
|
+
def rank
|
|
43
|
+
5000
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# The Wiki parsing routine uses Token.tokenlist to determine the
|
|
47
|
+
# tokens that are processed, and the order in which they are
|
|
48
|
+
# processed. See Token.rank for more information.
|
|
49
|
+
def tokenlist
|
|
50
|
+
unless @@sorted
|
|
51
|
+
head = @@tokenlist.shift
|
|
52
|
+
@@tokenlist.sort! { |aa, bb| aa.rank <=> bb.rank }
|
|
53
|
+
@@tokenlist.unshift(head)
|
|
54
|
+
sorted = true
|
|
55
|
+
end
|
|
56
|
+
@@tokenlist
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def inherited(child_class) #:nodoc:
|
|
60
|
+
@@tokenlist << Token if @@tokenlist.empty?
|
|
61
|
+
|
|
62
|
+
# Make the child class post_replace a blank function because we
|
|
63
|
+
# don't want to propogate the currently defined post_replace.
|
|
64
|
+
# The current post_replace is specific to Token_Base only.
|
|
65
|
+
class << child_class
|
|
66
|
+
def self.post_replace(content)
|
|
67
|
+
content
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
@@tokenlist << child_class
|
|
72
|
+
@@sorted = false
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# The replacement regular expression.
|
|
76
|
+
def regexp
|
|
77
|
+
/TOKEN_(\d*)/
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# All Token classes must match this header signature if they define
|
|
82
|
+
# #initialize.
|
|
83
|
+
#
|
|
84
|
+
# [match] The MatchData object for this Token.
|
|
85
|
+
# [project] The project being processed.
|
|
86
|
+
# [backend] The backend for the wiki. This is used to determine if
|
|
87
|
+
# the page or project exists. The object passed must
|
|
88
|
+
# respond to #project_exists?(project) and
|
|
89
|
+
# #page_exists?(page, project).
|
|
90
|
+
# [script] The URI to the script.
|
|
91
|
+
# [message] The message hash for localized messages.
|
|
92
|
+
# [title] The title of the Wiki.
|
|
93
|
+
def initialize(match, project, backend, script, message, title)
|
|
94
|
+
@match = match
|
|
95
|
+
@project = project
|
|
96
|
+
@backend = backend
|
|
97
|
+
@script = script
|
|
98
|
+
@message = message
|
|
99
|
+
@title = title
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# The replacement method. Uses @match to replace the token with the
|
|
103
|
+
# appropriate values.
|
|
104
|
+
def replace
|
|
105
|
+
"TOKEN_#{@match[1]}"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Restores the token without replacement. By default, replaces
|
|
109
|
+
# "dangerous" HTML characters.
|
|
110
|
+
def restore
|
|
111
|
+
Ruwiki.clean_entities(@match[0])
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# The content may need massaging after processing.
|
|
115
|
+
def self.post_replace(content)
|
|
116
|
+
content
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Load the tokens from the ruwiki/wiki/tokens directory.
|
|
123
|
+
tokens_dir = 'ruwiki/wiki/tokens'
|
|
124
|
+
|
|
125
|
+
$LOAD_PATH.each do |path|
|
|
126
|
+
target = "#{path}/#{tokens_dir}"
|
|
127
|
+
if File.exists?(target) and File.directory?(target)
|
|
128
|
+
Dir::glob("#{target}/*.rb") do |token|
|
|
129
|
+
begin
|
|
130
|
+
require token
|
|
131
|
+
rescue LoadError
|
|
132
|
+
nil
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|