Soks 0.0.7 → 1.0.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/LICENSE.txt +2 -0
- data/README.txt +3 -2
- data/TODO.txt +31 -0
- data/bin/soks-create-wiki.rb +0 -1
- data/lib/authenticators.rb +30 -4
- data/lib/helpers/counter-helpers.rb +132 -0
- data/lib/helpers/default-helpers.rb +170 -169
- data/lib/helpers/mail2wiki-helper.rb +18 -22
- data/lib/helpers/maintenance-helpers.rb +149 -0
- data/lib/helpers/rss2wiki-helper.rb +7 -8
- data/lib/soks-model.rb +82 -54
- data/lib/soks-servlet.rb +126 -108
- data/lib/soks-storage.rb +74 -11
- data/lib/soks-utils.rb +77 -3
- data/lib/soks-view.rb +169 -103
- data/lib/soks.rb +5 -23
- data/templates/default/attachment/newpage.js +4 -13
- data/templates/default/attachment/print_stylesheet.css +2 -7
- data/templates/default/caches/readme.txt +1 -0
- data/templates/default/content/Api%20for%20classes%20to%20modify%20the%20wiki.textile +2 -0
- data/templates/default/content/Author.textile +4 -1
- data/templates/default/content/Automatic%20Summaries.textile +16 -53
- data/templates/default/content/Automatic%20linking%20between%20pages.textile +3 -3
- data/templates/default/content/{bug%3A%20competing%20edits.textile → Bug%3A%20Competing%20edits.textile} +9 -0
- data/templates/default/content/Bug%3A%20Does%20not%20make%20use%20of%20if%2Dmodified%2Dsince%20r.textile +2 -0
- data/templates/default/content/Bug%3A%20E%2Dmail%20addresses%20with%20hyphens%20not%20recognised.textile +17 -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 -1
- data/templates/default/content/Bug%3A%20Memory%20leak.textile +13 -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 +4 -0
- data/templates/default/content/Bug%3A%20Unanticipated%20Rollbacks.textile +2 -0
- data/templates/default/content/Bug%3A%20notextile%20does%20not%20prevent%20page%20inserts.textile +2 -0
- data/templates/default/content/Home%20Page.textile +3 -1
- data/templates/default/content/How%20to%20administrate%20this%20wiki.textile +23 -13
- data/templates/default/content/How%20to%20change%20the%20way%20this%20wiki%20looks.textile +3 -1
- data/templates/default/content/How%20to%20export%20a%20site%20from%20this%20wiki.textile +22 -0
- data/templates/default/content/How%20to%20get%20the%20latest%20Soks%20from%20cvs.textile +2 -0
- data/templates/default/content/How%20to%20hack%20soks.textile +2 -0
- data/templates/default/content/How%20to%20import%20a%20site%20from%20instiki.textile +2 -0
- data/templates/default/content/{How%20to%20import%20data%20to%20this%20wiki.textile → How%20to%20import%20data.textile} +3 -7
- data/templates/default/content/How%20to%20install%20Soks.textile +2 -0
- data/templates/default/content/How%20to%20password%20protect%20your%20wiki.textile +21 -11
- data/templates/default/content/How%20to%20report%20a%20bug.textile +2 -1
- data/templates/default/content/How%20to%20upgrade%20soks.textile +22 -0
- data/templates/default/content/How%20to%20use%20the%20keyboard%20shortcuts.textile +2 -2
- data/templates/default/content/How%20to%20use%20this%20wiki.textile +3 -1
- data/templates/default/content/List%20of%20changes.textile +84 -118
- data/templates/default/content/News%3A%20Version%201%2D0%2D0%20released.textile +19 -0
- data/templates/default/content/Pages%20to%20include%20in%20the%20distribution.textile +51 -0
- data/templates/default/content/Per%20Wiki%20Templates.textile +2 -0
- data/templates/default/content/Planned%20Features.textile +30 -9
- data/templates/default/content/README.textile +3 -2
- data/templates/default/content/RSS%20feed.textile +1 -1
- data/templates/default/content/Recent%20changes%20to%20this%20site.textile +283 -0
- data/templates/default/content/SOKS%20features.textile +3 -0
- data/templates/default/content/Site%20Index.textile +202 -0
- data/templates/default/content/Soks%20Licence.textile +2 -0
- data/templates/default/content/Tag%3A%20Include%20this%20page%20in%20the%20distribution.textile +6 -0
- data/templates/default/start.rb +67 -123
- data/templates/default/version.txt +1 -1
- data/templates/default/views/Page_edit.rhtml +7 -7
- data/templates/default/views/{Page_search_results.rhtml → Page_find.rhtml} +9 -3
- 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 +1 -1
- data/templates/default/views/Page_revision.rhtml +39 -0
- data/templates/default/views/Page_revisions.rhtml +13 -5
- data/templates/default/views/Page_rss.rhtml +8 -8
- data/templates/default/views/Page_view.rhtml +3 -3
- data/templates/default/views/UploadPage_edit.rhtml +8 -8
- data/templates/default/views/frame.rhtml +8 -8
- data/templates/default/views/messages.yaml +1 -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/stress_url_calls.rb +33 -0
- data/test/stress_urls.txt +68 -0
- data/test/test_counter-helper.rb +158 -0
- data/test/test_soks-helper-maintenance.rb +106 -0
- data/test/test_soks-helpers.rb +104 -0
- data/test/test_soks-model.rb +144 -0
- data/test/test_soks-servlet.rb +231 -0
- data/test/test_soks-storage.rb +70 -31
- data/test/test_soks-utils.rb +112 -13
- data/test/test_soks-view.rb +141 -3
- metadata +38 -27
- data/templates/default/content/A%20page%20with%20an%20umlaut%20%F6%20in%20its%20title.textile +0 -1
- data/templates/default/content/All%20News.textile +0 -26
- data/templates/default/content/Bil%20Kleb.textile +0 -1
- data/templates/default/content/Bil.textile +0 -1
- data/templates/default/content/Bill%20Wood.textile +0 -3
- data/templates/default/content/Bug%3A%20RSS%20feed%20does%20not%20validate.textile +0 -10
- data/templates/default/content/Bug%3A%20Type%20a%20title%20here.textile +0 -31
- data/templates/default/content/Instructions%20and%20Howtos.textile +0 -21
- data/templates/default/content/Latest%20News.textile +0 -26
- data/templates/default/content/New%20Recent%20Changes%20class.textile +0 -68
- data/templates/default/content/New%20page%20templates%20or%20categories%20code.textile +0 -68
- data/templates/default/content/News%3A%20Version%200%2E0%2E6%20Released.textile +0 -13
- data/templates/default/content/Recent%20Blog%20Entries.textile +0 -5
- data/templates/default/content/Recent%20Changes%20to%20This%20Site.textile +0 -286
- data/templates/default/content/Ruby.textile +0 -9
- data/templates/default/content/Skorgu.textile +0 -3
- data/templates/default/content/ctrl%2Dn.textile +0 -1
- data/templates/default/content/let%20me%20know.textile +0 -1
- data/templates/default/content/sandbox.textile +0 -20
- data/templates/default/content/tamc.textile +0 -1
- data/templates/default/content/tamc2.textile +0 -1
data/LICENSE.txt
CHANGED
data/README.txt
CHANGED
|
@@ -63,15 +63,16 @@ I suspect there are many, as befits its version number. In particular it has:
|
|
|
63
63
|
* Insert page commands are not inhibited by notextile, pre or code tags
|
|
64
64
|
* Does not permit browsers to cache pages
|
|
65
65
|
* Not user friendly in the way it handles simultaneous edits of a page (last person to save wins)
|
|
66
|
-
* Some bugs in layout and formatting of text.
|
|
67
|
-
* RSS feed does not completely validate
|
|
68
66
|
|
|
69
67
|
See http://www.soks.org/wiki/KnownBugs for details.
|
|
70
68
|
|
|
71
69
|
h2. RELEASES
|
|
72
70
|
|
|
71
|
+
# 2005 July 24 - soks-1.0.0 - Sixth public release (beta)
|
|
73
72
|
# 2005 Mar 21 - soks-0.0.7 - Fifth public release (alpha)
|
|
74
73
|
# 2005 Mar 15 - soks-0.0.6 - Fourth public release (alpha)
|
|
75
74
|
# 2005 Feb 12 - soks-0.0.5 - Third public release (alpha)
|
|
76
75
|
# 2005 Jan 24 - soks-0.0.4 - Second public release (alpha)
|
|
77
76
|
# 2005 Jan 11 - soks-0.0.2 - First public release (alpha)
|
|
77
|
+
|
|
78
|
+
Tag: Include this page in the distribution
|
data/TODO.txt
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
h1. TODO
|
|
2
|
+
|
|
3
|
+
h2. PUNCTUATION
|
|
4
|
+
# Make sure autolink works with all punctuation
|
|
5
|
+
# Make the page urls ignore punctuation
|
|
6
|
+
# Refactor so that urls all created in single method
|
|
7
|
+
# Make sure that input fields are correctly escaped
|
|
8
|
+
|
|
9
|
+
h2. FEATURES
|
|
10
|
+
# Seb's attachment fix
|
|
11
|
+
# Page count automatic summaries
|
|
12
|
+
|
|
13
|
+
h2. RECENT CHANGES
|
|
14
|
+
# Fix changes view (and associated recent changes view) to deal with revision merges and deletes
|
|
15
|
+
# Alter Recent Changes to only merge change runs if they happen with X (12?) hours of each other?)
|
|
16
|
+
|
|
17
|
+
h2. RSS
|
|
18
|
+
# Check order of rss on new templates
|
|
19
|
+
# Make sure that default template doesn't require authentication for new rss feeds
|
|
20
|
+
# Make the recent changes feed put the name and time of changes
|
|
21
|
+
|
|
22
|
+
h2. SPEED
|
|
23
|
+
# Profile
|
|
24
|
+
# Allow caches to be saved to disk and re-loaded
|
|
25
|
+
## Pages
|
|
26
|
+
## Links
|
|
27
|
+
## Redcloth
|
|
28
|
+
|
|
29
|
+
h2. HELPERS
|
|
30
|
+
# Check mail import
|
|
31
|
+
# Check rss feed import
|
data/bin/soks-create-wiki.rb
CHANGED
data/lib/authenticators.rb
CHANGED
|
@@ -6,11 +6,31 @@ require 'base64'
|
|
|
6
6
|
module WEBrick
|
|
7
7
|
module HTTPAuth
|
|
8
8
|
|
|
9
|
+
module SoksUserCookie
|
|
10
|
+
|
|
11
|
+
def username_from_cookie(request)
|
|
12
|
+
cookie = request.cookies.find { |cookie| cookie.name == 'username' }
|
|
13
|
+
return cookie.value if cookie
|
|
14
|
+
return nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def add_cookie(request,response)
|
|
18
|
+
cookie = WEBrick::Cookie.new( 'username', request.user )
|
|
19
|
+
cookie.path = '/'
|
|
20
|
+
cookie.expires = Time.now + ( 60 * 60 * 24 * 180 ) # Expires in 180 days
|
|
21
|
+
response.cookies << cookie
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
|
|
9
26
|
class NoAuthenticationRequired
|
|
27
|
+
include SoksUserCookie
|
|
10
28
|
|
|
11
29
|
def authenticate(req, res)
|
|
12
|
-
req.user = req.meta_vars["HTTP_X_FORWARDED_FOR"] || req.meta_vars["REMOTE_ADDR"]
|
|
30
|
+
req.user = username_from_cookie(req) || req.meta_vars["HTTP_X_FORWARDED_FOR"] || req.meta_vars["REMOTE_ADDR"]
|
|
13
31
|
end
|
|
32
|
+
|
|
33
|
+
|
|
14
34
|
end
|
|
15
35
|
|
|
16
36
|
class NotPermitted
|
|
@@ -22,14 +42,14 @@ module WEBrick
|
|
|
22
42
|
end
|
|
23
43
|
|
|
24
44
|
class AskForUserName
|
|
25
|
-
|
|
26
45
|
include WEBrick::HTTPAuth::Authenticator
|
|
46
|
+
include SoksUserCookie
|
|
27
47
|
|
|
28
48
|
AuthScheme = "Basic"
|
|
29
49
|
|
|
30
|
-
def
|
|
50
|
+
def initialize( realm = "editing" )
|
|
31
51
|
config = { :UserDB => "nodb" , :Realm => realm }
|
|
32
|
-
|
|
52
|
+
check_init(config)
|
|
33
53
|
@config = Config::BasicAuth.dup.update(config)
|
|
34
54
|
end
|
|
35
55
|
|
|
@@ -44,16 +64,20 @@ module WEBrick
|
|
|
44
64
|
end
|
|
45
65
|
info("%s: authentication succeeded.", userid)
|
|
46
66
|
req.user = userid
|
|
67
|
+
add_cookie(req,res)
|
|
68
|
+
userid
|
|
47
69
|
end
|
|
48
70
|
|
|
49
71
|
def challenge(req, res)
|
|
50
72
|
res[@response_field] = "#{@auth_scheme} realm=\"#{@realm}\""
|
|
51
73
|
raise @auth_exception
|
|
52
74
|
end
|
|
75
|
+
|
|
53
76
|
end
|
|
54
77
|
|
|
55
78
|
class SiteWidePassword
|
|
56
79
|
include Authenticator
|
|
80
|
+
include SoksUserCookie
|
|
57
81
|
|
|
58
82
|
AuthScheme = "Basic"
|
|
59
83
|
|
|
@@ -83,6 +107,8 @@ module WEBrick
|
|
|
83
107
|
end
|
|
84
108
|
info("%s: authentication succeeded.", userid)
|
|
85
109
|
req.user = userid
|
|
110
|
+
add_cookie(req,res)
|
|
111
|
+
userid
|
|
86
112
|
end
|
|
87
113
|
|
|
88
114
|
def challenge(req, res)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
class CounterObject
|
|
2
|
+
|
|
3
|
+
attr_reader :total,:start_time
|
|
4
|
+
|
|
5
|
+
def initialize( show_top = 20 )
|
|
6
|
+
@show_top = show_top
|
|
7
|
+
@counts = {}
|
|
8
|
+
@total, @start_time = 0, Time.now
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def count( thing_to_count )
|
|
12
|
+
@counts[thing_to_count] = ( @counts[thing_to_count] || 0 ) + 1
|
|
13
|
+
@total += 1
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def each
|
|
17
|
+
running_total, displayed = 0, 0
|
|
18
|
+
@counts.sort_by { |thing,count| count }.reverse_each do |thing,count|
|
|
19
|
+
break if @show_top && ( displayed >= @show_top )
|
|
20
|
+
displayed += 1
|
|
21
|
+
running_total += count
|
|
22
|
+
yield thing, count
|
|
23
|
+
end
|
|
24
|
+
if @show_top && (@show_top < @counts.size)
|
|
25
|
+
yield "#{@counts.size - @show_top} others", @total-running_total
|
|
26
|
+
end
|
|
27
|
+
yield "*Total*", "*#{@total}*"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def [](thing)
|
|
31
|
+
@counts[thing]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def empty?
|
|
35
|
+
@counts.empty?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class ViewCountHelper
|
|
41
|
+
|
|
42
|
+
attr_reader :counts
|
|
43
|
+
|
|
44
|
+
def initialize( wiki, views_to_count = [ 'view' ], page_name = 'Popular Pages', update_page_every = :hour, show_top = 20, cache_name = 'viewcount' )
|
|
45
|
+
@wiki = wiki
|
|
46
|
+
@counts = wiki.load_cache(cache_name) || CounterObject.new(show_top)
|
|
47
|
+
@views_to_count, @page_name = views_to_count, page_name
|
|
48
|
+
@wiki.watch_for(:page_viewed) { |event,page,view,author| count page, view, author }
|
|
49
|
+
@wiki.watch_for(update_page_every) { render_count_page }
|
|
50
|
+
@wiki.watch_for(:shutdown) { wiki.save_cache(cache_name,@counts)}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def count( page, view, author )
|
|
54
|
+
return unless should_count?( page, view, author )
|
|
55
|
+
@counts.count(count_key( page, view, author ))
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def should_count?( page, view, author )
|
|
59
|
+
@views_to_count.include?( view.downcase )
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def count_key( page, view, author )
|
|
63
|
+
page.name
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def render_count_page
|
|
67
|
+
content = "h1. #{@page_name}\n\n"
|
|
68
|
+
content << "Count since #{@counts.start_time}. Updated on #{Time.now}\n\n"
|
|
69
|
+
@counts.each do |page,count|
|
|
70
|
+
content << "| #{page} | #{count} |\n"
|
|
71
|
+
end
|
|
72
|
+
@wiki.revise( @page_name, content, 'AutomaticCounter' )
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class ViewerCountHelper < ViewCountHelper
|
|
78
|
+
|
|
79
|
+
def initialize( wiki, views_to_count = [ 'view' ], page_name = 'Prolific Viewers', update_page_every = :hour, show_top = 20, cache_name = 'viewercount' )
|
|
80
|
+
super( wiki, views_to_count, page_name, update_page_every, show_top, cache_name )
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def count_key( page, view, author )
|
|
84
|
+
author
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
class AuthorCountHelper
|
|
89
|
+
|
|
90
|
+
attr_reader :counts
|
|
91
|
+
|
|
92
|
+
def initialize( wiki, page_name = 'Principal Authors', update_page_every = :hour, show_top = 20, cache_name = 'authorcount' )
|
|
93
|
+
@wiki = wiki
|
|
94
|
+
@counts = wiki.load_cache(cache_name) || CounterObject.new(show_top)
|
|
95
|
+
count_from_scratch if @counts.empty?
|
|
96
|
+
@page_name = page_name
|
|
97
|
+
@wiki.watch_for(:page_revised) { |event,page,revision| count page, revision }
|
|
98
|
+
@wiki.watch_for(update_page_every) { render_count_page }
|
|
99
|
+
@wiki.watch_for(:shutdown) { wiki.save_cache(cache_name,@counts)}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def count_from_scratch
|
|
103
|
+
@wiki.each do |pagename,page|
|
|
104
|
+
page.revisions.each do |revision|
|
|
105
|
+
count( page, revision )
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def count( page, revision )
|
|
111
|
+
return unless should_count?( page, revision )
|
|
112
|
+
@counts.count(count_key( page, revision ))
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def should_count?( page, revision )
|
|
116
|
+
revision.author !~ /^Automatic/
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def count_key( page, revision )
|
|
120
|
+
revision.author
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def render_count_page
|
|
124
|
+
content = "h1. #{@page_name}\n\n"
|
|
125
|
+
content << "Count since #{@counts.start_time}. Updated on #{Time.now}\n\n"
|
|
126
|
+
@counts.each do |page,count|
|
|
127
|
+
content << "| #{page} | #{count} |\n"
|
|
128
|
+
end
|
|
129
|
+
@wiki.revise( @page_name, content, 'AutomaticCounter' )
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
end
|
|
@@ -14,13 +14,15 @@ class AutomaticUpdateCrossLinks
|
|
|
14
14
|
def new_page( page )
|
|
15
15
|
return if title_banned? page.name
|
|
16
16
|
@view.rollingmatch[ page.name ] = page
|
|
17
|
-
titleregex = Regexp.new( page.name, Regexp::IGNORECASE )
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
titleregex = Regexp.new( Regexp.escape(page.name), Regexp::IGNORECASE )
|
|
18
|
+
# Refresh any page that might mention the title of the new page
|
|
19
|
+
@wiki.each do |name, linkedpage |
|
|
20
|
+
next if linkedpage.is_a? UploadPage
|
|
21
|
+
next unless linkedpage.textile =~ titleregex
|
|
22
|
+
@view.refresh_redcloth( linkedpage )
|
|
23
|
+
# Pages can be inserted into other pages, so need to refresh those as well
|
|
24
|
+
linkedpage.inserted_into.each { |insert| @view.refresh_redcloth( insert ) }
|
|
25
|
+
end
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
def delete_page( page )
|
|
@@ -29,178 +31,196 @@ class AutomaticUpdateCrossLinks
|
|
|
29
31
|
end
|
|
30
32
|
|
|
31
33
|
def page_revised( page )
|
|
32
|
-
page.inserted_into.each { |including_page|
|
|
33
|
-
@view.refresh_redcloth( including_page )
|
|
34
|
-
}
|
|
34
|
+
page.inserted_into.each { |including_page| @view.refresh_redcloth( including_page ) }
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def update_all_pages
|
|
38
|
-
@wiki.each
|
|
39
|
-
|
|
38
|
+
@wiki.each do |pagename, page|
|
|
39
|
+
@view.rollingmatch[ page.name ] = page unless title_banned?( page.name )
|
|
40
|
+
@view.links.links[ page ] = page.links_from
|
|
41
|
+
end
|
|
42
|
+
# Not needed now, because notife d
|
|
43
|
+
#@wiki.each do |pagename, page|
|
|
44
|
+
# @view.redcloth( page )
|
|
45
|
+
#end
|
|
40
46
|
end
|
|
41
47
|
|
|
42
48
|
def title_banned?( title )
|
|
43
49
|
@banned_titles.include? title
|
|
44
|
-
end
|
|
45
|
-
|
|
50
|
+
end
|
|
46
51
|
end
|
|
47
52
|
|
|
48
53
|
class AutomaticSummary
|
|
49
54
|
|
|
50
55
|
DEFAULT_SETTINGS = {
|
|
51
|
-
:regexp_for_title => /.*/, # These six regexps act as an AND, with the exclude three acting as NOT their contents
|
|
52
|
-
:regexp_for_author => nil,
|
|
53
|
-
:regexp_for_content => nil,
|
|
54
|
-
:regexp_to_exclude_title => nil,
|
|
55
|
-
:regexp_to_exclude_author => nil,
|
|
56
|
-
:regexp_to_exclude_content => nil,
|
|
57
56
|
:max_pages_to_show => nil,
|
|
58
|
-
:
|
|
57
|
+
:description => 'This summary was created automatically',
|
|
59
58
|
:author => 'AutomaticSummary',
|
|
60
|
-
:lines_to_include =>
|
|
61
|
-
:only_new_pages => false,
|
|
59
|
+
:lines_to_include => 10,
|
|
62
60
|
:sort_pages_by => :created_on, # Could be :revised_on or :score or :name or :name_for_index, or :author
|
|
63
61
|
:reverse_sort => false,
|
|
64
|
-
:
|
|
65
|
-
:summarise_revisions => false, # If true, then can contain several revisions for same page
|
|
62
|
+
:event => :page_created, # Only summarises new pages. :page_revised would summarise changed pages
|
|
66
63
|
:remove_deleted_pages => true, # If false will keep references to deleted pages
|
|
67
|
-
:
|
|
64
|
+
:summarise_revisions => false, # If true will list revisions rather than pages
|
|
65
|
+
:merge_revisions_within => false, # If set to a number, repeats with the same author within that many seconds will be merged
|
|
68
66
|
}
|
|
69
67
|
|
|
70
|
-
attr_reader :settings, :pages_in_summary
|
|
68
|
+
attr_reader :name, :settings, :pages_in_summary
|
|
71
69
|
|
|
72
|
-
def initialize( wiki, settings = {}, &
|
|
73
|
-
@wiki, @
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
70
|
+
def initialize( wiki, name, settings = {}, &decision )
|
|
71
|
+
@wiki, @name, @decision = wiki, name, decision
|
|
72
|
+
@settings = DEFAULT_SETTINGS.merge( settings )
|
|
73
|
+
@pages_in_summary = FiniteUniqueList.new( @settings[:max_pages_to_show], @settings[:reverse_sort], @settings[:sort_pages_by] )
|
|
74
|
+
if @settings[:summarise_revisions]
|
|
75
|
+
scan_revisions_allready_in_wiki
|
|
76
|
+
else
|
|
77
|
+
scan_pages_allready_in_wiki
|
|
78
|
+
end
|
|
77
79
|
start_watching wiki
|
|
78
80
|
end
|
|
81
|
+
|
|
82
|
+
private
|
|
79
83
|
|
|
80
|
-
def
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
def start_watching( wiki )
|
|
85
|
+
wiki.watch_for( @settings[:event] ) do |event, page, revision|
|
|
86
|
+
check_whether_to_add_page( @settings[:summarise_revisions] ? revision : page )
|
|
87
|
+
end
|
|
88
|
+
return unless @settings[:remove_deleted_pages]
|
|
89
|
+
wiki.watch_for( :page_deleted ) do |event, page, revision|
|
|
90
|
+
check_whether_to_remove_page( @settings[:summarise_revisions] ? revision : page )
|
|
91
|
+
end
|
|
85
92
|
end
|
|
86
93
|
|
|
87
|
-
def
|
|
88
|
-
return unless @settings[:remove_deleted_pages]
|
|
94
|
+
def check_whether_to_add_page( page )
|
|
89
95
|
return unless summarise?( page )
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
remove_previous_revisions( page )
|
|
97
|
+
@pages_in_summary.add( page )
|
|
98
|
+
render_summary_page
|
|
92
99
|
end
|
|
93
100
|
|
|
94
|
-
|
|
101
|
+
def check_whether_to_remove_page( page )
|
|
102
|
+
render_summary_page if @pages_in_summary.remove( page )
|
|
103
|
+
end
|
|
95
104
|
|
|
96
105
|
def summarise?( page )
|
|
97
106
|
return false if page.name == @settings[:pagename]
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def scan_for_pages_to_summarise_in( wiki )
|
|
108
|
-
wiki.each( @settings[:remove_deleted_pages] ) do |name,page|
|
|
109
|
-
if @settings[:summarise_revisions]
|
|
110
|
-
scan_for_revisions_to_summarise_in( page )
|
|
111
|
-
else
|
|
112
|
-
next unless summarise? page
|
|
113
|
-
@pages_in_summary.add page
|
|
114
|
-
end
|
|
107
|
+
@decision.call( page )
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def scan_pages_allready_in_wiki
|
|
111
|
+
@wiki.each( @settings[:remove_deleted_pages] ) do |name,page|
|
|
112
|
+
next unless summarise?(page)
|
|
113
|
+
@pages_in_summary.add(page)
|
|
115
114
|
end
|
|
115
|
+
render_summary_page
|
|
116
116
|
end
|
|
117
117
|
|
|
118
|
-
def
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
118
|
+
def scan_revisions_allready_in_wiki
|
|
119
|
+
@wiki.each( @settings[:remove_deleted_pages] ) do |name,page|
|
|
120
|
+
page.revisions.each do |revision|
|
|
121
|
+
next unless summarise? revision
|
|
122
|
+
remove_previous_revisions( revision )
|
|
123
|
+
@pages_in_summary.add revision
|
|
124
|
+
end
|
|
123
125
|
end
|
|
126
|
+
render_summary_page
|
|
124
127
|
end
|
|
125
128
|
|
|
126
|
-
def remove_previous_revisions(
|
|
129
|
+
def remove_previous_revisions( revision )
|
|
130
|
+
return unless @settings[:summarise_revisions]
|
|
131
|
+
return unless @settings[:merge_revisions_within]
|
|
127
132
|
revision.number.downto(0) do |previous_number|
|
|
128
|
-
previous_revision =
|
|
133
|
+
previous_revision = revision.revisions.at( previous_number )
|
|
129
134
|
break unless previous_revision.author == revision.author
|
|
135
|
+
break unless (revision.revised_on - previous_revision.revised_on) < @settings[:merge_revisions_within]
|
|
130
136
|
@pages_in_summary.remove( previous_revision )
|
|
131
137
|
end
|
|
132
138
|
end
|
|
133
139
|
|
|
140
|
+
# These methods relate to how the summary is shown.
|
|
141
|
+
|
|
142
|
+
def render_summary_page
|
|
143
|
+
@wiki.page( name ).content =~ /(.*?<automaticsummary.*?>).*?(<\/automaticsummary>.*)/mi
|
|
144
|
+
@wiki.revise( name, ($1 || new_top) + "\n\n" + render_summary + "\n\n" + ($2 || new_tail), @settings[:author] )
|
|
145
|
+
end
|
|
146
|
+
|
|
134
147
|
def render_summary
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
content << "No pages found to summarise"
|
|
145
|
-
elsif @renderer
|
|
146
|
-
@pages_in_summary.each { |page| content << @renderer.call(page) }
|
|
147
|
-
elsif @settings[:lines_to_include]
|
|
148
|
-
@pages_in_summary.each { |page| content << render_page(page) }
|
|
148
|
+
return "No pages found to summarise" if @pages_in_summary.empty?
|
|
149
|
+
@pages_in_summary.map { |page| render_summary_of_page(page) }.to_s
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def render_summary_of_page( page )
|
|
153
|
+
content = "<div class='subpage'>"
|
|
154
|
+
content << "[[ #{page.name} ]]<br />\n\n"
|
|
155
|
+
if page.is_a? UploadPage
|
|
156
|
+
content << "[[ insert #{page.name} ]]"
|
|
149
157
|
else
|
|
150
|
-
|
|
158
|
+
content << page.content.first_lines( @settings[:lines_to_include] ).close_unmatched_html
|
|
151
159
|
end
|
|
152
|
-
content << "\n\n"
|
|
160
|
+
content << "\n\np(more). [[(more) => #{page.name}]]\n\n</div>\n"
|
|
153
161
|
end
|
|
154
|
-
|
|
155
|
-
def
|
|
156
|
-
|
|
162
|
+
|
|
163
|
+
def new_top
|
|
164
|
+
"h2. #{name}
|
|
165
|
+
|
|
166
|
+
p{font-size: x-small;}. #{@settings[:description]}
|
|
167
|
+
|
|
168
|
+
<automaticsummary warning='DO NOT EDIT between these automatic summary tags, anything you write may be overwritten without warning'>
|
|
169
|
+
"
|
|
157
170
|
end
|
|
158
171
|
|
|
159
|
-
def
|
|
160
|
-
"
|
|
172
|
+
def new_tail
|
|
173
|
+
"</automaticsummary>"
|
|
161
174
|
end
|
|
162
175
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
176
|
+
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
class AutomaticList < AutomaticSummary
|
|
166
180
|
|
|
167
|
-
def
|
|
168
|
-
@pages_in_summary
|
|
181
|
+
def render_summary
|
|
182
|
+
@pages_in_summary.map { |page| render_list_item(page) }.to_s
|
|
169
183
|
end
|
|
170
184
|
|
|
171
|
-
def
|
|
172
|
-
|
|
173
|
-
wiki.watch_for( :page_deleted ) { |event, page, revision| check_whether_to_remove_page( page, revision ) } if @settings[:remove_deleted_pages]
|
|
185
|
+
def render_list_item( page )
|
|
186
|
+
"* [[ #{page.name} ]]\n"
|
|
174
187
|
end
|
|
175
188
|
|
|
176
189
|
end
|
|
177
190
|
|
|
191
|
+
class AutomaticDetailedList < AutomaticList
|
|
192
|
+
|
|
193
|
+
def render_list_item( page )
|
|
194
|
+
"* [[ #{page.name} ]] revised on #{page.revised_on.strftime('%Y %b %d %H:%M')} by #{page.author}\n"
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
end
|
|
198
|
+
|
|
178
199
|
class AutomaticRecentChanges < AutomaticSummary
|
|
179
200
|
|
|
180
|
-
def initialize( wiki, changes = 200,
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
def render_list
|
|
201
|
+
def initialize( wiki, changes = 200, pagename = "Recent changes to this site", author = "AutomaticRecentChanges", exclude_automatic_helpers = true, merge_revisions_within = 60*60*12 )
|
|
202
|
+
super( wiki, pagename, :summarise_revisions => true,
|
|
203
|
+
:max_pages_to_show => changes,
|
|
204
|
+
:author => author,
|
|
205
|
+
:sort_pages_by => :revised_on,
|
|
206
|
+
:event => :page_revised,
|
|
207
|
+
:reverse_sort => true,
|
|
208
|
+
:remove_deleted_pages => false,
|
|
209
|
+
:merge_revisions_within => merge_revisions_within ) do |revision|
|
|
210
|
+
exclude_automatic_helpers ? revision.author !~ /^Automatic/i : true
|
|
211
|
+
end
|
|
212
|
+
wiki.watch_for( :day ) { render_summary_page }
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def render_summary
|
|
196
216
|
content = "<div class='recentchanges'>\n\nh2. Today\n\n"
|
|
197
217
|
previous_time = Time.now
|
|
198
218
|
@pages_in_summary.each do |revision|
|
|
199
219
|
unless revision.revised_on.same_day?( previous_time )
|
|
200
|
-
content << "\nh2. #{revision.revised_on.
|
|
220
|
+
content << "\nh2. #{revision.revised_on.relative_day}\n\n"
|
|
201
221
|
previous_time = revision.revised_on
|
|
202
222
|
end
|
|
203
|
-
content << "* #{revision.revised_on.strftime('%H:%M')} - [[#{revision.name}]] revised by #{revision.author}\n"
|
|
223
|
+
content << "* #{revision.revised_on.strftime('%H:%M')} - [[#{revision.name}]] revised by #{revision.author} ([[changes => /revision/#{revision.name}?time=#{revision.created_on.to_i}]])\n"
|
|
204
224
|
end
|
|
205
225
|
content << "\n\n</div>"
|
|
206
226
|
end
|
|
@@ -210,55 +230,52 @@ end
|
|
|
210
230
|
class AutomaticOnePageIndex
|
|
211
231
|
|
|
212
232
|
def initialize( wiki, pagename = "Site Index", author = "AutomaticIndex" )
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
:only_new_pages => true,
|
|
217
|
-
:sort_pages_by => :name_for_index,
|
|
218
|
-
} )
|
|
233
|
+
AutomaticList.new( wiki, pagename, :author => author, :sort_pages_by => :name_for_index ) do |page|
|
|
234
|
+
page.name !~ /#{Regexp.escape(pagename)}/io
|
|
235
|
+
end
|
|
219
236
|
end
|
|
220
237
|
|
|
221
238
|
end
|
|
222
239
|
|
|
223
240
|
class AutomaticMultiPageIndex
|
|
224
|
-
|
|
241
|
+
|
|
225
242
|
def initialize( wiki, pageroot = "Site Index", author = "AutomaticIndex" )
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
end
|
|
249
|
-
|
|
243
|
+
('A'..'Z').each do |letter|
|
|
244
|
+
AutomaticList.new( wiki, "#{pageroot} #{letter}.", :author => author, :sort_pages_by => :name_for_index ) do |page|
|
|
245
|
+
if page.name =~ /^#{Regexp.escape(pageroot)} ([a-z]|Other)\./i
|
|
246
|
+
false
|
|
247
|
+
else
|
|
248
|
+
page.name =~ /^#{letter}.*/i
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
AutomaticList.new( wiki, "#{pageroot} Other.", :author => author, :sort_pages_by => :name_for_index ) do |page|
|
|
254
|
+
if page.name =~ /^#{Regexp.escape(pageroot)} ([a-z]|Other)\./i
|
|
255
|
+
false
|
|
256
|
+
else
|
|
257
|
+
page.name =~ /^[^A-Za-z].*/i
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
AutomaticList.new( wiki, pageroot, :author => author, :sort_pages_by => :name_for_index ) do |page|
|
|
262
|
+
page.name =~ /^#{Regexp.escape(pageroot)} ([a-z]|Other)\./i
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
250
267
|
class AutomaticCalendar
|
|
251
268
|
|
|
252
269
|
attr_reader :month_pagename, :day_pagename
|
|
253
270
|
|
|
254
271
|
def initialize( wiki, month_pagename = '%Y %b', day_pagename = '%Y %b %d', author = "AutomaticCalendar" )
|
|
255
272
|
@wiki, @month_pagename, @day_pagename, @author = wiki, month_pagename, day_pagename, author
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
273
|
+
render_coming_year
|
|
274
|
+
@wiki.watch_for(:month) { render_coming_year }
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def render_coming_year
|
|
278
|
+
Time.now.month.upto( Time.now.month+12 ) { |m| render_month( m ) }
|
|
262
279
|
end
|
|
263
280
|
|
|
264
281
|
def render_month( month )
|
|
@@ -278,7 +295,7 @@ class AutomaticCalendar
|
|
|
278
295
|
end
|
|
279
296
|
day.wday.upto( 5 ) { content << "| . " }
|
|
280
297
|
content << "|\n"
|
|
281
|
-
content << "\n\n
|
|
298
|
+
content << "\n\n#{month_pagename( month-1 )} #{month_pagename( month+1 )} \n"
|
|
282
299
|
content << "\n</div>"
|
|
283
300
|
end
|
|
284
301
|
|
|
@@ -308,12 +325,7 @@ class AutomaticUpcomingEvents
|
|
|
308
325
|
def initialize( wiki, calendar, days_passed = 0, days_future = 7, pagename = 'Upcoming Events', author = "AutomaticUpcomingEvents" )
|
|
309
326
|
@wiki, @calendar, @days_passed, @days__future, @pagename, @author = wiki, calendar, days_passed, days_future, pagename, author
|
|
310
327
|
@wiki.watch_for( :page_revised ) { |event, page| page_revised( page ) }
|
|
311
|
-
|
|
312
|
-
loop do
|
|
313
|
-
render_upcoming_events
|
|
314
|
-
sleep(60*60)
|
|
315
|
-
end
|
|
316
|
-
end
|
|
328
|
+
@wiki.watch_for( :day ) { render_upcoming_events }
|
|
317
329
|
end
|
|
318
330
|
|
|
319
331
|
def page_revised( page )
|
|
@@ -324,7 +336,7 @@ class AutomaticUpcomingEvents
|
|
|
324
336
|
content = "<div class='upcomingevents'>\n\n"
|
|
325
337
|
Time.now.day.upto( Time.now.day+7 ) do |day|
|
|
326
338
|
time = @calendar.time_for( Time.now.month, day )
|
|
327
|
-
content << "| [[ #{relative_day
|
|
339
|
+
content << "| [[ #{time.relative_day} => #{@calendar.day_pagename( time )} ]] |"
|
|
328
340
|
content << (@wiki.exists?( @calendar.day_pagename(time) ) ? render_event( @calendar.day_pagename( time ) ) : " |\n")
|
|
329
341
|
end
|
|
330
342
|
content << "\n\np(more). [[(more) => #{@calendar.month_pagename}]]\n\n"
|
|
@@ -332,17 +344,6 @@ class AutomaticUpcomingEvents
|
|
|
332
344
|
@wiki.revise( "Upcoming Events", content , "AutomaticCalendar" )
|
|
333
345
|
end
|
|
334
346
|
|
|
335
|
-
def relative_day( date )
|
|
336
|
-
case ( (date - @calendar.time_for( Time.now.month, Time.now.day ) ) / ( 60 * 60 * 24 ) ) # Days difference
|
|
337
|
-
when -7..-2 ; date.strftime('Last %A')
|
|
338
|
-
when -1 ; "Yesterday"
|
|
339
|
-
when 0 ; "Today"
|
|
340
|
-
when 1 ; "Tomorrow"
|
|
341
|
-
when 2..7 ; date.strftime('%A')
|
|
342
|
-
else ; date.strftime('%A %d')
|
|
343
|
-
end
|
|
344
|
-
end
|
|
345
|
-
|
|
346
347
|
def render_event( name )
|
|
347
348
|
page = @wiki.page( name )
|
|
348
349
|
headings = page.textile.select { |line| line =~ /^h\d\./ }
|