Soks 0.0.2 → 0.0.3
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.txt +5 -4
- data/bin/soks-create-wiki.rb +153 -19
- data/contrib/easyprompt.rb +58 -0
- data/contrib/easyprompt_licence.txt +504 -0
- data/contrib/redcloth-2.0.11.rb +3 -1
- data/lib/authenticators.rb +18 -2
- data/lib/soks-helpers.rb +207 -157
- data/lib/soks-model.rb +131 -114
- data/lib/soks-servlet.rb +54 -35
- data/lib/soks-storage.rb +134 -0
- data/lib/soks-upgrade-0.0.2.rb +70 -0
- data/lib/soks-utils.rb +129 -19
- data/lib/soks-view.rb +136 -62
- data/lib/soks.rb +3 -1
- data/{template → 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 +7 -0
- data/templates/default/attachment/rss.png +0 -0
- data/{template → templates/default}/attachment/stylesheet.css +44 -17
- data/templates/default/banned_titles.txt +31 -0
- data/templates/default/content/Bug%3A%20In%20a%20list%20of%20links%2C%20the%20last%20link%20is%20sometimes%20not%20linked.textile +10 -0
- data/templates/default/content/Bug%3A%20Symbols%20are%20not%20always%20correctly%20rendered%20in%20html.textile +3 -0
- data/templates/default/content/Bug%3A%20Uploads%20are%20not%20password%20protected.textile +3 -0
- data/templates/default/content/How%20to%20administrate%20this%20wiki.textile +62 -0
- data/templates/default/content/How%20to%20change%20the%20way%20this%20wiki%20looks.textile +30 -0
- data/templates/default/content/How%20to%20export%20a%20site%20from%20this%20wiki.textile +60 -0
- data/{template → templates/default}/content/How%20to%20hack%20soks.textile +3 -2
- data/{template → templates/default}/content/How%20to%20import%20a%20site%20from%20instiki.textile +1 -1
- data/templates/default/content/How%20to%20use%20this%20wiki.textile +27 -0
- data/templates/default/content/List%20of%20changes.textile +35 -0
- data/{template → templates/default}/content/Picture%20of%20a%20pair%20of%20soks.textile +0 -0
- data/{template → templates/default}/content/Soks%20Licence.textile +0 -0
- data/templates/default/content/home%20page.textile +17 -0
- data/templates/default/start.rb +94 -0
- data/templates/default/version.txt +1 -0
- data/{template → templates/default}/views/Page_content.rhtml +0 -0
- data/templates/default/views/Page_edit.rhtml +61 -0
- data/templates/default/views/Page_meta.rhtml +40 -0
- data/templates/default/views/Page_print.rhtml +6 -0
- data/templates/default/views/Page_revisions.rhtml +19 -0
- data/templates/default/views/Page_rss.rhtml +55 -0
- data/{template → templates/default}/views/Page_search_results.rhtml +1 -1
- data/templates/default/views/Page_view.rhtml +4 -0
- data/templates/default/views/UploadPage_edit.rhtml +38 -0
- data/templates/default/views/frame.rhtml +41 -0
- data/templates/default/views/messages.yaml +6 -0
- data/templates/instiki/attachment/header_backdrop.png +0 -0
- data/templates/instiki/attachment/instiki_style_sheet.css +199 -0
- data/templates/instiki/attachment/logo.tiff +0 -0
- data/templates/instiki/attachment/logotext.png +0 -0
- data/templates/instiki/attachment/newpage.js +41 -0
- data/templates/instiki/attachment/rss.png +0 -0
- data/templates/instiki/banned_titles.txt +31 -0
- data/templates/instiki/content/AutomaticSummary.textile +24 -0
- data/templates/instiki/content/How%20to%20export%20a%20site%20from%20this%20wiki.textile +60 -0
- data/templates/instiki/content/How%20to%20hack%20soks.textile +61 -0
- data/templates/instiki/content/How%20to%20import%20a%20site%20from%20instiki.textile +13 -0
- data/{template → templates/instiki}/content/Improving%20the%20style%20of%20this%20wiki.textile +2 -2
- data/templates/instiki/content/Known%20bugs.textile +8 -0
- data/templates/instiki/content/List%20of%20changes.textile +34 -0
- data/templates/instiki/content/Picture%20of%20a%20pair%20of%20soks.textile +1 -0
- data/templates/instiki/content/Pointers%20on%20adjusting%20the%20settings.textile +62 -0
- data/templates/instiki/content/Pointers%20on%20how%20to%20use%20this%20wiki.textile +27 -0
- data/templates/instiki/content/Recent%20Blog%20Entries.textile +3 -0
- data/templates/instiki/content/Recent%20Changes%20to%20This%20Site.textile +48 -0
- data/templates/instiki/content/Site%20Index.textile +16 -0
- data/templates/instiki/content/Soks%20Licence.textile +64 -0
- data/{template → templates/instiki}/content/home%20page.textile +9 -4
- data/templates/instiki/start.rb +85 -0
- data/templates/instiki/version.txt +1 -0
- data/templates/instiki/views/Page_content.rhtml +1 -0
- data/templates/instiki/views/Page_edit.rhtml +8 -0
- data/templates/instiki/views/Page_meta.rhtml +34 -0
- data/templates/instiki/views/Page_print.rhtml +6 -0
- data/templates/instiki/views/Page_revisions.rhtml +17 -0
- data/templates/instiki/views/Page_rss.rhtml +55 -0
- data/templates/instiki/views/Page_search_results.rhtml +18 -0
- data/templates/instiki/views/Page_view.rhtml +2 -0
- data/templates/instiki/views/UploadPage_edit.rhtml +16 -0
- data/templates/instiki/views/frame.rhtml +90 -0
- data/templates/instiki/views/messages.yaml +6 -0
- data/templates/rails/attachment/2colheader.css +77 -0
- data/templates/rails/attachment/basics.css +98 -0
- data/templates/rails/attachment/header_backdrop.png +0 -0
- data/templates/rails/attachment/logo.tiff +0 -0
- data/templates/rails/attachment/logotext.png +0 -0
- data/templates/rails/attachment/newpage.js +41 -0
- data/templates/rails/attachment/rss.png +0 -0
- data/templates/rails/banned_titles.txt +31 -0
- data/templates/rails/content/AutomaticSummary.textile +24 -0
- data/templates/rails/content/How%20to%20export%20a%20site%20from%20this%20wiki.textile +60 -0
- data/templates/rails/content/How%20to%20hack%20soks.textile +61 -0
- data/templates/rails/content/How%20to%20import%20a%20site%20from%20instiki.textile +13 -0
- data/templates/rails/content/Improving%20the%20style%20of%20this%20wiki.textile +30 -0
- data/templates/rails/content/Known%20bugs.textile +8 -0
- data/templates/rails/content/List%20of%20changes.textile +34 -0
- data/templates/rails/content/Picture%20of%20a%20pair%20of%20soks.textile +1 -0
- data/templates/rails/content/Pointers%20on%20adjusting%20the%20settings.textile +62 -0
- data/templates/rails/content/Pointers%20on%20how%20to%20use%20this%20wiki.textile +27 -0
- data/templates/rails/content/Recent%20Blog%20Entries.textile +3 -0
- data/templates/rails/content/Recent%20Changes%20to%20This%20Site.textile +48 -0
- data/templates/rails/content/Site%20Index.textile +16 -0
- data/templates/rails/content/Soks%20Licence.textile +64 -0
- data/templates/rails/content/home%20page.textile +23 -0
- data/templates/rails/start.rb +85 -0
- data/templates/rails/version.txt +1 -0
- data/templates/rails/views/Page_content.rhtml +1 -0
- data/templates/rails/views/Page_edit.rhtml +61 -0
- data/templates/rails/views/Page_meta.rhtml +38 -0
- data/templates/rails/views/Page_print.rhtml +6 -0
- data/templates/rails/views/Page_revisions.rhtml +19 -0
- data/templates/rails/views/Page_rss.rhtml +55 -0
- data/templates/rails/views/Page_search_results.rhtml +19 -0
- data/templates/rails/views/Page_view.rhtml +3 -0
- data/templates/rails/views/UploadPage_edit.rhtml +38 -0
- data/templates/rails/views/frame.rhtml +60 -0
- data/templates/rails/views/messages.yaml +6 -0
- metadata +122 -28
- data/template/content/How%20to%20export%20a%20site%20from%20this%20wiki.textile +0 -5
- data/template/content/Pointers%20on%20adjusting%20the%20settings.textile +0 -39
- data/template/content/Pointers%20on%20how%20to%20use%20this%20wiki.textile +0 -21
- data/template/content/Recent%20Changes%20to%20This%20Site.textile +0 -203
- data/template/start.rb +0 -74
- data/template/views/AttachmentPage_edit.rhtml +0 -36
- data/template/views/ImagePage_edit.rhtml +0 -36
- data/template/views/Page_edit.rhtml +0 -34
- data/template/views/Page_print.rhtml +0 -5
- data/template/views/Page_revisions.rhtml +0 -18
- data/template/views/Page_rss.rhtml +0 -34
- data/template/views/Page_view.rhtml +0 -3
- data/template/views/frame.rhtml +0 -34
data/lib/soks-storage.rb
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
module WikiFlatFileStore
|
|
2
|
+
|
|
3
|
+
CONTENT_EXTENSION = '.textile'
|
|
4
|
+
REVISIONS_EXTENSION = '.yaml'
|
|
5
|
+
DEFAULT_AUTHOR = 'AutomaticImport'
|
|
6
|
+
|
|
7
|
+
def save( page )
|
|
8
|
+
add_page_to_index( page )
|
|
9
|
+
save_content( page )
|
|
10
|
+
save_revisions( page )
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def load_all_pages
|
|
14
|
+
move_files_if_names_are_not_url_encoded
|
|
15
|
+
pages_on_disk = Dir[ File.join( @folder, "*#{CONTENT_EXTENSION}" ) ].map { |filename| page_name_for( filename )}
|
|
16
|
+
pages_in_memory = @pages.values.map { |page| page && page.name }
|
|
17
|
+
( pages_in_memory.compact | pages_on_disk ).each do |pagename|
|
|
18
|
+
page = page_named( pagename )
|
|
19
|
+
|
|
20
|
+
if page && !File.exists?( filename_for_content( pagename ) )
|
|
21
|
+
@pages[ page.name.downcase ] = nil
|
|
22
|
+
notify :page_revised, page, page.revisions.last
|
|
23
|
+
notify :page_deleted, page, page.revisions.last
|
|
24
|
+
next
|
|
25
|
+
end
|
|
26
|
+
unless page
|
|
27
|
+
page = load_page( filename_for_content( pagename ) )
|
|
28
|
+
notify :page_revised, page, page.revisions.last
|
|
29
|
+
notify :page_created, page, page.revisions.last
|
|
30
|
+
next
|
|
31
|
+
end
|
|
32
|
+
if check_disk_for_updated_page pagename
|
|
33
|
+
page = page_named( pagename )
|
|
34
|
+
notify :page_revised, page, page.revisions.last
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def load_page( filename )
|
|
40
|
+
page = new_page( page_name_for( filename ) )
|
|
41
|
+
page.revisions = load_revisions( page )
|
|
42
|
+
content = load_content( page )
|
|
43
|
+
|
|
44
|
+
if page_named( page.name ) # Save having to find this out again
|
|
45
|
+
page.inserted_into = page_named( page.name ).inserted_into
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
if page.revisions.empty? # There is a textile file but no array file
|
|
49
|
+
page.revise(content, DEFAULT_AUTHOR )
|
|
50
|
+
save_revisions( page )
|
|
51
|
+
elsif content_newer_than_revisions?( page ) # The textile file has been modified, but the array file has not been updated to match
|
|
52
|
+
page.content = reconstruct_content_from_revisions( page.revisions )
|
|
53
|
+
page.revise( content, DEFAULT_AUTHOR )
|
|
54
|
+
save_revisions( page )
|
|
55
|
+
else # The textile file and the array file are in sync.
|
|
56
|
+
page.content = content
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
add_page_to_index( page )
|
|
60
|
+
page
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def load_content( page )
|
|
64
|
+
IO.readlines( filename_for_content( page.name ) ).join
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def load_revisions( page )
|
|
68
|
+
return [] unless File.exists?( filename_for_revisions( page.name ) )
|
|
69
|
+
revisions = []
|
|
70
|
+
File.open( filename_for_revisions( page.name ) ) { |file|
|
|
71
|
+
YAML.each_document( file ) { |array|
|
|
72
|
+
revisions[ array[0] ] = Revision.new( page, *array )
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
revisions.each_with_index { |r,i| $stderr.puts "#{page.name} missing revision #{i}" unless r }
|
|
76
|
+
revisions
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def save_content( page )
|
|
80
|
+
File.open(filename_for_content( page.name ), 'w' ) { |file| file.puts page.content }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Appends the last revision onto the yaml file
|
|
84
|
+
def save_revisions( page )
|
|
85
|
+
$stderr.puts "Saving revisions for #{page.name}"
|
|
86
|
+
File.open(filename_for_revisions( page.name ), 'a' ) { |file|
|
|
87
|
+
YAML.dump( page.revisions.last.to_a, file )
|
|
88
|
+
file.puts
|
|
89
|
+
}
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def check_disk_for_updated_page( pagename )
|
|
93
|
+
return unless $SETTINGS[:check_files_every] # We don't care about file changes
|
|
94
|
+
filename = filename_for_content( pagename )
|
|
95
|
+
return nil unless File.exists?( filename ) # File doesn't exist on disk
|
|
96
|
+
return load_page( filename ) unless page_named pagename # File is new on the disk, but not yet in memory
|
|
97
|
+
return load_page( filename ) if ( page_named(pagename).revised_on < File.ctime( filename ) ) # File is newer on disk
|
|
98
|
+
return nil
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def content_newer_than_revisions?( page )
|
|
102
|
+
page_file = filename_for_content page.name
|
|
103
|
+
revision_file = filename_for_revisions page.name
|
|
104
|
+
File.ctime(page_file) > File.ctime(revision_file)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def reconstruct_content_from_revisions( revisions )
|
|
108
|
+
content = []
|
|
109
|
+
revisions.each { |revision| content = Diff::LCS.patch( content, revision.changes, :patch ) }
|
|
110
|
+
content.join("\n")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def move_files_if_names_are_not_url_encoded
|
|
114
|
+
Dir[ File.join( @folder, "*#{CONTENT_EXTENSION}" ) ].each do |filename|
|
|
115
|
+
basename = File.basename( filename, '.*')
|
|
116
|
+
next if basename.url_decode.url_encode == basename # All ok, so no worry
|
|
117
|
+
new_name = File.join( File.dirname(filename), basename.url_decode.url_encode) + File.extname( filename)
|
|
118
|
+
File.rename(filename, new_name )
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def page_name_for( filename )
|
|
123
|
+
File.basename( filename, '.*').url_decode
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def filename_for_content( pagename )
|
|
127
|
+
File.join( @folder, "#{pagename.url_encode}#{CONTENT_EXTENSION}" )
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def filename_for_revisions( pagename )
|
|
131
|
+
File.join( @folder, "#{pagename.url_encode}#{REVISIONS_EXTENSION}" )
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require 'soks-utils'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
|
|
4
|
+
# This is the definition of Revision from v-0-0-2.
|
|
5
|
+
# Much smaller than the current definition. sigh.
|
|
6
|
+
class Revision
|
|
7
|
+
attr_reader :number, :changes, :created_at, :author
|
|
8
|
+
|
|
9
|
+
def initialize( number, changes, author )
|
|
10
|
+
@number, @changes, @author = number, changes, author
|
|
11
|
+
@created_at = Time.now
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def content( page )
|
|
15
|
+
page.revision( @number + 1 ) ? page.revision( @number + 1 ).previous_content( page ) : page.content
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def previous_content( page )
|
|
19
|
+
content( page ).split("\n").unpatch!(@changes).join("\n")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class SoksUpgrade
|
|
24
|
+
|
|
25
|
+
def load_old_revisions( filename )
|
|
26
|
+
File.open( filename ) { |file| return Marshal.load( file ) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def save_new_revisions( old_filename, revisions )
|
|
30
|
+
File.open(new_filename_for_old( old_filename ), 'w' ) do |file|
|
|
31
|
+
revisions.each do |revision|
|
|
32
|
+
YAML.dump( [revision.number, revision.changes, revision.author, revision.created_at ] , file )
|
|
33
|
+
file.puts
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def new_filename_for_old( old_filename )
|
|
39
|
+
basename = File.basename( old_filename, '.*')
|
|
40
|
+
new_extension = '.yaml'
|
|
41
|
+
File.join( File.dirname(old_filename), basename ) + new_extension
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def upgrade_revisions( directory )
|
|
45
|
+
search = File.join( directory,'content', "*.marshal" )
|
|
46
|
+
Dir[ search ].each do |filename|
|
|
47
|
+
puts "Upgrading #{filename}"
|
|
48
|
+
save_new_revisions( filename, load_old_revisions( filename ))
|
|
49
|
+
File.delete filename
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def upgrade_textile( filename )
|
|
54
|
+
textile = IO.readlines( filename ).join
|
|
55
|
+
textile.gsub!(/\[\[\s*(.*?)\s*(|:\s*(.*?)\s*)\]\]/) do |m|
|
|
56
|
+
title, page = $1, $3
|
|
57
|
+
page ? "[[ #{title} => #{page} ]]" : "[[ #{title} ]]"
|
|
58
|
+
end
|
|
59
|
+
File.open( filename, 'w' ) { |f| f.puts textile }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def upgrade_content( directory )
|
|
63
|
+
search = File.join( directory,'content', "*.textile" )
|
|
64
|
+
Dir[ search ].each do |filename|
|
|
65
|
+
puts "Upgrading #{filename}"
|
|
66
|
+
upgrade_textile( filename )
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
end
|
data/lib/soks-utils.rb
CHANGED
|
@@ -1,32 +1,76 @@
|
|
|
1
|
+
# This is a bit like observable, but for events
|
|
2
|
+
module Notify
|
|
3
|
+
|
|
4
|
+
# Will notify in a separate low priority thread
|
|
5
|
+
def watch_for( *events , &action_block )
|
|
6
|
+
self.event_queue.watch_for( events, action_block )
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Will notify in the running high priority thread (ie will block response to user)
|
|
10
|
+
def watch_attentively_for( *events, &action_block )
|
|
11
|
+
self.event_queue.watch_attentively_for( events, action_block )
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def notify( event, *messages)
|
|
15
|
+
self.event_queue.event( event, messages )
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def event_queue
|
|
19
|
+
@event_queue ||= EventQueue.new
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
1
23
|
class EventQueue
|
|
2
24
|
|
|
3
25
|
def initialize
|
|
4
26
|
@queue = Queue.new
|
|
5
|
-
|
|
6
|
-
loop do
|
|
7
|
-
check_for_events
|
|
8
|
-
end
|
|
9
|
-
end.priority = -1
|
|
27
|
+
start_thread
|
|
10
28
|
end
|
|
11
29
|
|
|
12
30
|
def event( event, messages )
|
|
31
|
+
check_thread_ok
|
|
13
32
|
@queue.enq [ event, messages ]
|
|
33
|
+
notify_attentive_watchers( event, *messages )
|
|
14
34
|
end
|
|
15
35
|
|
|
36
|
+
# Will call the action_block lazily
|
|
16
37
|
def watch_for( events , action_block )
|
|
17
38
|
events.each { |event| watchers_for(event) << action_block }
|
|
18
39
|
end
|
|
19
40
|
|
|
41
|
+
# Will call the action_block imediately
|
|
42
|
+
def watch_attentively_for( events, action_block )
|
|
43
|
+
events.each { |event| attentive_watchers_for(event) << action_block }
|
|
44
|
+
end
|
|
45
|
+
|
|
20
46
|
private
|
|
21
47
|
|
|
48
|
+
def check_thread_ok
|
|
49
|
+
start_thread unless @thread && @thread.alive?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def start_thread
|
|
53
|
+
@thread = Thread.new do
|
|
54
|
+
loop do
|
|
55
|
+
check_for_events
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
@thread.priority = -1
|
|
59
|
+
end
|
|
60
|
+
|
|
22
61
|
def check_for_events
|
|
23
62
|
event, messages = @queue.deq
|
|
24
63
|
notify( event, *messages )
|
|
25
64
|
end
|
|
26
65
|
|
|
27
66
|
def notify( event, *messages)
|
|
28
|
-
watchers_for( event ).each { |action_block|
|
|
29
|
-
|
|
67
|
+
watchers_for( event ).each { |action_block|
|
|
68
|
+
begin
|
|
69
|
+
action_block.call(event, *messages)
|
|
70
|
+
rescue StandardError => err
|
|
71
|
+
$stderr.puts "ERROR #{err}: #{event} - #{messages.join(' ')}"
|
|
72
|
+
err.backtrace.each { |s| $stderr.puts s }
|
|
73
|
+
end
|
|
30
74
|
}
|
|
31
75
|
end
|
|
32
76
|
|
|
@@ -37,31 +81,37 @@ class EventQueue
|
|
|
37
81
|
def watchers
|
|
38
82
|
@watchers ||= {}
|
|
39
83
|
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# This is a bit like observable, but for events
|
|
43
|
-
module Notify
|
|
44
84
|
|
|
45
|
-
def
|
|
46
|
-
|
|
85
|
+
def notify_attentive_watchers( event, *messages )
|
|
86
|
+
attentive_watchers_for( event ).each { |action_block|
|
|
87
|
+
begin
|
|
88
|
+
action_block.call(event, *messages)
|
|
89
|
+
rescue StandardError => err
|
|
90
|
+
$stderr.puts "ERROR #{err}: #{event} - #{messages.join(' ')}"
|
|
91
|
+
err.backtrace.each { |s| $stderr.puts s }
|
|
92
|
+
end
|
|
93
|
+
}
|
|
47
94
|
end
|
|
48
95
|
|
|
49
|
-
def
|
|
50
|
-
|
|
96
|
+
def attentive_watchers_for( event )
|
|
97
|
+
attentive_watchers[ event ] ||= []
|
|
51
98
|
end
|
|
52
99
|
|
|
53
|
-
def
|
|
54
|
-
@
|
|
100
|
+
def attentive_watchers
|
|
101
|
+
@attentive_watchers ||= {}
|
|
55
102
|
end
|
|
103
|
+
|
|
56
104
|
end
|
|
57
105
|
|
|
106
|
+
|
|
107
|
+
|
|
58
108
|
class String
|
|
59
109
|
# Return the left bit of a string e.g. "String".left(2) => "St"
|
|
60
110
|
def left( length ) self.slice( 0, length ) end
|
|
61
111
|
|
|
62
112
|
# Encode the string so it can be used in urls (code coppied from CGI)
|
|
63
113
|
def url_encode
|
|
64
|
-
self.gsub(/([^a-zA-Z0-
|
|
114
|
+
self.gsub(/([^a-zA-Z0-9]+)/n) do
|
|
65
115
|
'%' + $1.unpack('H2' * $1.size).join('%').upcase
|
|
66
116
|
end.tr(' ', '+')
|
|
67
117
|
end
|
|
@@ -77,4 +127,64 @@ class String
|
|
|
77
127
|
def first_lines( lines = 1 )
|
|
78
128
|
self.split("\n")[0,lines].join("\n")
|
|
79
129
|
end
|
|
80
|
-
|
|
130
|
+
|
|
131
|
+
def close_unmatched_html
|
|
132
|
+
start_tags = self.scan(/<(\w+)[^>\/]*?(?!\/)>/)
|
|
133
|
+
end_tags = self.scan(/<\/(\w+)[^>\/]*?>/)
|
|
134
|
+
return self if start_tags.size == end_tags.size
|
|
135
|
+
missing_tags = start_tags - end_tags
|
|
136
|
+
text = self.dup
|
|
137
|
+
missing_tags.each do |tag|
|
|
138
|
+
text << "</#{tag[0]}>"
|
|
139
|
+
end
|
|
140
|
+
text
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
class FiniteUniqueList
|
|
145
|
+
include Enumerable
|
|
146
|
+
|
|
147
|
+
attr_accessor :max_size
|
|
148
|
+
|
|
149
|
+
def initialize( max_size = nil, reverse = false, sort_by = nil )
|
|
150
|
+
@max_size = max_size
|
|
151
|
+
@list = Array.new
|
|
152
|
+
@sort_by = sort_by
|
|
153
|
+
@reverse = reverse
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def add( item )
|
|
157
|
+
remove( item )
|
|
158
|
+
@list << item
|
|
159
|
+
sort_items
|
|
160
|
+
remove_excess_items
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def remove( item )
|
|
164
|
+
@list.delete( item )
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def each
|
|
168
|
+
if @reverse
|
|
169
|
+
@list.reverse_each { |item| yield item }
|
|
170
|
+
else
|
|
171
|
+
@list.each { |item| yield item }
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def empty?; @list.empty? end
|
|
176
|
+
|
|
177
|
+
private
|
|
178
|
+
|
|
179
|
+
def remove_excess_items
|
|
180
|
+
return unless @max_size
|
|
181
|
+
while @list.size > @max_size
|
|
182
|
+
@list.shift
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def sort_items
|
|
187
|
+
return unless @sort_by
|
|
188
|
+
@list = @list.sort_by { |item| item.send( @sort_by ) }
|
|
189
|
+
end
|
|
190
|
+
end
|
data/lib/soks-view.rb
CHANGED
|
@@ -15,13 +15,11 @@ class Links
|
|
|
15
15
|
linksto = Array.new
|
|
16
16
|
@links.each do | pagefrom, pagesto |
|
|
17
17
|
next if pagefrom == thispage
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
end
|
|
22
|
-
end
|
|
18
|
+
next if [ "site index", "recent changes to this site" ].include? pagefrom.name.downcase
|
|
19
|
+
next if linksto.include? pagefrom
|
|
20
|
+
linksto << pagefrom if pagesto.include? thispage
|
|
23
21
|
end
|
|
24
|
-
return linksto.
|
|
22
|
+
return linksto.sort
|
|
25
23
|
end
|
|
26
24
|
end
|
|
27
25
|
|
|
@@ -30,8 +28,8 @@ end
|
|
|
30
28
|
# the first thing it can, rather than skipping a shorter match to enable
|
|
31
29
|
# a longer one that starts at a later point in the text
|
|
32
30
|
class RollingMatch
|
|
33
|
-
WORD_REGEX =
|
|
34
|
-
SPACE_REGEX =
|
|
31
|
+
WORD_REGEX = /\w*/
|
|
32
|
+
SPACE_REGEX = /\W*/
|
|
35
33
|
IGNORE_CASE = true
|
|
36
34
|
|
|
37
35
|
def initialize
|
|
@@ -117,29 +115,32 @@ if RedCloth::VERSION == '2.0.11'
|
|
|
117
115
|
|
|
118
116
|
@urlrefs = {}
|
|
119
117
|
@shelf = []
|
|
120
|
-
|
|
118
|
+
@internal_links_from_page = [] # NEW
|
|
121
119
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
clean_white_space text
|
|
128
|
-
|
|
129
|
-
get_refs text
|
|
120
|
+
insert_sub_strings text # NEW
|
|
121
|
+
|
|
122
|
+
shelve_divs_and_spans text # NEW
|
|
123
|
+
|
|
124
|
+
clean_white_space text
|
|
130
125
|
|
|
131
126
|
no_textile text
|
|
132
127
|
|
|
133
128
|
pre_list = rip_offtags text # NEW wasteful to duplicate, but reduces overloading
|
|
134
129
|
hide_textile_links text # NEW
|
|
135
130
|
hide_textile_image_tags text # NEW
|
|
136
|
-
inline_soks_external_link text # NEW
|
|
137
131
|
inline_soks_bracketed_link text # NEW
|
|
132
|
+
inline_soks_external_link text # NEW
|
|
138
133
|
inline_soks_automatic_link text # NEW
|
|
139
134
|
@wiki.links.set_links_from( @page, @internal_links_from_page ) # NEW
|
|
140
135
|
unhide_textile text # NEW
|
|
141
136
|
smooth_offtags text, pre_list # NEW wasteful to duplicate
|
|
142
137
|
|
|
138
|
+
incoming_entities text
|
|
139
|
+
## encode_entities text
|
|
140
|
+
## fix_entities text
|
|
141
|
+
|
|
142
|
+
get_refs text
|
|
143
|
+
|
|
143
144
|
inline text
|
|
144
145
|
|
|
145
146
|
unless @lite
|
|
@@ -153,8 +154,12 @@ if RedCloth::VERSION == '2.0.11'
|
|
|
153
154
|
text.gsub!( /x%x%/, '&' )
|
|
154
155
|
text.gsub!( /<br \/>/, "<br />\n" )
|
|
155
156
|
text.strip!
|
|
157
|
+
|
|
158
|
+
clean_html text if $SETTINGS[:strict_html_removal]
|
|
159
|
+
|
|
156
160
|
text
|
|
157
161
|
end
|
|
162
|
+
|
|
158
163
|
end
|
|
159
164
|
|
|
160
165
|
if RedCloth::VERSION == '3.0.1'
|
|
@@ -173,7 +178,7 @@ if RedCloth::VERSION == '3.0.1'
|
|
|
173
178
|
markdown_rules = [:refs_markdown, :block_markdown_setext, :block_markdown_atx, :block_markdown_rule,
|
|
174
179
|
:block_markdown_bq, :block_markdown_lists,
|
|
175
180
|
:inline_markdown_reflink, :inline_markdown_link]
|
|
176
|
-
soks_rules = [ :hide_textile_links, :hide_textile_image_tags, :
|
|
181
|
+
soks_rules = [ :hide_textile_links, :hide_textile_image_tags, :inline_soks_bracketed_link, :inline_soks_external_link, :inline_soks_automatic_link, :unhide_textile ]
|
|
177
182
|
@rules = rules.collect do |rule|
|
|
178
183
|
case rule
|
|
179
184
|
when :markdown
|
|
@@ -188,7 +193,7 @@ if RedCloth::VERSION == '3.0.1'
|
|
|
188
193
|
end.flatten
|
|
189
194
|
|
|
190
195
|
# insert sub pages
|
|
191
|
-
|
|
196
|
+
insert_sub_strings text
|
|
192
197
|
|
|
193
198
|
# standard clean up
|
|
194
199
|
incoming_entities text
|
|
@@ -208,11 +213,81 @@ if RedCloth::VERSION == '3.0.1'
|
|
|
208
213
|
text.gsub!( /<\/?notextile>/, '' )
|
|
209
214
|
text.gsub!( /x%x%/, '&' )
|
|
210
215
|
text.strip!
|
|
216
|
+
|
|
217
|
+
clean_html text
|
|
218
|
+
|
|
211
219
|
text
|
|
212
220
|
end
|
|
213
221
|
end
|
|
214
222
|
|
|
215
223
|
private
|
|
224
|
+
|
|
225
|
+
def shelve_divs_and_spans( text )
|
|
226
|
+
text.gsub!(/<\/+(div|span).*?>/) { |m| shelve m }
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
## Dictionary describing allowable HTML
|
|
230
|
+
## tags and attributes.
|
|
231
|
+
BASIC_TAGS = {
|
|
232
|
+
'a' => ['href', 'title'],
|
|
233
|
+
'img' => ['src', 'alt', 'title'],
|
|
234
|
+
'br' => [],
|
|
235
|
+
'i' => nil,
|
|
236
|
+
'u' => nil,
|
|
237
|
+
'b' => nil,
|
|
238
|
+
'pre' => nil,
|
|
239
|
+
'kbd' => nil,
|
|
240
|
+
'code' => ['lang'],
|
|
241
|
+
'cite' => nil,
|
|
242
|
+
'strong' => nil,
|
|
243
|
+
'em' => nil,
|
|
244
|
+
'ins' => nil,
|
|
245
|
+
'sup' => nil,
|
|
246
|
+
'sub' => nil,
|
|
247
|
+
'del' => nil,
|
|
248
|
+
'table' => [ 'class', 'style' ],
|
|
249
|
+
'tr' => [ 'class', 'style' ],
|
|
250
|
+
'td' => [ 'class', 'style' ],
|
|
251
|
+
'th' => [ 'class', 'style' ],
|
|
252
|
+
'ol' => nil,
|
|
253
|
+
'ul' => nil,
|
|
254
|
+
'li' => nil,
|
|
255
|
+
'p' => [ 'class', 'style' ],
|
|
256
|
+
'h1' => nil,
|
|
257
|
+
'h2' => nil,
|
|
258
|
+
'h3' => nil,
|
|
259
|
+
'h4' => nil,
|
|
260
|
+
'h5' => nil,
|
|
261
|
+
'h6' => nil,
|
|
262
|
+
'blockquote' => ['cite'],
|
|
263
|
+
'div' => [ 'class', 'id', 'style' ],
|
|
264
|
+
'span' => [ 'class', 'id','style'],
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
## Method which cleans the String of HTML tags
|
|
268
|
+
## and attributes outside of the allowed list.
|
|
269
|
+
def clean_html( text, tags = BASIC_TAGS )
|
|
270
|
+
text.gsub!( /<(\/*)(\w+)([^>\/]*?)( *\/*)>/ ) do
|
|
271
|
+
raw = $~
|
|
272
|
+
tag = raw[2].downcase
|
|
273
|
+
if tags.has_key? tag
|
|
274
|
+
pcs = [tag]
|
|
275
|
+
tags[tag].each do |prop|
|
|
276
|
+
['"', "'", ''].each do |q|
|
|
277
|
+
q2 = ( q != '' ? q : '\s' )
|
|
278
|
+
if raw[3] =~ /#{prop}\s*=\s*#{q}([^#{q2}]+)#{q}/i
|
|
279
|
+
pcs << "#{prop}=\"#{$1.gsub('"', '\\"')}\""
|
|
280
|
+
break
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end if tags[tag]
|
|
284
|
+
"<#{raw[1]}#{pcs.join " "}#{raw[4]}>"
|
|
285
|
+
else
|
|
286
|
+
" "
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
text
|
|
290
|
+
end
|
|
216
291
|
|
|
217
292
|
def insert_sub_strings( text, count = 0 )
|
|
218
293
|
return text if count > 5 # Stops us getting locked into a cycle if people mess up the insert
|
|
@@ -228,21 +303,21 @@ end
|
|
|
228
303
|
end
|
|
229
304
|
|
|
230
305
|
def inline_soks_external_link( text )
|
|
231
|
-
text.gsub!(/http:\/\/[^ \n<]+/i)
|
|
232
|
-
text.gsub!(/https:\/\/[^ \n<]+/i)
|
|
233
|
-
text.gsub!(/www
|
|
306
|
+
text.gsub!(/http:\/\/[^ \n<]+/i) { |m| link m }
|
|
307
|
+
text.gsub!(/https:\/\/[^ \n<]+/i) { |m| link m }
|
|
308
|
+
text.gsub!(/www\.[^ \n<]*/i) { |m| link( "http://#{m}", m) }
|
|
234
309
|
text.gsub!(/[A-Za-z0-9.]+?@[A-Za-z0-9.]+/) { |m| link( "mailto:#{m}", m) }
|
|
235
310
|
end
|
|
236
311
|
|
|
237
312
|
def inline_soks_bracketed_link( text )
|
|
238
|
-
text.gsub!(/\[\[(
|
|
239
|
-
|
|
240
|
-
|
|
313
|
+
text.gsub!(/\[\[\s*(.*?)\s*(|=>\s*(.*?)\s*)\]\]/) do |m|
|
|
314
|
+
title, pagename = $1, $3
|
|
315
|
+
pagename ||= title
|
|
241
316
|
case pagename
|
|
242
|
-
when /^www
|
|
317
|
+
when /^www\./i ; link("http://#{pagename}", title )
|
|
243
318
|
when /[A-Za-z0-9.]+?@[A-Za-z0-9.]+/ ; link("mailto:#{pagename}",title)
|
|
244
|
-
when /^http
|
|
245
|
-
else
|
|
319
|
+
when /^http:\/\//i ; link(pagename,title)
|
|
320
|
+
else ; wiki_link( pagename, title )
|
|
246
321
|
end
|
|
247
322
|
end
|
|
248
323
|
end
|
|
@@ -256,17 +331,28 @@ end
|
|
|
256
331
|
text.replace( linkedtext )
|
|
257
332
|
end
|
|
258
333
|
|
|
259
|
-
def wiki_link( pagename, title,
|
|
334
|
+
def wiki_link( pagename, title, css_class = nil )
|
|
260
335
|
if @wiki.exists? pagename
|
|
261
336
|
@internal_links_from_page << @wiki.page( pagename )
|
|
262
|
-
|
|
263
|
-
else
|
|
264
|
-
link(pagename,title, css || 'missing')
|
|
337
|
+
css_class ||= ''
|
|
265
338
|
end
|
|
339
|
+
link(url_encode_pagename( pagename ),title, css_class || 'missing')
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def link( url, title = url, css_class = '' )
|
|
343
|
+
shelve "<a href='#{url}' class='#{css_class}'>#{title}</a>"
|
|
266
344
|
end
|
|
267
345
|
|
|
268
|
-
|
|
269
|
-
|
|
346
|
+
# This makes sure punctuation can be used in the name of the page
|
|
347
|
+
# by url encoding anything that isn't a letter of a number.
|
|
348
|
+
# The complexity is to check that a command hasn't been embedded inside
|
|
349
|
+
# a pagename (e.g. /edit/home page ) in which case the command should not
|
|
350
|
+
# be encoded.
|
|
351
|
+
def url_encode_pagename( pagename )
|
|
352
|
+
if pagename =~ /\/(\w+)\/(.*)/
|
|
353
|
+
return "/#{$1}/#{$2.url_encode}"
|
|
354
|
+
end
|
|
355
|
+
return pagename.url_encode
|
|
270
356
|
end
|
|
271
357
|
|
|
272
358
|
def hide_textile_image_tags( text )
|
|
@@ -276,7 +362,6 @@ end
|
|
|
276
362
|
text
|
|
277
363
|
end
|
|
278
364
|
|
|
279
|
-
|
|
280
365
|
def hide_textile_links( text )
|
|
281
366
|
text.gsub!( LINK_RE ) do |match|
|
|
282
367
|
hide_textile match
|
|
@@ -303,13 +388,13 @@ end
|
|
|
303
388
|
end
|
|
304
389
|
|
|
305
390
|
class View
|
|
306
|
-
include Notify
|
|
307
391
|
attr_reader :rollingmatch, :links
|
|
308
392
|
|
|
309
393
|
def initialize( wiki, name )
|
|
310
394
|
@wikiname = name
|
|
311
395
|
@rollingmatch, @links, @redcloth_cache, @erb_cache = RollingMatch.new, Links.new, Hash.new, Hash.new
|
|
312
396
|
@wiki = wiki
|
|
397
|
+
wiki.watch_attentively_for( :page_revised ) { |event,page,revision| refresh_redcloth( page ) }
|
|
313
398
|
end
|
|
314
399
|
|
|
315
400
|
def view( pagename, view = 'view', person = nil )
|
|
@@ -325,6 +410,7 @@ class View
|
|
|
325
410
|
|
|
326
411
|
def find( pagename )
|
|
327
412
|
return view( pagename ) if @wiki.exists?( pagename )
|
|
413
|
+
view = 'find'
|
|
328
414
|
search_term = /#{pagename}/i
|
|
329
415
|
title_results = @wiki.select { |name,page| name=~ search_term }
|
|
330
416
|
text_results = @wiki.select { |name,page| page.content=~ search_term }
|
|
@@ -334,42 +420,30 @@ class View
|
|
|
334
420
|
end
|
|
335
421
|
|
|
336
422
|
def revise( pagename, content, person, newpagename = pagename )
|
|
337
|
-
if @wiki.exists? pagename
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
423
|
+
if @wiki.exists?( pagename ) && (newpagename != pagename)
|
|
424
|
+
@wiki.revise( pagename, "#{$MESSAGES[:content_moved_to]} [[#{newpagename}]]", person )
|
|
425
|
+
@wiki.revise( newpagename, "#{$MESSAGES[:content_moved_from]} [[#{pagename}]]", 'AutomaticPageMover' )
|
|
426
|
+
@wiki.revise( newpagename, content, person )
|
|
427
|
+
else
|
|
428
|
+
@wiki.revise( newpagename, content, person )
|
|
342
429
|
end
|
|
343
|
-
|
|
430
|
+
|
|
344
431
|
end
|
|
345
432
|
|
|
346
433
|
def move( oldpagename, person, newpagename )
|
|
347
434
|
unless newpagename == oldpagename
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
435
|
+
@wiki.revise( newpagename, "#{$MESSAGES[:content_moved_from]} [[#{pagename}]]", 'AutomaticPageMover')
|
|
436
|
+
@wiki.revise( newpagename, @wiki.page( oldpagename ).content, person)
|
|
437
|
+
@wiki.revise( oldpagename, "#{$MESSAGES[:content_moved_to]} [[#{newpagename}]]", person)
|
|
351
438
|
end
|
|
352
439
|
end
|
|
353
440
|
|
|
354
441
|
def rollback( pagename, revision, person )
|
|
355
|
-
|
|
442
|
+
@wiki.rollback( pagename, revision, person )
|
|
356
443
|
end
|
|
357
444
|
|
|
358
445
|
def delete( pagename, person )
|
|
359
|
-
|
|
360
|
-
end
|
|
361
|
-
|
|
362
|
-
def mutate( pagename )
|
|
363
|
-
didexist = @wiki.exists? pagename
|
|
364
|
-
yield
|
|
365
|
-
page = @wiki.page( pagename )
|
|
366
|
-
clear_redcloth_cache( page )
|
|
367
|
-
notify :page_revised, page, page.revisions.last
|
|
368
|
-
if page.deleted?
|
|
369
|
-
notify :page_deleted, page
|
|
370
|
-
elsif !didexist
|
|
371
|
-
notify :page_created, page
|
|
372
|
-
end
|
|
446
|
+
@wiki.revise( pagename, $MESSAGES[:page_deleted], person )
|
|
373
447
|
end
|
|
374
448
|
|
|
375
449
|
def refresh_redcloth( page )
|
|
@@ -403,7 +477,7 @@ class View
|
|
|
403
477
|
def path_for( klass, view ) "#{$SETTINGS[:root_directory]}/views/#{klass}_#{view}.rhtml" end
|
|
404
478
|
|
|
405
479
|
def should_frame?( view )
|
|
406
|
-
return true unless [
|
|
480
|
+
return true unless $SETTINGS[:dont_frame_templates].include? view.downcase
|
|
407
481
|
end
|
|
408
482
|
|
|
409
483
|
def frame_erb
|