OSDN Git Service

Fix: myopen doesn't LOCK_EX when mode is RDWR or Integer.
[feedblog/feedgenerator.git] / feedgenerator.rb
1 #!/usr/local/bin/ruby
2 # -*- coding: utf-8 -*-
3 #
4 #= Atom Feed 1.0を管理するWEBアプリケーション
5 #
6 #Autohr::    Kureha Hisame (http://lunardial.sakura.ne.jp/) & Yui Naruse (http://airemix.com/)
7 #Version::   2.0.0.0
8 #Copyright:: Copyright 2009 FeedBlog Project (http://sourceforge.jp/projects/feedblog/)
9 #License::   GPLv3
10
11 require "cgi"
12 require "cgi/session"
13 require "erb"
14 require "rexml/document"
15 require "pstore"
16 require 'time'
17 require "date"
18 require "fileutils"
19
20 # ログインID
21 LOGINID = "login"
22 # ログインパスワード
23 PASSWORD = "password"
24 # インターフェースのテーブルの幅
25 TABLEWIDTH = 800
26 # XMLファイル格納先までの相対パス
27 XMLPATH = "./../lunardial/xml/"
28 # loglist.xmlファイルの定義
29 LISTXMLPATH = "#{XMLPATH}loglist.xml"
30 # FeedBlog上の表示ページからログ格納ディレクトリまでのパス
31 FEEDXMLDIR = "./xml/"
32 # デバッガモード
33 DEBUG = false
34 # ファイルマネージャー機能を使用するならtrue
35 USEFILEMANAGER = true
36 # ファイルマネージャー機能スクリプト(filemanager.rb)のパス
37 FILEMANAGER = "./filemanager.rb"
38 # XMLに書き込む際、改行部分を<br>のまま保持するか、改行記号に直すか
39 REPLACEBRTAG = false
40 # ファイルの書き込み時にENTRYのIDおよびURLを、FEEDオブジェクトから自動生成した値に置換するか否か
41 REPLACEENTRYIDANDURL = false
42
43 # バージョン情報を示す文字列です
44 APPVERSION = "- FeedGenerator for Ruby version 2.0.0.0 -<br>Copyright(c) 2009 Kureha.H (<a href=\"http://lunardial.sakura.ne.jp/\" target=\"_blank\">http://lunardial.sakura.ne.jp/</a>) & Yui Naruse (<a href=\"http://airemix.com/\" target=\"_blank\">http://airemix.com/</a>)"
45 # タイトル領域に表示される文字列です
46 APPTITLE = "FeedGenerator for Ruby version 2.0.0.0"
47
48 # = Objectクラス
49 #
50 # 基本クラスのオーバーライドを行います
51 class Object
52   # myopenメソッド
53   #
54   # ruby-1.9.x以降ではファイルを開いた際、エンコードの指定を行わないとエラーの原因になります。
55   # ただしruby-1.8.6以前はエンコードの指定に対応していないため、独自メソッドを定義してファイルの入出力を行います。
56   #
57   # _arg[0]_ :: 入出力を行うファイルのパス
58   # _arg[1]_ :: モードの指定。例 : w:utf-8(書き込みモード・UTF-8エンコードでファイルを開く)
59   def myopen(*arg)
60     mode = arg[1]
61     rdonly_p = true
62     case mode
63     when String
64       arg[1] = mode[/[^:]+/] if RUBY_VERSION < "1.8.7" && mode.include?(':')
65       rdonly_p = /\A[^:]*[wa+]/ !~ mode
66     when Numeric
67       rdonly_p = !(mode & (IO::WRONY | IO::RDWR))
68     end
69     open(*arg) do |f|
70       f.flock(rdonly_p ? File::LOCK_SH : File::LOCK_EX)
71       return yield(f)
72     end
73   end
74 end
75
76 class NilClass
77   def blank?
78     nil?
79   end
80 end
81
82 class Array
83   def blank?
84     empty?
85   end
86 end
87
88 class String
89   def blank?
90     empty?
91   end
92 end
93
94 # = Feed/Entryのスーパークラス
95
96 # 入出力用のメソッドなど、共通する機能を提供します
97 class AbstractEntry
98   # 初期化メソッドです
99   #
100   # _hash_ :: 値を格納したhash配列。superfeed.new(CGI::Session.new(new CGI).params)のようにして使う。
101   def initialize(hash)
102     # 内部データ保持用のハッシュ配列です
103     @attr = {}
104     
105     # 引数を格納します
106     @paramlist.each do |key|
107       val = hash[key.to_sym] || hash[key.to_s]
108       if val
109         val.strip!
110         val.gsub!(/\r\n|\r/, "\n")
111         @attr[key.to_sym] = CGI.escapeHTML(val)
112       else
113         # 空の場合の対策
114         @attr[key.to_sym] = ""
115       end
116     end
117     
118     # 可視・不可視を示すハッシュキーを格納する配列です
119     @display = {}
120     
121     # ハッシュキーの日本語説明を格納する配列です
122     @name = []
123   end
124   
125   # Accessor
126   attr_reader :attr, :paramlist, :name, :display
127   
128   # 内部の@attrハッシュにアクセスする手段を提供するメソッドです
129   #
130   # AbstractEntry.attr[:title]にアクセスしたい場合はAbstractEntry.titleで可能になります。
131   # AbstractEntry.send("title")という方法でもアクセス可能です。
132   def method_missing(methname, *args)
133     methname = methname.to_s
134     
135     if methname[-1] == ?=
136       # setter
137       raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" unless args.length == 1
138       methname.chop!
139       methname = @paramlist.find{|par|par == methname}
140       return @attr[methname.to_sym] = args[0] if methname
141     else
142       # getter
143       raise ArgumentError, "wrong number of arguments (#{args.length} for 0)" unless args.empty?
144       return @attr[methname.to_sym] if @attr.key?(methname.to_sym)
145     end
146     # attr上にキーがない値を入れようとした場合はNoMethodError
147     raise NoMethodError
148   end
149   
150   def [](key)
151     @attr[key.to_sym]
152   end
153   
154   def []=(key, value)
155     @attr[key.to_sym] = value
156   end
157 end
158
159 # = Feedクラス
160 #
161 # Feedの基礎情報を保有するクラスです
162 class Feed < AbstractEntry
163   # 初期化メソッドです
164   #
165   # _hash_ :: 値を格納したhash配列。feed.new(CGI::Session.new(new CGI).params)のようにして使います。
166   def initialize(hash)
167     # 内部データ保持用のハッシュ配列です
168     @attr = {}
169     
170     # 内部データの項目名を格納する配列です
171     @paramlist = ["feedattr", "title", "subtitle", "self", "url", "updated", "feedid", "rights", "aname", "amail", "others"]
172     
173     # AbstractEntryのinitializeメソッドを呼び出し、値を@attr配列に格納します
174     super(hash)
175     
176     # 可視・不可視を示すハッシュキーを格納する配列です
177     @display = {"feedattr" => "none", "title" => "", "subtitle" => "", 
178       "self" => "", "url" => "", 
179       "updated" => "none", "feedid" => "", 
180       "rights" => "", "aname" => "", 
181       "amail" => "", "others" => "none"}
182     
183     # デバッグモードの場合、全ての入力要素を表示します
184     if DEBUG == true
185       @display.each do |key, val|
186         @display[key] = ""
187       end
188     end
189     
190     # ハッシュキーの日本語説明を格納する配列です
191     @name = {"feedattr" => "feedの保持している属性", "title" => "ウェブページのタイトル", "subtitle" => "ウェブページの簡単な説明", 
192       "self" => "このXMLファイルのURL", "url" => "ウェブページのURL", 
193       "updated" => "XMLファイルの最終更新日", "feedid" => "あなたのページ独自のID", 
194       "rights" => "ページの著作権表記", "aname" => "ページの製作者", 
195       "amail" => "ページ製作者のメールアドレス", "others" => "その他の要素"}
196     
197   end
198   
199   # Atom XMLファイルを読み込んで解析し、等価なFeedオブジェクトを返却するメソッドです
200   #
201   # _path_ :: Atom XMLファイルのパス
202   def self.readxml(path)
203     
204     # ファイルを読み込みます
205     doc = REXML::Document.new(myopen(path, "r:utf-8"){|f|f.read})
206     xml = {}
207     others = []
208     
209     # Feedの属性値を取得します
210     xmlattr = []
211     doc.elements["feed"].attributes.each_attribute do |attr|
212       xmlattr.push("#{attr.to_string} ")
213     end
214     xml[:feedattr] = xmlattr.join("\n").gsub(/"/, "'")
215     
216     # XML解析部分です。各element毎に判定を行います
217     doc.elements.each("feed") do |elm|
218       elm.elements.each { |child|
219         begin
220           case child.name
221             when "id"
222             xml[:feedid] = child.text
223             when "title", "subtitle", "updated", "rights"
224             xml[child.name.to_sym] = child.text
225             when "author"
226             child.elements.each do |gchild|
227               case gchild.name
228                 when "name"
229                 xml[:aname] = gchild.text
230                 when "email"
231                 xml[:amail] = gchild.text
232               end
233             end
234             when "link"
235             child.attributes.each do |k, v|
236               if k == "rel"
237                 if v == "self"
238                   xml[:self] = child.attributes["href"]
239                 elsif v == "alternate"
240                   xml[:url] = child.attributes["href"]
241                 end
242               end
243             end
244             when "entry"
245             # Entry要素は無視します
246           else
247             # 上記判定以外の全要素は配列に格納します
248             others.push(child.to_s)
249           end
250         rescue NoMethodError
251         end
252         
253       }
254     end
255     
256     # Others要素を結合して代入します
257     xml[:others] = others.join("\n")
258     feed = Feed.new(xml)
259     return feed
260   end
261   
262   # 内部に保持している情報を、Atom Feed1.0形式の文字列に出力するメソッドです
263   def to_s
264     buf = []
265     
266     buf.push("<feed #{@attr[:feedattr]}>")
267     buf.push("<title type=\"text\">#{@attr[:title]}</title>")
268     buf.push("<subtitle type=\"text\">#{@attr[:subtitle]}</subtitle>")
269     buf.push("<link rel=\"self\" type=\"application/atom+xml\" href=\"#{@attr[:self]}\" />")
270     buf.push("<link rel=\"alternate\" type=\"text/html\" href=\"#{@attr[:url]}\" />")
271     buf.push("<updated>#{Time.now.iso8601}</updated>")
272     buf.push("<id>#{@attr[:feedid]}</id>")
273     buf.push("<rights type=\"text\">#{@attr[:rights]}</rights>")
274     buf.push("<author>")
275     buf.push("\t<name>#{@attr[:aname]}</name>")
276     buf.push("\t<email>#{@attr[:amail]}</email>")
277     buf.push("</author>")
278     buf.push("#{CGI.unescapeHTML(@attr[:others])}") if @attr[:others] != ""
279     
280     return buf.join("\n")
281   end
282   
283   # Feed情報更新メソッド
284   def self.update(path, feed)
285     entrylist = Entry.readxml(path)
286     Feed.to_xml(path, feed, entrylist)
287     
288     true
289   end
290   
291   # XMLファイル出力用メソッドです
292   #
293   # _feed_ :: Feedオブジェクト
294   # _entry_ :: Entryオブジェクトの配列
295   def self.to_xml(path, feed, entrylist_tmp)
296     buf = []
297     entrylist = entrylist_tmp.dup
298     buf.push("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
299     buf.push("#{feed.to_s}\n")
300     entrylist.each { |entry|
301       if REPLACEENTRYIDANDURL
302         entry.entryid.gsub!(/^[^\?]*\?/, "")
303         entry.entryid = feed.url + "?" + entry.entryid
304         entry.url = feed.url + "#" + entry.entryid
305       end
306       buf.push("#{entry.to_s}\n")
307     }
308     buf.push("</feed>")
309     
310     myopen(path, "w") do |f|
311       f.print buf.join("\n")
312     end
313   end
314   
315   # XMLファイル出力用メソッドです
316   #
317   # _feed_ :: Feedオブジェクト
318   # _entry_ :: Entryオブジェクトの配列
319   def self.to_xml_plain(path, feed, entrylist)
320     buf = []
321     buf.push("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
322     buf.push("#{feed.to_s}\n")
323     entrylist.each { |entry|
324       buf.push("#{entry.to_s}\n")
325     }
326     buf.push("</feed>")
327     
328     myopen(path, "w") do |f|
329       f.print buf.join("\n")
330     end
331   end
332   
333 end
334
335 # = Entryクラス
336 #
337 # Entryの基礎情報を保有するクラスです
338 class Entry < AbstractEntry
339   # 初期化メソッドです
340   #
341   # _hash_ :: 値を格納したhash配列。entry.new(CGI::Session.new(new CGI).params)のようにして使います。
342   def initialize(hash)
343     # 内部データ保持用のハッシュ配列です
344     @attr = {}
345     
346     # 内部データの項目名を格納する配列です
347     @paramlist = ["entryid", "title", "summary", "published", "updated", "url", "content", "others"]
348     
349     # AbstractEntryのinitializeメソッドを呼び出し、値を@attr配列に格納します
350     super(hash)
351     
352     # 可視・不可視を示すハッシュキーを格納する配列です
353     @display = {"entryid" => "none", "title" => "",
354       "summary" => "", "published" => "none",
355       "updated" => "none", "url" => "none",
356       "content" => "", "others"=>"none"}
357     
358     # デバッグモードの場合、全ての入力要素を表示します
359     if DEBUG == true
360       @display.each do |key, val|
361         @display[key] = ""
362       end
363     end
364     
365     # ハッシュキーの日本語説明を格納する配列です
366     @name = {"entryid" => "記事固有のID", "title" => "記事のタイトル",
367       "summary" => "記事の簡単な説明", "published" => "記事の出版時刻",
368       "updated" => "記事の更新時刻", "url" => "記事へのURLアドレス",
369       "content" => "記事の本文", "others"=>"その他の項目"}
370   end
371   
372   # Atom XMLファイルを読み込んで解析し、等価なEntryオブジェクト配列を返却するメソッドです
373   #
374   # _path_ :: Atom XMLファイルのパス
375   def self.readxml(path)
376     
377     # ファイルを読み込みます
378     doc = REXML::Document.new(myopen(path, "r:utf-8"){|f|f.read})
379     entrylist = []
380     xml = {}
381     
382     # XML解析部分です。各element毎に判定を行います
383     doc.elements.each("feed/entry") do |elm|
384       xml = {}
385       others = []
386       elm.elements.each do |child|
387         begin
388           case child.name
389             when "id"
390             xml[:entryid] = child.text
391             when "link"
392             xml[:url] = child.attributes["href"]
393             when "title", "summary", "summary", "published", "updated", "content"
394             xml[child.name.to_sym] = child.text
395           else
396             # 上記判定以外の全要素は配列に格納します
397             others.push(child.to_s)
398           end
399         rescue NoMethodError
400         end
401       end
402       # Others要素を結合して代入します
403       xml[:others] = others.join("\n")
404       entrylist.push(Entry.new(xml))
405     end
406     
407     return entrylist
408   end
409   
410   # Entry挿入メソッド
411   def self.insert(path, entry)
412     feed = Feed.readxml(path)
413     entrylist = Entry.readxml(path)
414     entrylist.unshift entry
415     
416     Feed.to_xml(path, feed, entrylist)
417     
418     true
419   end
420   
421   # Entry更新メソッド
422   def self.update(path, entry)
423     feed = Feed.readxml(path)
424     entrylist = Entry.readxml(path)
425     
426     successed = false
427     entrylist.each_with_index { |e, i|
428       if e.entryid == entry.entryid
429         entrylist[i] = entry 
430         successed = true
431       end
432     }
433     Feed.to_xml(path, feed, entrylist) if successed
434     
435     successed
436   end
437   
438   # Entryロードメソッド
439   def self.select(path, entryid)
440     feed = Feed.readxml(path)
441     entrylist = Entry.readxml(path)
442     
443     entry = nil
444     entrylist.each_with_index { |e, i|
445       entry = entrylist[i].dup if e.entryid == entryid
446     }
447     
448     entry
449   end
450   
451   # Entry消去メソッド
452   def self.delete(path, entryid)
453     feed = Feed.readxml(path)
454     entrylist = Entry.readxml(path)
455     
456     successed = false
457     delete_index = -1
458     entrylist.each_with_index { |e, i|
459       if e.entryid == entryid
460         delete_index = i
461         successed = true
462       end
463     }
464     
465     if successed
466       entrylist.delete_at delete_index
467       Feed.to_xml(path, feed, entrylist)
468     end
469     
470     successed
471   end
472   
473   # データソースから読み取ったHTMLを、エディタで編集可能な形式に変換するメソッドです
474   def content_for_generator
475     str = @attr[:content].dup
476     str.strip!
477     str.gsub!(/(&lt;\/(?:p|h\d|div)(?:&gt;|>))\n/i, '\1')
478     str.gsub!(/\n/, '&lt;br&gt;') if REPLACEBRTAG
479     str.gsub!(/(&lt;(?:(?!&gt;).)*?)#{Regexp.escape(FEEDXMLDIR)}/) { "#$1#{XMLPATH}" }
480     str
481   end
482   
483   # エディタで編集されたCONTENT要素を、データソースに書き込める形式に変換するメソッドです
484   def content_for_blog
485     str = @attr[:content].dup
486     str = CGI.unescapeHTML(str)
487     str.strip!
488     str.gsub!(/(\r\n|\n)/, "")
489     str.gsub!(/<br>/i, "\n") if REPLACEBRTAG
490     str.gsub!(/(<br>|<\/p>|<\/h\d>|<\/div>)(?=[^\n])/i) { "#$1\n" } unless REPLACEBRTAG
491     str.gsub!(/(<[^>]*?)#{Regexp.escape(XMLPATH)}/) { "#$1#{FEEDXMLDIR}" }
492     CGI.escapeHTML(str)
493   end
494   
495   # 確認画面で表示されるCONTENT要素を生成するメソッドです
496   def content_for_view
497     str = @attr[:content].dup
498     str = CGI.unescapeHTML(str)
499     str.strip!
500     str.gsub!(/<br>/i, "\n") if REPLACEBRTAG
501     str.gsub!(/(<[^>]*?)#{Regexp.escape(FEEDXMLDIR)}/) { "#$1#{XMLPATH}" }
502     str
503   end
504   
505   # 内部に保持している情報を、Atom Feed1.0形式の文字列に出力するメソッドです
506   def to_s
507     buf = []
508     buf.push("<entry>")
509     buf.push("<id>#{@attr[:entryid]}</id>")
510     buf.push("<title>#{@attr[:title]}</title>")
511     buf.push("<summary>#{@attr[:summary]}</summary>")
512     buf.push("<published>#{@attr[:published]}</published>")
513     buf.push("<updated>#{@attr[:updated]}</updated>")
514     buf.push("<link href=\"#{@attr[:url]}\" />")
515     buf.push("<content type=\"html\">#{@attr[:content]}</content>")
516     buf.push("#{CGI.unescapeHTML(@attr[:others])}") if @attr[:others] != ""
517     buf.push("</entry>")
518     
519     return buf.join("\n")
520   end
521 end
522
523 # = HtmlWriterクラス
524
525 # テンプレートファイル(*.erb)を読み込み、管理するクラスです
526 class HtmlWriter
527   # 初期化メソッドです
528   #
529   # _template_ :: テンプレートファイル(*.erb)のパス
530   # _binding_ :: binding変数
531   def initialize(template, binding)
532     @erb = ERB.new(myopen(template, "r:utf-8") {|f| f.read}, nil, "-")
533     @binding = binding
534   end
535   
536   # テンプレートファイルの文字列を返却するメソッドです
537   def to_code
538     @erb.result(@binding)
539   end
540 end
541
542 # = LogListクラス
543 #
544 # loglist.xmlにかかわる入出力を行います
545 class LogList
546   # 初期化メソッドです
547   #
548   # _display_ :: 画面表示用文字の配列
549   # _path_ :: XMLファイルパスの配列
550   # _logpath_ :: loglist.xmlのパス
551   def initialize(display, path, logpath)
552     @display = display
553     @path = path
554     @logpath = logpath
555   end
556   
557   attr_accessor :display, :path, :logpath
558   
559   # loglist.xmlファイルを読み込んで解析し、LogListオブジェクトを返却するメソッドです
560   #
561   # _logpath_ :: loglist.xmlへのパス
562   def LogList.readxml(logpath)
563     
564     # ファイルを読み込みます
565     lines = []
566     myopen(logpath, "r:utf-8") { |f|
567       lines = f.readlines
568     }
569     
570     doc = REXML::Document.new(lines.join("\n"))
571     @display = []
572     @path = []
573     
574     doc.elements.each("list/file") { |elm|
575       elm.elements.each {|child|
576         case child.name
577           when "display"
578           @display.push(child.text)
579           when "path"
580           @path.push(child.text)
581         else 
582           # With no action
583         end
584       }
585     }
586     
587     return LogList.new(@display, @path, logpath)
588   end
589   
590   # loglist.xmlにオブジェクトの内容を反映するメソッドです
591   def to_xml
592     buf = []
593     buf.push("<list>")
594     path.each_with_index do |path, i|
595       buf.push("<file>")
596       buf.push("<display>#{@display[i]}</display>")
597       buf.push("<path>#{@path[i]}</path>")
598       buf.push("</file>")
599     end
600     buf.push("</list>")
601     
602     myopen(@logpath, "w") do |f|
603       f.print buf.join("\n")
604     end
605   end
606   
607 end
608
609 # = FileUploaderクラス
610 #
611 # 画像の管理を行うクラスです
612 class FileUploader
613   # 初期化メソッドです
614   def initialize
615   end
616   
617   # ファイルの一覧を取得し、ファイル名称を格納した配列を返却します
618   def filelist
619     arr = Dir::entries(XMLPATH).sort
620     arr.delete(".")
621     arr.delete("..")
622     
623     return arr
624   end
625   
626   # ファイルの消去を実行します
627   #
628   # _name_ :: 消去するファイル名
629   def delete(name)
630     File.delete(XMLPATH + name.match(/[^\/]*?$/).to_a[0])
631   end
632   
633   # ファイルのアップロードを実行します
634   #
635   # _name_ :: アップロードファイルの名称
636   # _img_ :: アップロードファイル
637   def upload(name, img)
638     open(XMLPATH + File.basename(name), "w") do |f|
639       f.binmode
640       f.write img
641     end
642   end
643 end
644
645 # = Controllerクラス
646 #
647 # コントローラ部分に相当する処理を受け持つクラスです
648 class Controller
649   def Controller.NormalForm(cgi, session, params, db)
650     db.transaction do
651       # modeとactionの相関図は以下のようになります。
652       # [mode] + [action]
653       #        + [action]
654       #        + [action]
655       #        + ... and more
656       case params["mode"]
657         # ログ選択画面
658         when "logselect"
659         session["filepath"] = params["logpath"]
660         db["loglist"] = LogList.readxml(LISTXMLPATH)
661         # 初期状態で選択されるログは「loglist.xml」の最上に位置するログになります
662         session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
663         db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
664         db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
665         
666         # 新規記事追加部分
667         when "newentry"
668         case params["action"]
669           # 確認画面
670           when "confirm"
671           session["target_filepath"] = params["target_filepath"]
672           db["newentry"] = Entry.new(params)
673           db["newentry"].content = db["newentry"].content_for_blog
674           # 記事の追記を実際にファイルに反映
675           when "exec"
676           session["target_filepath"] = params["target_filepath"]
677           successed = Entry.insert(XMLPATH + File.basename(params["target_filepath"]), Entry.new(params))
678           unless successed
679             db["error"] = "日記の新規追加に失敗しました。" 
680             params["mode"] = "error"
681           end
682           # 画面を戻った際の処理
683           when "back"
684           session["target_filepath"] = params["target_filepath"]
685           db["newentry"] = Entry.new(params)
686         else 
687           # New Diary - Default
688           session["target_filepath"] = session["filepath"]
689           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
690           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
691         end
692         
693         # 記事編集部分
694         when "editentry"
695         case params["action"]
696           # 編集画面
697           when "edit"
698           session["target_filepath"] = params["target_filepath"]
699           session["editid"] = cgi["editid"].to_s
700           db["editentry"] = Entry.select(XMLPATH + File.basename(session["target_filepath"]), session["editid"])
701           # 確認画面
702           when "confirm"
703           session["target_filepath"] = params["target_filepath"]
704           session["editid"] = cgi["editid"].to_s
705           db["editentry"] = Entry.new(params)
706           db["editentry"].content = db["editentry"].content_for_blog
707           # 記事の変更を実際にファイルに反映
708           when "exec"
709           session["target_filepath"] = params["target_filepath"]
710           successed = Entry.update(XMLPATH + File.basename(params["target_filepath"]), Entry.new(params))
711           unless successed
712             db["error"] = "日記の編集処理に失敗しました。<br>該当の日記が既に存在しない可能性があります。" 
713             params["mode"] = "error"
714           end
715           when "back"
716           session["target_filepath"] = params["target_filepath"]
717           db["editentry"] = Entry.new(params)
718         else
719           # Edit Diary - Default
720           session["target_filepath"] = session["filepath"]
721           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
722           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
723         end
724         
725         # 記事削除部分
726         when "delentry"
727         case params["action"]
728           # 確認画面
729           when "confirm"
730           session["target_filepath"] = params["target_filepath"]
731           session["delid"] = cgi["delid"].to_s
732           db["delentry"] = Entry.select(XMLPATH + File.basename(session["target_filepath"]), session["delid"])
733           # 記事の削除を実際にファイルに反映
734           when "exec"
735           session["target_filepath"] = params["target_filepath"]
736           successed = Entry.delete(XMLPATH + File.basename(params["target_filepath"]), cgi["delid"].to_s)
737           unless successed
738             db["error"] = "日記の編集処理に失敗しました。<br>該当の日記が既に存在しない可能性があります。" 
739             params["mode"] = "error"
740           end
741           when "back"
742           session["target_filepath"] = params["target_filepath"]
743           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["target_filepath"]))
744           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["target_filepath"]))
745         else
746           # Delete Diary - Default
747           session["target_filepath"] = session["filepath"]
748           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
749           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
750         end
751         
752         # Feed情報変更部分
753         when "editfeed"
754         case params["action"]
755           # 確認画面
756           when "confirm"
757           session["target_filepath"] = params["target_filepath"]
758           db["feed"] = Feed.new(params)
759           # 実際にFeed情報の変更をファイルに反映
760           when "back"
761           session["target_filepath"] = params["target_filepath"]
762           db["feed"] = Feed.new(params)
763           when "exec"
764           session["target_filepath"] = params["target_filepath"]
765           Feed.update(XMLPATH + File.basename(params["target_filepath"]), Feed.new(params))
766         else
767           session["target_filepath"] = session["filepath"]
768           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
769         end
770         
771         # ログ編集モード
772         when "log"
773         # 必ず内部データをリフレッシュする
774         db["loglist"] = LogList.readxml(LISTXMLPATH)
775         case params["action"]
776           # ログファイルの編集を実際にファイルに反映
777           when "addexec"
778           # エラーチェック。この段階のエラーは強度のエラーを発する。
779           db["loglist"].path.each do |val|
780             if val == cgi["logpath"]
781               # 重複していた場合エラーメッセージを表示し、処理を行わない
782               params["action"] = ""
783               params["mode"] = "error"
784               db["error"] = "ログファイルの追加中に重大なエラーが発生しました。<br>環境を見直してください。"
785               return
786             end
787           end
788           
789           # 入力された内容を保持する配列に追加
790           db["loglist"].path[1, 0] = cgi["logpath"]
791           db["loglist"].display[1, 0] = cgi["logdisplay"]
792           db["loglist"].to_xml
793           # 既存のdiary.xmlを指定された名称でコピーして保存
794           FileUtils.copy_file(XMLPATH + File.basename(db["loglist"].path[0]), XMLPATH + File.basename(db["logpath"]), true)
795           # 保持している情報を更新する
796           db["loglist"] = LogList.readxml(LISTXMLPATH)
797           db["entry"] = []
798           # 新たなdiary.xmlを生成。この際保持する情報は同一のFeedオブジェクトnew()
799           Feed.to_xml(XMLPATH + File.basename(db["loglist"].path[0]), db["feed"], [])
800           # 確認画面
801           when "addconfirm"
802           # 入力されたログが既に存在するかを確認
803           db["loglist"].path.each do |val|
804             if val == cgi["logpath"]
805               # 重複していた場合エラーメッセージを表示し、処理を行わない
806               params["action"] = ""
807               db["error"] = "<span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
808               return
809             end
810           end
811           
812           db["logpath"] = cgi["logpath"]
813           db["logdisplay"] = cgi["logdisplay"]
814           
815           if db["logpath"].blank? || db["logdisplay"].blank?
816             params["action"] = ""
817           end
818           when "back"
819           
820           # 削除確認画面
821           when "delconfirm"
822           db["logdelindex"] = params["logdelindex"].to_i
823           
824           if db["logdelindex"] < 1
825             db["error"] = "<span style='color: #ff0000'>ログファイルの削除パラメタが不正です。</span><br>"
826             params["action"] = ""
827           end
828           # 削除処理
829           when "delexec"
830           if cgi["logdelindex"].to_i < 1
831             params["action"] = ""
832             params["mode"] = "error"
833             db["error"] = "ログファイルの削除中に重大なエラーが発生しました。<br>環境を見直してください。"
834             return
835           else
836             # 記事ファイルを削除します
837             File.delete(XMLPATH + File.basename(db["loglist"].path[db["logdelindex"]]))
838             # ログリストから削除します
839             db["loglist"].path.delete_at(cgi["logdelindex"].to_i)
840             db["loglist"].display.delete_at(cgi["logdelindex"].to_i)
841             db["loglist"].to_xml
842             # 保持している情報を更新する
843             db["loglist"] = LogList.readxml(LISTXMLPATH)
844             db["entry"] = []
845           end
846           
847           # 編集画面
848           when "edit"
849           db["logeditindex"] = params["logdelindex"].to_i
850           
851           db["logpath"] = db["loglist"].path[db["logeditindex"]]
852           db["logdisplay"] = db["loglist"].display[db["logeditindex"]]
853           
854           if db["logeditindex"] == 0
855             db["error"] = "<span style='color: #ff0000'>ログファイルの編集パラメタが不正です。</span><br>"
856             params["action"] = ""
857           end
858           # 編集確認画面
859           when "editconfirm"
860           checkflag = true
861           db["loglist"].path.each_with_index do |val, i|
862             if db["logeditindex"] != i
863               if params["logpath"].to_s == db["loglist"].path[i].to_s
864                 checkflag = false
865               end
866             end
867           end
868           
869           if checkflag == false
870             params["action"] = "edit"
871             db["error"] = "<span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
872           else
873             db["loginsertindex"] = params["loginsertindex"].to_i
874             
875             db["logpath"] = params["logpath"].to_s
876             db["logdisplay"] = params["logdisplay"].to_s
877           end
878           # 編集実行
879           when "editexec"
880           checkflag = true
881           db["loglist"].path.each_with_index do |val, i|
882             if db["logeditindex"] != i
883               if params["logpath"].to_s == db["loglist"].path[i].to_s
884                 checkflag = false
885               end
886             end
887           end
888           
889           if checkflag == false
890             params["action"] = ""
891             params["mode"] = "error"
892             db["error"] = "ログファイルの編集中に重大なエラーが発生しました。<br>環境を見直してください。"
893             return
894           else
895             db["loginsertindex"] = params["loginsertindex"].to_i
896             
897             db["logpath"] = params["logpath"].to_s
898             db["logdisplay"] = params["logdisplay"].to_s
899             
900             # ファイルを移動します
901             if XMLPATH + File.basename(db["loglist"].path[db["logeditindex"]]) != XMLPATH + File.basename(db["logpath"])
902               FileUtils.move(XMLPATH + File.basename(db["loglist"].path[db["logeditindex"]]), XMLPATH + File.basename(db["logpath"]))
903               # ログリストを更新します
904               db["loglist"].path.delete_at(db["logeditindex"])
905               db["loglist"].display.delete_at(db["logeditindex"])
906               db["loglist"].path.insert(db["loginsertindex"] + 1, db["logpath"])
907               db["loglist"].display.insert(db["loginsertindex"] + 1, db["logdisplay"])
908               db["loglist"].to_xml
909             end
910           end
911           
912           # 初期表示画面
913         else
914           # 現在編集中のデータを強制的に最上のファイルパスに変更
915           session["filepath"] = db["loglist"].path[0]
916           
917           # 前月の時刻を作成します
918           prevmonth = (DateTime.now << 1)
919           
920           # 前月の時刻を元に、ディフォルト書式を生成します
921           db["logpath"] = FEEDXMLDIR + prevmonth.strftime("%Y%m") + ".xml"
922           db["logdisplay"] = prevmonth.strftime("%Y年%m月").gsub("年0", "年")
923         end
924         
925         # インポートモードの場合の処理
926         when "import"
927         db["loglist"] = LogList.readxml(LISTXMLPATH)
928         
929         # リセットモードの場合の処理
930         when "reset"
931         case params["action"]
932           # リセット実行時の処理
933           when "exec"
934           file = FileUploader.new
935           file.filelist().each { |fname|
936             file.delete(fname) if File.ftype(XMLPATH + fname) == "file"
937           }
938           
939           # 全ファイルの初期化を実行する
940           # loglist.xmlの初期化
941           loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}diary.xml"], LISTXMLPATH)
942           loglist.to_xml
943           
944           db["loglist"] = LogList.readxml(LISTXMLPATH)
945           
946           # diary.xmlの初期化
947           Feed.to_xml(XMLPATH + db["loglist"].path[0].match(/[^\/]*?$/).to_a[0], Feed.new({}), [])
948           
949           # 初期の編集ファイルはloglist.xml上の最も上のファイル
950           session["filepath"] = db["loglist"].path[0]
951           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
952           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
953           
954           # 画面の遷移先をトップページにする
955           params["mode"] = ""
956           params["action"] = ""
957           # パラメタ無しの処理
958         else
959           # ファイル一覧を取得する
960           filelist = FileUploader.new.filelist()
961           
962           # タイプがファイルのみリストとして取得する
963           db["filelist"] = []
964           filelist.each { |fname| db["filelist"] << fname if File.ftype(XMLPATH + fname) == "file"}
965         end
966         
967         # ログイン時の画面
968       else
969         # loglist.xmlが存在するかチェック
970         if File.exist?(LISTXMLPATH) == false
971           # なかった場合はloglist.xmlを自動生成
972           loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}diary.xml"], LISTXMLPATH)
973           loglist.to_xml
974           Feed.to_xml(XMLPATH + File.basename(loglist.path[0]), Feed.new({}), [])
975         end
976         
977         db["loglist"] = LogList.readxml(LISTXMLPATH)
978         
979         # diary.xmlが存在するかチェック
980         if File.exist?(XMLPATH + File.basename(db["loglist"].path[0])) == false
981           # なかった場合はdiary.xmlを自動生成
982           Feed.to_xml(XMLPATH + File.basename(db["loglist"].path[0]), Feed.new({}), [])
983         end
984         
985         # 初期の編集ファイルはloglist.xml上の最も上のファイル
986         session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
987         db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
988         db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
989       end
990     end
991   end
992   
993   # マルチパートフォームの場合の処理です
994   def Controller.MultiForm(cgi, session, params, db)
995     db.transaction do
996       # loglist.xmlをロードします
997       db["loglist"] = LogList.readxml(LISTXMLPATH)
998       case params["mode"]
999         # 特定位置に挿入する場合の処理
1000         when "insert"
1001         case params["action"]
1002           when "exec"
1003           # この段階のエラーに対しては強度のエラーを発する
1004           checkflag = true
1005           db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
1006           db["loglist"].path.each do |val|
1007             if val == db["logpath"]
1008               # 重複していた場合エラーメッセージを表示し、処理を行わない
1009               db["logpath"] = ""
1010               params["action"] = ""
1011               params["mode"] = "error"
1012               db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1013               return
1014             end
1015           end
1016           
1017           db["loginsertindex"] = cgi["loginsertindex"].read.to_i
1018           db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1019           
1020           # 0位置への挿入防止
1021           if db["loginsertindex"] == 0
1022             params["action"] = ""
1023             params["mode"] = "error"
1024             db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1025             return
1026           end
1027           
1028           if File.basename(db["logpath"]).blank? || db["logdisplay"].blank?
1029             params["action"] = ""
1030             params["mode"] = "error"
1031             db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1032             return
1033           end
1034           
1035           # ファイルを実際にアップロードします
1036           file = FileUploader.new
1037           file.upload(db["logpath"], db["importxml"])
1038           
1039           # loglist.xmlを更新します
1040           db["loglist"].path[db["loginsertindex"], 0] = db["logpath"]
1041           db["loglist"].display[db["loginsertindex"], 0] = db["logdisplay"]
1042           db["loglist"].to_xml
1043           
1044           # 情報を更新します
1045           db["loglist"] = LogList.readxml(LISTXMLPATH)
1046           session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
1047           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
1048           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
1049           
1050           when "confirm"
1051           # 入力されたログファイルパスが既に存在するかを確認
1052           checkflag = true
1053           db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
1054           db["loglist"].path.each do |val|
1055             if val == db["logpath"]
1056               # 重複していた場合エラーメッセージを表示し、処理を行わない
1057               db["logpath"] = ""
1058               params["action"] = ""
1059               db["error"] = "<br><span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
1060               checkflag = false
1061               return
1062             end
1063           end
1064           
1065           if checkflag
1066             db["loginsertindex"] = cgi["loginsertindex"].read.to_i
1067             db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1068             db["importxml"] = cgi["updata"].read
1069             
1070             # XMLの整合性をチェックします
1071             begin
1072               REXML::Document.new(Controller.fix_updata_enc(db["importxml"].to_s))
1073             rescue => exception
1074               db["error"] = "<br><span style='color: #ff0000'>不正な整形のXMLです!ファイルを見直してください。</span><br>"
1075               db["error"] << "<div class='divstyle' style='width: 560px; color: #ff0000; text-align: left;'>"
1076               db["error"] << CGI.escapeHTML(exception.to_s).gsub("\n", "<br>")
1077               db["error"] << "</div>"
1078               params["action"] = ""
1079               return
1080             end
1081           end
1082           
1083           # 0位置への挿入防止
1084           if db["loginsertindex"] == 0
1085             params["action"] = ""
1086             db["error"] = "<br><span style='color: #ff0000'>ラジオボックスでログの挿入位置を選択してください!</span><br>"
1087             return
1088           end
1089           
1090           if db["logpath"] == FEEDXMLDIR || db["logdisplay"].blank?
1091             params["action"] = ""
1092             db["error"] = "<br><span style='color: #ff0000'>インポートファイル、及び、ログの表示名は空欄にできません。</span><br>"
1093             return
1094           end
1095           
1096         else
1097         end
1098         
1099         # diary.xmlを入れ替える処理
1100         when "replace"
1101         case params["action"]
1102           when "exec"
1103           # この段階のエラーに対しては強度のエラーを発する
1104           checkflag = true
1105           db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
1106           db["loglist"].path.each do |val|
1107             if val == db["logpath"]
1108               # 重複していた場合エラーメッセージを表示し、処理を行わない
1109               params["action"] = ""
1110               params["mode"] = "error"
1111               db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1112               return
1113             end
1114           end
1115           
1116           db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1117           
1118           if File.basename(db["logpath"]).blank? || db["logdisplay"].blank? || db["importxml"].blank?
1119             params["action"] = ""
1120             params["mode"] = "error"
1121             db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1122             return
1123           end
1124           
1125           # diary.xmlを移動します
1126           FileUtils.move(XMLPATH + "diary.xml", XMLPATH + File.basename(db["logpath"]))
1127           # ファイルをアップロードします
1128           file = FileUploader.new
1129           file.upload("diary.xml", db["importxml"])
1130           
1131           # loglist.xmlを更新します
1132           db["loglist"].path[1, 0] = db["logpath"]
1133           db["loglist"].display[1, 0] = db["logdisplay"]
1134           db["loglist"].to_xml
1135           
1136           # 情報を更新します
1137           db["loglist"] = LogList.readxml(LISTXMLPATH)
1138           session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
1139           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
1140           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
1141           
1142           when "confirm"
1143           # 入力されたログファイルパスが既に存在するかを確認
1144           checkflag = true
1145           db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
1146           db["loglist"].path.each do |val|
1147             if val == db["logpath"]
1148               # 重複していた場合エラーメッセージを表示し、処理を行わない
1149               params["action"] = ""
1150               db["error"] = "<br><span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
1151               checkflag = false
1152               return
1153             end
1154           end
1155           
1156           if checkflag == true
1157             db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1158             db["importxml"] = cgi["updata"].read
1159             
1160             # XMLの整合性をチェックします
1161             begin
1162               REXML::Document.new(Controller.fix_updata_enc(db["importxml"].to_s))
1163             rescue => exception
1164               db["error"] = "<br><span style='color: #ff0000'>不正な整形のXMLです!ファイルを見直してください。</span><br>"
1165               db["error"] << "<div class='divstyle' style='width: 560px; color: #ff0000; text-align: left;'>"
1166               db["error"] << CGI.escapeHTML(exception.to_s).gsub("\n", "<br>")
1167               db["error"] << "</div>"
1168               params["action"] = ""
1169               return
1170             end
1171           end
1172           
1173           if db["logpath"].blank? || db["logdisplay"].blank? || db["importxml"].blank?
1174             params["action"] = ""
1175             db["error"] = "<br><span style='color: #ff0000'>インポートファイル、及び、ログの表示名、ログのパスは空欄にできません。</span><br>"
1176             return
1177           end
1178           
1179         else
1180         end
1181         
1182       else
1183       end
1184     end
1185   end
1186   
1187   # Formのenctypeがmultypartだった場合、入力された値をrubyバージョンに左右されずに読み取るメソッドです。
1188   def Controller.get_mpart_value(cgi_param)
1189     if RUBY_VERSION >= "1.9.0"
1190       cgi_param[0..cgi_param.length].to_s
1191     else
1192       cgi_param.read.to_s
1193     end
1194   end
1195   
1196   # アップロードされたXMLをバージョンに左右されずに読み取るために、ruby-1.9.1以上ではエンコーディングを強制指定します
1197   def Controller.fix_updata_enc(file_to_s)
1198     if RUBY_VERSION >= "1.9.0"
1199       file_to_s.force_encoding("UTF-8")
1200     else
1201       file_to_s
1202     end
1203   end
1204   
1205 end
1206
1207 def main
1208   # SESSION変数、パラメータなどを取得します
1209   cgi = CGI.new
1210   session = CGI::Session.new(cgi)
1211   params = Hash[*cgi.params.to_a.map{|k, v| [k, v[0].to_s]}.flatten]
1212   
1213   # コントローラー部分
1214   # セッション管理
1215   if session["login"] != "true"
1216     if (cgi["loginid"] == LOGINID && cgi["password"] == PASSWORD)
1217       session["login"] = "true"
1218       
1219       # ワークフォルダの中をクリーンアップします
1220       filelist = Dir::entries("./work")
1221       # 削除条件 : 最終変更日時から1日(60*60*24sec)かつ、ファイルタイプがファイルの場合
1222       filelist.each do |file|
1223         File.delete("./work/#{file}") if Time.now - File.ctime("./work/#{file}") > 86400 && File.ftype("./work/#{file}") == "file"
1224       end
1225     end
1226   end
1227   
1228   # ログアウト処理
1229   if params["mode"] == "logout"
1230     session["login"] = nil
1231     session["logini"] = nil
1232     session["password"] = nil
1233     session.delete
1234   end
1235   
1236   begin
1237     # メインコントローラー
1238     # セッションが有効な場合のも実行します
1239     if session["login"] == "true"
1240       # PStore破損チェック!
1241       begin
1242         db = PStore.new("./work/#{session.session_id}.dat")
1243         db.transaction do
1244           db["error"] = ""
1245         end
1246       rescue
1247         # PStoreファイルを破棄する
1248         File.delete("./work/#{session.session_id}.dat")
1249         # PStoreが破損していた場合はセッション情報を破棄する
1250         session["login"] = nil
1251         session["logini"] = nil
1252         session["password"] = nil
1253         session.delete
1254       end
1255       
1256       # フォームによって挙動を変更します
1257       if cgi["mode"].respond_to?(:read) && cgi["action"].respond_to?(:read)
1258         params["mode"] = cgi["mode"].read
1259         params["action"] = cgi["action"].read
1260         Controller.MultiForm(cgi, session, params, db)
1261       else
1262         Controller.NormalForm(cgi, session, params, db)
1263       end
1264       
1265       # エラー画面移行時はセッション情報を破棄する
1266       if params["mode"] == "error"
1267         session["login"] = nil
1268         session["logini"] = nil
1269         session["password"] = nil
1270         session.delete
1271       end
1272     end
1273     
1274     # メニューとして表示されるHTML文字列です
1275     if params["target_filepath"].blank?
1276       menu = "<div class=\"divstyle\" style=\"width: #{TABLEWIDTH}px;\"><div class=\"divstyle\" align=\"center\" style=\"margin-bottom: 5px;\">現在編集中のファイル : #{session["filepath"]} "
1277     else
1278       menu = "<div class=\"divstyle\" style=\"width: #{TABLEWIDTH}px;\"><div class=\"divstyle\" align=\"center\" style=\"margin-bottom: 5px;\">現在編集中のファイル : #{session["target_filepath"]} "
1279     end
1280     if USEFILEMANAGER == true
1281       menu += "[ <a href=\"#{cgi.script_name}?mode=selectlog\">他のファイルを選択</a> ]</div>[ <a href=\"#{cgi.script_name}\">トップページ</a> | 記事管理 ( <a href=\"#{cgi.script_name}?mode=newentry\">作成</a> | <a href=\"#{cgi.script_name}?mode=editentry\">編集</a> | <a href=\"#{cgi.script_name}?mode=delentry\">消去</a> )  | <a href=\"#{cgi.script_name}?mode=editfeed\">XML情報編集</a> | <a href=\"#{cgi.script_name}?mode=log\">ログ管理</a> | <a href=\"#{cgi.script_name}?mode=import\">インポート</a> | <a href=\"#{FILEMANAGER}\" target=\"_blank\">ファイル管理</a> | <a href=\"#{cgi.script_name}?mode=reset\">初期化</a> | <a href=\"#{cgi.script_name}?mode=logout\">ログアウト</a> ]</div>"
1282     else
1283       menu += "[ <a href=\"#{cgi.script_name}?mode=selectlog\">他のファイルを選択</a> ]</div>[ <a href=\"#{cgi.script_name}\">トップページ</a> | 記事管理 ( <a href=\"#{cgi.script_name}?mode=newentry\">作成</a> | <a href=\"#{cgi.script_name}?mode=editentry\">編集</a> | <a href=\"#{cgi.script_name}?mode=delentry\">消去</a> )  | <a href=\"#{cgi.script_name}?mode=editfeed\">XML情報編集</a> | <a href=\"#{cgi.script_name}?mode=log\">ログ管理</a> | <a href=\"#{cgi.script_name}?mode=import\">インポート</a> | <a href=\"#{cgi.script_name}?mode=reset\">初期化</a> | <a href=\"#{cgi.script_name}?mode=logout\">ログアウト</a> ]</div>"
1284     end
1285     
1286     # ビュー部分
1287     # modeとactionの相関図は以下のようになります。
1288     # [mode] + [action]
1289     #        + [action]
1290     #        + [action]
1291     #        + ... and more
1292     if session["login"] != "true"
1293       # セッションが存在しない場合は強制的にトップページに遷移
1294       htmlwriter = HtmlWriter.new("./erbtemp/login.html.erb", binding)
1295     else
1296       case params["mode"]
1297         when "selectlog"
1298         htmlwriter = HtmlWriter.new("./erbtemp/select.html.erb", binding)
1299         when "newentry"
1300         htmlwriter = HtmlWriter.new("./erbtemp/newentry.html.erb", binding)
1301         when "editentry"
1302         htmlwriter = HtmlWriter.new("./erbtemp/editentry.html.erb", binding)
1303         when "delentry"
1304         htmlwriter = HtmlWriter.new("./erbtemp/delentry.html.erb", binding)
1305         when "editfeed"
1306         htmlwriter = HtmlWriter.new("./erbtemp/editfeed.html.erb", binding)
1307         when "log"
1308         htmlwriter = HtmlWriter.new("./erbtemp/log.html.erb", binding)
1309         when "insert"
1310         htmlwriter = HtmlWriter.new("./erbtemp/insertfeed.html.erb", binding)
1311         when "replace"
1312         htmlwriter = HtmlWriter.new("./erbtemp/replacefeed.html.erb", binding)
1313         when "import"
1314         htmlwriter = HtmlWriter.new("./erbtemp/indeximport.html.erb", binding)
1315         when "reset"
1316         htmlwriter = HtmlWriter.new("./erbtemp/reset.html.erb", binding)
1317         when "error"
1318         htmlwriter = HtmlWriter.new("./erbtemp/error.html.erb", binding)
1319       else
1320         htmlwriter = HtmlWriter.new("./erbtemp/index.html.erb", binding)
1321       end
1322     end
1323   rescue => exception
1324     # エラーが発生した場合、それを画面に表示します
1325     htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
1326   end
1327   
1328   # 実際にHTMLを出力します
1329   begin
1330     cgi.out{htmlwriter.to_code}
1331   rescue => exception
1332     # エラーが発生した場合、それを画面に表示します
1333     htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
1334     cgi.out{htmlwriter.to_code}
1335   end
1336 end
1337
1338 begin
1339   main
1340 rescue => evar
1341   # エラーが発生した場合、それを画面に表示します
1342   detail = ("%s: %s (%s)\n" %
1343   [evar.backtrace[0], evar.message, evar.send('class')]) +
1344   evar.backtrace[1..-1].join("\n")
1345   puts "content-type: text/html\n\n<plaintext>\n" + detail
1346 end