Soks 0.0.7 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. data/LICENSE.txt +2 -0
  2. data/README.txt +3 -2
  3. data/TODO.txt +31 -0
  4. data/bin/soks-create-wiki.rb +0 -1
  5. data/lib/authenticators.rb +30 -4
  6. data/lib/helpers/counter-helpers.rb +132 -0
  7. data/lib/helpers/default-helpers.rb +170 -169
  8. data/lib/helpers/mail2wiki-helper.rb +18 -22
  9. data/lib/helpers/maintenance-helpers.rb +149 -0
  10. data/lib/helpers/rss2wiki-helper.rb +7 -8
  11. data/lib/soks-model.rb +82 -54
  12. data/lib/soks-servlet.rb +126 -108
  13. data/lib/soks-storage.rb +74 -11
  14. data/lib/soks-utils.rb +77 -3
  15. data/lib/soks-view.rb +169 -103
  16. data/lib/soks.rb +5 -23
  17. data/templates/default/attachment/newpage.js +4 -13
  18. data/templates/default/attachment/print_stylesheet.css +2 -7
  19. data/templates/default/caches/readme.txt +1 -0
  20. data/templates/default/content/Api%20for%20classes%20to%20modify%20the%20wiki.textile +2 -0
  21. data/templates/default/content/Author.textile +4 -1
  22. data/templates/default/content/Automatic%20Summaries.textile +16 -53
  23. data/templates/default/content/Automatic%20linking%20between%20pages.textile +3 -3
  24. data/templates/default/content/{bug%3A%20competing%20edits.textile → Bug%3A%20Competing%20edits.textile} +9 -0
  25. data/templates/default/content/Bug%3A%20Does%20not%20make%20use%20of%20if%2Dmodified%2Dsince%20r.textile +2 -0
  26. data/templates/default/content/Bug%3A%20E%2Dmail%20addresses%20with%20hyphens%20not%20recognised.textile +17 -0
  27. data/templates/default/content/Bug%3A%20Email%20adresses%20in%20page%20titles%20cause%20incorrec.textile +3 -0
  28. data/templates/default/content/Bug%3A%20GEM%20limits%20title%20lengths.textile +3 -1
  29. data/templates/default/content/Bug%3A%20Memory%20leak.textile +13 -0
  30. data/templates/default/content/Bug%3A%20Pages%20that%20link%20here%20may%20not%20appear%20on%20r.textile +13 -0
  31. data/templates/default/content/Bug%3A%20Textile%20mishandles%20paragraphs.textile +4 -0
  32. data/templates/default/content/Bug%3A%20Unanticipated%20Rollbacks.textile +2 -0
  33. data/templates/default/content/Bug%3A%20notextile%20does%20not%20prevent%20page%20inserts.textile +2 -0
  34. data/templates/default/content/Home%20Page.textile +3 -1
  35. data/templates/default/content/How%20to%20administrate%20this%20wiki.textile +23 -13
  36. data/templates/default/content/How%20to%20change%20the%20way%20this%20wiki%20looks.textile +3 -1
  37. data/templates/default/content/How%20to%20export%20a%20site%20from%20this%20wiki.textile +22 -0
  38. data/templates/default/content/How%20to%20get%20the%20latest%20Soks%20from%20cvs.textile +2 -0
  39. data/templates/default/content/How%20to%20hack%20soks.textile +2 -0
  40. data/templates/default/content/How%20to%20import%20a%20site%20from%20instiki.textile +2 -0
  41. data/templates/default/content/{How%20to%20import%20data%20to%20this%20wiki.textile → How%20to%20import%20data.textile} +3 -7
  42. data/templates/default/content/How%20to%20install%20Soks.textile +2 -0
  43. data/templates/default/content/How%20to%20password%20protect%20your%20wiki.textile +21 -11
  44. data/templates/default/content/How%20to%20report%20a%20bug.textile +2 -1
  45. data/templates/default/content/How%20to%20upgrade%20soks.textile +22 -0
  46. data/templates/default/content/How%20to%20use%20the%20keyboard%20shortcuts.textile +2 -2
  47. data/templates/default/content/How%20to%20use%20this%20wiki.textile +3 -1
  48. data/templates/default/content/List%20of%20changes.textile +84 -118
  49. data/templates/default/content/News%3A%20Version%201%2D0%2D0%20released.textile +19 -0
  50. data/templates/default/content/Pages%20to%20include%20in%20the%20distribution.textile +51 -0
  51. data/templates/default/content/Per%20Wiki%20Templates.textile +2 -0
  52. data/templates/default/content/Planned%20Features.textile +30 -9
  53. data/templates/default/content/README.textile +3 -2
  54. data/templates/default/content/RSS%20feed.textile +1 -1
  55. data/templates/default/content/Recent%20changes%20to%20this%20site.textile +283 -0
  56. data/templates/default/content/SOKS%20features.textile +3 -0
  57. data/templates/default/content/Site%20Index.textile +202 -0
  58. data/templates/default/content/Soks%20Licence.textile +2 -0
  59. data/templates/default/content/Tag%3A%20Include%20this%20page%20in%20the%20distribution.textile +6 -0
  60. data/templates/default/start.rb +67 -123
  61. data/templates/default/version.txt +1 -1
  62. data/templates/default/views/Page_edit.rhtml +7 -7
  63. data/templates/default/views/{Page_search_results.rhtml → Page_find.rhtml} +9 -3
  64. data/templates/default/views/Page_linksfromrss.rhtml +24 -0
  65. data/templates/default/views/Page_listrss.rhtml +46 -0
  66. data/templates/default/views/Page_meta.rhtml +1 -1
  67. data/templates/default/views/Page_revision.rhtml +39 -0
  68. data/templates/default/views/Page_revisions.rhtml +13 -5
  69. data/templates/default/views/Page_rss.rhtml +8 -8
  70. data/templates/default/views/Page_view.rhtml +3 -3
  71. data/templates/default/views/UploadPage_edit.rhtml +8 -8
  72. data/templates/default/views/frame.rhtml +8 -8
  73. data/templates/default/views/messages.yaml +1 -0
  74. data/test/html/2006Mar.html +66 -0
  75. data/test/html/poignant.html +36 -0
  76. data/test/html/poignant.textile +36 -0
  77. data/test/mock-objects.rb +69 -0
  78. data/test/stress_url_calls.rb +33 -0
  79. data/test/stress_urls.txt +68 -0
  80. data/test/test_counter-helper.rb +158 -0
  81. data/test/test_soks-helper-maintenance.rb +106 -0
  82. data/test/test_soks-helpers.rb +104 -0
  83. data/test/test_soks-model.rb +144 -0
  84. data/test/test_soks-servlet.rb +231 -0
  85. data/test/test_soks-storage.rb +70 -31
  86. data/test/test_soks-utils.rb +112 -13
  87. data/test/test_soks-view.rb +141 -3
  88. metadata +38 -27
  89. data/templates/default/content/A%20page%20with%20an%20umlaut%20%F6%20in%20its%20title.textile +0 -1
  90. data/templates/default/content/All%20News.textile +0 -26
  91. data/templates/default/content/Bil%20Kleb.textile +0 -1
  92. data/templates/default/content/Bil.textile +0 -1
  93. data/templates/default/content/Bill%20Wood.textile +0 -3
  94. data/templates/default/content/Bug%3A%20RSS%20feed%20does%20not%20validate.textile +0 -10
  95. data/templates/default/content/Bug%3A%20Type%20a%20title%20here.textile +0 -31
  96. data/templates/default/content/Instructions%20and%20Howtos.textile +0 -21
  97. data/templates/default/content/Latest%20News.textile +0 -26
  98. data/templates/default/content/New%20Recent%20Changes%20class.textile +0 -68
  99. data/templates/default/content/New%20page%20templates%20or%20categories%20code.textile +0 -68
  100. data/templates/default/content/News%3A%20Version%200%2E0%2E6%20Released.textile +0 -13
  101. data/templates/default/content/Recent%20Blog%20Entries.textile +0 -5
  102. data/templates/default/content/Recent%20Changes%20to%20This%20Site.textile +0 -286
  103. data/templates/default/content/Ruby.textile +0 -9
  104. data/templates/default/content/Skorgu.textile +0 -3
  105. data/templates/default/content/ctrl%2Dn.textile +0 -1
  106. data/templates/default/content/let%20me%20know.textile +0 -1
  107. data/templates/default/content/sandbox.textile +0 -20
  108. data/templates/default/content/tamc.textile +0 -1
  109. data/templates/default/content/tamc2.textile +0 -1
data/LICENSE.txt CHANGED
@@ -62,3 +62,5 @@ GPL (see the file GPL), or the conditions below:
62
62
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
63
63
  PURPOSE.
64
64
  </pre>
65
+
66
+ Tag: Include this page in the distribution
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
@@ -181,5 +181,4 @@ if start && File.exists?( script )
181
181
  end
182
182
  puts "Starting #{destination} on #{url}"
183
183
  require script
184
- StartSoks::start
185
184
  end
@@ -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 initialize( realm = "editing" )
50
+ def initialize( realm = "editing" )
31
51
  config = { :UserDB => "nodb" , :Realm => realm }
32
- check_init(config)
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
- @wiki.each { |name, linkedpage |
19
- if linkedpage.textile =~ titleregex
20
- @view.refresh_redcloth( linkedpage )
21
- linkedpage.inserted_into.each { |insert| @view.refresh_redcloth( insert ) }
22
- end
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 { |pagename, page| @view.rollingmatch[ page.name ] = page unless title_banned?( page.name )}
39
- @wiki.each { |pagename, page| @view.refresh_redcloth( page ) }
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
- :pagename => 'Summary',
57
+ :description => 'This summary was created automatically',
59
58
  :author => 'AutomaticSummary',
60
- :lines_to_include => nil, # Or pass a block
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
- :include_metadata => false, # Includes author and time in summary
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
- :merge_revisions => false, # If true, repeats with the same author will be merged
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 = {}, &renderer )
73
- @wiki, @settings, @renderer = wiki, DEFAULT_SETTINGS.merge( settings ), renderer
74
- setup_list
75
- scan_for_pages_to_summarise_in wiki
76
- render_summary
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 check_whether_to_add_page( page, revision )
81
- return unless summarise?( page )
82
- remove_previous_revisions( page, revision ) if @settings[:merge_revisions]
83
- @pages_in_summary.add( @settings[:summarise_revisions] ? revision : page )
84
- render_summary
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 check_whether_to_remove_page( page, revision )
88
- return unless @settings[:remove_deleted_pages]
94
+ def check_whether_to_add_page( page )
89
95
  return unless summarise?( page )
90
- return unless @pages_in_summary.remove( @settings[:summarise_revisions] ? revision : page )
91
- render_summary
96
+ remove_previous_revisions( page )
97
+ @pages_in_summary.add( page )
98
+ render_summary_page
92
99
  end
93
100
 
94
- private
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
- return false if @settings[:regexp_for_title] && page.name !~ @settings[:regexp_for_title]
99
- return false if @settings[:regexp_for_author] && page.author !~ @settings[:regexp_for_author]
100
- return false if @settings[:regexp_for_content] && page.content !~ @settings[:regexp_for_content]
101
- return false if @settings[:regexp_to_exclude_title] && page.name =~ @settings[:regexp_to_exclude_title]
102
- return false if @settings[:regexp_to_exclude_author] && page.author =~ @settings[:regexp_to_exclude_author]
103
- return false if @settings[:regexp_to_exclude_content] && page.content =~ @settings[:regexp_to_exclude_content]
104
- return true
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 scan_for_revisions_to_summarise_in( page )
119
- page.revisions.each do |revision|
120
- next unless summarise? revision
121
- remove_previous_revisions( page, revision ) if @settings[:merge_revisions]
122
- @pages_in_summary.add revision
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( page, revision )
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 = page.revisions.at( previous_number )
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
- @wiki.page( @settings[:pagename] ).textile =~ /(.*?<automaticsummary>).*?(<\/automaticsummary>.*)/mi
136
- top = $1 || "h2. #{@settings[:pagename]}\n\np{font-size: x-small;}. #{@settings[:pagename]} was created automatically from pages whose title matches ==#{@settings[:regexp_for_title].inspect}==.\n\n<automaticsummary>"
137
- tail = $2 || "</automaticsummary>"
138
- @wiki.revise( @settings[:pagename], top + render_list + tail , @settings[:author] )
139
- end
140
-
141
- def render_list
142
- content = "\n\n"
143
- if @pages_in_summary.empty?
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
- @pages_in_summary.each { |page| content << render_link_to(page) }
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 render_page( page )
156
- "<div class='subpage'>\n[[ #{page.name} ]] #{ @settings[:include_metadata] ? render_meta_data( page ) : "" }<br />\n\n#{page.textile.first_lines( @settings[:lines_to_include] ).close_unmatched_html}\n\np(more). [[(more) => #{page.name}]]\n\n</div>\n"
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 render_link_to( page )
160
- "* [[ #{page.name} ]] #{ @settings[:include_metadata] ? render_meta_data( page ) : "" }\n"
172
+ def new_tail
173
+ "</automaticsummary>"
161
174
  end
162
175
 
163
- def render_meta_data( page )
164
- "revised on #{page.revised_on.strftime('%Y %b %d %H:%M')} by #{page.author}"
165
- end
176
+
177
+ end
178
+
179
+ class AutomaticList < AutomaticSummary
166
180
 
167
- def setup_list
168
- @pages_in_summary = FiniteUniqueList.new( @settings[:max_pages_to_show], @settings[:reverse_sort], @settings[:sort_pages_by] )
181
+ def render_summary
182
+ @pages_in_summary.map { |page| render_list_item(page) }.to_s
169
183
  end
170
184
 
171
- def start_watching( wiki )
172
- wiki.watch_for( @settings[:only_new_pages] == true ? :page_created : :page_revised ) { |event, page, revision| check_whether_to_add_page( page, revision ) }
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, title_regexp = /.*/, pagename = "Recent Changes to This Site", author = "AutomaticRecentChanges", exclude_automatic_helpers = true, merge_revisions = true )
181
- super( wiki, { :pagename => pagename,
182
- :regexp_for_title => title_regexp,
183
- :regexp_to_exclude_author => exclude_automatic_helpers && /^Automatic/i,
184
- :max_pages_to_show => changes,
185
- :author => author,
186
- :only_new_pages => false,
187
- :sort_pages_by => :revised_on,
188
- :summarise_revisions => true,
189
- :reverse_sort => true,
190
- :remove_deleted_pages => false,
191
- :merge_revisions => merge_revisions,
192
- } )
193
- end
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.strftime('%a %d %b %Y')}\n\n"
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
- AutomaticSummary.new( wiki, { :pagename => pagename,
214
- :regexp_to_exclude_title => /#{Regexp.escape(pagename)}/i,
215
- :author => author,
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
- content = "h1. #{pageroot}\n\n"
227
- ('A'..'Z').each do |letter|
228
- AutomaticSummary.new( wiki, { :regexp_for_title => /^#{letter}.*/i,
229
- :pagename => "#{pageroot} #{letter}.",
230
- :regexp_to_exclude_title => /^#{Regexp.escape(pageroot)} ([a-z]|Other)\./i,
231
- :author => author,
232
- :only_new_pages => true,
233
- :sort_pages_by => :name_for_index,
234
- } )
235
- content << "* #{pageroot} #{letter}.\n"
236
- end
237
- AutomaticSummary.new( wiki, { :regexp_for_title => /^[^A-Za-z].*/,
238
- :pagename => "#{pageroot} Other.",
239
- :regexp_to_exclude_title => /^#{Regexp.escape(pageroot)} ([a-z]|Other)\./i,
240
- :author => author,
241
- :only_new_pages => true,
242
- :sort_pages_by => :name_for_index,
243
- } )
244
- content << "* #{pageroot} Other.\n"
245
- wiki.revise( pageroot, content, author )
246
- end
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
- Thread.new do
257
- loop do
258
- Time.now.month.upto( Time.now.month+12 ) { |m| render_month( m ) }
259
- sleep(60*60*24)
260
- end
261
- end
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< #{month_pagename( month-1 )} #{month_pagename( month+1 )} >\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
- Thread.new do
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( time ) } => #{@calendar.day_pagename( time )} ]] |"
339
+ content << "| [[ #{time.relative_day} => #{@calendar.day_pagename( time )} ]] |"
328
340
  content << (@wiki.exists?( @calendar.day_pagename(time) ) ? render_event( @calendar.day_pagename( time ) ) : "&nbsp; |\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\./ }