Pimki 1.4.092 → 1.5.092

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.
@@ -0,0 +1,61 @@
1
+ <% @title = "Advanced Search Options" %>
2
+ <%= sub_template "top" %>
3
+
4
+ <h3>Advanced Search Options:</h3>
5
+
6
+ <form action="../search/" method="post">
7
+ <table>
8
+ <tr>
9
+ <td>Search string:</td>
10
+ <td colspan="3"><input type='text' name='query' id='query' size='60' /></td>
11
+ </tr>
12
+ <tr><td>&nbsp;</td></tr>
13
+ <tr>
14
+ <td colspan="3"><input type='checkbox' name='case'>Case sensitive</input></td>
15
+ </tr>
16
+ <tr>
17
+ <td>Search string as</td>
18
+ <td><input type='Radio' name='expression' value='regex' checked>Regular Expression</input></td>
19
+ <td><input type='Radio' name='expression' value='all'>All words</input></td>
20
+ <td><input type='Radio' name='expression' value='exact'>Exact phrase</input></td>
21
+ </tr>
22
+ <tr>
23
+ <td>Search in</td>
24
+ <td><input type='Radio' name='where' value='all' checked>Pages &amp; Bliki entries</input></td>
25
+ <td><input type='Radio' name='where' value='pages'>Pages only</input></td>
26
+ <td><input type='Radio' name='where' value='bliki'>Bliki entries only</input></td>
27
+ </tr>
28
+ <tr>
29
+ <td>Search on</td>
30
+ <td><input type='Radio' name='fields' value='both' checked>Pages names &amp; contents</input></td>
31
+ <td><input type='Radio' name='fields' value='names'>Page names only</input></td>
32
+ <td><input type='Radio' name='fields' value='contents'>Page contents only</input></td>
33
+ </tr>
34
+ <tr>
35
+ <td colspan="2">Limit search to selected categories:</td>
36
+ <td colspan="2"><select id="category" name="category" size="3" multiple style="width:200">
37
+ <option value="noselect">------------
38
+ <% for category in web.categories %>
39
+ <option value="<%= category %>"><%= category %>
40
+ <% end %>
41
+ </select>
42
+ </td>
43
+ </tr>
44
+ <tr>
45
+ <td colspan="2">Limit search to selected authors:</td>
46
+ <td colspan="2"><select id="author" name="author" size="3" multiple style="width:200">
47
+ <option value="noselect">------------
48
+ <% for author in web.authors %>
49
+ <option value="<%= author %>"><%= author %>
50
+ <% end %>
51
+ </select>
52
+ </tr>
53
+ <tr>
54
+ <td><input type='submit' value='Search'></td>
55
+ </tr>
56
+ </table>
57
+ </form>
58
+
59
+
60
+ <%= sub_template "bottom" %>
61
+
@@ -79,9 +79,9 @@ function validateSelection() {
79
79
  </script>
80
80
 
81
81
  <h2>Apply filter:</h2>
82
+ <form class="navigation" action="bliki" action="post" onSubmit="return validateSelection();">
82
83
  <table><tr>
83
84
  <td>Show only pages authored by:</td>
84
- <form class="navigation" action="bliki" action="post" onSubmit="return validateSelection();">
85
85
  <td>
86
86
  <select id="sel_author" name="authorname" size="1">
87
87
  <option value="noselect">------------
@@ -94,8 +94,10 @@ function validateSelection() {
94
94
  <td>Regexp search on all entries:</td>
95
95
  <td><input type="text" id="search_regex" name="regexp"></td>
96
96
  <td><input type="submit" name="Search" value="Search"></td>
97
- </form>
97
+ </tr><tr>
98
+ <td>Or go to <a href='../adv_search/'>Advanced Search</a></td>
98
99
  </tr></table>
100
+ </form>
99
101
 
100
102
  <%= sub_template "bottom" %>
101
103
 
@@ -63,7 +63,7 @@
63
63
 
64
64
  <% if @page.revisions.length > 1 %>
65
65
  <span id="show_changes">
66
- | <a href="#" onClick="toggleChanges(); return false;">See changes</a>
66
+ | <a href="#" onClick="toggleChanges(); return false;">Show changes</a>
67
67
  </span>
68
68
  <span id="hide_changes" style="display: none">
69
69
  | <a href="#" onClick="toggleChanges(); return false;">Hide changes</a>
@@ -1,8 +1,8 @@
1
1
  <%
2
2
  @title = "Editing #{@page.plain_name}"
3
- @content_width = 800
3
+ @content_width = 810
4
4
  @hide_navigation = true
5
- @style_additions = "#Container, #Content {padding-left:100px;}"
5
+ @style_additions = "#Content, #Container {padding-left:100px;}"
6
6
  %>
7
7
  <%= sub_template "top" %>
8
8
 
@@ -10,6 +10,7 @@
10
10
 
11
11
  <%= render_markup_help %>
12
12
 
13
+ <div id='main'>
13
14
  <form style="float:right" id="editForm" action="../save/<%= @page.name %>" method="post" onSubmit="cleanAuthorName();">
14
15
  <p>
15
16
  <textarea name="content" style="width: 550px; height: 430px"><%= @page.content %></textarea>
@@ -31,7 +32,7 @@
31
32
  | <a href="../cancel_edit/<%= @page.name %>">Cancel</a> <small>(unlocks page)</small>
32
33
  </p>
33
34
  </form>
34
-
35
+ </div>
35
36
  <script language="JavaScript1.2">
36
37
  function cleanAuthorName() {
37
38
  if (document.getElementById('authorName').value == "") {
@@ -24,7 +24,7 @@
24
24
  </p>
25
25
  <p>For the above options you can choose if you want to limit the number of results. Enter the number of items you wish, or 0 for all items: <input type="textfield" size="20", name="limit" value="<%= @list_limit %>" /></p>
26
26
  <p><input type="radio" name="type" value="user" <%= 'checked' if @menu_type == 'user' %>>Or just write your own menu contents (as a regular Wiki page):
27
- <textarea name="content" style="width: 400px; height: 300px"><%= @menu_content.content %></textarea>
27
+ <textarea name="content" style="width: 400px; height: 300px"><%= !@menu_content.nil? && Page === @menu_content ? @menu_content.content : @menu_content %></textarea>
28
28
  </p>
29
29
 
30
30
  <% if Page === @menu_content && @menu_content.revisions.length > 1
@@ -1,6 +1,8 @@
1
1
  <% @title = "Edit Web" %>
2
2
  <%= sub_template "top" %>
3
3
 
4
+ <%= "<p style='font-size: 15px; font-variant: italic; color: red;'>#{@message}</span>" if @message %>
5
+
4
6
  <form action="../update_web" id="setup" method="post" onSubmit="cleanAddress(); return validateSetup()">
5
7
  <h2 style="margin-bottom: 3px">Name and address</h2>
6
8
  <div class="help">
@@ -54,6 +56,15 @@
54
56
  <textarea id="additionalStyle" style="display: none; margin-top: 10px; margin-bottom: 5px; width: 560px; height: 200px" name="additional_style"><%= @web.additional_style %></textarea>
55
57
  </div>
56
58
 
59
+ <h2 style="margin-bottom: 3px">Persistence Options (all webs)</h2>
60
+ <div class="help">
61
+ You can set how often to take snapshots of the web data. (All incremental changes are saved as command_logs; snapshots supercede those with the full data). Recommended values are 1-2 hours if you turn the web on and off often, or 24-48 hours if this is a long running service.<br />
62
+ Snapshots will not be taken if there are no incremental changes (in the form of command_logs). You can always force a snapshot in the administrative tasks below.
63
+ </div>
64
+ <div class="inputBox">
65
+ <input align='right' type="text" name="snapshots_interval" value='<%= @snapshot_interval %>' /> hour(s).
66
+ </div>
67
+
57
68
  <h2 style="margin-bottom: 3px">Password protection for this web (<%= @web.name %>)</h2>
58
69
  <div class="help">
59
70
  This is the password that visitors need to login to view and edit this web. Setting the password to nothing will remove the password protection.
@@ -148,27 +159,29 @@
148
159
  <input type="password" id="system_password" name="system_password">
149
160
  and
150
161
  <input type="submit" value="Update Web">
151
- <br/><br/>
152
- ...or forget changes and <a href="/new_web/">create a new web</a>
153
162
  </small>
154
163
  </p>
155
-
156
164
  </form>
157
165
 
158
166
  <br/>
159
- <h1>Other administrative tasks</h1>
160
-
161
- <form action="../remove_orphaned_pages" id="remove_orphaned_pages" method="post">
167
+ <h2>Other administrative tasks</h2>
168
+ <form action="../administrate" id="administrate" method="post">
169
+ <p style="font-size: 10px;">
170
+ Clean up by entering system password
171
+ <input type="password" id="system_password" name="system_password">
172
+ and...
173
+ </p>
174
+ <p align='right'>
175
+ <input type="submit" name='action' value="Delete Orphan Pages"><br/>
176
+ <input type="submit" name='action' value="Clear Render Cache"><br/>
177
+ <input type="submit" name='action' value="Force Data Snapshot"><br/>
178
+ <input type="submit" name='action' value="Clean Storage"><br/>
179
+ </p>
180
+ </form>
162
181
 
163
- <p align="right">
164
- <small>
165
- Clean up by entering system password
166
- <input type="password" id="system_password" name="system_password">
167
- and
168
- <input type="submit" value="Delete Orphan Pages">
169
- </small>
170
- </p>
182
+ <p style='font-size: 10px;'>...or forget all your changes and <a href="/new_web/">create a new web</a>.</p>
171
183
 
184
+
172
185
  <script>
173
186
  function proposeAddress() {
174
187
  document.getElementById('address').value =
@@ -36,7 +36,7 @@
36
36
 
37
37
  <% if @page.revisions.length > 1 %>
38
38
  <span id="show_changes">
39
- | <a href="#" onClick="toggleChanges(); return false;">See changes</a>
39
+ | <a href="#" onClick="toggleChanges(); return false;">Show changes</a>
40
40
  </span>
41
41
  <span id="hide_changes" style="display: none">
42
42
  | <a href="#" onClick="toggleChanges(); return false;">Hide changes</a>
@@ -23,6 +23,15 @@
23
23
 
24
24
  <div class="navigation">
25
25
 
26
+ <% if @revision.previous_revision %>
27
+ <a href="../revision/<%= @page.name %>?rev=<%= @revision.previous_revision.number %>" class="navlink">Back in time</a>
28
+ (<%= @revision.previous_revision.number + 1 %> more)
29
+ <% end %>
30
+
31
+ <% if @revision.next_revision && @revision.previous_revision %>
32
+ |
33
+ <% end %>
34
+
26
35
  <% if @revision.next_revision %>
27
36
  <% if @revision.next_revision.number < (@page.revisions.length - 1) %>
28
37
  <a href="../revision/<%= @page.name %>?rev=<%= @revision.next_revision.number %>" class="navlink">
@@ -33,20 +42,11 @@
33
42
  (<%= @revision.page.revisions.length - @revision.next_revision.number %> more)
34
43
  <% end %>
35
44
 
36
- <% if @revision.next_revision && @revision.previous_revision %>
37
- |
38
- <% end %>
39
-
40
- <% if @revision.previous_revision %>
41
- <a href="../revision/<%= @page.name %>?rev=<%= @revision.previous_revision.number %>" class="navlink">Back in time</a>
42
- (<%= @revision.previous_revision.number + 1 %> more)
43
- <% end %>
44
-
45
- | <a href="../show/<%= @page.name %>" class="navlink">See current</a>
45
+ | <a href="../show/<%= @page.name %>" class="navlink">Show current</a>
46
46
 
47
47
  <% if @revision.previous_revision %>
48
48
  <span id="show_changes">
49
- | <a href="#" onClick="toggleChanges(); return false;">See changes</a>
49
+ | <a href="#" onClick="toggleChanges(); return false;">Show changes</a>
50
50
  </span>
51
51
  <span id="hide_changes" style="display: none">
52
52
  | <a href="#" onClick="toggleChanges(); return false;">Hide changes</a>
@@ -60,6 +60,12 @@
60
60
  | Linked from: <%= @page.references.collect { |ref| "<a href='#{ref.name}'>#{ref.name}</a>" }.join(", ") %>
61
61
  </small>
62
62
  <% end %>
63
+
64
+ <% if @page.bliki_references.length > 0 %>
65
+ <small>
66
+ <br />| Bliki Linked from: <%= @page.bliki_references.collect { |ref| link_to_bliki(ref) }.join(", ") %>
67
+ </small>
68
+ <% end %>
63
69
  </div>
64
70
 
65
71
  <script language="Javascript">
@@ -11,6 +11,9 @@
11
11
  <textarea name="content" style="font-size: 12px; width: 450px; height: 500px"><%= @revision.content %></textarea>
12
12
  </p>
13
13
  <p>
14
+ <% if @web.check_pass_on_edit %>
15
+ <p>Please enter edit password: <input type="password" name="password" id="password"></p>
16
+ <% end %>
14
17
  <input type="submit" value="Update"> as
15
18
  <input type="text" name="author" id="authorName" value="<%= @author %>"
16
19
  onClick="this.value == 'AnonymousCoward' ? this.value = '' : true">
@@ -1,7 +1,10 @@
1
- <% @title = @results.length > 0 ? "#{@results.length} pages contains \"#{@params["query"]}\"" : "No pages contains \"#{@query}\"" %><%= sub_template "top" %>
1
+ <% num_results = @results.length + @bliki_results.length
2
+ @title = num_results > 0 ? "#{num_results} page#{ 's' unless num_results == 1} contain#{ 's' if num_results == 1} \"#{@params["query"]}\"" : "No pages contains \"#{@query}\""
3
+ %>
4
+ <%= sub_template "top" %>
2
5
 
3
6
  <% if @results.length > 0 %>
4
- <h3>Matching Pages:</h3>
7
+ <h3><%= @results.length %> Matching Page<%= 's' unless @results.length == 1 %>:</h3>
5
8
  <ul>
6
9
  <% for page in @results %>
7
10
  <li><a href="../show/<%= page.name %>"><%= page.plain_name %></a><br />
@@ -20,7 +23,7 @@
20
23
  </ul>
21
24
  <% end %>
22
25
  <% if @bliki_results.length > 0 %>
23
- <h3>Matching Bliki Entries:</h3>
26
+ <h3><%= @bliki_results.length %> Matching Bliki Entr<%= @bliki_results.length == 1 ? 'y' : 'ies' %>:</h3>
24
27
  <ul>
25
28
  <% for entry in @bliki_results %>
26
29
  <li><a href="../bliki_revision/<%= entry.name %>?rev=<%= entry.revisions.size-1 %>"><%= entry.name %></a><br />
data/favicon.png CHANGED
Binary file
@@ -6,11 +6,11 @@ include WEBrick
6
6
  require 'view_helper'
7
7
 
8
8
  class FavIconHandler < HTTPServlet::AbstractServlet
9
+ LOCAL_PATH = File.join(File.dirname(__FILE__), '../favicon.png')
10
+ ICO_FILE = File.open(LOCAL_PATH, "rb") { |f| f.read }
9
11
  def do_GET(req, res)
10
- ico = File.read(File.join(Dir.pwd, 'favicon.png'))
11
12
  res['content-type'] = 'image/png'
12
- res['content-length'] = ico.size
13
- res.body = ico
13
+ res.body = ICO_FILE
14
14
  end
15
15
  end
16
16
 
@@ -171,8 +171,13 @@ class ActionControllerServlet < HTTPServlet::AbstractServlet
171
171
  begin
172
172
  ERB.new(IO.readlines(template_path).join).result(binding)
173
173
  rescue Exception => detail
174
- @logger.error "Processing #{template_path}"
174
+ @logger.error "Error processing #{template_path}"
175
+ line = /:(\d+):/.match(detail.backtrace[0]).captures[0].to_i - 172
176
+ @logger.error "On Line: #{line}"
175
177
  @logger.error detail
178
+ src = ERB.new(IO.readlines(template_path).join).src
179
+ lines = src.split("\n")[(line-1)..(line+1)]
180
+ @logger.error "\n\n" + lines.join("\n\n") + "\n\n"
176
181
  raise
177
182
  end
178
183
  end
@@ -72,7 +72,7 @@ module Madeleine
72
72
  # keeping track of which methods are read only
73
73
  #
74
74
  def self.included(klass)
75
- class <<klass
75
+ class << klass
76
76
  alias_method :_old_new, :new
77
77
 
78
78
  def new(*args, &block)
@@ -8,6 +8,8 @@ class MadeleineService
8
8
  include Madeleine::Automatic::Interceptor
9
9
 
10
10
  @@storage_path = self.name.downcase + "_storage"
11
+ automatic_read_only :snapshot_interval_hours, :take_snapshot, :clean_old_snapshots,
12
+ :restart, :request_stop
11
13
 
12
14
  class << self
13
15
  def storage_path
@@ -19,25 +21,56 @@ class MadeleineService
19
21
  end
20
22
 
21
23
  def instance
22
- @system = MadeleineServer.new(self).system if @system.nil?
24
+ if @system.nil?
25
+ @madeleine_server = MadeleineServer.new(self)
26
+ @system = @madeleine_server.system
27
+ end
23
28
  @system
24
29
  end
25
30
 
26
31
  def restart
27
- MadeleineServer.clean_storage(self)
28
- @system = MadeleineServer.new(self).system
32
+ MadeleineServer.delete_storage(self)
33
+ @system = nil
34
+ instance
35
+ end
36
+
37
+ def clean_old_snapshots
38
+ instance
39
+ @madeleine_server.clean_storage(self)
40
+ end
41
+
42
+ def take_snapshot
43
+ instance
44
+ @madeleine_server.force_snapshot
45
+ end
46
+
47
+ def snapshot_interval_hours
48
+ instance
49
+ @madeleine_server.snapshot_interval.div MadeleineServer::ONE_HOUR rescue 1
29
50
  end
51
+
52
+ def snapshot_interval_hours= hours
53
+ instance
54
+ @madeleine_server.snapshot_interval = hours.to_i * MadeleineServer::ONE_HOUR rescue MadeleineServer::ONE_HOUR
55
+ end
56
+
57
+ def request_stop
58
+ instance
59
+ @madeleine_server.request_stop
60
+ end
61
+
30
62
  end
31
63
  end
32
64
 
65
+ require 'fileutils'
33
66
  class MadeleineServer
34
- SNAPSHOT_INTERVAL = 30 * 60 * 24 # Each day
35
- AUTOMATIC_SNAPSHOTS = true
67
+
68
+ attr_reader :storage_path
69
+ attr_accessor :snapshot_interval
36
70
 
37
71
  # Clears all the command_log and snapshot files located in the storage directory, so the
38
72
  # database is essentially dropped and recreated as blank. Used in tests.
39
- def self.clean_storage(service)
40
- require 'fileutils'
73
+ def self.delete_storage(service)
41
74
  if (File.directory?(service.storage_path))
42
75
  FileUtils.rm_rf(Dir[service.storage_path + '/*.command_log'])
43
76
  FileUtils.rm_rf(Dir[service.storage_path + '/*.snapshot'])
@@ -45,24 +78,86 @@ class MadeleineServer
45
78
  FileUtils.mkdir_p(service.storage_path)
46
79
  end
47
80
  end
81
+
82
+ def clean_storage(service)
83
+ force_snapshot
84
+ command_logs = Dir[service.storage_path + '/*.command_log']
85
+ raise 'Error: existing command_logs after snapshot' unless command_logs.empty?
86
+
87
+ snapshots = Dir[service.storage_path + '/*.snapshot']
88
+ FileUtils.rm_rf(snapshots.sort[0..-2])
89
+ end
90
+
48
91
 
49
92
  def initialize(service)
93
+ @storage_path = service.storage_path
94
+ @snapshot_interval = ONE_HOUR
50
95
  marshaller = Madeleine::ZMarshal.new()
51
96
  @server = Madeleine::Automatic::AutomaticSnapshotMadeleine.new(service.storage_path,
52
97
  marshaller) { service.new }
53
- start_snapshot_thread if AUTOMATIC_SNAPSHOTS
98
+ start_snapshot_thread
54
99
  end
55
100
 
56
101
  def system
57
102
  @server.system
58
103
  end
59
104
 
105
+ def command_log_present?
106
+ not Dir[storage_path + '/*.command_log'].empty?
107
+ end
108
+
109
+ def force_snapshot
110
+ begin
111
+ hours_since_last_snapshot = 0
112
+ @server.take_snapshot
113
+ rescue => e
114
+ sleep(ONE_MINUTE)
115
+ retry
116
+ end
117
+ end
118
+
119
+
120
+ ONE_MINUTE = 60
121
+ ONE_HOUR = ONE_MINUTE * 60
122
+ MAX_INTERVAL_HOURS = 24 * 2
123
+
60
124
  def start_snapshot_thread
61
- Thread.new(@server) {
62
- while true
63
- sleep(SNAPSHOT_INTERVAL)
64
- @server.take_snapshot
125
+ @snapshot_thread = Thread.new(@server) {
126
+ hours_since_last_snapshot = 0
127
+ while not @request_stop
128
+ sleep(snapshot_interval)
129
+ hours_since_last_snapshot += snapshot_interval.div ONE_HOUR
130
+ begin
131
+ # Take a snapshot if there is a command log
132
+ if command_log_present? or hours_since_last_snapshot > MAX_INTERVAL_HOURS
133
+ # 'Taking a Madeleine snapshot'
134
+ @server.take_snapshot
135
+ hours_since_last_snapshot = 0
136
+ puts "[#{DateTime.now.strftime '%F %T'}] INFO Taking snapshot"
137
+ else
138
+ puts "[#{DateTime.now.strftime '%F %T'}] INFO Skipping snapshot (no command logs)"
139
+ end
140
+ rescue => e
141
+ # wait for a minute (not to spoof the log with the same error)
142
+ # and go back into the loop, to keep trying
143
+ sleep(ONE_MINUTE)
144
+ retry
145
+ end
65
146
  end
66
147
  }
67
148
  end
149
+
150
+ def request_stop
151
+ begin
152
+ @request_stop = true
153
+ if @snapshot_thread and @snapshot_thread.alive?
154
+ @snapshot_thread.wakeup
155
+ @snapshot_thread.join
156
+ end
157
+ @server.take_snapshot if command_log_present?
158
+ rescue => detail
159
+ puts detail
160
+ end
161
+ end
162
+
68
163
  end