gf-Soks 1.0.4
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/LICENSE.txt +66 -0
- data/README.txt +64 -0
- data/bin/soks-create-wiki.rb +193 -0
- data/contrib/diff/lcs.rb +1105 -0
- data/contrib/diff/lcs/array.rb +21 -0
- data/contrib/diff/lcs/block.rb +51 -0
- data/contrib/diff/lcs/callbacks.rb +322 -0
- data/contrib/diff/lcs/change.rb +169 -0
- data/contrib/diff/lcs/hunk.rb +257 -0
- data/contrib/diff/lcs/ldiff.rb +226 -0
- data/contrib/diff/lcs/string.rb +19 -0
- data/contrib/diff_licence.txt +76 -0
- data/contrib/easyprompt.rb +58 -0
- data/contrib/easyprompt_licence.txt +504 -0
- data/contrib/redcloth-3.0.3.rb +1113 -0
- data/contrib/redcloth_license.txt +27 -0
- data/lib/authenticators.rb +121 -0
- data/lib/helpers/counter-helpers.rb +132 -0
- data/lib/helpers/default-helpers.rb +416 -0
- data/lib/helpers/mail2wiki-helper.rb +105 -0
- data/lib/helpers/maintenance-helpers.rb +149 -0
- data/lib/helpers/rss2wiki-helper.rb +47 -0
- data/lib/helpers/wiki2html.rb +60 -0
- data/lib/soks-model.rb +271 -0
- data/lib/soks-servlet.rb +177 -0
- data/lib/soks-storage.rb +187 -0
- data/lib/soks-upgrade-0.0.2.rb +70 -0
- data/lib/soks-utils.rb +327 -0
- data/lib/soks-view.rb +399 -0
- data/lib/soks.rb +27 -0
- data/rakefile +109 -0
- data/templates/default/attachment/favicon.ico +0 -0
- data/templates/default/attachment/logo.jpg +0 -0
- data/templates/default/attachment/logo.png +0 -0
- data/templates/default/attachment/logo.tiff +0 -0
- data/templates/default/attachment/newpage.js +41 -0
- data/templates/default/attachment/print_stylesheet.css +2 -0
- data/templates/default/attachment/robots.txt +6 -0
- data/templates/default/attachment/rss.png +0 -0
- data/templates/default/attachment/stylesheet.css +219 -0
- data/templates/default/banned_titles.txt +67 -0
- data/templates/default/caches/readme.txt +1 -0
- data/templates/default/content/Api%20for%20classes%20to%20modify%20the%20wiki.textile +30 -0
- data/templates/default/content/Author.textile +16 -0
- data/templates/default/content/Automatic%20Summaries.textile +40 -0
- data/templates/default/content/Automatic%20counters.textile +22 -0
- data/templates/default/content/Automatic%20exporters.textile +23 -0
- data/templates/default/content/Automatic%20importers.textile +59 -0
- data/templates/default/content/Automatic%20linking.textile +7 -0
- data/templates/default/content/Automatic%20maintenance%20helpers.textile +39 -0
- data/templates/default/content/Bug%3A%20Competing%20edits.textile +22 -0
- data/templates/default/content/Bug%3A%20Does%20not%20make%20use%20of%20if%2Dmodified%2Dsince%20r.textile +3 -0
- data/templates/default/content/Bug%3A%20Email%20adresses%20in%20page%20titles%20cause%20incorrec.textile +3 -0
- data/templates/default/content/Bug%3A%20GEM%20limits%20title%20lengths.textile +3 -0
- data/templates/default/content/Bug%3A%20Memory%20leak.textile +13 -0
- data/templates/default/content/Bug%3A%20Page%2Einserted%5Finto%20is%20never%20purged.textile +17 -0
- data/templates/default/content/Bug%3A%20Pages%20that%20link%20here%20may%20not%20appear%20on%20r.textile +13 -0
- data/templates/default/content/Bug%3A%20Textile%20mishandles%20paragraphs.textile +37 -0
- data/templates/default/content/Bug%3A%20Unanticipated%20Rollbacks.textile +23 -0
- data/templates/default/content/Bug%3A%20notextile%20does%20not%20prevent%20page%20inserts.textile +3 -0
- data/templates/default/content/Home%20Page.textile +22 -0
- data/templates/default/content/How%20to%20administrate%20this%20wiki.textile +57 -0
- data/templates/default/content/How%20to%20change%20the%20way%20this%20wiki%20looks.textile +32 -0
- data/templates/default/content/How%20to%20export%20a%20site%20from%20this%20wiki.textile +82 -0
- data/templates/default/content/How%20to%20get%20the%20latest%20Soks%20from%20cvs.textile +45 -0
- data/templates/default/content/How%20to%20hack%20soks.textile +66 -0
- data/templates/default/content/How%20to%20import%20a%20site%20from%20instiki.textile +15 -0
- data/templates/default/content/How%20to%20import%20data.textile +41 -0
- data/templates/default/content/How%20to%20install%20Soks.textile +33 -0
- data/templates/default/content/How%20to%20password%20protect%20your%20wiki.textile +53 -0
- data/templates/default/content/How%20to%20re%2Dbuild%20the%20page%20cache.textile +71 -0
- data/templates/default/content/How%20to%20report%20a%20bug.textile +9 -0
- data/templates/default/content/How%20to%20upgrade%20soks.textile +32 -0
- data/templates/default/content/How%20to%20use%20the%20Automatic%20Helper%20classes.textile +12 -0
- data/templates/default/content/How%20to%20use%20this%20wiki.textile +30 -0
- data/templates/default/content/List%20of%20changes.textile +10 -0
- data/templates/default/content/News%3A%20Version%201%2D0%2D0%20released.textile +19 -0
- data/templates/default/content/News%3A%20Version%201%2D0%2D1%20released.textile +12 -0
- data/templates/default/content/Pages%20to%20include%20in%20the%20distribution.textile +55 -0
- data/templates/default/content/Per%20Wiki%20Templates.textile +37 -0
- data/templates/default/content/Picture%20of%20a%20pair%20of%20soks.textile +1 -0
- data/templates/default/content/Planned%20Features.textile +74 -0
- data/templates/default/content/README.textile +64 -0
- data/templates/default/content/RSS%20feed.textile +9 -0
- data/templates/default/content/Recent%20changes%20to%20this%20site.textile +352 -0
- data/templates/default/content/SOKS%20features.textile +19 -0
- data/templates/default/content/Sidebar%20Page.textile +6 -0
- data/templates/default/content/Site%20Index.textile +241 -0
- data/templates/default/content/Soks%27s%20Licence.textile +66 -0
- data/templates/default/content/Tag%3A%20Include%20this%20page%20in%20the%20distribution.textile +6 -0
- data/templates/default/start.rb +90 -0
- data/templates/default/version.txt +1 -0
- data/templates/default/views/Page_content.rhtml +1 -0
- data/templates/default/views/Page_edit.rhtml +79 -0
- data/templates/default/views/Page_find.rhtml +35 -0
- data/templates/default/views/Page_linksfromrss.rhtml +24 -0
- data/templates/default/views/Page_listrss.rhtml +46 -0
- data/templates/default/views/Page_meta.rhtml +44 -0
- data/templates/default/views/Page_print.rhtml +6 -0
- data/templates/default/views/Page_revision.rhtml +39 -0
- data/templates/default/views/Page_revisions.rhtml +36 -0
- data/templates/default/views/Page_rss.rhtml +57 -0
- data/templates/default/views/Page_view.rhtml +8 -0
- data/templates/default/views/UploadPage_edit.rhtml +63 -0
- data/templates/default/views/frame.rhtml +63 -0
- data/templates/default/views/messages.yaml +7 -0
- data/test/html/2006Mar.html +66 -0
- data/test/html/poignant.html +36 -0
- data/test/html/poignant.textile +36 -0
- data/test/mock-objects.rb +69 -0
- data/test/test_counter-helper.rb +162 -0
- data/test/test_soks-helper-maintenance.rb +106 -0
- data/test/test_soks-helpers.rb +145 -0
- data/test/test_soks-model.rb +144 -0
- data/test/test_soks-servlet.rb +240 -0
- data/test/test_soks-storage.rb +108 -0
- data/test/test_soks-utils.rb +226 -0
- data/test/test_soks-view.rb +193 -0
- data/test/test_soks.rb +9 -0
- metadata +182 -0
data/lib/soks-view.rb
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
# Extend the model classes to provide view functions
|
|
2
|
+
class Page
|
|
3
|
+
def textile( view = nil )
|
|
4
|
+
return "[[#{$MESSAGES[:Create]} #{name} => /edit/#{name} ]]" if empty?
|
|
5
|
+
content
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class ImagePage
|
|
10
|
+
def textile( view )
|
|
11
|
+
return "[[#{$MESSAGES[:Create]} #{name} => /edit/#{name} ]]" if empty?
|
|
12
|
+
return content if deleted?
|
|
13
|
+
"!#{view.file2(content)}!:#{view.url(name)}"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class AttachmentPage
|
|
18
|
+
def textile( view )
|
|
19
|
+
return "[[#{$MESSAGES[:Create]} #{name} => /edit/#{name} ]]" if empty?
|
|
20
|
+
return content if deleted?
|
|
21
|
+
%Q{[[ #{name} => #{view.file2(content)} ]]\n}
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# This module is used to extend each erb page to provide helper methods
|
|
26
|
+
module ErbHelper
|
|
27
|
+
|
|
28
|
+
attr_accessor :name
|
|
29
|
+
attr_accessor :description
|
|
30
|
+
attr_accessor :author_to_email_conversion
|
|
31
|
+
|
|
32
|
+
def url( name, view = 'view', query = '' )
|
|
33
|
+
return unless name
|
|
34
|
+
"#{root_url}/#{view}/#{url_name_for_page_name(name)}#{query}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def file( name )
|
|
38
|
+
"#{root_url}/attachment/#{name}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Will refactor this out eventually and merge with above
|
|
42
|
+
def file2( name )
|
|
43
|
+
name = name[1..-1] if name[0,1] == '/'
|
|
44
|
+
"#{root_url}/#{name.strip}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# This class records all the links between pages
|
|
49
|
+
class Links
|
|
50
|
+
|
|
51
|
+
attr_reader :links
|
|
52
|
+
|
|
53
|
+
def initialize
|
|
54
|
+
@links = Hash.new
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def links_from( page ) ; return @links[ page ] ; end
|
|
58
|
+
|
|
59
|
+
def set_links_from( page, linkarray )
|
|
60
|
+
|
|
61
|
+
page.links_lock.synchronize do
|
|
62
|
+
page.links_from = @links[ page ] = linkarray.uniq
|
|
63
|
+
set_links_to( page )
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
page.links_from.each { |linked_page|
|
|
67
|
+
linked_page.links_lock.synchronize do
|
|
68
|
+
set_links_to( linked_page )
|
|
69
|
+
end
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def set_links_to( thispage )
|
|
76
|
+
linksto = Array.new
|
|
77
|
+
@links.each do | pagefrom, pagesto |
|
|
78
|
+
next if pagefrom == thispage
|
|
79
|
+
next if linksto.include? pagefrom
|
|
80
|
+
linksto << pagefrom if pagesto.include? thispage
|
|
81
|
+
end
|
|
82
|
+
thispage.links_to = linksto.sort
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class BruteMatch
|
|
87
|
+
|
|
88
|
+
IGNORE_CASE = true
|
|
89
|
+
|
|
90
|
+
def initialize
|
|
91
|
+
@matches = Hash.new
|
|
92
|
+
@titles = Array.new #sorted array for speed
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def []( title ) @matches[ lower_case(title) ] end
|
|
96
|
+
alias :object_for :[]
|
|
97
|
+
|
|
98
|
+
def delete( title )
|
|
99
|
+
@matches.delete( title )
|
|
100
|
+
update_titles
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Use this to add a string to match and an associated object to return
|
|
104
|
+
# if an object is matched.
|
|
105
|
+
def []=( title, object )
|
|
106
|
+
@matches[lower_case(title)] = object
|
|
107
|
+
update_titles
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def match( text, do_not_match = [] )
|
|
111
|
+
do_not_match = do_not_match.map { |title| lower_case(title) }
|
|
112
|
+
@titles.each do |title,regexp,page|
|
|
113
|
+
next if do_not_match.include? title
|
|
114
|
+
text.gsub!( regexp ) { |match| "#{$1}#{yield $2, page}#{$3}" }
|
|
115
|
+
end
|
|
116
|
+
text
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
# The title, a regexp to match for the title, and the page are stored in a sorted array
|
|
122
|
+
def update_titles
|
|
123
|
+
@titles = @matches.keys.sort_by { |title| title.length }.reverse.map do |title|
|
|
124
|
+
[ title, /(^|\W)(#{Regexp.escape( title )})(\W|$)/i, @matches[ title ] ]
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def lower_case( text )
|
|
129
|
+
IGNORE_CASE ? text.downcase : text
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# This adds some extra match types to (a bodged version of) RedCloth
|
|
134
|
+
#
|
|
135
|
+
# Specifically:
|
|
136
|
+
# * Inserting other pages
|
|
137
|
+
# * Square bracketed wiki links
|
|
138
|
+
# * Automaticlly links anytime the title of another page appears in the text
|
|
139
|
+
# * Automatically links things that look like email addresses and urls
|
|
140
|
+
class WikiRedCloth < RedCloth
|
|
141
|
+
|
|
142
|
+
RULES = [:refs_soks_bracketed_link, :refs_textile, :block_textile_table, :block_textile_lists,:block_textile_prefix, :inline_textile_image, :inline_textile_link, :inline_textile_code, :inline_soks, :inline_textile_glyphs, :inline_textile_span, :refs_markdown, :block_markdown_setext, :block_markdown_atx, :block_markdown_rule, :block_markdown_bq, :block_markdown_lists, :inline_markdown_reflink, :inline_markdown_link ]
|
|
143
|
+
|
|
144
|
+
def initialize( wiki, page, string, hard_breaks = false )
|
|
145
|
+
@wiki, @view, @page = wiki, wiki, page
|
|
146
|
+
@internal_links_from_page = []
|
|
147
|
+
super(insert_sub_strings( string.dup ),[:no_span_caps])
|
|
148
|
+
self.hard_breaks = hard_breaks
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def to_html
|
|
152
|
+
super( *RULES ).to_s
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def inline_soks( text )
|
|
156
|
+
hide_html_links text
|
|
157
|
+
hide_html_tags text
|
|
158
|
+
inline_soks_external_link text
|
|
159
|
+
inline_soks_automatic_link text
|
|
160
|
+
unhide text
|
|
161
|
+
@wiki.links.set_links_from( @page, @internal_links_from_page )
|
|
162
|
+
text
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private
|
|
166
|
+
|
|
167
|
+
def hide_html_links( text )
|
|
168
|
+
text.gsub!(/<a.*?<\/a.*?>/i) { |m| hide m }
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def hide_html_tags( text )
|
|
172
|
+
text.gsub!(/<.*?>/m) { |m| hide m }
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def unhide( text )
|
|
176
|
+
hidden.each_with_index do |r, i|
|
|
177
|
+
text.gsub!( / --!!#{ i + 1 }!!-- /, r )
|
|
178
|
+
end
|
|
179
|
+
text
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def hidden
|
|
183
|
+
@hidden ||= []
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def hide( text )
|
|
187
|
+
hidden << text
|
|
188
|
+
" --!!#{hidden.length}!!-- "
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def insert_sub_strings( text, count = 0 )
|
|
192
|
+
return text if count > 5 # Stops us getting locked into a cycle if people mess up the insert
|
|
193
|
+
text.gsub!(/\[\[\s*(insert (.+?))\s*\]\]/i) do |match|
|
|
194
|
+
if @wiki.exists? $1 # So we don't accidentlaly match a page whose name starts 'insert'
|
|
195
|
+
match
|
|
196
|
+
else
|
|
197
|
+
inserted_page = @wiki.page( $2 )
|
|
198
|
+
@internal_links_from_page << inserted_page if @wiki.exists? inserted_page.name
|
|
199
|
+
inserted_page.is_inserted_into( @page )
|
|
200
|
+
insert_sub_strings( "#{inserted_page.textile(@view)}\n", count + 1 )
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
text
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def inline_soks_external_link( text )
|
|
207
|
+
text.gsub!(/http:\/\/\S*\w\/?/i) { |m| link m }
|
|
208
|
+
text.gsub!(/https:\/\/\S*\w\/?/i) { |m| link m }
|
|
209
|
+
text.gsub!(/www\.\S*\w\/?/i) { |m| link( "http://#{m}", m) }
|
|
210
|
+
text.gsub!(/[A-Za-z0-9.-]+?@[A-Za-z0-9.-]*[A-Za-z]/) { |m| link( "mailto:#{m}", m) }
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def refs_soks_bracketed_link( text )
|
|
214
|
+
text.gsub!(/\[\[\s*(.*?)\s*(|=>\s*(.*?)\s*)\]\]/) do |m|
|
|
215
|
+
title, pagename = $1, $3
|
|
216
|
+
pagename ||= title
|
|
217
|
+
case pagename
|
|
218
|
+
|
|
219
|
+
# http://soks.rubyforge.org/index
|
|
220
|
+
when /^http(s)?:\/\//i ; link(pagename,title)
|
|
221
|
+
|
|
222
|
+
# www.soks.org
|
|
223
|
+
when /^www\./i ; link("http://#{pagename}", title )
|
|
224
|
+
|
|
225
|
+
# tamc@soks.org
|
|
226
|
+
when /[A-Za-z0-9.]+?@[A-Za-z0-9.]+/ ; link("mailto:#{pagename}",title)
|
|
227
|
+
|
|
228
|
+
# /revision/Home page?revision=10
|
|
229
|
+
when %r{^/(\w+)/(.+?)(\?\w+=\w+)$}
|
|
230
|
+
@internal_links_from_page << @wiki.page($2) if @wiki.exists?($2)
|
|
231
|
+
link( @view.url($2,$1,$3), title, @wiki.exists?($2) ? '':'missing' )
|
|
232
|
+
|
|
233
|
+
# /edit/a new page
|
|
234
|
+
when %r{^/(\w+)/(.+)}
|
|
235
|
+
@internal_links_from_page << @wiki.page($2) if @wiki.exists?($2)
|
|
236
|
+
link( @view.url($2,$1), title, @wiki.exists?($2) ? '':'missing' )
|
|
237
|
+
|
|
238
|
+
# the name of a page
|
|
239
|
+
else
|
|
240
|
+
@internal_links_from_page << @wiki.page(pagename) if @wiki.exists? pagename
|
|
241
|
+
link( @view.url( pagename ), title, @wiki.exists?(pagename) ? '':'missing' )
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def inline_soks_automatic_link( text )
|
|
247
|
+
@wiki.rollingmatch.match( text, [@page.name] ) do |title, page|
|
|
248
|
+
@internal_links_from_page << page
|
|
249
|
+
link( @view.url(page.name), title, 'automatic' )
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def link( url, title = url, css_class = '' )
|
|
254
|
+
shelve "<a href='#{url}' class='#{css_class}'>#{title}</a>"
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
module PageNameToUrlNameConversion
|
|
260
|
+
|
|
261
|
+
def setup_page_name_to_url_name_conversion
|
|
262
|
+
@urls_to_pages, @pages_to_urls = {}, {}
|
|
263
|
+
@wiki.each { |name,page| url_name_for_page_name( page.name ) }
|
|
264
|
+
@wiki.watch_attentively_for(:page_created) {|event,page,revision| url_name_for_page_name( page.name )}
|
|
265
|
+
@wiki.watch_attentively_for(:page_title_recapitalized) {|event,page| change_caps_for( page.name )}
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# To allow punctuation in page titles and not mess up urls
|
|
269
|
+
def url_name_for_page_name( page_name )
|
|
270
|
+
@pages_to_urls[ page_name.downcase ] || add_url_name_for_page_name( page_name )
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def page_name_for_url_name( url_name )
|
|
274
|
+
@urls_to_pages[ url_name.downcase ] || url_name
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def add_url_name_for_page_name( page_name )
|
|
278
|
+
url_name = create_url_name( page_name )
|
|
279
|
+
@pages_to_urls[ page_name.downcase ] = url_name
|
|
280
|
+
@urls_to_pages[ url_name.downcase ] = page_name
|
|
281
|
+
url_name
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def change_caps_for( page_name )
|
|
285
|
+
url_name = @pages_to_urls[ page_name.downcase ].downcase
|
|
286
|
+
@urls_to_pages[ url_name ] = page_name
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Turns text into a WikiWord by changing the capitalization and then dumping the punctuation
|
|
290
|
+
def create_url_name( page_name )
|
|
291
|
+
url_name = page_name.capitalize.gsub(/(\W+(\w))/) { |m| $1.upcase }.gsub(/\W+/,'')
|
|
292
|
+
url_name = "PunctuationOnlyInTitle" if url_name == ""
|
|
293
|
+
url_name = increment_url_name( url_name ) while @urls_to_pages.has_key? url_name.downcase
|
|
294
|
+
url_name
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def increment_url_name( url_name )
|
|
298
|
+
if url_name =~ /(\w+)-(\d+)/
|
|
299
|
+
"#{$1}-#{$2.to_i.succ}"
|
|
300
|
+
else
|
|
301
|
+
url_name+"-2"
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
class View
|
|
307
|
+
include ErbHelper
|
|
308
|
+
include PageNameToUrlNameConversion
|
|
309
|
+
|
|
310
|
+
REDCLOTH_CACHE_NAME = 'redcloth'
|
|
311
|
+
|
|
312
|
+
attr_reader :rollingmatch, :links
|
|
313
|
+
attr_accessor :view_folder
|
|
314
|
+
attr_accessor :root_url
|
|
315
|
+
attr_accessor :reload_erb_each_request
|
|
316
|
+
attr_accessor :dont_frame_views
|
|
317
|
+
attr_accessor :redcloth_hard_breaks
|
|
318
|
+
|
|
319
|
+
def initialize( wiki, root_url, view_folder )
|
|
320
|
+
@wiki, @root_url, @view_folder = wiki, root_url, view_folder
|
|
321
|
+
@rollingmatch, @links = BruteMatch.new, Links.new
|
|
322
|
+
@redcloth_cache = wiki.load_cache(REDCLOTH_CACHE_NAME) || Hash.new
|
|
323
|
+
@erb_cache = Hash.new
|
|
324
|
+
@reload_erb_each_request = false
|
|
325
|
+
@dont_frame_views = []
|
|
326
|
+
@redcloth_hard_breaks = false
|
|
327
|
+
setup_page_name_to_url_name_conversion
|
|
328
|
+
wiki.watch_attentively_for( :page_revised ) { |event,page,revision| refresh_redcloth( page ) }
|
|
329
|
+
wiki.watch_for(:shutdown) { wiki.save_cache(REDCLOTH_CACHE_NAME,@redcloth_cache) }
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def render( pagename, view = 'view', person = 'Anon.', query = {} )
|
|
333
|
+
page = @wiki.page( pagename )
|
|
334
|
+
renderedview = redcloth( page )
|
|
335
|
+
content_of_page = html( page.class, view, binding )
|
|
336
|
+
@wiki.notify(:page_viewed, page, view, person)
|
|
337
|
+
if should_frame? view
|
|
338
|
+
return frame_erb.result(binding)
|
|
339
|
+
else
|
|
340
|
+
return content_of_page
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def refresh_redcloth( page )
|
|
345
|
+
$LOG.info "Refreshing #{page}"
|
|
346
|
+
@redcloth_cache[ page.name ] = WikiRedCloth.new( self, page, page.textile(self), redcloth_hard_breaks).to_html
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def redcloth( page )
|
|
350
|
+
textile = page.textile(self)
|
|
351
|
+
@redcloth_cache[ page.name ] || refresh_redcloth( page )
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def clear_redcloth_cache( page = :all_pages )
|
|
355
|
+
( page == :all_pages ) ? @redcloth_cache.clear : @redcloth_cache.delete( page )
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def html( klass, view, _binding )
|
|
359
|
+
@erb_cache.clear if reload_erb_each_request
|
|
360
|
+
( @erb_cache[ path_for( klass, view ) ] ||= ERB.new( IO.readlines( erb_filename( klass, view ) ).join ) ).result( _binding )
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def erb_filename( klass, view )
|
|
364
|
+
$LOG.info "Looking for #{path_for( klass, view)}"
|
|
365
|
+
until File.exists?( path_for( klass, view ) )
|
|
366
|
+
if klass.superclass
|
|
367
|
+
klass = klass.superclass
|
|
368
|
+
else
|
|
369
|
+
$LOG.warn "Not found #{path_for( klass, view)}"
|
|
370
|
+
raise WEBrick::HTTPStatus::NotFound
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
path_for( klass, view )
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
def path_for( klass, view ) "#{view_folder}/#{klass}_#{view.downcase}.rhtml" end
|
|
377
|
+
|
|
378
|
+
def should_frame?( view )
|
|
379
|
+
not( dont_frame_views.include? view.downcase )
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def frame_erb
|
|
383
|
+
@frame_erb = nil if reload_erb_each_request
|
|
384
|
+
@frame_erb ||= load_frame_erb
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def load_frame_erb
|
|
388
|
+
if File.exists? "#{view_folder}/frame.rhtml"
|
|
389
|
+
ERB.new( IO.readlines( "#{view_folder}/frame.rhtml" ).join )
|
|
390
|
+
else
|
|
391
|
+
ERB.new( "<%= content_of_page %>" )
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
def method_missing( method, *args, &block )
|
|
396
|
+
# $LOG.debug "View method missing called for #{method} with #{args.inspect}"
|
|
397
|
+
@wiki.send( method, *args, &block )
|
|
398
|
+
end
|
|
399
|
+
end
|
data/lib/soks.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/local/bin/ruby
|
|
2
|
+
|
|
3
|
+
SOKS_VERSION = '1.0.3'
|
|
4
|
+
|
|
5
|
+
require 'webrick'
|
|
6
|
+
require 'erb'
|
|
7
|
+
require 'redcloth-3.0.3'
|
|
8
|
+
require 'ftools'
|
|
9
|
+
require 'diff/lcs'
|
|
10
|
+
require 'diff/lcs/array'
|
|
11
|
+
require 'fileutils'
|
|
12
|
+
require 'thread'
|
|
13
|
+
require 'yaml'
|
|
14
|
+
require 'logger'
|
|
15
|
+
|
|
16
|
+
require 'soks-utils'
|
|
17
|
+
require 'soks-storage'
|
|
18
|
+
require 'soks-model'
|
|
19
|
+
require 'soks-view'
|
|
20
|
+
require 'soks-servlet'
|
|
21
|
+
|
|
22
|
+
require 'helpers/default-helpers'
|
|
23
|
+
require 'helpers/maintenance-helpers'
|
|
24
|
+
require 'helpers/counter-helpers'
|
|
25
|
+
|
|
26
|
+
Thread.abort_on_exception = true
|
|
27
|
+
Socket.do_not_reverse_lookup = true
|
data/rakefile
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require 'jeweler'
|
|
6
|
+
Jeweler::Tasks.new do |gem|
|
|
7
|
+
|
|
8
|
+
gem.name = %q{Soks}
|
|
9
|
+
gem.authors = ["Tom Counsell"]
|
|
10
|
+
gem.date = %q{2005-10-18}
|
|
11
|
+
gem.default_executable = %q{soks-create-wiki.rb}
|
|
12
|
+
gem.description = %q{Another Ruby Wiki. See http://www.soks.org for details}
|
|
13
|
+
gem.email = %q{tamc@rubyforge.org}
|
|
14
|
+
gem.executables = ["soks-create-wiki.rb"]
|
|
15
|
+
|
|
16
|
+
gem.homepage = %q{http://rubyforge.org/projects/soks/}
|
|
17
|
+
gem.require_paths = ["lib", "contrib"]
|
|
18
|
+
gem.summary = %q{Yet another wiki.}
|
|
19
|
+
|
|
20
|
+
#
|
|
21
|
+
# files
|
|
22
|
+
#
|
|
23
|
+
gem.files = []
|
|
24
|
+
gem.files.concat Dir['bin/**/*']
|
|
25
|
+
gem.files.concat Dir['contrib/**/*']
|
|
26
|
+
gem.files.concat Dir['lib/**/*']
|
|
27
|
+
gem.files.concat Dir['templates/**/*']
|
|
28
|
+
gem.files.concat Dir['test/**/*']
|
|
29
|
+
|
|
30
|
+
gem.files << "rakefile"
|
|
31
|
+
gem.files << "LICENSE.txt"
|
|
32
|
+
gem.files << "README"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
#
|
|
36
|
+
# rubyforge
|
|
37
|
+
#
|
|
38
|
+
# gem.rubyforge_project = 'soks'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
rescue LoadError
|
|
42
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
require 'rake/testtask'
|
|
46
|
+
Rake::TestTask.new(:test) do |test|
|
|
47
|
+
test.libs << 'lib' << 'test'
|
|
48
|
+
test.pattern = 'test/**/tc_*.rb'
|
|
49
|
+
test.verbose = true
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
begin
|
|
53
|
+
require 'rcov/rcovtask'
|
|
54
|
+
Rcov::RcovTask.new do |test|
|
|
55
|
+
test.libs << 'test'
|
|
56
|
+
test.pattern = 'test/**/tc_*.rb'
|
|
57
|
+
test.verbose = true
|
|
58
|
+
end
|
|
59
|
+
rescue LoadError
|
|
60
|
+
task :rcov do
|
|
61
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
task :default => :test
|
|
66
|
+
|
|
67
|
+
require 'hanna/rdoctask'
|
|
68
|
+
Rake::RDocTask.new do |rdoc|
|
|
69
|
+
if File.exist?('VERSION.yml')
|
|
70
|
+
config = YAML.load(File.read('VERSION.yml'))
|
|
71
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
|
72
|
+
else
|
|
73
|
+
version = ""
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
77
|
+
rdoc.title = "tree_visitor #{version}"
|
|
78
|
+
rdoc.rdoc_files.include('README*')
|
|
79
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
#
|
|
83
|
+
# rubyforge
|
|
84
|
+
#
|
|
85
|
+
begin
|
|
86
|
+
require 'rake/contrib/sshpublisher'
|
|
87
|
+
namespace :rubyforge do
|
|
88
|
+
|
|
89
|
+
desc "Release gem and RDoc documentation to RubyForge"
|
|
90
|
+
task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
|
|
91
|
+
|
|
92
|
+
namespace :release do
|
|
93
|
+
desc "Publish RDoc to RubyForge."
|
|
94
|
+
task :docs => [:rdoc] do
|
|
95
|
+
config = YAML.load(
|
|
96
|
+
File.read(File.expand_path('~/.rubyforge/user-config.yml'))
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
host = "#{config['username']}@rubyforge.org"
|
|
100
|
+
remote_dir = "/var/www/gforge-projects/treevisitor/"
|
|
101
|
+
local_dir = 'rdoc'
|
|
102
|
+
|
|
103
|
+
Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
rescue LoadError
|
|
108
|
+
puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
|
|
109
|
+
end
|