tdiary-contrib 4.0.2.1 → 4.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,53 +0,0 @@
1
- ### This tool is obsolete,
2
- ### use alternative plugin: plugin/search-default.rb.
3
- ### This tool will be deleted in the near future.
4
-
5
-
6
- tdiarysearch README
7
- ===================
8
-
9
- This is tdiarysearch, simple tDiary search interface.
10
-
11
-
12
- Installation and setup
13
- ----------------------
14
-
15
- 1. Copy search.rb aside of tDiary's index.rb.
16
-
17
- $ ls
18
- index.rb*
19
- search.rb*
20
- tdiary.conf
21
- update.rb*
22
-
23
- 2. Make search.rb executable.
24
-
25
- $ chmod a+x search.rb
26
-
27
- 3. (Optional) Adjust shebang (#!) line of search.rb.
28
-
29
- 4. (Optional) If you'd like, edit embedded paramter in
30
- search.rb and turn on query logging.
31
-
32
- 5. Add following HTML form in your tDiary header:
33
-
34
- <form method="post" action="search.rb" class="searchform">
35
- <input type="text" name="q" size="20" value="">
36
- <input type="submit" value="Search">
37
- </form>
38
-
39
- 6. Done.
40
-
41
-
42
- License
43
- -------
44
-
45
- GNU GPL, General Public License version 2.
46
-
47
-
48
- Contact
49
- -------
50
-
51
- Minero Aoki <aamine@loveruby.net>
52
- http://i.loveruby.net/en/
53
-
@@ -1,99 +0,0 @@
1
- ■■このツールは最新のtDiaryに追従していません。
2
- ■■検索機能はプラグインとして実装されるようになったため、
3
- ■■同等の機能はplugin/search-default.rbで実現しています。
4
- ■■このツールは近々削除する予定です。
5
-
6
-
7
-
8
- tdiarysearch README
9
- ===================
10
-
11
- tDiary簡単検索CGIです。
12
- あらかじめインデックスを作る必要がないので、
13
- 一般的な検索エンジンに比べてインストールが
14
- 圧倒的に簡単です。また、書いた直後の日記も
15
- 検索対象になるという長所があります。
16
-
17
-
18
- 必要環境
19
- --------
20
-
21
- * テキスト形式のデータベースを使っていること (tDiary 1.5 以降)
22
- * tDiary が動いているサーバに独自の CGI を追加する権限があること
23
-
24
-
25
- セットアップ
26
- ------------
27
-
28
- 1. search.rb を tDiary の index.rb の隣にコピーします。
29
- 必要なら index.rb と同じようにシンボリックリンクを
30
- 張ったり名前を変えたりしてください。
31
-
32
- 2. CGI として実行可能にします。
33
-
34
- $ chmod a+x search.rb
35
-
36
- 3. 必要なら #! のパスを合わせます。
37
-
38
- 4. (オプション) search.rb 先頭のパラメータを変更して
39
- クエリーログを有効にしてください。
40
- ※ クエリーログの詳細については後述
41
-
42
- 5. 自分の tDiary の好きな場所 (例えばヘッダ) に以下の
43
- フォームを加えてください。
44
-
45
- <form method="post" action="search.rb" class="searchform">
46
- <input type="text" name="q" size="20" value="">
47
- <input type="submit" value="Search">
48
- </form>
49
-
50
- 以上です。
51
-
52
-
53
- 検索のしかた
54
- ------------
55
-
56
- tdiarysearch はトピックごとに AND 検索を行います。
57
- 指定した語は空白で分割され、全単語がトピック内のテキストに
58
- 全て含まれる場合にそのトピックを検索結果にリストします。
59
-
60
- AND 以外の演算はサポートしません。
61
-
62
- また rev 1.8 の段階ではまだツッコミが検索できません。
63
- 近いうちに追加します。
64
-
65
-
66
- クエリーログ
67
- ------------
68
-
69
- クエリーログ (query log) というのは、検索語のログの
70
- ことです。このログが存在すると他の人が何を検索したのか
71
- 見られるようになります。クエリーログを有効にするには、
72
- search.rb 内の定数 LOGGING を true にしてください。
73
-
74
- CGI からログを見られるようにするには、search.rb に
75
- パラメータ history=on を付けて呼びます。例えば
76
- 次のリンクを日記に入れておくと、リンクをたどるだけで
77
- 日記から最近の検索ログが見られます。
78
-
79
- <a href="search.rb?history=on">[検索ログ]</a>
80
-
81
- なお、クエリーログは tDiary のデータディレクトリに search.log
82
- という名前で作成されます。特にローテーション処理はしていないので、
83
- 適当な間隔でローテートしてください。
84
-
85
-
86
- 連絡先
87
- ------
88
-
89
- 青木峰郎 (あおき・みねろう)
90
- Minero Aoki <aamine@loveruby.net>
91
- http://i.loveruby.net/w/tdiarysearch.html
92
-
93
- バグ報告は以下のどこかにお願いします。
94
-
95
- * Wiki 上のバグ報告ページ
96
- http://i.loveruby.net/w/tdiarysearchBugs.html
97
- * tdiary-devel ML
98
- * 直接メール
99
-
@@ -1,504 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #
3
- # tdiarysearch
4
- #
5
- # Copyright (C) 2003-2005 Minero Aoki
6
- #
7
- # This program is free software.
8
- # You can distribute/modify this program under the terms of
9
- # the GNU GPL, General Public License version 2.
10
- #
11
- # $originalId: search.rb,v 1.14 2005/07/27 07:16:07 aamine Exp $
12
- #
13
- # Project home page: http://i.loveruby.net/w/tdiarysearch.html
14
- #
15
-
16
- #
17
- # Static Configurations
18
- #
19
-
20
- LOGGING = false
21
- LOGFILE_NAME = 'search.log'
22
- DEBUG = $DEBUG
23
-
24
- #
25
- # HTML Templates
26
- #
27
-
28
- def unindent(str)
29
- str.gsub(/^#{str[/\A(?:\t+| +)/]}/, '')
30
- end
31
-
32
- HEADER = unindent <<'EOS'
33
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
34
- <html lang="ja">
35
- <head>
36
- <meta http-equiv="Content-Type" content="text/html; charset=<%= TDIARY_ENCODING %>">
37
- <meta http-equiv="Content-Language" content="ja">
38
- <meta name="robots" content="noindex">
39
- <link rel="stylesheet" href="theme/base.css" type="text/css" media="all">
40
- <link rel="stylesheet" href="theme/<%= theme %>/<%= theme %>.css" title="<%= theme %>" type="text/css" media="all">
41
- <title>tDiary Search</title>
42
- </head>
43
- <body>
44
- <div class="whole-content">
45
- EOS
46
-
47
- FOOTER = unindent <<'EOS'
48
- </div>
49
- </body>
50
- </html>
51
- EOS
52
-
53
- SEARCH_FORM = unindent <<"EOS"
54
- <form method="post" action="#{File.basename(__FILE__)}">
55
- <input type="text" name="q" size="20" value="<%= patterns.map {|re| escape(re.source) }.join(' ') %>">
56
- <input type="submit" value="Search">
57
- <%
58
- if theme
59
- %><input type="hidden" name="theme" value="on"><%
60
- end
61
- %>
62
- </form>
63
- EOS
64
-
65
- SEARCH_PAGE = unindent <<"EOS"
66
- <h1>tDiary Search</h1>
67
- #{SEARCH_FORM}
68
- EOS
69
-
70
- TOO_MANY_HITS = 50
71
-
72
- SEARCH_RESULT = unindent <<"EOS"
73
- <h1>tDiary Search: Search Result</h1>
74
- #{SEARCH_FORM}
75
- <%
76
- nhits = 0
77
- toomanyhits = false
78
- match_components(patterns) {|diary, fragment, component|
79
- nhits += 1
80
- if nhits > TOO_MANY_HITS
81
- toomanyhits = true
82
- break
83
- end
84
- %>
85
- <div class="day">
86
- <h2><a href="<%= url(diary, fragment) %>"><%= diary.y_m_d %></a></h2>
87
- <div class="body">
88
- <div class="section">
89
- <p><%= short_html(component) %></p>
90
- </div>
91
- </div>
92
- </div><%
93
- }
94
- %>
95
- <p><%= toomanyhits ? 'too many hits.' : nhits.to_s+' hits.' %></p>
96
- #{SEARCH_FORM}
97
- EOS
98
-
99
- SEARCH_ERROR = unindent <<"EOS"
100
- #{SEARCH_FORM}
101
- <%= escape(reason) %>.
102
- EOS
103
-
104
- HISTORY = unindent <<"EOS"
105
- <h1>tDiary Search: Search History</h1>
106
- #{SEARCH_FORM}
107
- <ul>
108
- <%
109
- recent_queries.sort_by {|t,q| -t.to_i }.each do |time, query|
110
- %><li><%= time.strftime('%Y-%m-%d %H:%M:%S') %> | <a href="#{File.basename(__FILE__)}?q=<%= escape_url(query) %>"><%= escape(query) %></a></li>
111
- <%
112
- end
113
- %></ul>
114
- #{SEARCH_FORM}
115
- EOS
116
-
117
- #
118
- # Main
119
- #
120
-
121
- if File.symlink?(__FILE__)
122
- tdiarylib = File.dirname(File.readlink(__FILE__))
123
- else
124
- tdiarylib = File.dirname(__FILE__)
125
- end
126
- $:.unshift tdiarylib
127
- require 'tdiary'
128
- require 'tdiary/io/default'
129
- require 'erb'
130
-
131
- TDIARY_ENCODING = (TDIARY_VERSION >= '2.3.0') ? 'utf-8' : 'euc-jp'
132
-
133
- class WrongQuery < StandardError; end
134
-
135
- Z_SPACE = "\241\241" # zen-kaku space
136
-
137
- BEGIN { $stdout.binmode }
138
-
139
- def main
140
- $KCODE = TDIARY_ENCODING
141
- cgi = CGI.new
142
- @config = TDiary::Config.new(cgi)
143
- @config.options['apply_plugin'] = true
144
- html = '<html><head><title></title></head><body><p>error</p></body></html>'
145
- begin
146
- html = generate_page(cgi)
147
- ensure
148
- send_html cgi, html
149
- end
150
- exit 0
151
- end
152
-
153
- def generate_page(cgi)
154
- query = nil
155
- begin
156
- theme = @config.theme
157
- if LOGGING and File.file?(query_log()) and cgi.valid?('history')
158
- return history_page(theme)
159
- end
160
- begin
161
- return search_form_page(theme) unless cgi.valid?('q')
162
- initialize_tdiary_plugins cgi
163
- query = @config.to_native([cgi.params['q']].flatten.compact.join(' '))
164
- patterns = setup_patterns(query)
165
- html = search_result_page(theme, patterns)
166
- save_query(query, query_log()) if LOGGING
167
- return html
168
- rescue WrongQuery => err
169
- return search_error_page(theme, (patterns || []), err.message)
170
- end
171
- rescue Exception => err
172
- html = ''
173
- html << HEADER
174
- html << "<pre>\n"
175
- html << 'q=' << escape(query) << "\n" if query
176
- html << escape(err.class.name) << "\n" if DEBUG
177
- html << escape(err.message) << "\n"
178
- html << err.backtrace.map {|i| escape(i) }.join("\n") if DEBUG
179
- html << "</pre>\n"
180
- html << FOOTER
181
- return html
182
- end
183
- end
184
-
185
- def send_html(cgi, html)
186
- print cgi.header('status' => '200 OK',
187
- 'type' => 'text/html',
188
- 'charset' => TDIARY_ENCODING,
189
- 'Content-Length' => html.length.to_s,
190
- 'Cache-Control' => 'no-cache',
191
- 'Pragma' => 'no-cache')
192
- print html unless cgi.request_method == 'HEAD'
193
- end
194
-
195
- def setup_patterns(query)
196
- patterns = split_string(query).map {|pat|
197
- check_pattern pat
198
- Regexp.new( Regexp.quote(pat), Regexp::IGNORECASE, TDIARY_ENCODING )
199
- }
200
- raise WrongQuery, 'no pattern' if patterns.empty?
201
- raise WrongQuery, 'too many sub patterns' if patterns.length > 8
202
- patterns
203
- end
204
-
205
- def check_pattern(pat)
206
- raise WrongQuery, 'no pattern' unless pat
207
- raise WrongQuery, 'empty pattern' if pat.empty?
208
- raise WrongQuery, "pattern too short: #{pat}" if pat.length < 2
209
- raise WrongQuery, 'pattern too long' if pat.length > 128
210
- end
211
-
212
- def split_string(str)
213
- str.split(/[\s#{Z_SPACE}]+/ou).reject {|w| w.empty? }
214
- end
215
-
216
- def save_query(query, file)
217
- File.open(file, 'a') {|f|
218
- begin
219
- f.flock(File::LOCK_EX)
220
- f.puts "#{Time.now.to_i}: #{query.dump}"
221
- ensure
222
- f.flock(File::LOCK_UN)
223
- end
224
- }
225
- end
226
-
227
- #
228
- # eRuby Dispatchers and Helper Routines
229
- #
230
-
231
- def search_form_page(theme)
232
- patterns = []
233
- ERB.new(HEADER + SEARCH_FORM + FOOTER).result(binding())
234
- end
235
-
236
- def search_result_page(theme, patterns)
237
- ERB.new(HEADER + SEARCH_RESULT + FOOTER).result(binding())
238
- end
239
-
240
- def search_error_page(theme, patterns, reason)
241
- ERB.new(HEADER + SEARCH_ERROR + FOOTER).result(binding())
242
- end
243
-
244
- def history_page(theme)
245
- patterns = []
246
- ERB.new(HEADER + HISTORY + FOOTER).result(binding())
247
- end
248
-
249
- def query_log
250
- "#{@config.data_path}#{LOGFILE_NAME}"
251
- end
252
-
253
- N_SHOW_QUERY_MAX = 20
254
-
255
- def recent_queries
256
- return unless File.file?(query_log())
257
- File.readlines(query_log()).reverse[0, N_SHOW_QUERY_MAX].map {|line|
258
- time, q = *line.split(/:/, 2)
259
- [Time.at(time.to_i), eval(q)]
260
- }
261
- end
262
-
263
- INF = 1 / 0.0
264
-
265
- def match_components(patterns)
266
- foreach_diary_from_latest do |diary|
267
- next unless diary.visible?
268
- num = 1
269
- diary.each_section do |sec|
270
- if patterns.all? {|re| re =~ sec.to_src }
271
- yield diary, fragment('p', num), sec
272
- end
273
- num += 1
274
- end
275
- diary.each_visible_comment(INF) do |cmt, num|
276
- if patterns.all? {|re| re =~ cmt.body }
277
- yield diary, fragment('c', num), cmt
278
- end
279
- end
280
- end
281
- end
282
-
283
- def fragment(type, num)
284
- sprintf('%s%02d', type, num)
285
- end
286
-
287
- #
288
- # tDiary Implementation Dependent
289
- #
290
-
291
- def foreach_diary_from_latest(&block)
292
- foreach_data_file(@config.data_path.sub(%r</+\z>, '')) do |path|
293
- read_diaries(path).sort_by {|diary| diary.date }.reverse_each(&block)
294
- end
295
- end
296
-
297
- def foreach_data_file(data_path, &block)
298
- Dir.glob("#{data_path}/[0-9]*/*.td2").sort.reverse_each do |path|
299
- yield path.untaint
300
- end
301
- end
302
-
303
- def read_diaries(path)
304
- d = nil
305
- diaries = {}
306
- load_tdiary_textdb(path) do |header, body|
307
- d = diary_class(header['Format']).new(header['Date'], '', body)
308
- d.show(header['Visible'] != 'false')
309
- diaries[d.ymd] = d
310
- end
311
- (Years[d.y] ||= []).push(d.m) if d
312
- load_comments diaries, path
313
- diaries.values
314
- end
315
-
316
- DIARY_CLASS_CACHE = {}
317
-
318
- def diary_class(style)
319
- c = DIARY_CLASS_CACHE[style]
320
- return c if c
321
- require "tdiary/style/#{style.downcase}_style.rb"
322
- c = eval("TDiary::#{style.capitalize}Diary")
323
- c.__send__(:include, DiaryClassDelta)
324
- DIARY_CLASS_CACHE[style] = c
325
- c
326
- end
327
-
328
- module DiaryClassDelta
329
- def ymd
330
- date().strftime('%Y%m%d')
331
- end
332
-
333
- def y_m_d
334
- date().strftime('%Y-%m-%d')
335
- end
336
-
337
- def y
338
- '%04d' % date().year
339
- end
340
-
341
- def m
342
- '%02d' % date().month
343
- end
344
- end
345
-
346
- def load_comments(diaries, path)
347
- cmtfile = path.sub(/2\z/, 'c')
348
- return unless File.file?(cmtfile)
349
- load_tdiary_textdb(cmtfile) do |header, body|
350
- c = TDiary::Comment.new(header['Name'], header['Mail'], body,
351
- Time.at(header['Last-Modified'].to_i))
352
- c.show = (header['Visible'] != 'false')
353
- d = diaries[header['Date']]
354
- d.add_comment c if d
355
- end
356
- end
357
-
358
- def load_tdiary_textdb(path)
359
- File.open(path) {|f|
360
- ver = f.gets.strip
361
- raise "unkwnown format: #{ver}" unless ver == 'TDIARY2.00.00' or ver == 'TDIARY2.01.00'
362
- f.each('') do |header|
363
- h = {}
364
- header.untaint.strip.each do |line|
365
- n, v = *line.split(':', 2)
366
- h[n.strip] = v.strip
367
- end
368
- yield h, f.gets("\n.\n").chomp(".\n").untaint
369
- end
370
- }
371
- end
372
-
373
- def short_html(component)
374
- # Section classes do not have common superclass, we can't use class here.
375
- case component.class.name
376
- when /Section/
377
- section = component
378
- if section.subtitle
379
- sprintf('%s<br>%s',
380
- tdiary2text(section.subtitle_to_html),
381
- tdiary2text(section.body_to_html))
382
- else
383
- tdiary2text(section.body_to_html)
384
- end
385
- when /Comment/
386
- cmt = component
387
- shorten(escape((cmt.name + ': ' + cmt.body)))
388
- else
389
- raise "must not happen: #{component.class}"
390
- end
391
- end
392
-
393
- def tdiary2text(html)
394
- re = Regexp.new('<[^>]*>', Regexp::EXTENDED, TDIARY_ENCODING)
395
- shorten(apply_tdiary_plugins(html).gsub(re, ''))
396
- end
397
-
398
- Years = {}
399
-
400
- TDiary::Plugin.__send__(:public, :apply_plugin)
401
- def apply_tdiary_plugins(html)
402
- @plugin.apply_plugin(html, false)
403
- end
404
-
405
- @plugin = nil
406
-
407
- def initialize_tdiary_plugins(cgi)
408
- @plugin = TDiary::Plugin.new('conf' => @config,
409
- 'secure' => false,
410
- 'diaries' => {},
411
- 'cgi' => cgi,
412
- 'index' => @config.index,
413
- 'years' => Years,
414
- 'cache_path' => @config.cache_path ||
415
- @config.data_path)
416
- end
417
-
418
- #
419
- # Utils
420
- #
421
-
422
- HTML_ESCAPE_TABLE = {
423
- '&' => '&amp;',
424
- '<' => '&lt;',
425
- '>' => '&gt;',
426
- '"' => '&quot;'
427
- }
428
-
429
- def escape(str)
430
- tbl = HTML_ESCAPE_TABLE
431
- str.gsub(/[&"<>]/) {|ch| tbl[ch] }
432
- end
433
-
434
- def escape_url(u)
435
- escape(urlencode(u))
436
- end
437
-
438
- def urlencode(str)
439
- str.gsub(/[^\w-]/n) {|ch| sprintf('%%%02x', ch[0]) }
440
- end
441
-
442
- def shorten(str)
443
- re = Regexp.new('\A.{0,120}', Regexp::MULTILINE, TDIARY_ENCODING)
444
- str.slice(re)
445
- end
446
-
447
- def url(diary, fragment)
448
- if ( html_anchor_enabled? )
449
- "#{@config.index}#{diary.ymd}.html\##{fragment}"
450
- else
451
- "#{@config.index}?date=#{diary.ymd}\##{fragment}"
452
- end
453
- end
454
-
455
- def html_anchor_enabled?
456
- if ( @html_anchor.nil? )
457
- @html_anchor = @config.options2['sp.selected'].include?( 'html_anchor.rb' )
458
- end
459
-
460
- return @html_anchor
461
- end
462
-
463
- #
464
- # Old Ruby Compatibility
465
- #
466
-
467
- if RUBY_VERSION < '1.8.0'
468
- class String
469
- remove_method :slice
470
- def slice(re, n = 0)
471
- m = re.match(self) or return nil
472
- m[n]
473
- end
474
- end
475
- end
476
-
477
- unless Array.method_defined?(:all?)
478
- module Enumerable
479
- def all?
480
- each do |i|
481
- return false unless yield(i)
482
- end
483
- true
484
- end
485
- end
486
- end
487
-
488
- unless Array.method_defined?(:sort_by)
489
- module Enumerable
490
- def sort_by
491
- map {|i| [yield(i), i] }.sort.map {|val, i| i }
492
- end
493
- end
494
- end
495
-
496
- unless MatchData.method_defined?(:captures)
497
- class MatchData
498
- def captures
499
- to_a()[1..-1]
500
- end
501
- end
502
- end
503
-
504
- main