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>のまま保持するか、改行記号に直すか
40 # ファイルの書き込み時にENTRYのIDおよびURLを、FEEDオブジェクトから自動生成した値に置換するか否か
41 REPLACEENTRYIDANDURL = false
44 APPVERSION = "- FeedGenerator for Ruby version 2.0.0.0 -<br>Copyright(c) 2009 Kureha.H (<a href=\"http://lunardial.sakura.ne.jp/\" target=\"_blank\">http://lunardial.sakura.ne.jp/</a>) & Yui Naruse (<a href=\"http://airemix.com/\" target=\"_blank\">http://airemix.com/</a>)"
46 APPTITLE = "FeedGenerator for Ruby version 2.0.0.0"
54 # ruby-1.9.x以降ではファイルを開いた際、エンコードの指定を行わないとエラーの原因になります。
55 # ただしruby-1.8.6以前はエンコードの指定に対応していないため、独自メソッドを定義してファイルの入出力を行います。
57 # _arg[0]_ :: 入出力を行うファイルのパス
58 # _arg[1]_ :: モードの指定。例 : w:utf-8(書き込みモード・UTF-8エンコードでファイルを開く)
64 arg[1] = mode[/[^:]+/] if RUBY_VERSION < "1.8.7" && mode.include?(':')
65 rdonly_p = /\A[^:]*[wa+]/ !~ mode
67 rdonly_p = !(mode & (IO::WRONY | IO::RDWR))
70 f.flock(rdonly_p ? File::LOCK_SH : File::LOCK_EX)
94 # = Feed/Entryのスーパークラス
96 # 入出力用のメソッドなど、共通する機能を提供します
100 # _hash_ :: 値を格納したhash配列。superfeed.new(CGI::Session.new(new CGI).params)のようにして使う。
106 @paramlist.each do |key|
107 val = hash[key.to_sym] || hash[key.to_s]
110 val.gsub!(/\r\n|\r/, "\n")
111 @attr[key.to_sym] = CGI.escapeHTML(val)
114 @attr[key.to_sym] = ""
118 # 可視・不可視を示すハッシュキーを格納する配列です
121 # ハッシュキーの日本語説明を格納する配列です
126 attr_reader :attr, :paramlist, :name, :display
128 # 内部の@attrハッシュにアクセスする手段を提供するメソッドです
130 # AbstractEntry.attr[:title]にアクセスしたい場合はAbstractEntry.titleで可能になります。
131 # AbstractEntry.send("title")という方法でもアクセス可能です。
132 def method_missing(methname, *args)
133 methname = methname.to_s
135 if methname[-1] == ?=
137 raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" unless args.length == 1
139 methname = @paramlist.find{|par|par == methname}
140 return @attr[methname.to_sym] = args[0] if methname
143 raise ArgumentError, "wrong number of arguments (#{args.length} for 0)" unless args.empty?
144 return @attr[methname.to_sym] if @attr.key?(methname.to_sym)
146 # attr上にキーがない値を入れようとした場合はNoMethodError
155 @attr[key.to_sym] = value
161 # Feedの基礎情報を保有するクラスです
162 class Feed < AbstractEntry
165 # _hash_ :: 値を格納したhash配列。feed.new(CGI::Session.new(new CGI).params)のようにして使います。
171 @paramlist = ["feedattr", "title", "subtitle", "self", "url", "updated", "feedid", "rights", "aname", "amail", "others"]
173 # AbstractEntryのinitializeメソッドを呼び出し、値を@attr配列に格納します
176 # 可視・不可視を示すハッシュキーを格納する配列です
177 @display = {"feedattr" => "none", "title" => "", "subtitle" => "",
178 "self" => "", "url" => "",
179 "updated" => "none", "feedid" => "",
180 "rights" => "", "aname" => "",
181 "amail" => "", "others" => "none"}
183 # デバッグモードの場合、全ての入力要素を表示します
185 @display.each do |key, val|
190 # ハッシュキーの日本語説明を格納する配列です
191 @name = {"feedattr" => "feedの保持している属性", "title" => "ウェブページのタイトル", "subtitle" => "ウェブページの簡単な説明",
192 "self" => "このXMLファイルのURL", "url" => "ウェブページのURL",
193 "updated" => "XMLファイルの最終更新日", "feedid" => "あなたのページ独自のID",
194 "rights" => "ページの著作権表記", "aname" => "ページの製作者",
195 "amail" => "ページ製作者のメールアドレス", "others" => "その他の要素"}
199 # Atom XMLファイルを読み込んで解析し、等価なFeedオブジェクトを返却するメソッドです
201 # _path_ :: Atom XMLファイルのパス
202 def self.readxml(path)
205 doc = REXML::Document.new(myopen(path, "r:utf-8"){|f|f.read})
211 doc.elements["feed"].attributes.each_attribute do |attr|
212 xmlattr.push("#{attr.to_string} ")
214 xml[:feedattr] = xmlattr.join("\n").gsub(/"/, "'")
216 # XML解析部分です。各element毎に判定を行います
217 doc.elements.each("feed") do |elm|
218 elm.elements.each { |child|
222 xml[:feedid] = child.text
223 when "title", "subtitle", "updated", "rights"
224 xml[child.name.to_sym] = child.text
226 child.elements.each do |gchild|
229 xml[:aname] = gchild.text
231 xml[:amail] = gchild.text
235 child.attributes.each do |k, v|
238 xml[:self] = child.attributes["href"]
239 elsif v == "alternate"
240 xml[:url] = child.attributes["href"]
247 # 上記判定以外の全要素は配列に格納します
248 others.push(child.to_s)
257 xml[:others] = others.join("\n")
262 # 内部に保持している情報を、Atom Feed1.0形式の文字列に出力するメソッドです
266 buf.push("<feed #{@attr[:feedattr]}>")
267 buf.push("<title type=\"text\">#{@attr[:title]}</title>")
268 buf.push("<subtitle type=\"text\">#{@attr[:subtitle]}</subtitle>")
269 buf.push("<link rel=\"self\" type=\"application/atom+xml\" href=\"#{@attr[:self]}\" />")
270 buf.push("<link rel=\"alternate\" type=\"text/html\" href=\"#{@attr[:url]}\" />")
271 buf.push("<updated>#{Time.now.iso8601}</updated>")
272 buf.push("<id>#{@attr[:feedid]}</id>")
273 buf.push("<rights type=\"text\">#{@attr[:rights]}</rights>")
275 buf.push("\t<name>#{@attr[:aname]}</name>")
276 buf.push("\t<email>#{@attr[:amail]}</email>")
277 buf.push("</author>")
278 buf.push("#{CGI.unescapeHTML(@attr[:others])}") if @attr[:others] != ""
280 return buf.join("\n")
284 def self.update(path, feed)
285 entrylist = Entry.readxml(path)
286 Feed.to_xml(path, feed, entrylist)
293 # _feed_ :: Feedオブジェクト
294 # _entry_ :: Entryオブジェクトの配列
295 def self.to_xml(path, feed, entrylist_tmp)
297 entrylist = entrylist_tmp.dup
298 buf.push("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
299 buf.push("#{feed.to_s}\n")
300 entrylist.each { |entry|
301 if REPLACEENTRYIDANDURL
302 entry.entryid.gsub!(/^[^\?]*\?/, "")
303 entry.entryid = feed.url + "?" + entry.entryid
304 entry.url = feed.url + "#" + entry.entryid
306 buf.push("#{entry.to_s}\n")
310 myopen(path, "w") do |f|
311 f.print buf.join("\n")
317 # _feed_ :: Feedオブジェクト
318 # _entry_ :: Entryオブジェクトの配列
319 def self.to_xml_plain(path, feed, entrylist)
321 buf.push("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
322 buf.push("#{feed.to_s}\n")
323 entrylist.each { |entry|
324 buf.push("#{entry.to_s}\n")
328 myopen(path, "w") do |f|
329 f.print buf.join("\n")
337 # Entryの基礎情報を保有するクラスです
338 class Entry < AbstractEntry
341 # _hash_ :: 値を格納したhash配列。entry.new(CGI::Session.new(new CGI).params)のようにして使います。
347 @paramlist = ["entryid", "title", "summary", "published", "updated", "url", "content", "others"]
349 # AbstractEntryのinitializeメソッドを呼び出し、値を@attr配列に格納します
352 # 可視・不可視を示すハッシュキーを格納する配列です
353 @display = {"entryid" => "none", "title" => "",
354 "summary" => "", "published" => "none",
355 "updated" => "none", "url" => "none",
356 "content" => "", "others"=>"none"}
358 # デバッグモードの場合、全ての入力要素を表示します
360 @display.each do |key, val|
365 # ハッシュキーの日本語説明を格納する配列です
366 @name = {"entryid" => "記事固有のID", "title" => "記事のタイトル",
367 "summary" => "記事の簡単な説明", "published" => "記事の出版時刻",
368 "updated" => "記事の更新時刻", "url" => "記事へのURLアドレス",
369 "content" => "記事の本文", "others"=>"その他の項目"}
372 # Atom XMLファイルを読み込んで解析し、等価なEntryオブジェクト配列を返却するメソッドです
374 # _path_ :: Atom XMLファイルのパス
375 def self.readxml(path)
378 doc = REXML::Document.new(myopen(path, "r:utf-8"){|f|f.read})
382 # XML解析部分です。各element毎に判定を行います
383 doc.elements.each("feed/entry") do |elm|
386 elm.elements.each do |child|
390 xml[:entryid] = child.text
392 xml[:url] = child.attributes["href"]
393 when "title", "summary", "summary", "published", "updated", "content"
394 xml[child.name.to_sym] = child.text
396 # 上記判定以外の全要素は配列に格納します
397 others.push(child.to_s)
403 xml[:others] = others.join("\n")
404 entrylist.push(Entry.new(xml))
411 def self.insert(path, entry)
412 feed = Feed.readxml(path)
413 entrylist = Entry.readxml(path)
414 entrylist.unshift entry
416 Feed.to_xml(path, feed, entrylist)
422 def self.update(path, entry)
423 feed = Feed.readxml(path)
424 entrylist = Entry.readxml(path)
427 entrylist.each_with_index { |e, i|
428 if e.entryid == entry.entryid
433 Feed.to_xml(path, feed, entrylist) if successed
439 def self.select(path, entryid)
440 feed = Feed.readxml(path)
441 entrylist = Entry.readxml(path)
444 entrylist.each_with_index { |e, i|
445 entry = entrylist[i].dup if e.entryid == entryid
452 def self.delete(path, entryid)
453 feed = Feed.readxml(path)
454 entrylist = Entry.readxml(path)
458 entrylist.each_with_index { |e, i|
459 if e.entryid == entryid
466 entrylist.delete_at delete_index
467 Feed.to_xml(path, feed, entrylist)
473 # データソースから読み取ったHTMLを、エディタで編集可能な形式に変換するメソッドです
474 def content_for_generator
475 str = @attr[:content].dup
477 str.gsub!(/(<\/(?:p|h\d|div)(?:>|>))\n/i, '\1')
478 str.gsub!(/\n/, '<br>') if REPLACEBRTAG
479 str.gsub!(/(<(?:(?!>).)*?)#{Regexp.escape(FEEDXMLDIR)}/) { "#$1#{XMLPATH}" }
483 # エディタで編集されたCONTENT要素を、データソースに書き込める形式に変換するメソッドです
485 str = @attr[:content].dup
486 str = CGI.unescapeHTML(str)
488 str.gsub!(/(\r\n|\n)/, "")
489 str.gsub!(/<br>/i, "\n") if REPLACEBRTAG
490 str.gsub!(/(<br>|<\/p>|<\/h\d>|<\/div>)(?=[^\n])/i) { "#$1\n" } unless REPLACEBRTAG
491 str.gsub!(/(<[^>]*?)#{Regexp.escape(XMLPATH)}/) { "#$1#{FEEDXMLDIR}" }
495 # 確認画面で表示されるCONTENT要素を生成するメソッドです
497 str = @attr[:content].dup
498 str = CGI.unescapeHTML(str)
500 str.gsub!(/<br>/i, "\n") if REPLACEBRTAG
501 str.gsub!(/(<[^>]*?)#{Regexp.escape(FEEDXMLDIR)}/) { "#$1#{XMLPATH}" }
505 # 内部に保持している情報を、Atom Feed1.0形式の文字列に出力するメソッドです
509 buf.push("<id>#{@attr[:entryid]}</id>")
510 buf.push("<title>#{@attr[:title]}</title>")
511 buf.push("<summary>#{@attr[:summary]}</summary>")
512 buf.push("<published>#{@attr[:published]}</published>")
513 buf.push("<updated>#{@attr[:updated]}</updated>")
514 buf.push("<link href=\"#{@attr[:url]}\" />")
515 buf.push("<content type=\"html\">#{@attr[:content]}</content>")
516 buf.push("#{CGI.unescapeHTML(@attr[:others])}") if @attr[:others] != ""
519 return buf.join("\n")
525 # テンプレートファイル(*.erb)を読み込み、管理するクラスです
529 # _template_ :: テンプレートファイル(*.erb)のパス
530 # _binding_ :: binding変数
531 def initialize(template, binding)
532 @erb = ERB.new(myopen(template, "r:utf-8") {|f| f.read}, nil, "-")
536 # テンプレートファイルの文字列を返却するメソッドです
538 @erb.result(@binding)
544 # loglist.xmlにかかわる入出力を行います
548 # _display_ :: 画面表示用文字の配列
549 # _path_ :: XMLファイルパスの配列
550 # _logpath_ :: loglist.xmlのパス
551 def initialize(display, path, logpath)
557 attr_accessor :display, :path, :logpath
559 # loglist.xmlファイルを読み込んで解析し、LogListオブジェクトを返却するメソッドです
561 # _logpath_ :: loglist.xmlへのパス
562 def LogList.readxml(logpath)
566 myopen(logpath, "r:utf-8") { |f|
570 doc = REXML::Document.new(lines.join("\n"))
574 doc.elements.each("list/file") { |elm|
575 elm.elements.each {|child|
578 @display.push(child.text)
580 @path.push(child.text)
587 return LogList.new(@display, @path, logpath)
590 # loglist.xmlにオブジェクトの内容を反映するメソッドです
594 path.each_with_index do |path, i|
596 buf.push("<display>#{@display[i]}</display>")
597 buf.push("<path>#{@path[i]}</path>")
602 myopen(@logpath, "w") do |f|
603 f.print buf.join("\n")
617 # ファイルの一覧を取得し、ファイル名称を格納した配列を返却します
619 arr = Dir::entries(XMLPATH).sort
628 # _name_ :: 消去するファイル名
630 File.delete(XMLPATH + name.match(/[^\/]*?$/).to_a[0])
635 # _name_ :: アップロードファイルの名称
636 # _img_ :: アップロードファイル
637 def upload(name, img)
638 open(XMLPATH + File.basename(name), "w") do |f|
647 # コントローラ部分に相当する処理を受け持つクラスです
649 def Controller.NormalForm(cgi, session, params, db)
651 # modeとactionの相関図は以下のようになります。
659 session["filepath"] = params["logpath"]
660 db["loglist"] = LogList.readxml(LISTXMLPATH)
661 # 初期状態で選択されるログは「loglist.xml」の最上に位置するログになります
662 session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
663 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
664 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
668 case params["action"]
671 session["target_filepath"] = params["target_filepath"]
672 db["newentry"] = Entry.new(params)
673 db["newentry"].content = db["newentry"].content_for_blog
676 session["target_filepath"] = params["target_filepath"]
677 successed = Entry.insert(XMLPATH + File.basename(params["target_filepath"]), Entry.new(params))
679 db["error"] = "日記の新規追加に失敗しました。"
680 params["mode"] = "error"
684 session["target_filepath"] = params["target_filepath"]
685 db["newentry"] = Entry.new(params)
687 # New Diary - Default
688 session["target_filepath"] = session["filepath"]
689 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
690 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
695 case params["action"]
698 session["target_filepath"] = params["target_filepath"]
699 session["editid"] = cgi["editid"].to_s
700 db["editentry"] = Entry.select(XMLPATH + File.basename(session["target_filepath"]), session["editid"])
703 session["target_filepath"] = params["target_filepath"]
704 session["editid"] = cgi["editid"].to_s
705 db["editentry"] = Entry.new(params)
706 db["editentry"].content = db["editentry"].content_for_blog
709 session["target_filepath"] = params["target_filepath"]
710 successed = Entry.update(XMLPATH + File.basename(params["target_filepath"]), Entry.new(params))
712 db["error"] = "日記の編集処理に失敗しました。<br>該当の日記が既に存在しない可能性があります。"
713 params["mode"] = "error"
716 session["target_filepath"] = params["target_filepath"]
717 db["editentry"] = Entry.new(params)
719 # Edit Diary - Default
720 session["target_filepath"] = session["filepath"]
721 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
722 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
727 case params["action"]
730 session["target_filepath"] = params["target_filepath"]
731 session["delid"] = cgi["delid"].to_s
732 db["delentry"] = Entry.select(XMLPATH + File.basename(session["target_filepath"]), session["delid"])
735 session["target_filepath"] = params["target_filepath"]
736 successed = Entry.delete(XMLPATH + File.basename(params["target_filepath"]), cgi["delid"].to_s)
738 db["error"] = "日記の編集処理に失敗しました。<br>該当の日記が既に存在しない可能性があります。"
739 params["mode"] = "error"
742 session["target_filepath"] = params["target_filepath"]
743 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["target_filepath"]))
744 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["target_filepath"]))
746 # Delete Diary - Default
747 session["target_filepath"] = session["filepath"]
748 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
749 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
754 case params["action"]
757 session["target_filepath"] = params["target_filepath"]
758 db["feed"] = Feed.new(params)
759 # 実際にFeed情報の変更をファイルに反映
761 session["target_filepath"] = params["target_filepath"]
762 db["feed"] = Feed.new(params)
764 session["target_filepath"] = params["target_filepath"]
765 Feed.update(XMLPATH + File.basename(params["target_filepath"]), Feed.new(params))
767 session["target_filepath"] = session["filepath"]
768 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
774 db["loglist"] = LogList.readxml(LISTXMLPATH)
775 case params["action"]
776 # ログファイルの編集を実際にファイルに反映
778 # エラーチェック。この段階のエラーは強度のエラーを発する。
779 db["loglist"].path.each do |val|
780 if val == cgi["logpath"]
781 # 重複していた場合エラーメッセージを表示し、処理を行わない
782 params["action"] = ""
783 params["mode"] = "error"
784 db["error"] = "ログファイルの追加中に重大なエラーが発生しました。<br>環境を見直してください。"
790 db["loglist"].path[1, 0] = cgi["logpath"]
791 db["loglist"].display[1, 0] = cgi["logdisplay"]
793 # 既存のdiary.xmlを指定された名称でコピーして保存
794 FileUtils.copy_file(XMLPATH + File.basename(db["loglist"].path[0]), XMLPATH + File.basename(db["logpath"]), true)
796 db["loglist"] = LogList.readxml(LISTXMLPATH)
798 # 新たなdiary.xmlを生成。この際保持する情報は同一のFeedオブジェクトnew()
799 Feed.to_xml(XMLPATH + File.basename(db["loglist"].path[0]), db["feed"], [])
803 db["loglist"].path.each do |val|
804 if val == cgi["logpath"]
805 # 重複していた場合エラーメッセージを表示し、処理を行わない
806 params["action"] = ""
807 db["error"] = "<span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
812 db["logpath"] = cgi["logpath"]
813 db["logdisplay"] = cgi["logdisplay"]
815 if db["logpath"].blank? || db["logdisplay"].blank?
816 params["action"] = ""
822 db["logdelindex"] = params["logdelindex"].to_i
824 if db["logdelindex"] < 1
825 db["error"] = "<span style='color: #ff0000'>ログファイルの削除パラメタが不正です。</span><br>"
826 params["action"] = ""
830 if cgi["logdelindex"].to_i < 1
831 params["action"] = ""
832 params["mode"] = "error"
833 db["error"] = "ログファイルの削除中に重大なエラーが発生しました。<br>環境を見直してください。"
837 File.delete(XMLPATH + File.basename(db["loglist"].path[db["logdelindex"]]))
839 db["loglist"].path.delete_at(cgi["logdelindex"].to_i)
840 db["loglist"].display.delete_at(cgi["logdelindex"].to_i)
843 db["loglist"] = LogList.readxml(LISTXMLPATH)
849 db["logeditindex"] = params["logdelindex"].to_i
851 db["logpath"] = db["loglist"].path[db["logeditindex"]]
852 db["logdisplay"] = db["loglist"].display[db["logeditindex"]]
854 if db["logeditindex"] == 0
855 db["error"] = "<span style='color: #ff0000'>ログファイルの編集パラメタが不正です。</span><br>"
856 params["action"] = ""
861 db["loglist"].path.each_with_index do |val, i|
862 if db["logeditindex"] != i
863 if params["logpath"].to_s == db["loglist"].path[i].to_s
869 if checkflag == false
870 params["action"] = "edit"
871 db["error"] = "<span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
873 db["loginsertindex"] = params["loginsertindex"].to_i
875 db["logpath"] = params["logpath"].to_s
876 db["logdisplay"] = params["logdisplay"].to_s
881 db["loglist"].path.each_with_index do |val, i|
882 if db["logeditindex"] != i
883 if params["logpath"].to_s == db["loglist"].path[i].to_s
889 if checkflag == false
890 params["action"] = ""
891 params["mode"] = "error"
892 db["error"] = "ログファイルの編集中に重大なエラーが発生しました。<br>環境を見直してください。"
895 db["loginsertindex"] = params["loginsertindex"].to_i
897 db["logpath"] = params["logpath"].to_s
898 db["logdisplay"] = params["logdisplay"].to_s
901 if XMLPATH + File.basename(db["loglist"].path[db["logeditindex"]]) != XMLPATH + File.basename(db["logpath"])
902 FileUtils.move(XMLPATH + File.basename(db["loglist"].path[db["logeditindex"]]), XMLPATH + File.basename(db["logpath"]))
904 db["loglist"].path.delete_at(db["logeditindex"])
905 db["loglist"].display.delete_at(db["logeditindex"])
906 db["loglist"].path.insert(db["loginsertindex"] + 1, db["logpath"])
907 db["loglist"].display.insert(db["loginsertindex"] + 1, db["logdisplay"])
914 # 現在編集中のデータを強制的に最上のファイルパスに変更
915 session["filepath"] = db["loglist"].path[0]
918 prevmonth = (DateTime.now << 1)
920 # 前月の時刻を元に、ディフォルト書式を生成します
921 db["logpath"] = FEEDXMLDIR + prevmonth.strftime("%Y%m") + ".xml"
922 db["logdisplay"] = prevmonth.strftime("%Y年%m月").gsub("年0", "年")
927 db["loglist"] = LogList.readxml(LISTXMLPATH)
931 case params["action"]
934 file = FileUploader.new
935 file.filelist().each { |fname|
936 file.delete(fname) if File.ftype(XMLPATH + fname) == "file"
941 loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}diary.xml"], LISTXMLPATH)
944 db["loglist"] = LogList.readxml(LISTXMLPATH)
947 Feed.to_xml(XMLPATH + db["loglist"].path[0].match(/[^\/]*?$/).to_a[0], Feed.new({}), [])
949 # 初期の編集ファイルはloglist.xml上の最も上のファイル
950 session["filepath"] = db["loglist"].path[0]
951 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
952 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
956 params["action"] = ""
960 filelist = FileUploader.new.filelist()
962 # タイプがファイルのみリストとして取得する
964 filelist.each { |fname| db["filelist"] << fname if File.ftype(XMLPATH + fname) == "file"}
969 # loglist.xmlが存在するかチェック
970 if File.exist?(LISTXMLPATH) == false
971 # なかった場合はloglist.xmlを自動生成
972 loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}diary.xml"], LISTXMLPATH)
974 Feed.to_xml(XMLPATH + File.basename(loglist.path[0]), Feed.new({}), [])
977 db["loglist"] = LogList.readxml(LISTXMLPATH)
979 # diary.xmlが存在するかチェック
980 if File.exist?(XMLPATH + File.basename(db["loglist"].path[0])) == false
981 # なかった場合はdiary.xmlを自動生成
982 Feed.to_xml(XMLPATH + File.basename(db["loglist"].path[0]), Feed.new({}), [])
985 # 初期の編集ファイルはloglist.xml上の最も上のファイル
986 session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
987 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
988 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
994 def Controller.MultiForm(cgi, session, params, db)
997 db["loglist"] = LogList.readxml(LISTXMLPATH)
1001 case params["action"]
1003 # この段階のエラーに対しては強度のエラーを発する
1005 db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
1006 db["loglist"].path.each do |val|
1007 if val == db["logpath"]
1008 # 重複していた場合エラーメッセージを表示し、処理を行わない
1010 params["action"] = ""
1011 params["mode"] = "error"
1012 db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1017 db["loginsertindex"] = cgi["loginsertindex"].read.to_i
1018 db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1021 if db["loginsertindex"] == 0
1022 params["action"] = ""
1023 params["mode"] = "error"
1024 db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1028 if File.basename(db["logpath"]).blank? || db["logdisplay"].blank?
1029 params["action"] = ""
1030 params["mode"] = "error"
1031 db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1036 file = FileUploader.new
1037 file.upload(db["logpath"], db["importxml"])
1040 db["loglist"].path[db["loginsertindex"], 0] = db["logpath"]
1041 db["loglist"].display[db["loginsertindex"], 0] = db["logdisplay"]
1042 db["loglist"].to_xml
1045 db["loglist"] = LogList.readxml(LISTXMLPATH)
1046 session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
1047 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
1048 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
1051 # 入力されたログファイルパスが既に存在するかを確認
1053 db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
1054 db["loglist"].path.each do |val|
1055 if val == db["logpath"]
1056 # 重複していた場合エラーメッセージを表示し、処理を行わない
1058 params["action"] = ""
1059 db["error"] = "<br><span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
1066 db["loginsertindex"] = cgi["loginsertindex"].read.to_i
1067 db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1068 db["importxml"] = cgi["updata"].read
1072 REXML::Document.new(Controller.fix_updata_enc(db["importxml"].to_s))
1074 db["error"] = "<br><span style='color: #ff0000'>不正な整形のXMLです!ファイルを見直してください。</span><br>"
1075 db["error"] << "<div class='divstyle' style='width: 560px; color: #ff0000; text-align: left;'>"
1076 db["error"] << CGI.escapeHTML(exception.to_s).gsub("\n", "<br>")
1077 db["error"] << "</div>"
1078 params["action"] = ""
1084 if db["loginsertindex"] == 0
1085 params["action"] = ""
1086 db["error"] = "<br><span style='color: #ff0000'>ラジオボックスでログの挿入位置を選択してください!</span><br>"
1090 if db["logpath"] == FEEDXMLDIR || db["logdisplay"].blank?
1091 params["action"] = ""
1092 db["error"] = "<br><span style='color: #ff0000'>インポートファイル、及び、ログの表示名は空欄にできません。</span><br>"
1101 case params["action"]
1103 # この段階のエラーに対しては強度のエラーを発する
1105 db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
1106 db["loglist"].path.each do |val|
1107 if val == db["logpath"]
1108 # 重複していた場合エラーメッセージを表示し、処理を行わない
1109 params["action"] = ""
1110 params["mode"] = "error"
1111 db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1116 db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1118 if File.basename(db["logpath"]).blank? || db["logdisplay"].blank? || db["importxml"].blank?
1119 params["action"] = ""
1120 params["mode"] = "error"
1121 db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1126 FileUtils.move(XMLPATH + "diary.xml", XMLPATH + File.basename(db["logpath"]))
1128 file = FileUploader.new
1129 file.upload("diary.xml", db["importxml"])
1132 db["loglist"].path[1, 0] = db["logpath"]
1133 db["loglist"].display[1, 0] = db["logdisplay"]
1134 db["loglist"].to_xml
1137 db["loglist"] = LogList.readxml(LISTXMLPATH)
1138 session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
1139 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
1140 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
1143 # 入力されたログファイルパスが既に存在するかを確認
1145 db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
1146 db["loglist"].path.each do |val|
1147 if val == db["logpath"]
1148 # 重複していた場合エラーメッセージを表示し、処理を行わない
1149 params["action"] = ""
1150 db["error"] = "<br><span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
1156 if checkflag == true
1157 db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1158 db["importxml"] = cgi["updata"].read
1162 REXML::Document.new(Controller.fix_updata_enc(db["importxml"].to_s))
1164 db["error"] = "<br><span style='color: #ff0000'>不正な整形のXMLです!ファイルを見直してください。</span><br>"
1165 db["error"] << "<div class='divstyle' style='width: 560px; color: #ff0000; text-align: left;'>"
1166 db["error"] << CGI.escapeHTML(exception.to_s).gsub("\n", "<br>")
1167 db["error"] << "</div>"
1168 params["action"] = ""
1173 if db["logpath"].blank? || db["logdisplay"].blank? || db["importxml"].blank?
1174 params["action"] = ""
1175 db["error"] = "<br><span style='color: #ff0000'>インポートファイル、及び、ログの表示名、ログのパスは空欄にできません。</span><br>"
1187 # Formのenctypeがmultypartだった場合、入力された値をrubyバージョンに左右されずに読み取るメソッドです。
1188 def Controller.get_mpart_value(cgi_param)
1189 if RUBY_VERSION >= "1.9.0"
1190 cgi_param[0..cgi_param.length].to_s
1196 # アップロードされたXMLをバージョンに左右されずに読み取るために、ruby-1.9.1以上ではエンコーディングを強制指定します
1197 def Controller.fix_updata_enc(file_to_s)
1198 if RUBY_VERSION >= "1.9.0"
1199 file_to_s.force_encoding("UTF-8")
1208 # SESSION変数、パラメータなどを取得します
1210 session = CGI::Session.new(cgi)
1211 params = Hash[*cgi.params.to_a.map{|k, v| [k, v[0].to_s]}.flatten]
1215 if session["login"] != "true"
1216 if (cgi["loginid"] == LOGINID && cgi["password"] == PASSWORD)
1217 session["login"] = "true"
1219 # ワークフォルダの中をクリーンアップします
1220 filelist = Dir::entries("./work")
1221 # 削除条件 : 最終変更日時から1日(60*60*24sec)かつ、ファイルタイプがファイルの場合
1222 filelist.each do |file|
1223 File.delete("./work/#{file}") if Time.now - File.ctime("./work/#{file}") > 86400 && File.ftype("./work/#{file}") == "file"
1229 if params["mode"] == "logout"
1230 session["login"] = nil
1231 session["logini"] = nil
1232 session["password"] = nil
1238 # セッションが有効な場合のも実行します
1239 if session["login"] == "true"
1242 db = PStore.new("./work/#{session.session_id}.dat")
1248 File.delete("./work/#{session.session_id}.dat")
1249 # PStoreが破損していた場合はセッション情報を破棄する
1250 session["login"] = nil
1251 session["logini"] = nil
1252 session["password"] = nil
1257 if cgi["mode"].respond_to?(:read) && cgi["action"].respond_to?(:read)
1258 params["mode"] = cgi["mode"].read
1259 params["action"] = cgi["action"].read
1260 Controller.MultiForm(cgi, session, params, db)
1262 Controller.NormalForm(cgi, session, params, db)
1265 # エラー画面移行時はセッション情報を破棄する
1266 if params["mode"] == "error"
1267 session["login"] = nil
1268 session["logini"] = nil
1269 session["password"] = nil
1274 # メニューとして表示されるHTML文字列です
1275 if params["target_filepath"].blank?
1276 menu = "<div class=\"divstyle\" style=\"width: #{TABLEWIDTH}px;\"><div class=\"divstyle\" align=\"center\" style=\"margin-bottom: 5px;\">現在編集中のファイル : #{session["filepath"]} "
1278 menu = "<div class=\"divstyle\" style=\"width: #{TABLEWIDTH}px;\"><div class=\"divstyle\" align=\"center\" style=\"margin-bottom: 5px;\">現在編集中のファイル : #{session["target_filepath"]} "
1280 if USEFILEMANAGER == true
1281 menu += "[ <a href=\"#{cgi.script_name}?mode=selectlog\">他のファイルを選択</a> ]</div>[ <a href=\"#{cgi.script_name}\">トップページ</a> | 記事管理 ( <a href=\"#{cgi.script_name}?mode=newentry\">作成</a> | <a href=\"#{cgi.script_name}?mode=editentry\">編集</a> | <a href=\"#{cgi.script_name}?mode=delentry\">消去</a> ) | <a href=\"#{cgi.script_name}?mode=editfeed\">XML情報編集</a> | <a href=\"#{cgi.script_name}?mode=log\">ログ管理</a> | <a href=\"#{cgi.script_name}?mode=import\">インポート</a> | <a href=\"#{FILEMANAGER}\" target=\"_blank\">ファイル管理</a> | <a href=\"#{cgi.script_name}?mode=reset\">初期化</a> | <a href=\"#{cgi.script_name}?mode=logout\">ログアウト</a> ]</div>"
1283 menu += "[ <a href=\"#{cgi.script_name}?mode=selectlog\">他のファイルを選択</a> ]</div>[ <a href=\"#{cgi.script_name}\">トップページ</a> | 記事管理 ( <a href=\"#{cgi.script_name}?mode=newentry\">作成</a> | <a href=\"#{cgi.script_name}?mode=editentry\">編集</a> | <a href=\"#{cgi.script_name}?mode=delentry\">消去</a> ) | <a href=\"#{cgi.script_name}?mode=editfeed\">XML情報編集</a> | <a href=\"#{cgi.script_name}?mode=log\">ログ管理</a> | <a href=\"#{cgi.script_name}?mode=import\">インポート</a> | <a href=\"#{cgi.script_name}?mode=reset\">初期化</a> | <a href=\"#{cgi.script_name}?mode=logout\">ログアウト</a> ]</div>"
1287 # modeとactionの相関図は以下のようになります。
1292 if session["login"] != "true"
1293 # セッションが存在しない場合は強制的にトップページに遷移
1294 htmlwriter = HtmlWriter.new("./erbtemp/login.html.erb", binding)
1298 htmlwriter = HtmlWriter.new("./erbtemp/select.html.erb", binding)
1300 htmlwriter = HtmlWriter.new("./erbtemp/newentry.html.erb", binding)
1302 htmlwriter = HtmlWriter.new("./erbtemp/editentry.html.erb", binding)
1304 htmlwriter = HtmlWriter.new("./erbtemp/delentry.html.erb", binding)
1306 htmlwriter = HtmlWriter.new("./erbtemp/editfeed.html.erb", binding)
1308 htmlwriter = HtmlWriter.new("./erbtemp/log.html.erb", binding)
1310 htmlwriter = HtmlWriter.new("./erbtemp/insertfeed.html.erb", binding)
1312 htmlwriter = HtmlWriter.new("./erbtemp/replacefeed.html.erb", binding)
1314 htmlwriter = HtmlWriter.new("./erbtemp/indeximport.html.erb", binding)
1316 htmlwriter = HtmlWriter.new("./erbtemp/reset.html.erb", binding)
1318 htmlwriter = HtmlWriter.new("./erbtemp/error.html.erb", binding)
1320 htmlwriter = HtmlWriter.new("./erbtemp/index.html.erb", binding)
1324 # エラーが発生した場合、それを画面に表示します
1325 htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
1330 cgi.out{htmlwriter.to_code}
1332 # エラーが発生した場合、それを画面に表示します
1333 htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
1334 cgi.out{htmlwriter.to_code}
1341 # エラーが発生した場合、それを画面に表示します
1342 detail = ("%s: %s (%s)\n" %
1343 [evar.backtrace[0], evar.message, evar.send('class')]) +
1344 evar.backtrace[1..-1].join("\n")
1345 puts "content-type: text/html\n\n<plaintext>\n" + detail