OSDN Git Service

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