Pimki 1.4.092 → 1.5.092

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