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.
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\./ }