root/platform/tdiary/plugin/windex.rb

Revision 7742, 13.4 kB (checked in by drry, 9 months ago)

platform/tdiary/doc/ja/comment_key.ja.html
platform/tdiary/plugin/ja/comment_size.rb
platform/tdiary/plugin/windex.rb:

  • documentation for the UTF-8.
Line 
1#!/usr/bin/env ruby
2
3# windex.rb $Revision: 1.2 $
4#
5# windex: 索引を生成する
6#   パラメタ:
7#     str:      キーワード文字列
8#     readname: 読み仮名
9#
10# wikw: 索引からアンカーを生成する
11#   パラメタ:
12#     str:      キーワード文字列
13#
14# このファイルをtDiaryのトップディレクトリにも配置し、CGIとして
15# 実行することで索引ページを出力できます。
16#
17# CGI動作時の引数
18#   http://(日記URL)/windex.rb?kw=(キーワード文字列)
19#   とキーワードを指定してアクセスすることでそのキーワードに関係する日記の
20#   日付一覧を出力できます。一つだけの場合にはその日付の日記への
21#   リダイレクトを出力します。
22#
23# tdiary.confによる設定
24#   @options['windex.generate_all'] = true
25#     全ての日記から索引を生成します。これは時間がかかるので、索引の全生成を
26#     行いたいときだけtrueに設定して更新を行うような使い方を想定しています。
27#
28# Copyright (c) 2003 Gony <gony@sm.rim.or.jp>
29# Distributed under the GPL
30#
31
32mode = ""
33if $0 == __FILE__
34        mode = "CGI"
35        if FileTest.symlink?(__FILE__) == true
36                org_path = File.dirname(File.readlink(__FILE__))
37        else
38                org_path = File.dirname(__FILE__)
39        end
40        $:.unshift(org_path)
41        require "pstore"
42        require "tdiary"
43
44        tdiarybase = TDiary::TDiaryBase
45else
46        tdiarybase = TDiaryBase
47end
48
49class WITDiary < tdiarybase
50        def load_plugins
51                super
52        end
53
54        def generate_wordindex(date,plugin,index)
55                wordindex = WIWordIndex.new
56                @io.transaction(date) do |diaries|
57                        wordindex.generate(diaries,plugin,index)
58                end
59
60                return wordindex
61        end
62end
63
64class WIWordIndex
65        def initialize
66                @windex = {}
67                @dates = []
68        end
69
70        def generate(diaries,plugin,index)
71                diaries.each_value do |diary|
72                        num_section = 1
73                        diary.each_section do |section|
74                                anchor = index \
75                                       + plugin.anchor(diary.date.strftime("%Y%m%d")) \
76                                       + "#p%02d" % num_section
77                                if section.subtitle != nil
78                                        scan(section.subtitle,anchor)
79                                end
80                                scan(section.body,anchor)
81                                num_section = num_section + 1
82                        end
83                end
84        end
85
86        def load(dir)
87                @windex = {}
88                Dir.mkdir(dir) unless File.directory?(dir)
89                PStore.new(dir + "/windex").transaction do |pstore|
90                        @dates = pstore.roots
91                        @dates.each do |key|
92                                windex_tmp = pstore[key]
93                                windex_tmp.each_key do |key_windex|
94                                        if @windex.has_key?(key_windex) == false
95                                                @windex[key_windex] = {"readname" => nil,
96                                                                       "anchor" => []}
97                                        end
98                                        if @windex[key_windex]["readname"] == nil \
99                                                && windex_tmp[key_windex].has_key?("readname") == true
100                                                        @windex[key_windex]["readname"] = windex_tmp[key_windex]["readname"]
101                                        end
102                                        @windex[key_windex]["anchor"].concat(windex_tmp[key_windex]["anchor"])
103                                end
104                        end
105                end
106        end
107
108        def save(dir,keyname)
109                if File.directory?(dir) == false
110                        Dir.mkdir(dir,0755)
111                end
112                PStore.new(dir + "/windex").transaction do |pstore|
113                        pstore[keyname] = @windex
114                end
115        end
116
117        def generate_html(page)
118                return page.generate_html(@windex)
119        end
120
121        def has_key?(key)
122                return @windex.has_key?(key)
123        end
124
125        def [](key)
126                return @windex[key]
127        end
128
129private
130        def scan(body,anchor)
131                to_delimiter_end = {
132                        "(" => ")","[" => "]","{" => "}","<" => ">",
133                }
134
135                wistrs = body.scan(%r[<%\s*=\s*windex\s*[^(<%)]*\s*%>])
136                wistrs.each do |wistr|
137                        # 引数抽出
138                        argstr = wistr.gsub(%r[<%\s*=\s*windex\s*],"")
139                        argstr = argstr.gsub(%r[\s*%>],"")
140                        args = []
141                        flag_done = false
142                        while flag_done == false
143                                pos_delimiter = argstr.index(%r['|"|%[Qq].]) #"'
144                                if pos_delimiter != nil
145                                        # デリミタ文字取得
146                                        delimiter = argstr.scan(%r['|"|%[Qq].])[0] #"'
147                                        if delimiter.length == 3
148                                                delimiter_end = delimiter[2].chr
149                                                if to_delimiter_end.has_key?(delimiter_end)
150                                                        delimiter_end = to_delimiter_end[delimiter_end]
151                                                end
152                                        else
153                                                delimiter_end = delimiter
154                                        end
155
156                                        # デリミタまでの文字列を削除
157                                        argstr = argstr[(pos_delimiter + delimiter.length)..-1]
158                                        pos_delimiter = argstr.index(delimiter_end)
159                                        if pos_delimiter != nil
160                                                if pos_delimiter > 0
161                                                        # 引数として取得
162                                                        args << argstr[0..(pos_delimiter - 1)]
163                                                else
164                                                        args << ""
165                                                end
166                                                # デリミタまでの文字列を削除
167                                                argstr = argstr[(pos_delimiter + delimiter_end.length)..-1]
168                                        else
169                                                flag_done = true
170                                        end
171                                else
172                                        flag_done = true
173                                end
174                        end
175
176                        if args.length > 0
177                                if @windex.has_key?(args[0]) == false
178                                        # ハッシュを生成
179                                        @windex[args[0]] = {"readname" => nil,"anchor" => []}
180                                end
181                                if args.length > 1 && @windex[args[0]]["readname"] == nil && args[1] != ""
182                                        @windex[args[0]]["readname"] = args[1]
183                                end
184                                @windex[args[0]]["anchor"] << anchor
185                        end
186                end
187        end
188end
189
190class WIIndexPage
191        def initialize(title,css)
192                @title = title
193                @css = css
194        end
195
196        def generate_html(windex)
197                body = ""
198
199                # 大項目名 => 名前の配列 のハッシュを生成
200                subindex_to_name = {}
201                windex.keys.each do |key|
202                        subindex = ""
203                        if windex[key]["readname"] != nil
204                                subindex = get_subindex(windex[key]["readname"])
205                        else
206                                subindex = get_subindex(key)
207                        end
208                        if subindex_to_name.has_key?(subindex) == false
209                                subindex_to_name[subindex] = []
210                        end
211                        subindex_to_name[subindex] << key
212                end
213
214                # 大項目名ごとにHTMLを生成
215                if subindex_to_name.has_key?("記号") == true
216                        body << generate_html_subindex(windex,subindex_to_name,"記号")
217                end
218                subindex_to_name.keys.sort.each do |key|
219                        if key != "記号"
220                                body << generate_html_subindex(windex,subindex_to_name,key)
221                        end
222                end
223
224                body = <<-BODY
225                        <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
226                        <html>
227                                <head>
228                                        <title>#{h @title}(索引)</title>
229                                        #{@css}
230                                </head>
231                                <body>
232                                        <h1>#{@title} [索引]</h1>
233                                        <div class="day"><div class="body">
234                                                #{body}
235                                        </div></div>
236                                </body>
237                        </html>
238                        BODY
239
240                return body
241        end
242
243private
244        def generate_html_subindex(windex,subindex_to_name,key)
245                readname_to_name = {}
246                subindex_to_name[key].each do |name|
247                        key_new = ""
248                        if windex[name]["readname"] != nil
249                                key_new = windex[name]["readname"]
250                        else
251                                key_new = name
252                        end
253                        if readname_to_name.has_key?(key_new) == false
254                                readname_to_name[key_new] = []
255                        end
256                        readname_to_name[key_new] << name
257                end
258
259                body = %Q[<div class="section"><h2>#{key}</h2>\n]
260
261                # 読み仮名のソートでループ -> 名前のソートでループ
262                keys = readname_to_name.keys
263                if keys.empty? == false
264                        keys.sort.each do |readname|
265                                readname_to_name[readname].sort.each do |name|
266                                        body << "<p>#{name} ... "
267                                        num_anchor = 1
268                                        windex[name]["anchor"].sort.each do |anchor|
269                                                body = body + %Q[<a href="#{h anchor}">#{num_anchor}</a>]
270                                                if num_anchor < windex[name]["anchor"].length
271                                                        body = body + ","
272                                                end
273                                                num_anchor = num_anchor + 1
274                                        end
275                                        body << "</p>"
276                                end
277                        end
278                end
279                body << "\n</div>\n"
280
281                return body
282        end
283
284        def get_subindex(name)
285                to_plainhiragana = {
286                        "ぁ" => "あ","ぃ" => "い","ぅ" => "う","ぇ" => "え","ぉ" => "お",
287                        "が" => "か","ぎ" => "き","ぐ" => "く","げ" => "け","ご" => "こ",
288                        "ざ" => "さ","じ" => "し","ず" => "す","ぜ" => "せ","ぞ" => "そ",
289                        "だ" => "た","ぢ" => "ち","っ" => "つ","づ" => "つ","で" => "て","ど" => "と",
290                        "ば" => "は","ぱ" => "は","び" => "ひ","ぴ" => "ひ","ぶ" => "ふ","ぷ" => "ふ","べ" => "へ","ぺ" => "へ","ぼ" => "ほ","ぽ" => "ほ",
291                        "ゃ" => "や","ゅ" => "ゆ","ょ" => "よ",
292                        "ゎ" => "わ","ヴ" => "う","ヵ" => "か","ヶ" => "け",
293                }
294                to_1byte = {
295                        "!" => "!",'”' => '"',"#" => "#","$" => "$","%" => "%","&" => "&","’" => "'","(" => "(",")" => ")","*" => "*","+" => "+","," => ",","−" => "-","." => ".","/" => "/",
296                        "0" => "0","1" => "1","2" => "2","3" => "3","4" => "4","5" => "5","6" => "6","7" => "7","8" => "8","9" => "9",":" => ":",";" => ";","<" => "<","=" => "=",">" => ">","?" => "?",
297                        "@" => "@","A" => "A","B" => "B","C" => "C","D" => "D","E" => "E","F" => "F","G" => "G","H" => "H","I" => "I","J" => "J","K" => "K","L" => "L","M" => "M","N" => "N","O" => "O",
298                        "P" => "P","Q" => "Q","R" => "R","S" => "S","T" => "T","U" => "U","V" => "V","W" => "W","X" => "X","Y" => "Y","Z" => "Z","[" => "[","¥" => "\\","]" => "]","^" => "^","_" => "_",
299                        "a" => "a","b" => "b","c" => "c","d" => "d","e" => "e","f" => "f","g" => "g","h" => "h","i" => "i","j" => "j","k" => "k","l" => "l","m" => "m","n" => "n","o" => "o",
300                        "p" => "p","q" => "q","r" => "r","s" => "s","t" => "t","u" => "u","v" => "v","w" => "w","x" => "x","y" => "y","z" => "z","{" => "{","|" => "|","}" => "}","‾" => "~",
301                }
302
303                topchr = name[0,1]
304                if topchr.count("\xA1-\xFE") == 1
305                        # マルチバイト文字
306                        topchr = name[0,2]
307                end
308                if to_1byte.has_key?(topchr) == true
309                        topchr = to_1byte[topchr]
310                end
311                if topchr.length == 1
312                        # シングルバイト文字の処理
313                        topchr = topchr.upcase
314
315                        if (0x21 <= topchr[0] && topchr[0] <= 0x2F) \
316                                || (0x3A <= topchr[0] && topchr[0] <= 0x40) \
317                                || (0x5B <= topchr[0] && topchr[0] <= 0x60) \
318                                || (0x7B <= topchr[0] && topchr[0] <= 0x7B)
319                                        topchr = "記号"
320                        end
321                else
322                        # マルチバイト文字の処理
323                        # カタカナ->ひらがな変換
324                        code = topchr[0] * 0x100 + topchr[1]
325                        if 0xA5A1 <= code && code <= 0xA5F3
326                                topchr = 0xA4.chr + topchr[1].chr
327                        end
328
329                        # 濁点 / 半濁点 撥音など変換
330                        if to_plainhiragana.has_key?(topchr) == true
331                                topchr = to_plainhiragana[topchr]
332                        end
333                end
334
335                return topchr
336        end
337end
338
339class WIRedirectPage
340        def initialize(key)
341                @key = key
342        end
343
344        def generate_html(windex)
345                anchor = windex[@key]["anchor"][0]
346                body = <<-BODY
347                        <html>
348                                <head>
349                                        <meta http-equiv="refresh" content="0;url=#{h anchor}">
350                                        <title>moving...</title>
351                                </head>
352                                <body>Wait or <a href="#{h anchor}">Click here!</a></body>
353                        </html>
354                        BODY
355
356                return body
357        end
358end
359
360class WISinglePage
361        def initialize(title,date_format,css,key)
362                @title = title
363                @date_format = date_format
364                @css = css
365                @key = key
366        end
367
368        def generate_html(windex)
369                anchors = windex[@key]["anchor"]
370                body = %Q[<div class="section"><h2>#{@key}</h2>\n]
371                anchors.sort.each do |anchor|
372                        str_date = anchor.scan(/\d{8}/)[0]
373                        date = Time.local(str_date[0..3].to_i,str_date[4..5].to_i,str_date[6..7].to_i)
374                        body << %Q[<p><a href="#{h anchor}">#{date.strftime(@date_format)}</a></p>\n]
375                end
376                body << "\n</div>\n"
377                body = <<-BODY
378                        <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
379                        <html>
380                                <head>
381                                        <title>#{h @title}(索引)</title>
382                                        #{@css}
383                                </head>
384                                <body>
385                                        <h1>#{@title} [索引]</h1>
386                                        <div class="day"><div class="body">
387                                                #{body}
388                                        </div></div>
389                                </body>
390                        </html>
391                        BODY
392
393                return body
394        end
395end
396
397class WIErrorPage
398        def initialize(title,css,key)
399                @title = title
400                @css = css
401                @key = key
402        end
403
404        def generate_html(windex)
405                body = <<-BODY
406                        <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
407                        <html>
408                                <head>
409                                        <title>#{h @title}(索引)</title>
410                                        #{@css}
411                                </head>
412                                <body>
413                                        <h1>#{@title} [索引]</h1>
414                                        <div class="day"><div class="body">
415                                                キーワード「#{h @key}」は登録されていません。
416                                        </div></div>
417                                </body>
418                        </html>
419                        BODY
420
421                return body
422        end
423end
424
425def windex(str,readname = "")
426        return str
427end
428
429def wikw(str)
430        if @wordindex.has_key?(str) == true
431                anchors = @wordindex[str]["anchor"]
432                if anchors.length == 1
433                        return %Q[<a href="#{h anchors[0]}">#{str}</a>]
434                else
435                        body = "#{str}("
436                        num_anchor = 1
437                        anchors.sort.each do |anchor|
438                                body << %Q[<a href="#{h anchor}">#{num_anchor}</a>]
439                                if num_anchor < anchors.length
440                                        body << ","
441                                end
442                                num_anchor = num_anchor + 1
443                        end
444                        body << ")"
445
446                        return body
447                end
448        end
449
450        return str
451end
452
453if mode != "CGI"
454        @wordindex = WIWordIndex.new
455        @wordindex.load(@cache_path + "/windex")
456
457        add_update_proc do
458                if @conf.options["windex.generate_all"] == true
459                        tdiary = WITDiary.new(@cgi,"day.rhtml",@conf)
460                        @years.each_key do |year|
461                                @years[year.to_s].each do |month|
462                                        date = Time::local(year,month)
463                                        wordindex = tdiary.generate_wordindex(date,self,@conf.index)
464                                        wordindex.save(@cache_path + "/windex",date.strftime('%Y%m'))
465                                end
466                        end
467                else
468                        wordindex = WIWordIndex.new
469                        wordindex.generate(@diaries,self,@conf.index)
470                        wordindex.save(@cache_path + "/windex",@date.strftime('%Y%m'))
471                end
472        end
473else
474        cgi = CGI.new
475        conf = TDiary::Config.new(cgi)
476        cache_path = conf.data_path + "cache"
477        plugin = WITDiary.new(cgi,"day.rhtml",conf).load_plugins
478
479        wordindex = WIWordIndex.new
480        wordindex.load(cache_path + "/windex")
481        if cgi.valid?('kw') == true
482                key = cgi.params['kw'][0]
483                if wordindex.has_key?(key) == true
484                        num_anchor = wordindex[key]["anchor"].length
485                        if num_anchor == 0
486                                page = WIErrorPage.new(conf.html_title,plugin.css_tag,key)
487                        elsif num_anchor == 1
488                                page = WIRedirectPage.new(key)
489                        else
490                                page = WISinglePage.new(conf.html_title,conf.date_format,plugin.css_tag,key)
491                        end
492                else
493                        page = WIErrorPage.new(conf.html_title,plugin.css_tag,key)
494                end
495        else
496                page = WIIndexPage.new(conf.html_title,plugin.css_tag)
497        end
498        body = wordindex.generate_html(page)
499
500        header = {
501                "type" => "text/html",
502                "charset" => "UTF-8",
503                "Content-Length" => body.size.to_s,
504                "Pragma" => "no-cache",
505                "Cache-Control" => "no-cache",
506                "Vary" => "User-Agent",
507        }
508        head = cgi.header(header)
509
510        print head
511        print body
512end
Note: See TracBrowser for help on using the browser.