OSDN Git Service

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