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.
data/README-PIMKI CHANGED
@@ -48,23 +48,35 @@ Pimki added features:
48
48
  * Expanded Search:
49
49
  * Search also tries to match the page names (not just contents).
50
50
  * Now showing part of sentance around match in search-results page
51
+ * Advanced Search
52
+ * Case sensitivity
53
+ * Search as regex, phrase or words
54
+ * Search pages and bliki entries
55
+ * Search in page names / contents
56
+ * Limit search to selected categories
57
+ * Limit search to selected authors
51
58
  * Removed unnecessary (for me) stuff from the nav-bar: RSS, authors, export.
52
59
  Moved to bottom of Home Page.
53
60
  * Minor stylesheet tweaks.
54
61
 
55
- ToDo:
56
-
57
- * Expand todo with @Context
58
- * Advanced Search
59
- * Advanced Search options (not just regexp, case sensitivity etc)
60
- * Highlight match on individual page when clicked form search results
61
- * Editable left-side menu options: by search results? by author?
62
-
63
62
 
64
63
  Command-line options:
65
64
  Run "ruby pimki.rb --help"
66
65
 
66
+ Please note also that RedCloth (http://redcloth.rubyforge.org) is set to
67
+ version 2.0.11 by default. Version 3.0.1 is a bit experimental. You can select
68
+ which version of redcloth via --redcloth command line argument.
69
+
67
70
  History:
71
+ 1.5.092 Bug fixes & new features release
72
+ - Several bug fixes
73
+ - RedCloth is now defaulting to 2.0.11. Controllable thru commandline.
74
+ - Advanced Search
75
+ - c2 wiki links via [c2[PageName]]
76
+ - FavIcon (working in Firefox)
77
+ - Madeleine Snapshot Controls (thru edit_web)
78
+
79
+
68
80
  1.4.092 New features release
69
81
  - Added a major/minor edit override for the default 'continous edit' when
70
82
  revising a page.
@@ -5,7 +5,7 @@ RenderedTodo = Struct.new( :text, :context, :due_date )
5
5
 
6
6
  class WikiController < ActionControllerServlet
7
7
  EXPORT_DIRECTORY = WikiService.storage_path unless const_defined?("EXPORT_DIRECTORY")
8
-
8
+
9
9
  def index
10
10
  if web_address
11
11
  redirect_show "HomePage"
@@ -76,12 +76,80 @@ class WikiController < ActionControllerServlet
76
76
  def search
77
77
  set_menu_pages
78
78
  @query = @params["query"]
79
- rex = /#{@query}/i
80
- @results = web.select { |page| rex.match(page.name) or rex.match(page.content) }
81
- @bliki_results = web.bliki.values.select do |entry|
82
- rex.match(entry.name) or rex.match(entry.content)
79
+ search_content = [nil, 'both', 'contents'].include? @params['fields']
80
+ search_names = [nil, 'both', 'names'].include? @params['fields']
81
+
82
+ case @params['expression']
83
+ when 'regex', nil
84
+ rex = Regexp.new @query, (Regexp::IGNORECASE unless @params['case'])
85
+ @results = if [nil, 'all', 'pages', nil].include? @params['where']
86
+ web.select do |page|
87
+ (search_names and rex.match(page.name)) or (search_content and rex.match(page.content))
88
+ end
89
+ else
90
+ []
91
+ end
92
+ @bliki_results = if [nil, 'all', 'bliki'].include? @params['where']
93
+ web.bliki.values.select do |entry|
94
+ (search_names and rex.match(entry.name)) or (search_content and rex.match(entry.content))
95
+ end
96
+ else
97
+ []
98
+ end
99
+
100
+ when 'all'
101
+ words = @query.split(/\s/).reject { |w| w.empty? }
102
+ @results = if [nil, 'all', 'pages'].include? @params['where']
103
+ web.select do |page|
104
+ words.all? do |word|
105
+ (search_names and page.name.index(word)) or (search_content and page.content.index(word))
106
+ end
107
+ end
108
+ else
109
+ []
110
+ end
111
+ @bliki_results = if [nil, 'all', 'bliki'].include? @params['where']
112
+ web.bliki.values.select do |entry|
113
+ words.all? do |word|
114
+ (search_names and entry.name.index(word)) or (search_content and entry.content.index(word))
115
+ end
116
+ end
117
+ else
118
+ []
119
+ end
120
+
121
+ when 'exact'
122
+ @results = if [nil, 'all', 'pages'].include? @params['where']
123
+ web.select do |page|
124
+ (search_names and page.name.index(@query)) or (search_content and page.content.index(@query))
125
+ end
126
+ else
127
+ []
128
+ end
129
+ @bliki_results = if [nil, 'all', 'bliki'].include? @params['where']
130
+ web.bliki.values.select do |entry|
131
+ (search_names and entry.name.index(@query)) or (search_content and entry.content.index(@query))
132
+ end
133
+ else
134
+ []
135
+ end
136
+ end
137
+
138
+ if !@params['category'].nil? and @params['category'] != 'noselect'
139
+ @selected_categories = parse_multi_select 'category'
140
+ @results.reject! { |page| (page.categories & @selected_categories).empty? }
141
+ @bliki_results.reject! { |page| (page.categories & @selected_categories).empty? }
142
+ end
143
+
144
+ if !@params['author'].nil? and @params['author'] != 'noselect'
145
+ @selected_authors = parse_multi_select 'author'
146
+ @results.reject! { |page| (page.authors & @selected_authors).empty? }
147
+ @bliki_results.reject! { |page| (page.authors & @selected_authors).empty? }
83
148
  end
84
- @results.length == 1 ? redirect_show(@results.first.name) : render
149
+
150
+ redirect_show(@results.first.name) if @results.length == 1 && @bliki_results.length == 0
151
+ redirect_path("/#{web_address}/bliki_revision/#{@bliki_results.first.name}?rev=#{@bliki_results.first.revisions.size-1}") if @results.length == 0 && @bliki_results.length == 1
152
+ render_action "search"
85
153
  end
86
154
 
87
155
  def authors
@@ -151,7 +219,7 @@ class WikiController < ActionControllerServlet
151
219
  end
152
220
 
153
221
  def export_pdf
154
- file_name = "#{web.address}-tex-#{web.revised_on.strftime("%Y-%m-%d-%H-%M")}"
222
+ file_name = "#{web.address}-tex-#{web.revised_on.strftime('%Y-%m-%d-%H-%M')}"
155
223
  file_path = EXPORT_DIRECTORY + file_name
156
224
 
157
225
  export_web_to_tex(file_path + ".tex") unless FileTest.exists?(file_path + ".tex")
@@ -160,7 +228,7 @@ class WikiController < ActionControllerServlet
160
228
  end
161
229
 
162
230
  def export_tex
163
- file_name = "#{web.address}-tex-#{web.revised_on.strftime("%Y-%m-%d-%H-%M")}.tex"
231
+ file_name = "#{web.address}-tex-#{web.revised_on.strftime('%Y-%m-%d-%H-%M')}.tex"
164
232
  file_path = EXPORT_DIRECTORY + file_name
165
233
 
166
234
  export_web_to_tex(file_path) unless FileTest.exists?(file_path)
@@ -170,6 +238,7 @@ class WikiController < ActionControllerServlet
170
238
  def edit_web #{{{
171
239
  parse_category
172
240
  set_mm_options
241
+ @snapshot_interval = MadeleineService.snapshot_interval_hours
173
242
  end #}}}
174
243
 
175
244
  def update_web
@@ -178,8 +247,8 @@ class WikiController < ActionControllerServlet
178
247
  set_mm_options
179
248
 
180
249
  wiki.update_web(
181
- web.address, @params["address"], @params["name"],
182
- @params["markup"].intern,
250
+ web.address, @params["address"], @params["name"],
251
+ @params["markup"].intern,
183
252
  @params["color"], @params["additional_style"],
184
253
  @params["safe_mode"] ? true : false,
185
254
  @params["password"].empty? ? nil : @params["password"],
@@ -189,6 +258,7 @@ class WikiController < ActionControllerServlet
189
258
  @params['mind_map_size'],
190
259
  @params['symbols_map'],
191
260
  @params['links_map'],
261
+ @params['snapshots_interval'],
192
262
  @params['enable_dclick_edit'],
193
263
  @params['check_pass_on_edit'] == 'each_edit',
194
264
  @prog, @graph_type, @missing, @show_authors, @show_leaves, @selected_categories
@@ -197,12 +267,27 @@ class WikiController < ActionControllerServlet
197
267
  redirect_show("HomePage", @params["address"])
198
268
  end
199
269
 
200
- def remove_orphaned_pages
201
- if wiki.authenticate(@params["system_password"])
202
- wiki.remove_orphaned_pages(web_address)
203
- redirect_action "list/"
270
+ def administrate
271
+ @logger.info "Taking administrative action: #{@params['action']}"
272
+ if wiki.authenticate(@params['system_password'])
273
+ case @params['action']
274
+ when 'Delete Orphan Pages'
275
+ wiki.remove_orphaned_pages(web_address)
276
+
277
+ when 'Clear Render Cache'
278
+ clear_render_cache true
279
+
280
+ when 'Force Data Snapshot'
281
+ MadeleineService.take_snapshot
282
+
283
+ when 'Clean Storage'
284
+ MadeleineService.clean_old_snapshots
285
+
286
+ end
287
+ @message = 'Operation succeeded'
288
+ redirect_action 'edit_web/'
204
289
  else
205
- redirect_show "HomePage"
290
+ redirect_show 'HomePage'
206
291
  end
207
292
  end
208
293
 
@@ -216,7 +301,8 @@ class WikiController < ActionControllerServlet
216
301
  @context = @params['context']
217
302
  @sort_order = @params['sort_order']
218
303
 
219
- clear_render_cache # hack to make sure we don't have old-style rendered todos.
304
+ # the clear_render_cache hack to make sure we don't have old-style rendered
305
+ # todos is no longer needed - can go thru the edit web to explicitely clear the cache.
220
306
  @todo_items = analyse_rendered_todo_items @pages_in_category.by_name
221
307
  @bliki_todo_items = analyse_rendered_todo_items web.bliki.values
222
308
 
@@ -281,9 +367,9 @@ class WikiController < ActionControllerServlet
281
367
  (due_date <=> Date.today) > -1 ? "todoFuture" : "todo"
282
368
  end
283
369
 
284
- def clear_render_cache do_redirect=false
370
+ def clear_render_cache dont_redirect=false
285
371
  web.refresh_revisions
286
- redirect_show 'HomePage' if do_redirect
372
+ redirect_path "/#{web_address}/edit_web/" unless dont_redirect
287
373
  end
288
374
 
289
375
  def set_menu_pages #{{{
@@ -294,8 +380,14 @@ class WikiController < ActionControllerServlet
294
380
  when 'recent' then @all_pages.by_last_visited
295
381
  when 'viewed' then @all_pages.by_most_viewed
296
382
  when 'revised' then @all_pages.by_revision
297
- when 'user' then @menu_content = web.menu_content.revisions.last.display_content; nil
298
383
  when 'category' then web.select { |page| page.in_category?(web.menu_category) }
384
+ when 'user'
385
+ @menu_content = if Page === web.menu_content
386
+ web.menu_content.revisions.last.display_content
387
+ else
388
+ web.menu_content
389
+ end
390
+ nil
299
391
  when 'linkers'
300
392
  web.select { |page|
301
393
  page.wiki_words.size > 0
@@ -358,15 +450,22 @@ class WikiController < ActionControllerServlet
358
450
  @show_authors = @req.query['show_authors'] == 'on'
359
451
  @show_leaves = @req.query.empty? || @req.query['show_leaves'] == 'on'
360
452
 
361
- # TODO: fix handling of multiple-select for whole application
362
- @selected_categories = @req.body.split('&').map { |pair|
363
- pair.split('=') }.select { |k,v|
364
- k == 'selected_categs' }.map { |k,v| v } if @req.body
365
- @selected_categories ||= []
453
+ @selected_categories = parse_multi_select 'selected_categs'
366
454
  @selected_categories = [] if @selected_categories.include? 'all'
367
455
  end
368
456
  end #}}}
369
457
 
458
+ def parse_multi_select field #{{{
459
+ if @req.body
460
+ @req.body.split('&').map { |pair|
461
+ pair.split('=') }.select { |k,v|
462
+ k == field }.map { |k,v| v }
463
+ else
464
+ []
465
+ end
466
+ end #}}}
467
+
468
+
370
469
  def edit_menu #{{{
371
470
  @menu_type = web.menu_type
372
471
  @menu_content = web.menu_content
@@ -14,7 +14,7 @@ module Engines
14
14
  class Textile < MarkupEngine
15
15
  def mask(content)
16
16
  rc = RedCloth.new(text,content.options[:engine_opts])
17
- rc.rules = [:textile]
17
+ rc.rules = [:textile] if RedCloth::VERSION >= '3.0.0'
18
18
  rc.to_html
19
19
  end
20
20
  end
@@ -39,6 +39,10 @@ module Engines
39
39
  end
40
40
  end
41
41
 
42
- MAP = { :textile => Textile, :red_markdown => RedMarkdown, :blue_markdown => BlueMarkdown, :rdoc => RDoc }
42
+ MAP = { :textile => Textile,
43
+ :red_markdown => RedMarkdown,
44
+ :blue_markdown => BlueMarkdown,
45
+ :markdown => BlueMarkdown,
46
+ :rdoc => RDoc }
43
47
  end
44
48
 
@@ -22,7 +22,7 @@ class URIChunk < Chunk::Abstract
22
22
  COUNTRY = '(?:au|at|be|ca|ch|de|dk|fr|hk|in|ir|it|jp|nl|no|pt|ru|se|sw|tv|tw|uk|us)'
23
23
 
24
24
  # These are needed otherwise HOST will match almost anything
25
- TLDS = "\\.(?:#{GENERIC}|#{COUNTRY})\b"
25
+ TLDS = "\\.(?:#{GENERIC}|#{COUNTRY})\\b"
26
26
 
27
27
  # Redefine USERINFO so that it must have non-zero length
28
28
  USERINFO = "(?:[#{UNRESERVED};:&=+$,]|#{ESCAPED})+"
@@ -81,34 +81,35 @@ module WikiChunk
81
81
  @page_name, @link_text = match_data[1], match_data[1]
82
82
  end
83
83
  end
84
-
85
- def mask(content) pre_mask + post_mask; end
86
- def regexp() Regexp.new(pre_mask + post_mask) end
87
- def escaped_text() (@escape.nil? ? nil : page_name) end # TODO: never set?
88
- def link_text() WikiWords.separate(page_name) end
89
84
  end
90
85
 
91
86
  # This chunk handles [bliki[entry name]].
92
87
  # This format can be easily duplicated for any other pre-configured redirection.
93
88
  class BlikiLink < WikiLink
94
- def self.pattern() /\[bliki\[([\w\d\s]+)\]\]/ end
89
+ def self.pattern() /\[(\w+)\[([\w\d\s]+)\]\]/ end
95
90
 
96
91
  attr_reader :page_name, :link_text, :entry
97
92
 
98
93
  def initialize(match_data, revision)
99
94
  super(match_data, revision)
100
- @page_name, @link_text = match_data[1], match_data[1]
95
+ @target, @page_name, @link_text = match_data[1], match_data[2], match_data[2]
101
96
  end
102
97
 
103
98
  def unmask(content)
104
99
  return self if content.sub!(regexp) { |match|
105
- web = revision.page.web
106
- entry = web.bliki[page_name]
107
- if entry.nil?
108
- "<span style='background:lightgrey;font-style:italic'>Unknown Bliki entry: '#{page_name}'</span>"
109
- else
110
- "<a class='existingWikiWord' href='/#{web.address}/bliki_revision/#{entry.name}?rev=#{entry.revisions.size-1}'>#{entry.name}</a>"
111
- end
100
+ case @target
101
+ when 'bliki'
102
+ web = revision.page.web
103
+ entry = web.bliki[page_name]
104
+ if entry.nil?
105
+ "<span style='background:lightgrey;font-style:italic'>Unknown Bliki entry: '#{page_name}'</span>"
106
+ else
107
+ "<a class='existingWikiWord' href='/#{web.address}/bliki_revision/#{entry.name}?rev=#{entry.revisions.size-1}'>#{entry.name}</a>"
108
+ end
109
+
110
+ when 'c2'
111
+ "<a href='http://c2.com/cgi/wiki?#{@page_name}'>C2::#{@page_name}</a>"
112
+ end
112
113
  }
113
114
  end
114
115
  end
data/app/models/page.rb CHANGED
@@ -111,7 +111,7 @@ class Page
111
111
  end
112
112
 
113
113
  def revise_minor(content, created_at, author) #{{{
114
- @revisions.last.created_at = Time.now
114
+ @revisions.last.created_at = created_at
115
115
  @revisions.last.content = content
116
116
  @revisions.last.clear_display_cache
117
117
  end #}}}
@@ -46,7 +46,7 @@ class WikiContent < String
46
46
  # markup of "link":URL.
47
47
  PRE_ENGINE_ACTIONS = [ NoWiki, Category, Include, Literal::Pre, WikiSymbol,
48
48
  WikiChunk::Link, WikiChunk::BlikiLink ]
49
- POST_ENGINE_ACTIONS = [ Literal::Tags, WikiChunk::Word, URIChunk, Todo ]
49
+ POST_ENGINE_ACTIONS = [ Literal::Tags, URIChunk, WikiChunk::Word, Todo ]
50
50
 
51
51
 
52
52
  DEFAULT_OPTS = {
@@ -69,23 +69,22 @@ class WikiContent < String
69
69
  @options = Marshal.load(Marshal.dump(DEFAULT_OPTS)).update(options)
70
70
  @options[:engine] = Engines::MAP[@web.markup] || Engines::Textile
71
71
 
72
- @options[:engine_opts] = []
73
- case @options[:engine]
74
- when Engines::Textile
72
+ @options[:engine_opts] = DEFAULT_OPTS[:engine_opts]
73
+ case @options[:engine].name
74
+ when 'Engines::Textile'
75
75
  if RedCloth::VERSION >= '3.0.0'
76
76
  # RedCloth v3 changes the default behaviour from not folding lines.
77
- DEFAULT_OPTS[:engine_opts] = [:hard_breaks]
77
+ @options[:engine_opts] += [:hard_breaks]
78
78
  end
79
79
  @options[:engine_opts] += (@web.safe_mode ? [:filter_html, :filter_styles] : [])
80
80
 
81
- when Engines::BlueMarkdown, Engines::RedMarkdown
81
+ when 'Engines::BlueMarkdown', 'Engines::RedMarkdown'
82
82
  @options[:engine_opts] += (@web.safe_mode ? [:filter_html, :filter_styles] : [])
83
83
 
84
- when Engines::RDoc
84
+ when 'Engines::RDoc'
85
85
  nil
86
86
 
87
87
  end
88
-
89
88
 
90
89
  @options[:post_engine_actions].delete(WikiChunk::Word) if @web.brackets_only
91
90
 
@@ -94,6 +93,8 @@ class WikiContent < String
94
93
  begin
95
94
  render!(@options[:pre_engine_actions] + [@options[:engine]] + @options[:post_engine_actions])
96
95
  rescue => e
96
+ puts "[#{DateTime.now.strftime '%F %T'}] DEBUG: #{e}"
97
+ puts e.backtrace
97
98
  @rendered = "<strong>#{e.message}</strong><br/><br/>#{e.backtrace.join('<br/>')}"
98
99
  end
99
100
  end
@@ -8,6 +8,10 @@ require "author"
8
8
  class WikiService < MadeleineService
9
9
  attr_reader :webs, :system
10
10
 
11
+ # These methods do not change the state of persistent objects, and
12
+ # should not be logged by Madeleine
13
+ automatic_read_only :authenticate, :read_page, :read_bliki_entry, :setup?, :webs
14
+
11
15
  def initialize
12
16
  @webs, @system = {}, {}
13
17
  end
@@ -31,7 +35,8 @@ class WikiService < MadeleineService
31
35
 
32
36
  def update_web(old_address, new_address, name, markup, color, additional_style, safe_mode = false,
33
37
  password = nil, published = false, brackets_only = false, count_pages = false,
34
- mind_map_size="7,7", symbols_map=nil, links_map=nil,
38
+ mind_map_size="7,7", symbols_map=nil, links_map=nil,
39
+ snapshots_interval = 1,
35
40
  enable_dclick_edit=nil, check_pass_on_edit=false,
36
41
  prog=nil, graph_type=nil, missing=nil, show_authors=nil, show_leaves=nil, selected_categories=nil)
37
42
 
@@ -40,6 +45,8 @@ class WikiService < MadeleineService
40
45
  @webs.delete(old_address)
41
46
  @webs[new_address].address = new_address
42
47
  end
48
+
49
+ MadeleineService.snapshot_interval_hours = snapshots_interval
43
50
 
44
51
  web = @webs[new_address]
45
52
  web.refresh_revisions if settings_changed?(web, markup, safe_mode, brackets_only)
@@ -23,4 +23,5 @@ end
23
23
  <%= list_item "Export", "../export/", "Download a zip with all the pages in this wiki", "X" %> |
24
24
  -->
25
25
  <input type="text" id="searchField" name="query" style="font-size: 10px" value="Search" onClick="this.value == 'Search' ? this.value = '' : true">
26
+ <%= list_item "<small>(Adv)</small>", "../adv_search/", "Advanced Search" %>
26
27
  </form>