OSDN Git Service

- Fix plugin bug (add param "mode")
[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, entries)
701     l_feed = feed.dup.freeze
702     l_entries = entries.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.exec(mode, l_feed, l_entries)
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"], db["entry"])
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"], db["entry"])
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           else
816             # 成功時はプラグイン処理を実施する
817             FeedGenPluginManager.exec("delentry", db["feed"], db["entry"])
818           end
819         when "back"
820           session["target_filepath"] = params["target_filepath"]
821           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["target_filepath"]))
822           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["target_filepath"]))
823         else
824           # Delete Diary - Default
825           session["target_filepath"] = session["filepath"]
826           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
827           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
828         end
829         
830         # Feed情報変更部分
831       when "editfeed"
832         case params["action"]
833           # 確認画面
834         when "confirm"
835           session["target_filepath"] = params["target_filepath"]
836           db["feed"] = Feed.new(params)
837           # 実際にFeed情報の変更をファイルに反映
838         when "back"
839           session["target_filepath"] = params["target_filepath"]
840           db["feed"] = Feed.new(params)
841         when "exec"
842           session["target_filepath"] = params["target_filepath"]
843           Feed.update(XMLPATH + File.basename(params["target_filepath"]), Feed.new(params))
844         else
845           session["target_filepath"] = session["filepath"]
846           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
847         end
848         
849         # ログ編集モード
850       when "log"
851         # 必ず内部データをリフレッシュする
852         db["loglist"] = LogList.readxml(LISTXMLPATH)
853         case params["action"]
854           # ログファイルの編集を実際にファイルに反映
855         when "addexec"
856           # エラーチェック。この段階のエラーは強度のエラーを発する。
857           db["loglist"].path.each do |val|
858             if val == cgi["logpath"]
859               # 重複していた場合エラーメッセージを表示し、処理を行わない
860               params["action"] = ""
861               params["mode"] = "error"
862               db["error"] = "ログファイルの追加中に重大なエラーが発生しました。<br>環境を見直してください。"
863               return
864             end
865           end
866           
867           # 入力された内容を保持する配列に追加
868           db["loglist"].path[1, 0] = cgi["logpath"]
869           db["loglist"].display[1, 0] = cgi["logdisplay"]
870           db["loglist"].to_xml
871           # 既存のdiary.xmlを指定された名称でコピーして保存
872           FileUtils.copy_file(XMLPATH + File.basename(db["loglist"].path[0]), XMLPATH + File.basename(db["logpath"]), true)
873           # 保持している情報を更新する
874           db["loglist"] = LogList.readxml(LISTXMLPATH)
875           db["entry"] = []
876           # 新たなdiary.xmlを生成。この際保持する情報は同一のFeedオブジェクトnew()
877           Feed.to_xml(XMLPATH + File.basename(db["loglist"].path[0]), db["feed"], [])
878           # 確認画面
879         when "addconfirm"
880           # 入力されたログが既に存在するかを確認
881           db["loglist"].path.each do |val|
882             if val == cgi["logpath"]
883               # 重複していた場合エラーメッセージを表示し、処理を行わない
884               params["action"] = ""
885               db["error"] = "<span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
886               return
887             end
888           end
889           
890           db["logpath"] = cgi["logpath"]
891           db["logdisplay"] = cgi["logdisplay"]
892           
893           if db["logpath"].blank? || db["logdisplay"].blank?
894             params["action"] = ""
895           end
896         when "back"
897           
898           # 削除確認画面
899         when "delconfirm"
900           db["logdelindex"] = params["logdelindex"].to_i
901           
902           if db["logdelindex"] < 1
903             db["error"] = "<span style='color: #ff0000'>ログファイルの削除パラメタが不正です。</span><br>"
904             params["action"] = ""
905           end
906           # 削除処理
907         when "delexec"
908           if cgi["logdelindex"].to_i < 1
909             params["action"] = ""
910             params["mode"] = "error"
911             db["error"] = "ログファイルの削除中に重大なエラーが発生しました。<br>環境を見直してください。"
912             return
913           else
914             # 記事ファイルを削除します
915             File.delete(XMLPATH + File.basename(db["loglist"].path[db["logdelindex"]]))
916             # ログリストから削除します
917             db["loglist"].path.delete_at(cgi["logdelindex"].to_i)
918             db["loglist"].display.delete_at(cgi["logdelindex"].to_i)
919             db["loglist"].to_xml
920             # 保持している情報を更新する
921             db["loglist"] = LogList.readxml(LISTXMLPATH)
922             db["entry"] = []
923           end
924           
925           # 編集画面
926         when "edit"
927           db["logeditindex"] = params["logdelindex"].to_i
928           
929           db["logpath"] = db["loglist"].path[db["logeditindex"]]
930           db["logdisplay"] = db["loglist"].display[db["logeditindex"]]
931           
932           if db["logeditindex"] == 0
933             db["error"] = "<span style='color: #ff0000'>ログファイルの編集パラメタが不正です。</span><br>"
934             params["action"] = ""
935           end
936           # 編集確認画面
937         when "editconfirm"
938           checkflag = true
939           db["loglist"].path.each_with_index do |val, i|
940             if db["logeditindex"] != i
941               if params["logpath"].to_s == db["loglist"].path[i].to_s
942                 checkflag = false
943               end
944             end
945           end
946           
947           if checkflag == false
948             params["action"] = "edit"
949             db["error"] = "<span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
950           else
951             db["loginsertindex"] = params["loginsertindex"].to_i
952             
953             db["logpath"] = params["logpath"].to_s
954             db["logdisplay"] = params["logdisplay"].to_s
955           end
956           # 編集実行
957         when "editexec"
958           checkflag = true
959           db["loglist"].path.each_with_index do |val, i|
960             if db["logeditindex"] != i
961               if params["logpath"].to_s == db["loglist"].path[i].to_s
962                 checkflag = false
963               end
964             end
965           end
966           
967           if checkflag == false
968             params["action"] = ""
969             params["mode"] = "error"
970             db["error"] = "ログファイルの編集中に重大なエラーが発生しました。<br>環境を見直してください。"
971             return
972           else
973             db["loginsertindex"] = params["loginsertindex"].to_i
974             
975             db["logpath"] = params["logpath"].to_s
976             db["logdisplay"] = params["logdisplay"].to_s
977             
978             # ファイルを移動します
979             if XMLPATH + File.basename(db["loglist"].path[db["logeditindex"]]) != XMLPATH + File.basename(db["logpath"])
980               FileUtils.move(XMLPATH + File.basename(db["loglist"].path[db["logeditindex"]]), XMLPATH + File.basename(db["logpath"]))
981               # ログリストを更新します
982               db["loglist"].path.delete_at(db["logeditindex"])
983               db["loglist"].display.delete_at(db["logeditindex"])
984               db["loglist"].path.insert(db["loginsertindex"] + 1, db["logpath"])
985               db["loglist"].display.insert(db["loginsertindex"] + 1, db["logdisplay"])
986               db["loglist"].to_xml
987             end
988           end
989           
990           # 初期表示画面
991         else
992           # 現在編集中のデータを強制的に最上のファイルパスに変更
993           session["filepath"] = db["loglist"].path[0]
994           
995           # 前月の時刻を作成します
996           prevmonth = (DateTime.now << 1)
997           
998           # 前月の時刻を元に、ディフォルト書式を生成します
999           db["logpath"] = FEEDXMLDIR + prevmonth.strftime("%Y%m") + ".xml"
1000           db["logdisplay"] = prevmonth.strftime("%Y年%m月").gsub("年0", "年")
1001         end
1002         
1003         # インポートモードの場合の処理
1004       when "import"
1005         db["loglist"] = LogList.readxml(LISTXMLPATH)
1006         
1007         # リセットモードの場合の処理
1008       when "reset"
1009         case params["action"]
1010           # リセット実行時の処理
1011         when "exec"
1012           file = FileUploader.new
1013           file.filelist().each { |fname|
1014             file.delete(fname) if File.ftype(XMLPATH + fname) == "file"
1015           }
1016           
1017           # 全ファイルの初期化を実行する
1018           # loglist.xmlの初期化
1019           loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}diary.xml"], LISTXMLPATH)
1020           loglist.to_xml
1021           
1022           db["loglist"] = LogList.readxml(LISTXMLPATH)
1023           
1024           # diary.xmlの初期化
1025           Feed.to_xml(XMLPATH + db["loglist"].path[0].match(/[^\/]*?$/).to_a[0], Feed.new({}), [])
1026           
1027           # 初期の編集ファイルはloglist.xml上の最も上のファイル
1028           session["filepath"] = db["loglist"].path[0]
1029           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
1030           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
1031           
1032           # 画面の遷移先をトップページにする
1033           params["mode"] = ""
1034           params["action"] = ""
1035           # パラメタ無しの処理
1036         else
1037           # ファイル一覧を取得する
1038           filelist = FileUploader.new.filelist()
1039           
1040           # タイプがファイルのみリストとして取得する
1041           db["filelist"] = []
1042           filelist.each { |fname| db["filelist"] << fname if File.ftype(XMLPATH + fname) == "file"}
1043         end
1044         
1045         # ログイン時の画面
1046       else
1047         # loglist.xmlが存在するかチェック
1048         if File.exist?(LISTXMLPATH) == false
1049           # なかった場合はloglist.xmlを自動生成
1050           loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}diary.xml"], LISTXMLPATH)
1051           loglist.to_xml
1052           Feed.to_xml(XMLPATH + File.basename(loglist.path[0]), Feed.new({}), [])
1053         end
1054         
1055         db["loglist"] = LogList.readxml(LISTXMLPATH)
1056         
1057         # diary.xmlが存在するかチェック
1058         if File.exist?(XMLPATH + File.basename(db["loglist"].path[0])) == false
1059           # なかった場合はdiary.xmlを自動生成
1060           Feed.to_xml(XMLPATH + File.basename(db["loglist"].path[0]), Feed.new({}), [])
1061         end
1062         
1063         # 初期の編集ファイルはloglist.xml上の最も上のファイル
1064         session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
1065         db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
1066         db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
1067       end
1068     end
1069   end
1070   
1071   # マルチパートフォームの場合の処理です
1072   def Controller.MultiForm(cgi, session, params, db)
1073     db.transaction do
1074       # loglist.xmlをロードします
1075       db["loglist"] = LogList.readxml(LISTXMLPATH)
1076       case params["mode"]
1077         # 特定位置に挿入する場合の処理
1078       when "insert"
1079         case params["action"]
1080         when "exec"
1081           # この段階のエラーに対しては強度のエラーを発する
1082           checkflag = true
1083           db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
1084           db["loglist"].path.each do |val|
1085             if val == db["logpath"]
1086               # 重複していた場合エラーメッセージを表示し、処理を行わない
1087               db["logpath"] = ""
1088               params["action"] = ""
1089               params["mode"] = "error"
1090               db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1091               return
1092             end
1093           end
1094           
1095           db["loginsertindex"] = cgi["loginsertindex"].read.to_i
1096           db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1097           
1098           # 0位置への挿入防止
1099           if db["loginsertindex"] == 0
1100             params["action"] = ""
1101             params["mode"] = "error"
1102             db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1103             return
1104           end
1105           
1106           if File.basename(db["logpath"]).blank? || db["logdisplay"].blank?
1107             params["action"] = ""
1108             params["mode"] = "error"
1109             db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1110             return
1111           end
1112           
1113           # ファイルを実際にアップロードします
1114           file = FileUploader.new
1115           file.upload(db["logpath"], db["importxml"])
1116           
1117           # loglist.xmlを更新します
1118           db["loglist"].path[db["loginsertindex"], 0] = db["logpath"]
1119           db["loglist"].display[db["loginsertindex"], 0] = db["logdisplay"]
1120           db["loglist"].to_xml
1121           
1122           # 情報を更新します
1123           db["loglist"] = LogList.readxml(LISTXMLPATH)
1124           session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
1125           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
1126           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
1127           
1128         when "confirm"
1129           # 入力されたログファイルパスが既に存在するかを確認
1130           checkflag = true
1131           db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
1132           db["loglist"].path.each do |val|
1133             if val == db["logpath"]
1134               # 重複していた場合エラーメッセージを表示し、処理を行わない
1135               db["logpath"] = ""
1136               params["action"] = ""
1137               db["error"] = "<br><span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
1138               checkflag = false
1139               return
1140             end
1141           end
1142           
1143           if checkflag
1144             db["loginsertindex"] = cgi["loginsertindex"].read.to_i
1145             db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1146             db["importxml"] = cgi["updata"].read
1147             
1148             # XMLの整合性をチェックします
1149             begin
1150               REXML::Document.new(Controller.fix_updata_enc(db["importxml"].to_s))
1151             rescue => exception
1152               db["error"] = "<br><span style='color: #ff0000'>不正な整形のXMLです!ファイルを見直してください。</span><br>"
1153               db["error"] << "<div class='divstyle' style='width: 560px; color: #ff0000; text-align: left;'>"
1154               db["error"] << CGI.escapeHTML(exception.to_s).gsub("\n", "<br>")
1155               db["error"] << "</div>"
1156               params["action"] = ""
1157               return
1158             end
1159           end
1160           
1161           # 0位置への挿入防止
1162           if db["loginsertindex"] == 0
1163             params["action"] = ""
1164             db["error"] = "<br><span style='color: #ff0000'>ラジオボックスでログの挿入位置を選択してください!</span><br>"
1165             return
1166           end
1167           
1168           if db["logpath"] == FEEDXMLDIR || db["logdisplay"].blank?
1169             params["action"] = ""
1170             db["error"] = "<br><span style='color: #ff0000'>インポートファイル、及び、ログの表示名は空欄にできません。</span><br>"
1171             return
1172           end
1173           
1174         else
1175         end
1176         
1177         # diary.xmlを入れ替える処理
1178       when "replace"
1179         case params["action"]
1180         when "exec"
1181           # この段階のエラーに対しては強度のエラーを発する
1182           checkflag = true
1183           db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
1184           db["loglist"].path.each do |val|
1185             if val == db["logpath"]
1186               # 重複していた場合エラーメッセージを表示し、処理を行わない
1187               params["action"] = ""
1188               params["mode"] = "error"
1189               db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1190               return
1191             end
1192           end
1193           
1194           db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1195           
1196           if File.basename(db["logpath"]).blank? || db["logdisplay"].blank? || db["importxml"].blank?
1197             params["action"] = ""
1198             params["mode"] = "error"
1199             db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1200             return
1201           end
1202           
1203           # diary.xmlを移動します
1204           FileUtils.move(XMLPATH + "diary.xml", XMLPATH + File.basename(db["logpath"]))
1205           # ファイルをアップロードします
1206           file = FileUploader.new
1207           file.upload("diary.xml", db["importxml"])
1208           
1209           # loglist.xmlを更新します
1210           db["loglist"].path[1, 0] = db["logpath"]
1211           db["loglist"].display[1, 0] = db["logdisplay"]
1212           db["loglist"].to_xml
1213           
1214           # 情報を更新します
1215           db["loglist"] = LogList.readxml(LISTXMLPATH)
1216           session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
1217           db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
1218           db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
1219           
1220         when "confirm"
1221           # 入力されたログファイルパスが既に存在するかを確認
1222           checkflag = true
1223           db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
1224           db["loglist"].path.each do |val|
1225             if val == db["logpath"]
1226               # 重複していた場合エラーメッセージを表示し、処理を行わない
1227               params["action"] = ""
1228               db["error"] = "<br><span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
1229               checkflag = false
1230               return
1231             end
1232           end
1233           
1234           if checkflag == true
1235             db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1236             db["importxml"] = cgi["updata"].read
1237             
1238             # XMLの整合性をチェックします
1239             begin
1240               REXML::Document.new(Controller.fix_updata_enc(db["importxml"].to_s))
1241             rescue => exception
1242               db["error"] = "<br><span style='color: #ff0000'>不正な整形のXMLです!ファイルを見直してください。</span><br>"
1243               db["error"] << "<div class='divstyle' style='width: 560px; color: #ff0000; text-align: left;'>"
1244               db["error"] << CGI.escapeHTML(exception.to_s).gsub("\n", "<br>")
1245               db["error"] << "</div>"
1246               params["action"] = ""
1247               return
1248             end
1249           end
1250           
1251           if db["logpath"].blank? || db["logdisplay"].blank? || db["importxml"].blank?
1252             params["action"] = ""
1253             db["error"] = "<br><span style='color: #ff0000'>インポートファイル、及び、ログの表示名、ログのパスは空欄にできません。</span><br>"
1254             return
1255           end
1256           
1257         else
1258         end
1259         
1260       else
1261       end
1262     end
1263   end
1264   
1265   # Formのenctypeがmultypartだった場合、入力された値をrubyバージョンに左右されずに読み取るメソッドです。
1266   def Controller.get_mpart_value(cgi_param)
1267     if RUBY_VERSION >= "1.9.0"
1268       cgi_param[0..cgi_param.length].to_s
1269     else
1270       cgi_param.read.to_s
1271     end
1272   end
1273   
1274   # アップロードされたXMLをバージョンに左右されずに読み取るために、ruby-1.9.1以上ではエンコーディングを強制指定します
1275   def Controller.fix_updata_enc(file_to_s)
1276     if RUBY_VERSION >= "1.9.0"
1277       file_to_s.force_encoding("UTF-8")
1278     else
1279       file_to_s
1280     end
1281   end
1282   
1283 end
1284
1285 def main
1286   # SESSION変数、パラメータなどを取得します
1287   cgi = CGI.new
1288   session = CGI::Session.new(cgi)
1289   params = Hash[*cgi.params.to_a.map{|k, v| [k, v[0].to_s]}.flatten]
1290   
1291   # コントローラー部分
1292   # セッション管理
1293   if session["login"] != "true"
1294     if (cgi["loginid"] == LOGINID && cgi["password"] == PASSWORD)
1295       session["login"] = "true"
1296       
1297       # ワークフォルダの中をクリーンアップします
1298       filelist = Dir::entries("./work")
1299       # 削除条件 : 最終変更日時から1日(60*60*24sec)かつ、ファイルタイプがファイルの場合
1300       filelist.each do |file|
1301         File.delete("./work/#{file}") if Time.now - File.ctime("./work/#{file}") > 86400 && File.ftype("./work/#{file}") == "file"
1302       end
1303     end
1304   end
1305   
1306   # ログアウト処理
1307   if params["mode"] == "logout"
1308     session["login"] = nil
1309     session["logini"] = nil
1310     session["password"] = nil
1311     session.delete
1312   end
1313   
1314   begin
1315     # メインコントローラー
1316     # セッションが有効な場合のも実行します
1317     if session["login"] == "true"
1318       # PStore破損チェック!
1319       begin
1320         db = PStore.new("./work/#{session.session_id}.dat")
1321         db.transaction do
1322           db["error"] = ""
1323         end
1324       rescue
1325         # PStoreファイルを破棄する
1326         File.delete("./work/#{session.session_id}.dat")
1327         # PStoreが破損していた場合はセッション情報を破棄する
1328         session["login"] = nil
1329         session["logini"] = nil
1330         session["password"] = nil
1331         session.delete
1332       end
1333       
1334       # フォームによって挙動を変更します
1335       if cgi["mode"].respond_to?(:read) && cgi["action"].respond_to?(:read)
1336         params["mode"] = cgi["mode"].read
1337         params["action"] = cgi["action"].read
1338         Controller.MultiForm(cgi, session, params, db)
1339       else
1340         Controller.NormalForm(cgi, session, params, db)
1341       end
1342       
1343       # エラー画面移行時はセッション情報を破棄する
1344       if params["mode"] == "error"
1345         session["login"] = nil
1346         session["logini"] = nil
1347         session["password"] = nil
1348         session.delete
1349       end
1350     end
1351     
1352     # メニューとして表示されるHTML文字列です
1353     if params["target_filepath"].blank?
1354       menu = "<div class=\"divstyle\" style=\"width: #{TABLEWIDTH}px;\"><div class=\"divstyle\" align=\"center\" style=\"margin-bottom: 5px;\">現在編集中のファイル : #{session["filepath"]} "
1355     else
1356       menu = "<div class=\"divstyle\" style=\"width: #{TABLEWIDTH}px;\"><div class=\"divstyle\" align=\"center\" style=\"margin-bottom: 5px;\">現在編集中のファイル : #{session["target_filepath"]} "
1357     end
1358     if USEFILEMANAGER == true
1359       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>"
1360     else
1361       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>"
1362     end
1363     
1364     # ビュー部分
1365     # modeとactionの相関図は以下のようになります。
1366     # [mode] + [action]
1367     #        + [action]
1368     #        + [action]
1369     #        + ... and more
1370     if session["login"] != "true"
1371       # セッションが存在しない場合は強制的にトップページに遷移
1372       htmlwriter = HtmlWriter.new("./erbtemp/login.html.erb", binding)
1373     else
1374       case params["mode"]
1375       when "selectlog"
1376         htmlwriter = HtmlWriter.new("./erbtemp/select.html.erb", binding)
1377       when "newentry"
1378         htmlwriter = HtmlWriter.new("./erbtemp/newentry.html.erb", binding)
1379       when "editentry"
1380         htmlwriter = HtmlWriter.new("./erbtemp/editentry.html.erb", binding)
1381       when "delentry"
1382         htmlwriter = HtmlWriter.new("./erbtemp/delentry.html.erb", binding)
1383       when "editfeed"
1384         htmlwriter = HtmlWriter.new("./erbtemp/editfeed.html.erb", binding)
1385       when "log"
1386         htmlwriter = HtmlWriter.new("./erbtemp/log.html.erb", binding)
1387       when "insert"
1388         htmlwriter = HtmlWriter.new("./erbtemp/insertfeed.html.erb", binding)
1389       when "replace"
1390         htmlwriter = HtmlWriter.new("./erbtemp/replacefeed.html.erb", binding)
1391       when "import"
1392         htmlwriter = HtmlWriter.new("./erbtemp/indeximport.html.erb", binding)
1393       when "reset"
1394         htmlwriter = HtmlWriter.new("./erbtemp/reset.html.erb", binding)
1395       when "error"
1396         htmlwriter = HtmlWriter.new("./erbtemp/error.html.erb", binding)
1397       else
1398         htmlwriter = HtmlWriter.new("./erbtemp/index.html.erb", binding)
1399       end
1400     end
1401   rescue => exception
1402     # エラーが発生した場合、それを画面に表示します
1403     htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
1404   end
1405   
1406   # 実際にHTMLを出力します
1407   begin
1408     cgi.out{htmlwriter.to_code}
1409   rescue => exception
1410     # エラーが発生した場合、それを画面に表示します
1411     htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
1412     cgi.out{htmlwriter.to_code}
1413   end
1414 end
1415
1416 begin
1417   main
1418 rescue => evar
1419   # エラーが発生した場合、それを画面に表示します
1420   detail = ("%s: %s (%s)\n" %
1421             [evar.backtrace[0], evar.message, evar.send('class')]) +
1422     evar.backtrace[1..-1].join("\n")
1423   puts "content-type: text/html\n\n<plaintext>\n" + detail
1424 end