Soks 0.0.7 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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\./ }
|