Pimki 1.4.092 → 1.5.092

Sign up to get free protection for your applications and to get access to all the features.
@@ -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