2 # -*- coding: utf-8 -*-
4 #= Atom Feed 1.0を管理するWEBアプリケーション
6 #Autohr:: Kureha Hisame (http://lunardial.sakura.ne.jp/) & Yui Naruse (http://airemix.com/)
8 #Copyright:: Copyright 2009 FeedBlog Project (http://sourceforge.jp/projects/feedblog/)
14 require "rexml/document"
27 XMLPATH = "./../lunardial/xml/"
29 LISTXMLPATH = "#{XMLPATH}loglist.xml"
30 # FeedBlog上の表示ページからログ格納ディレクトリまでのパス
34 # ファイルマネージャー機能を使用するならtrue
36 # ファイルマネージャー機能スクリプト(filemanager.rb)のパス
37 FILEMANAGER = "./filemanager.rb"
38 # XMLに書き込む際、改行部分を<br>のまま保持するか、改行記号に直すか
42 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>)"
44 APPTITLE = "FeedGenerator for Ruby version 2.0.0.0"
52 # ruby-1.9.x以降ではファイルを開いた際、エンコードの指定を行わないとエラーの原因になります。
53 # ただしruby-1.8.6以前はエンコードの指定に対応していないため、独自メソッドを定義してファイルの入出力を行います。
55 # _arg[0]_ :: 入出力を行うファイルのパス
56 # _arg[1]_ :: モードの指定。例 : w:utf-8(書き込みモード・UTF-8エンコードでファイルを開く)
59 arg[1] = mode[/[^:]+/] if mode && RUBY_VERSION < "1.8.7" && mode.include?(':')
61 f.flock(/\A\w+r/ =~ mode ? File::LOCK_SH : File::LOCK_EX)
85 # = Feed/Entryのスーパークラス
87 # 入出力用のメソッドなど、共通する機能を提供します
91 # _hash_ :: 値を格納したhash配列。superfeed.new(CGI::Session.new(new CGI).params)のようにして使う。
97 @paramlist.each do |key|
98 val = hash[key.to_sym] || hash[key.to_s]
101 val.gsub!(/\r\n|\r/, "\n")
102 @attr[key.to_sym] = CGI.escapeHTML(val)
105 @attr[key.to_sym] = ""
109 # 可視・不可視を示すハッシュキーを格納する配列です
112 # ハッシュキーの日本語説明を格納する配列です
117 attr_reader :attr, :paramlist, :name, :display
119 # 内部の@attrハッシュにアクセスする手段を提供するメソッドです
121 # AbstractEntry.attr[:title]にアクセスしたい場合はAbstractEntry.titleで可能になります。
122 # AbstractEntry.send("title")という方法でもアクセス可能です。
123 def method_missing(methname, *args)
124 methname = methname.to_s
126 if methname[-1] == ?=
128 raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" unless args.length == 1
130 methname = @paramlist.find{|par|par == methname}
131 return @attr[methname.to_sym] = args[0] if methname
134 raise ArgumentError, "wrong number of arguments (#{args.length} for 0)" unless args.empty?
135 return @attr[methname.to_sym] if @attr.key?(methname.to_sym)
137 # attr上にキーがない値を入れようとした場合はNoMethodError
146 @attr[key.to_sym] = value
152 # Feedの基礎情報を保有するクラスです
153 class Feed < AbstractEntry
156 # _hash_ :: 値を格納したhash配列。feed.new(CGI::Session.new(new CGI).params)のようにして使います。
162 @paramlist = ["feedattr", "title", "subtitle", "self", "url", "updated", "feedid", "rights", "aname", "amail", "others"]
164 # AbstractEntryのinitializeメソッドを呼び出し、値を@attr配列に格納します
167 # 可視・不可視を示すハッシュキーを格納する配列です
168 @display = {"feedattr" => "none", "title" => "", "subtitle" => "",
169 "self" => "", "url" => "",
170 "updated" => "none", "feedid" => "",
171 "rights" => "", "aname" => "",
172 "amail" => "", "others" => "none"}
174 # デバッグモードの場合、全ての入力要素を表示します
176 @display.each do |key, val|
181 # ハッシュキーの日本語説明を格納する配列です
182 @name = {"feedattr" => "feedの保持している属性", "title" => "ウェブページのタイトル", "subtitle" => "ウェブページの簡単な説明",
183 "self" => "このXMLファイルのURL", "url" => "ウェブページのURL",
184 "updated" => "XMLファイルの最終更新日", "feedid" => "あなたのページ独自のID",
185 "rights" => "ページの著作権表記", "aname" => "ページの製作者",
186 "amail" => "ページ製作者のメールアドレス", "others" => "その他の要素"}
190 # Atom XMLファイルを読み込んで解析し、等価なFeedオブジェクトを返却するメソッドです
192 # _path_ :: Atom XMLファイルのパス
193 def Feed.readxml(path)
196 doc = REXML::Document.new(myopen(path, "r:utf-8"){|f|f.read})
202 doc.elements["feed"].attributes.each_attribute do |attr|
203 xmlattr.push("#{attr.to_string} ")
205 xml[:feedattr] = xmlattr.join("\n").gsub(/"/, "'")
207 # XML解析部分です。各element毎に判定を行います
208 doc.elements.each("feed") do |elm|
209 elm.elements.each { |child|
213 xml[:feedid] = child.text
214 when "title", "subtitle", "updated", "rights"
215 xml[child.name.to_sym] = child.text
217 child.elements.each do |gchild|
220 xml[:aname] = gchild.text
222 xml[:amail] = gchild.text
226 child.attributes.each do |k, v|
229 xml[:self] = child.attributes["href"]
230 elsif v == "alternate"
231 xml[:url] = child.attributes["href"]
238 # 上記判定以外の全要素は配列に格納します
239 others.push(child.to_s)
248 xml[:others] = others.join("\n")
253 # 内部に保持している情報を、Atom Feed1.0形式の文字列に出力するメソッドです
257 buf.push("<feed #{@attr[:feedattr]}>")
258 buf.push("<title type=\"text\">#{@attr[:title]}</title>")
259 buf.push("<subtitle type=\"text\">#{@attr[:subtitle]}</subtitle>")
260 buf.push("<link rel=\"self\" type=\"application/atom+xml\" href=\"#{@attr[:self]}\" />")
261 buf.push("<link rel=\"alternate\" type=\"text/html\" href=\"#{@attr[:url]}\" />")
262 buf.push("<updated>#{Time.now.iso8601}</updated>")
263 buf.push("<id>#{@attr[:feedid]}</id>")
264 buf.push("<rights type=\"text\">#{@attr[:rights]}</rights>")
266 buf.push("\t<name>#{@attr[:aname]}</name>")
267 buf.push("\t<email>#{@attr[:amail]}</email>")
268 buf.push("</author>")
269 buf.push("#{CGI.unescapeHTML(@attr[:others])}") if @attr[:others] != ""
271 return buf.join("\n")
277 # Entryの基礎情報を保有するクラスです
278 class Entry < AbstractEntry
281 # _hash_ :: 値を格納したhash配列。entry.new(CGI::Session.new(new CGI).params)のようにして使います。
287 @paramlist = ["entryid", "title", "summary", "published", "updated", "url", "content", "others"]
289 # AbstractEntryのinitializeメソッドを呼び出し、値を@attr配列に格納します
292 # 可視・不可視を示すハッシュキーを格納する配列です
293 @display = {"entryid" => "none", "title" => "",
294 "summary" => "", "published" => "none",
295 "updated" => "none", "url" => "none",
296 "content" => "", "others"=>"none"}
298 # デバッグモードの場合、全ての入力要素を表示します
300 @display.each do |key, val|
305 # ハッシュキーの日本語説明を格納する配列です
306 @name = {"entryid" => "記事固有のID", "title" => "記事のタイトル",
307 "summary" => "記事の簡単な説明", "published" => "記事の出版時刻",
308 "updated" => "記事の更新時刻", "url" => "記事へのURLアドレス",
309 "content" => "記事の本文", "others"=>"その他の項目"}
312 # Atom XMLファイルを読み込んで解析し、等価なEntryオブジェクト配列を返却するメソッドです
314 # _path_ :: Atom XMLファイルのパス
315 def Entry.readxml(path)
318 doc = REXML::Document.new(myopen(path, "r:utf-8"){|f|f.read})
322 # XML解析部分です。各element毎に判定を行います
323 doc.elements.each("feed/entry") do |elm|
326 elm.elements.each do |child|
330 xml[:entryid] = child.text
332 xml[:url] = child.attributes["href"]
333 when "title", "summary", "summary", "published", "updated", "content"
334 xml[child.name.to_sym] = child.text
336 # 上記判定以外の全要素は配列に格納します
337 others.push(child.to_s)
343 xml[:others] = others.join("\n")
344 entrylist.push(Entry.new(xml))
350 # データソースから読み取ったHTMLを、エディタで編集可能な形式に変換するメソッドです
351 def content_for_generator
352 str = @attr[:content].dup
354 str.gsub!(/(<\/(?:p|h\d|div)(?:>|>))\n/i, '\1')
355 str.gsub!(/\n/, '<br>') if REPLACEBRTAG
356 str.gsub!(/(<(?:(?!>).)*?)#{Regexp.escape(FEEDXMLDIR)}/) { "#$1#{XMLPATH}" }
360 # エディタで編集されたCONTENT要素を、データソースに書き込める形式に変換するメソッドです
362 str = @attr[:content].dup
363 str = CGI.unescapeHTML(str)
365 str.gsub!(/(\r\n|\n)/, "")
366 str.gsub!(/<br>/i, "\n") if REPLACEBRTAG
367 str.gsub!(/(<br>|<\/p>|<\/h\d>|<\/div>)(?=[^\n])/i) { "#$1\n" } unless REPLACEBRTAG
368 str.gsub!(/(<[^>]*?)#{Regexp.escape(XMLPATH)}/) { "#$1#{FEEDXMLDIR}" }
372 # 確認画面で表示されるCONTENT要素を生成するメソッドです
374 str = @attr[:content].dup
375 str = CGI.unescapeHTML(str)
377 str.gsub!(/<br>/i, "\n") if REPLACEBRTAG
378 str.gsub!(/(<[^>]*?)#{Regexp.escape(FEEDXMLDIR)}/) { "#$1#{XMLPATH}" }
382 # 内部に保持している情報を、Atom Feed1.0形式の文字列に出力するメソッドです
386 buf.push("<id>#{@attr[:entryid]}</id>")
387 buf.push("<title>#{@attr[:title]}</title>")
388 buf.push("<summary>#{@attr[:summary]}</summary>")
389 buf.push("<published>#{@attr[:published]}</published>")
390 buf.push("<updated>#{@attr[:updated]}</updated>")
391 buf.push("<link href=\"#{@attr[:url]}\" />")
392 buf.push("<content type=\"html\">#{@attr[:content]}</content>")
393 buf.push("#{CGI.unescapeHTML(@attr[:others])}") if @attr[:others] != ""
396 return buf.join("\n")
402 # Atom Feed 1.0 XMLファイルに書き込みを行うクラスです
406 # _path_ :: Atom Feed 1.0 XMLファイルの書き込みパス
413 # _feed_ :: Feedオブジェクト
414 # _entry_ :: Entryオブジェクトの配列
415 def to_xml(feed, entrylist)
417 buf.push("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
418 buf.push("#{feed.to_s}\n")
419 entrylist.each { |entry|
420 buf.push("#{entry.to_s}\n")
424 myopen(@path, "w") do |f|
425 f.print buf.join("\n")
432 # テンプレートファイル(*.erb)を読み込み、管理するクラスです
436 # _template_ :: テンプレートファイル(*.erb)のパス
437 # _binding_ :: binding変数
438 def initialize(template, binding)
439 @erb = ERB.new(myopen(template, "r:utf-8") {|f| f.read}, nil, "-")
443 # テンプレートファイルの文字列を返却するメソッドです
445 @erb.result(@binding)
451 # loglist.xmlにかかわる入出力を行います
455 # _display_ :: 画面表示用文字の配列
456 # _path_ :: XMLファイルパスの配列
457 # _logpath_ :: loglist.xmlのパス
458 def initialize(display, path, logpath)
464 attr_accessor :display, :path, :logpath
466 # loglist.xmlファイルを読み込んで解析し、LogListオブジェクトを返却するメソッドです
468 # _logpath_ :: loglist.xmlへのパス
469 def LogList.readxml(logpath)
473 myopen(logpath, "r:utf-8") { |f|
477 doc = REXML::Document.new(lines.join("\n"))
481 doc.elements.each("list/file") { |elm|
482 elm.elements.each {|child|
485 @display.push(child.text)
487 @path.push(child.text)
494 return LogList.new(@display, @path, logpath)
497 # loglist.xmlにオブジェクトの内容を反映するメソッドです
501 path.each_with_index do |path, i|
503 buf.push("<display>#{@display[i]}</display>")
504 buf.push("<path>#{@path[i]}</path>")
509 myopen(@logpath, "w") do |f|
510 f.print buf.join("\n")
524 # ファイルの一覧を取得し、ファイル名称を格納した配列を返却します
526 arr = Dir::entries(XMLPATH).sort
535 # _name_ :: 消去するファイル名
537 File.delete(XMLPATH + name.match(/[^\/]*?$/).to_a[0])
542 # _name_ :: アップロードファイルの名称
543 # _img_ :: アップロードファイル
544 def upload(name, img)
545 open(XMLPATH + File.basename(name), "w") do |f|
554 # コントローラ部分に相当する処理を受け持つクラスです
556 def Controller.NormalForm(cgi, session, params, db)
558 # modeとactionの相関図は以下のようになります。
566 session["filepath"] = params["logpath"]
567 db["loglist"] = LogList.readxml(LISTXMLPATH)
568 # 初期状態で選択されるログは「loglist.xml」の最上に位置するログになります
569 session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
570 db["feed"] = Feed.readxml(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0])
571 db["entry"] = Entry.readxml(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0])
575 case params["action"]
578 db["newentry"] = Entry.new(params)
579 db["newentry"].content = db["newentry"].content_for_blog
582 db["entry"].unshift db["newentry"]
583 feedwriter = FeedWriter.new(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0])
584 feedwriter.to_xml(db["feed"], db["entry"])
586 # New Diary - Default
587 db["feed"] = Feed.readxml(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0])
588 db["entry"] = Entry.readxml(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0])
593 case params["action"]
596 session["editid"] = cgi["editid"].to_s
599 session["editid"] = cgi["editid"].to_s
600 db["editentry"] = Entry.new(params)
601 db["editentry"].content = db["editentry"].content_for_blog
604 db["entry"].each_with_index { |e, i|
605 db["entry"][i] = db["editentry"] if e.entryid == cgi["editid"].to_s
607 feedwriter = FeedWriter.new(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0])
608 feedwriter.to_xml(db["feed"], db["entry"])
610 # Edit Diary - Default
611 db["feed"] = Feed.readxml(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0])
612 db["entry"] = Entry.readxml(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0])
617 case params["action"]
620 session["delid"] = cgi["delid"].to_s
624 db["entry"].each_with_index { |e, i|
625 delindex = i if e.entryid == cgi["delid"].to_s
627 db["entry"].delete_at(delindex) unless delindex == nil
628 feedwriter = FeedWriter.new(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0])
629 feedwriter.to_xml(db["feed"], db["entry"])
631 # Delete Diary - Default
632 db["feed"] = Feed.readxml(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0])
633 db["entry"] = Entry.readxml(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0])
638 case params["action"]
641 db["feed"] = Feed.new(params)
642 # 実際にFeed情報の変更をファイルに反映
644 feedwriter = FeedWriter.new(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0])
645 feedwriter.to_xml(db["feed"], db["entry"])
652 case params["action"]
653 # ログファイルの編集を実際にファイルに反映
656 db["loglist"].path[1, 0] = db["logpath"]
657 db["loglist"].display[1, 0] = db["logdisplay"]
659 # 既存のdiary.xmlを指定された名称でコピーして保存
660 FileUtils.copy_file(XMLPATH + db["loglist"].path[0].match(/[^\/]*?$/).to_a[0], XMLPATH + db["logpath"].match(/[^\/]*?$/).to_a[0], true)
662 db["loglist"] = LogList.readxml(LISTXMLPATH)
664 # 新たなdiary.xmlを生成。この際保持する情報は同一のFeedオブジェクト
665 writer = FeedWriter.new(XMLPATH + db["loglist"].path[0].match(/[^\/]*?$/).to_a[0])
666 writer.to_xml(db["feed"], [])
670 db["loglist"].path.each do |val|
671 if val == cgi["logpath"]
672 # 重複していた場合エラーメッセージを表示し、処理を行わない
673 params["action"] = ""
674 error = "<span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
679 db["logpath"] = cgi["logpath"]
680 db["logdisplay"] = cgi["logdisplay"]
682 if db["logpath"].blank? || db["logdisplay"].blank?
683 params["action"] = ""
688 db["logdelindex"] = params["logdelindex"].to_i
690 if db["logdelindex"] == 0
691 params["action"] = ""
695 if db["logdelindex"] == 0
696 params["action"] = ""
699 File.delete(XMLPATH + db["loglist"].path[db["logdelindex"]].match(/[^\/]*?$/).to_s)
701 db["loglist"].path.delete_at(db["logdelindex"])
702 db["loglist"].display.delete_at(db["logdelindex"])
705 db["loglist"] = LogList.readxml(LISTXMLPATH)
710 db["logeditindex"] = params["logdelindex"].to_i
712 db["logpath"] = db["loglist"].path[db["logeditindex"]]
713 db["logdisplay"] = db["loglist"].display[db["logeditindex"]]
715 if db["logeditindex"] == 0
716 params["action"] = ""
722 db["loglist"].path.each_with_index do |val, i|
723 if db["logeditindex"] != i
724 if params["logpath"].to_s == db["loglist"].path[i].to_s
730 if checkflag == false
731 params["action"] = "edit"
732 db["error"] = "<span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
734 db["loginsertindex"] = params["loginsertindex"].to_i
736 db["logpath"] = params["logpath"].to_s
737 db["logdisplay"] = params["logdisplay"].to_s
742 if XMLPATH + db["loglist"].path[db["logeditindex"]].match(/[^\/]*?$/).to_s != XMLPATH + db["logpath"].match(/[^\/]*?$/).to_s
743 FileUtils.move(XMLPATH + db["loglist"].path[db["logeditindex"]].match(/[^\/]*?$/).to_s, XMLPATH + db["logpath"].match(/[^\/]*?$/).to_s)
746 db["loglist"].path.delete_at(db["logeditindex"])
747 db["loglist"].display.delete_at(db["logeditindex"])
748 db["loglist"].path.insert(db["loginsertindex"] + 1, db["logpath"])
749 db["loglist"].display.insert(db["loginsertindex"] + 1, db["logdisplay"])
753 # 初期表示の際に内部保持データをリフレッシュ
754 db["loglist"] = LogList.readxml(LISTXMLPATH)
756 # 現在編集中のデータを強制的に最上のファイルパスに変更
757 session["filepath"] = db["loglist"].path[0]
760 prevmonth = (DateTime.now << 1)
762 # 前月の時刻を元に、ディフォルト書式を生成します
763 db["logpath"] = FEEDXMLDIR + prevmonth.strftime("%Y%m") + ".xml"
764 db["logdisplay"] = prevmonth.strftime("%Y年%m月").gsub("年0", "年")
769 db["loglist"] = LogList.readxml(LISTXMLPATH)
773 case params["action"]
776 file = FileUploader.new
777 file.filelist().each { |fname|
778 file.delete(fname) if File.ftype(XMLPATH + fname) == "file"
783 loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}diary.xml"], LISTXMLPATH)
785 writer = FeedWriter.new(XMLPATH + loglist.path[0].match(/[^\/]*?$/).to_a[0])
786 writer.to_xml(Feed.new({}), [])
788 db["loglist"] = LogList.readxml(LISTXMLPATH)
791 writer = FeedWriter.new(XMLPATH + db["loglist"].path[0].match(/[^\/]*?$/).to_a[0])
792 writer.to_xml(Feed.new({}), [])
794 # 初期の編集ファイルはloglist.xml上の最も上のファイル
795 session["filepath"] = db["loglist"].path[0]
796 db["feed"] = Feed.readxml(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0].to_s)
797 db["entry"] = Entry.readxml(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0].to_s)
801 params["action"] = ""
805 filelist = FileUploader.new.filelist()
807 # タイプがファイルのみリストとして取得する
809 filelist.each { |fname| db["filelist"] << fname if File.ftype(XMLPATH + fname) == "file"}
814 # loglist.xmlが存在するかチェック
815 if File.exist?(LISTXMLPATH) == false
816 # なかった場合はloglist.xmlを自動生成
817 loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}diary.xml"], LISTXMLPATH)
819 writer = FeedWriter.new(XMLPATH + loglist.path[0].match(/[^\/]*?$/).to_a[0])
820 writer.to_xml(Feed.new({}), [])
823 db["loglist"] = LogList.readxml(LISTXMLPATH)
825 # diary.xmlが存在するかチェック
826 if File.exist?(XMLPATH + db["loglist"].path[0].match(/[^\/]*?$/).to_a[0]) == false
827 # なかった場合はdiary.xmlを自動生成
828 writer = FeedWriter.new(XMLPATH + db["loglist"].path[0].match(/[^\/]*?$/).to_a[0])
829 writer.to_xml(Feed.new({}), [])
832 # 初期の編集ファイルはloglist.xml上の最も上のファイル
833 session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
834 db["feed"] = Feed.readxml(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0].to_s)
835 db["entry"] = Entry.readxml(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0].to_s)
841 def Controller.MultiForm(cgi, session, params, db)
846 case params["action"]
849 file = FileUploader.new
850 file.upload(db["logpath"], db["importxml"])
853 db["loglist"].path[db["loginsertindex"], 0] = db["logpath"]
854 db["loglist"].display[db["loginsertindex"], 0] = db["logdisplay"]
858 db["loglist"] = LogList.readxml(LISTXMLPATH)
859 session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
860 db["feed"] = Feed.readxml(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0].to_s)
861 db["entry"] = Entry.readxml(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0].to_s)
864 # 入力されたログファイルパスが既に存在するかを確認
866 db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
867 db["loglist"].path.each do |val|
868 if val == db["logpath"]
869 # 重複していた場合エラーメッセージを表示し、処理を行わない
871 params["action"] = ""
872 db["error"] = "<br><span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
879 db["loginsertindex"] = cgi["loginsertindex"].read.to_i
880 db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
881 db["importxml"] = cgi["updata"].read
885 REXML::Document.new(Controller.fix_updata_enc(db["importxml"].to_s))
887 db["error"] = "<br><span style='color: #ff0000'>不正な整形のXMLです!ファイルを見直してください。</span><br>"
888 db["error"] << "<div class='divstyle' style='width: 560px; color: #ff0000; text-align: left;'>"
889 db["error"] << CGI.escapeHTML(exception.to_s).gsub("\n", "<br>")
890 db["error"] << "</div>"
891 params["action"] = ""
896 if db["loginsertindex"] == 0
897 params["action"] = ""
898 db["error"] = "<br><span style='color: #ff0000'>ラジオボックスでログの挿入位置を選択してください!</span><br>"
901 if db["logpath"] == FEEDXMLDIR || db["logdisplay"].blank?
902 params["action"] = ""
903 db["error"] = "<br><span style='color: #ff0000'>インポートファイル、及び、ログの表示名は空欄にできません。</span><br>"
907 db["loglist"] = LogList.readxml(LISTXMLPATH)
912 case params["action"]
915 FileUtils.move(XMLPATH + "diary.xml", XMLPATH + db["logpath"].match(/[^\/]*?$/).to_s)
917 file = FileUploader.new
918 file.upload("diary.xml", db["importxml"])
921 db["loglist"].path[1, 0] = db["logpath"]
922 db["loglist"].display[1, 0] = db["logdisplay"]
926 db["loglist"] = LogList.readxml(LISTXMLPATH)
927 session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
928 db["feed"] = Feed.readxml(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0].to_s)
929 db["entry"] = Entry.readxml(XMLPATH + session["filepath"].match(/[^\/]*?$/).to_a[0].to_s)
932 # 入力されたログファイルパスが既に存在するかを確認
934 db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
935 db["loglist"].path.each do |val|
936 if val == db["logpath"]
937 # 重複していた場合エラーメッセージを表示し、処理を行わない
938 params["action"] = ""
939 db["error"] = "<br><span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
946 db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
947 db["importxml"] = cgi["updata"].read
951 REXML::Document.new(Controller.fix_updata_enc(db["importxml"].to_s))
953 db["error"] = "<br><span style='color: #ff0000'>不正な整形のXMLです!ファイルを見直してください。</span><br>"
954 db["error"] << "<div class='divstyle' style='width: 560px; color: #ff0000; text-align: left;'>"
955 db["error"] << CGI.escapeHTML(exception.to_s).gsub("\n", "<br>")
956 db["error"] << "</div>"
957 params["action"] = ""
961 if db["logpath"].blank? || db["logdisplay"].blank? || db["importxml"].blank?
962 params["action"] = ""
963 db["error"] = "<span style='color: #ff0000'>インポートファイル、及び、ログの表示名、ログのパスは空欄にできません。</span><br>"
967 db["loglist"] = LogList.readxml(LISTXMLPATH)
971 db["loglist"] = LogList.readxml(LISTXMLPATH)
976 # Formのenctypeがmultypartだった場合、入力された値をrubyバージョンに左右されずに読み取るメソッドです。
977 def Controller.get_mpart_value(cgi_param)
978 if RUBY_VERSION >= "1.9.0"
979 cgi_param[0..cgi_param.length].to_s
985 # アップロードされたXMLをバージョンに左右されずに読み取るために、ruby-1.9.1以上ではエンコーディングを強制指定します
986 def Controller.fix_updata_enc(file_to_s)
987 if RUBY_VERSION >= "1.9.0"
988 file_to_s.force_encoding("UTF-8")
997 # SESSION変数、パラメータなどを取得します
999 session = CGI::Session.new(cgi)
1000 params = Hash[*cgi.params.to_a.map{|k, v| [k, v[0].to_s]}.flatten]
1004 if session["login"] != "true"
1005 if (cgi["loginid"] == LOGINID && cgi["password"] == PASSWORD)
1006 session["login"] = "true"
1008 # ワークフォルダの中をクリーンアップします
1009 filelist = Dir::entries("./work")
1010 # 削除条件 : 最終変更日時から1日(60*60*24sec)かつ、ファイルタイプがファイルの場合
1011 filelist.each do |file|
1012 File.delete("./work/#{file}") if Time.now - File.ctime("./work/#{file}") > 86400 && File.ftype("./work/#{file}") == "file"
1018 if params["mode"] == "logout"
1019 session["login"] = nil
1020 session["logini"] = nil
1021 session["password"] = nil
1027 # セッションが有効な場合のも実行します
1028 if session["login"] == "true"
1031 db = PStore.new("./work/#{session.session_id}.dat")
1037 File.delete("./work/#{session.session_id}.dat")
1038 # PStoreが破損していた場合はセッション情報を破棄する
1039 session["login"] = nil
1040 session["logini"] = nil
1041 session["password"] = nil
1046 if cgi["mode"].respond_to?(:read) && cgi["action"].respond_to?(:read)
1047 params["mode"] = cgi["mode"].read
1048 params["action"] = cgi["action"].read
1049 Controller.MultiForm(cgi, session, params, db)
1051 Controller.NormalForm(cgi, session, params, db)
1055 # メニューとして表示されるHTML文字列です
1057 if USEFILEMANAGER == true
1058 menu = "<div class=\"divstyle\" style=\"width: #{TABLEWIDTH}px;\"><div class=\"divstyle\" align=\"center\" style=\"margin-bottom: 5px;\">現在編集中のファイル : #{session["filepath"]} [ <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>"
1060 menu = "<div class=\"divstyle\" style=\"width: #{TABLEWIDTH}px;\"><div class=\"divstyle\" align=\"center\" style=\"margin-bottom: 5px;\">現在編集中のファイル : #{session["filepath"]} [ <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>"
1064 # メニューとして表示されるHTML文字列です
1065 if USEFILEMANAGER == true
1066 menu = "<div class=\"divstyle\" style=\"width: #{TABLEWIDTH}px;\"><div class=\"divstyle\" align=\"center\" style=\"margin-bottom: 5px;\">現在編集中のファイル : #{session["filepath"]} [ <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=selectlog'\">他のファイルを選択</a> ]</div>[ <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}'\">トップページ</a> | 記事管理 ( <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=newentry'\">作成</a> | <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=editentry'\">編集</a> | <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=delentry'\">消去</a> ) | <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=editfeed'\">XML情報編集</a> | <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=log'\">ログ管理</a> | <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=import'\">インポート</a> | <a href=\"#{FILEMANAGER}\" target=\"_blank\">ファイル管理</a> | <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=reset'\">初期化</a> | <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=logout'\">ログアウト</a> ]</div>"
1068 menu = "<div class=\"divstyle\" style=\"width: #{TABLEWIDTH}px;\"><div class=\"divstyle\" align=\"center\" style=\"margin-bottom: 5px;\">現在編集中のファイル : #{session["filepath"]} [ <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=selectlog'\">他のファイルを選択</a> ]</div>[ <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}'\">トップページ</a> | 記事管理 ( <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=newentry'\">作成</a> | <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=editentry'\">編集</a> | <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=delentry'\">消去</a> ) | <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=editfeed'\">XML情報編集</a> | <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=log'\">ログ管理</a> | <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=import'\">インポート</a> | <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=reset'\">初期化</a> | <a class=\"menu_link\" onclick=\"location.href ='#{cgi.script_name}?mode=logout\">ログアウト</a> ]</div>"
1072 # modeとactionの相関図は以下のようになります。
1077 if session["login"] != "true"
1078 # セッションが存在しない場合は強制的にトップページに遷移
1079 htmlwriter = HtmlWriter.new("./erbtemp/login.html.erb", binding)
1083 htmlwriter = HtmlWriter.new("./erbtemp/select.html.erb", binding)
1085 htmlwriter = HtmlWriter.new("./erbtemp/newentry.html.erb", binding)
1087 htmlwriter = HtmlWriter.new("./erbtemp/editentry.html.erb", binding)
1089 htmlwriter = HtmlWriter.new("./erbtemp/delentry.html.erb", binding)
1091 htmlwriter = HtmlWriter.new("./erbtemp/editfeed.html.erb", binding)
1093 htmlwriter = HtmlWriter.new("./erbtemp/log.html.erb", binding)
1095 htmlwriter = HtmlWriter.new("./erbtemp/insertfeed.html.erb", binding)
1097 htmlwriter = HtmlWriter.new("./erbtemp/replacefeed.html.erb", binding)
1099 htmlwriter = HtmlWriter.new("./erbtemp/indeximport.html.erb", binding)
1101 htmlwriter = HtmlWriter.new("./erbtemp/reset.html.erb", binding)
1103 htmlwriter = HtmlWriter.new("./erbtemp/index.html.erb", binding)
1107 # エラーが発生した場合、それを画面に表示します
1108 htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
1113 cgi.out{htmlwriter.to_code}
1115 # エラーが発生した場合、それを画面に表示します
1116 htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
1117 cgi.out{htmlwriter.to_code}
1124 # エラーが発生した場合、それを画面に表示します
1125 detail = ("%s: %s (%s)\n" %
1126 [evar.backtrace[0], evar.message, evar.send('class')]) +
1127 evar.backtrace[1..-1].join("\n")
1128 puts "content-type: text/html\n\n<plaintext>\n" + detail