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/"
28 # FeedBlogを設置したディレクトリのURL
29 HOMEBASE = "https://lunardial.sakura.ne.jp/"
30 # 入力されたフルパスURL(HOMEBASE)を置換する文字列
33 LISTXMLPATH = "#{XMLPATH}loglist.xml"
34 # FeedBlog上の表示ページからログ格納ディレクトリまでのパス
38 # ファイルマネージャー機能を使用するならtrue
40 # ファイルマネージャー機能スクリプト(filemanager.rb)のパス
41 FILEMANAGER = "./filemanager.rb"
42 # XMLに書き込む際、改行部分を<br>のまま保持するか、改行記号に直すか
44 # ファイルの書き込み時にENTRYのIDおよびURLを、FEEDオブジェクトから自動生成した値に置換するか否か
45 REPLACEENTRYIDANDURL = false
47 PLUGINDIR = "./plugins/"
49 # EXPERIMENTAL AREA START
50 TO_HTML_ENTRYTEMPLATE = 'htmltemp/diary.html.erb'
51 TO_HTML_MAINTEMPLATE = 'htmltemp/main.html.erb'
52 # EXPERIMENTAL AREA END
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>)"
57 APPTITLE = "FeedGenerator for Ruby version 2.0.0.0"
65 # ruby-1.9.x以降ではファイルを開いた際、エンコードの指定を行わないとエラーの原因になります。
66 # ただしruby-1.8.6以前はエンコードの指定に対応していないため、独自メソッドを定義してファイルの入出力を行います。
68 # _arg[0]_ :: 入出力を行うファイルのパス
69 # _arg[1]_ :: モードの指定。例 : w:utf-8(書き込みモード・UTF-8エンコードでファイルを開く)
75 arg[1] = mode[/[^:]+/] if RUBY_VERSION < "1.8.7" && mode.include?(':')
76 rdonly_p = /\A[^:]*[wa+]/ !~ mode
78 rdonly_p = !(mode & (IO::WRONY | IO::RDWR))
81 f.flock(rdonly_p ? File::LOCK_SH : File::LOCK_EX)
105 # = Feed/Entryのスーパークラス
107 # 入出力用のメソッドなど、共通する機能を提供します
111 # _hash_ :: 値を格納したhash配列。superfeed.new(CGI::Session.new(new CGI).params)のようにして使う。
117 @paramlist.each do |key|
118 val = hash[key.to_sym] || hash[key.to_s]
121 val.gsub!(/\r\n|\r/, "\n")
122 @attr[key.to_sym] = CGI.escapeHTML(val)
125 @attr[key.to_sym] = ""
129 # 可視・不可視を示すハッシュキーを格納する配列です
132 # ハッシュキーの日本語説明を格納する配列です
137 attr_reader :attr, :paramlist, :name, :display
139 # 内部の@attrハッシュにアクセスする手段を提供するメソッドです
141 # AbstractEntry.attr[:title]にアクセスしたい場合はAbstractEntry.titleで可能になります。
142 # AbstractEntry.send("title")という方法でもアクセス可能です。
143 def method_missing(methname, *args)
144 methname = methname.to_s
146 if methname[-1] == ?=
148 raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" unless args.length == 1
150 methname = @paramlist.find{|par|par == methname}
151 return @attr[methname.to_sym] = args[0] if methname
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)
157 # attr上にキーがない値を入れようとした場合はNoMethodError
166 @attr[key.to_sym] = value
172 # Feedの基礎情報を保有するクラスです
173 class Feed < AbstractEntry
176 # _hash_ :: 値を格納したhash配列。feed.new(CGI::Session.new(new CGI).params)のようにして使います。
182 @paramlist = ["feedattr", "title", "subtitle", "self", "url", "updated", "feedid", "rights", "aname", "amail", "others"]
184 # AbstractEntryのinitializeメソッドを呼び出し、値を@attr配列に格納します
187 # 可視・不可視を示すハッシュキーを格納する配列です
188 @display = {"feedattr" => "none", "title" => "", "subtitle" => "",
189 "self" => "", "url" => "",
190 "updated" => "none", "feedid" => "",
191 "rights" => "", "aname" => "",
192 "amail" => "", "others" => "none"}
194 # デバッグモードの場合、全ての入力要素を表示します
196 @display.each do |key, val|
201 # ハッシュキーの日本語説明を格納する配列です
202 @name = {"feedattr" => "feedの保持している属性", "title" => "ウェブページのタイトル", "subtitle" => "ウェブページの簡単な説明",
203 "self" => "このXMLファイルのURL", "url" => "ウェブページのURL",
204 "updated" => "XMLファイルの最終更新日", "feedid" => "あなたのページ独自のID",
205 "rights" => "ページの著作権表記", "aname" => "ページの製作者",
206 "amail" => "ページ製作者のメールアドレス", "others" => "その他の要素"}
210 # Atom XMLファイルを読み込んで解析し、等価なFeedオブジェクトを返却するメソッドです
212 # _path_ :: Atom XMLファイルのパス
213 def self.readxml(path)
216 doc = REXML::Document.new(myopen(path, "r:utf-8"){|f|f.read})
222 doc.elements["feed"].attributes.each_attribute do |attr|
223 xmlattr.push("#{attr.to_string} ")
225 xml[:feedattr] = xmlattr.join("\n").gsub(/"/, "'")
227 # XML解析部分です。各element毎に判定を行います
228 doc.elements.each("feed") do |elm|
229 elm.elements.each { |child|
233 xml[:feedid] = child.text
234 when "title", "subtitle", "updated", "rights"
235 xml[child.name.to_sym] = child.text
237 child.elements.each do |gchild|
240 xml[:aname] = gchild.text
242 xml[:amail] = gchild.text
246 child.attributes.each do |k, v|
249 xml[:self] = child.attributes["href"]
250 elsif v == "alternate"
251 xml[:url] = child.attributes["href"]
258 # 上記判定以外の全要素は配列に格納します
259 others.push(child.to_s)
268 xml[:others] = others.join("\n")
273 # 内部に保持している情報を、Atom Feed1.0形式の文字列に出力するメソッドです
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>")
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] != ""
292 return buf.join("\n")
296 def self.update(path, feed)
297 entrylist = Entry.readxml(path)
298 Feed.to_xml(path, feed, entrylist)
305 # _feed_ :: Feedオブジェクト
306 # _entry_ :: Entryオブジェクトの配列
307 def self.to_xml(path, feed, entrylist_tmp)
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
318 buf.push("#{entry.to_s}\n")
322 myopen(path, "w") do |f|
323 f.print buf.join("\n")
329 # _feed_ :: Feedオブジェクト
330 # _entry_ :: Entryオブジェクトの配列
331 def self.to_xml_plain(path, feed, entrylist)
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")
340 myopen(path, "w") do |f|
341 f.print buf.join("\n")
349 # Entryの基礎情報を保有するクラスです
350 class Entry < AbstractEntry
353 # _hash_ :: 値を格納したhash配列。entry.new(CGI::Session.new(new CGI).params)のようにして使います。
359 @paramlist = ["entryid", "title", "summary", "published", "updated", "url", "content", "others"]
361 # AbstractEntryのinitializeメソッドを呼び出し、値を@attr配列に格納します
364 # 可視・不可視を示すハッシュキーを格納する配列です
365 @display = {"entryid" => "none", "title" => "",
366 "summary" => "", "published" => "none",
367 "updated" => "none", "url" => "",
368 "content" => "", "others"=>"none"}
370 # デバッグモードの場合、全ての入力要素を表示します
372 @display.each do |key, val|
377 # ハッシュキーの日本語説明を格納する配列です
378 @name = {"entryid" => "記事固有のID", "title" => "記事のタイトル",
379 "summary" => "記事の簡単な説明", "published" => "記事の出版時刻",
380 "updated" => "記事の更新時刻", "url" => "記事へのURLアドレス",
381 "content" => "記事の本文", "others"=>"その他の項目"}
384 # Atom XMLファイルを読み込んで解析し、等価なEntryオブジェクト配列を返却するメソッドです
386 # _path_ :: Atom XMLファイルのパス
387 def self.readxml(path)
390 doc = REXML::Document.new(myopen(path, "r:utf-8"){|f|f.read})
394 # XML解析部分です。各element毎に判定を行います
395 doc.elements.each("feed/entry") do |elm|
398 elm.elements.each do |child|
402 xml[:entryid] = child.text
404 xml[:url] = child.attributes["href"]
405 when "title", "summary", "summary", "published", "updated", "content"
406 xml[child.name.to_sym] = child.text
408 # 上記判定以外の全要素は配列に格納します
409 others.push(child.to_s)
415 xml[:others] = others.join("\n")
416 entrylist.push(Entry.new(xml))
422 # Atom XMLファイルを読み込んで解析し、テンプレートファイルにしたがってHTMLに変換するメソッドです
423 def self.to_html(xmlpath, destpath, entry_temppath, html_temppath)
425 if xmlpath.empty? or destpath.empty? or entry_temppath.empty? or html_temppath.empty?
430 unless File.exist?(xmlpath) and File.exist?(entry_temppath) and File.exist?(html_temppath)
435 entrylist = Entry.readxml(xmlpath)
440 body << e.to_template(entry_temppath)
444 html_temp = HtmlWriter.new(html_temppath, binding)
447 myopen(destpath, 'w:utf-8') { |f|
448 f.write(CGI.pretty(html_temp.to_code))
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])
462 def self.insert(path, entry)
463 feed = Feed.readxml(path)
464 entrylist = Entry.readxml(path)
465 entrylist.unshift entry
467 Feed.to_xml(path, feed, entrylist)
473 def self.update(path, entry)
474 feed = Feed.readxml(path)
475 entrylist = Entry.readxml(path)
478 entrylist.each_with_index { |e, i|
479 if e.entryid == entry.entryid
484 Feed.to_xml(path, feed, entrylist) if successed
490 def self.select(path, entryid)
491 feed = Feed.readxml(path)
492 entrylist = Entry.readxml(path)
495 entrylist.each_with_index { |e, i|
496 entry = entrylist[i].dup if e.entryid == entryid
503 def self.delete(path, entryid)
504 feed = Feed.readxml(path)
505 entrylist = Entry.readxml(path)
509 entrylist.each_with_index { |e, i|
510 if e.entryid == entryid
517 entrylist.delete_at delete_index
518 Feed.to_xml(path, feed, entrylist)
524 # データソースから読み取ったHTMLを、エディタで編集可能な形式に変換するメソッドです
525 def content_for_generator
526 str = @attr[:content].dup
528 str.gsub!(/(<\/(?:p|h\d|div)(?:>|>))\n/i, '\1')
529 str.gsub!(/\n/, '<br>') if REPLACEBRTAG
530 str.gsub!(/(<(?:(?!>).)*?)#{Regexp.escape(RELAYPATH)}/) { "#$1#{HOMEBASE}" }
534 # エディタで編集されたCONTENT要素を、データソースに書き込める形式に変換するメソッドです
536 str = @attr[:content].dup
537 str = CGI.unescapeHTML(str)
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}" }
546 # 確認画面で表示されるCONTENT要素を生成するメソッドです
548 str = @attr[:content].dup
549 str = CGI.unescapeHTML(str)
551 str.gsub!(/<br>|<br[ ]*\/>/i, "\n") if REPLACEBRTAG
552 str.gsub!(/(<[^>]*?)#{Regexp.escape(RELAYPATH)}/) { "#$1#{HOMEBASE}" }
556 # 内部に保持している情報を、Atom Feed1.0形式の文字列に出力するメソッドです
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] != ""
570 return buf.join("\n")
576 # テンプレートファイル(*.erb)を読み込み、管理するクラスです
580 # _template_ :: テンプレートファイル(*.erb)のパス
581 # _binding_ :: binding変数
582 def initialize(template, binding)
583 @erb = ERB.new(myopen(template, "r:utf-8") {|f| f.read}, nil, "-")
587 # テンプレートファイルの文字列を返却するメソッドです
589 @erb.result(@binding)
595 # loglist.xmlにかかわる入出力を行います
599 # _display_ :: 画面表示用文字の配列
600 # _path_ :: XMLファイルパスの配列
601 # _logpath_ :: loglist.xmlのパス
602 def initialize(display, path, logpath)
608 attr_accessor :display, :path, :logpath
610 # loglist.xmlファイルを読み込んで解析し、LogListオブジェクトを返却するメソッドです
612 # _logpath_ :: loglist.xmlへのパス
613 def LogList.readxml(logpath)
617 myopen(logpath, "r:utf-8") { |f|
621 doc = REXML::Document.new(lines.join("\n"))
625 doc.elements.each("list/file") { |elm|
626 elm.elements.each {|child|
629 @display.push(child.text)
631 @path.push(child.text)
638 return LogList.new(@display, @path, logpath)
641 # loglist.xmlにオブジェクトの内容を反映するメソッドです
645 path.each_with_index do |path, i|
647 buf.push("<display>#{@display[i]}</display>")
648 buf.push("<path>#{@path[i]}</path>")
653 myopen(@logpath, "w") do |f|
654 f.print buf.join("\n")
668 # ファイルの一覧を取得し、ファイル名称を格納した配列を返却します
670 arr = Dir::entries(XMLPATH).sort
679 # _name_ :: 消去するファイル名
681 File.delete(XMLPATH + name.match(/[^\/]*?$/).to_a[0])
686 # _name_ :: アップロードファイルの名称
687 # _img_ :: アップロードファイル
688 def upload(name, img)
689 open(XMLPATH + File.basename(name), "w") do |f|
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)
716 # コントローラ部分に相当する処理を受け持つクラスです
718 def Controller.NormalForm(cgi, session, params, db)
720 # modeとactionの相関図は以下のようになります。
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"]))
737 case params["action"]
740 session["target_filepath"] = params["target_filepath"]
741 db["newentry"] = Entry.new(params)
742 db["newentry"].content = db["newentry"].content_for_blog
745 session["target_filepath"] = params["target_filepath"]
746 successed = Entry.insert(XMLPATH + File.basename(params["target_filepath"]), Entry.new(params))
748 db["error"] = "日記の新規追加に失敗しました。"
749 params["mode"] = "error"
752 FeedGenPluginManager.exec("newentry", db["feed"], db["entry"])
756 session["target_filepath"] = params["target_filepath"]
757 db["newentry"] = Entry.new(params)
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"]))
767 case params["action"]
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"])
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
781 session["target_filepath"] = params["target_filepath"]
782 successed = Entry.update(XMLPATH + File.basename(params["target_filepath"]), Entry.new(params))
784 db["error"] = "日記の編集処理に失敗しました。<br>該当の日記が既に存在しない可能性があります。"
785 params["mode"] = "error"
788 FeedGenPluginManager.exec("editentry", db["feed"], db["entry"])
791 session["target_filepath"] = params["target_filepath"]
792 db["editentry"] = Entry.new(params)
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"]))
802 case params["action"]
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"])
810 session["target_filepath"] = params["target_filepath"]
811 successed = Entry.delete(XMLPATH + File.basename(params["target_filepath"]), cgi["delid"].to_s)
813 db["error"] = "日記の編集処理に失敗しました。<br>該当の日記が既に存在しない可能性があります。"
814 params["mode"] = "error"
817 FeedGenPluginManager.exec("delentry", db["feed"], db["entry"])
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"]))
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"]))
832 case params["action"]
835 session["target_filepath"] = params["target_filepath"]
836 db["feed"] = Feed.new(params)
837 # 実際にFeed情報の変更をファイルに反映
839 session["target_filepath"] = params["target_filepath"]
840 db["feed"] = Feed.new(params)
842 session["target_filepath"] = params["target_filepath"]
843 Feed.update(XMLPATH + File.basename(params["target_filepath"]), Feed.new(params))
845 session["target_filepath"] = session["filepath"]
846 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
852 db["loglist"] = LogList.readxml(LISTXMLPATH)
853 case params["action"]
854 # ログファイルの編集を実際にファイルに反映
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>環境を見直してください。"
868 db["loglist"].path[1, 0] = cgi["logpath"]
869 db["loglist"].display[1, 0] = cgi["logdisplay"]
871 # 既存のdiary.xmlを指定された名称でコピーして保存
872 FileUtils.copy_file(XMLPATH + File.basename(db["loglist"].path[0]), XMLPATH + File.basename(db["logpath"]), true)
874 db["loglist"] = LogList.readxml(LISTXMLPATH)
876 # 新たなdiary.xmlを生成。この際保持する情報は同一のFeedオブジェクトnew()
877 Feed.to_xml(XMLPATH + File.basename(db["loglist"].path[0]), db["feed"], [])
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>"
890 db["logpath"] = cgi["logpath"]
891 db["logdisplay"] = cgi["logdisplay"]
893 if db["logpath"].blank? || db["logdisplay"].blank?
894 params["action"] = ""
900 db["logdelindex"] = params["logdelindex"].to_i
902 if db["logdelindex"] < 1
903 db["error"] = "<span style='color: #ff0000'>ログファイルの削除パラメタが不正です。</span><br>"
904 params["action"] = ""
908 if cgi["logdelindex"].to_i < 1
909 params["action"] = ""
910 params["mode"] = "error"
911 db["error"] = "ログファイルの削除中に重大なエラーが発生しました。<br>環境を見直してください。"
915 File.delete(XMLPATH + File.basename(db["loglist"].path[db["logdelindex"]]))
917 db["loglist"].path.delete_at(cgi["logdelindex"].to_i)
918 db["loglist"].display.delete_at(cgi["logdelindex"].to_i)
921 db["loglist"] = LogList.readxml(LISTXMLPATH)
927 db["logeditindex"] = params["logdelindex"].to_i
929 db["logpath"] = db["loglist"].path[db["logeditindex"]]
930 db["logdisplay"] = db["loglist"].display[db["logeditindex"]]
932 if db["logeditindex"] == 0
933 db["error"] = "<span style='color: #ff0000'>ログファイルの編集パラメタが不正です。</span><br>"
934 params["action"] = ""
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
947 if checkflag == false
948 params["action"] = "edit"
949 db["error"] = "<span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
951 db["loginsertindex"] = params["loginsertindex"].to_i
953 db["logpath"] = params["logpath"].to_s
954 db["logdisplay"] = params["logdisplay"].to_s
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
967 if checkflag == false
968 params["action"] = ""
969 params["mode"] = "error"
970 db["error"] = "ログファイルの編集中に重大なエラーが発生しました。<br>環境を見直してください。"
973 db["loginsertindex"] = params["loginsertindex"].to_i
975 db["logpath"] = params["logpath"].to_s
976 db["logdisplay"] = params["logdisplay"].to_s
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"]))
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"])
992 # 現在編集中のデータを強制的に最上のファイルパスに変更
993 session["filepath"] = db["loglist"].path[0]
996 prevmonth = (DateTime.now << 1)
998 # 前月の時刻を元に、ディフォルト書式を生成します
999 db["logpath"] = FEEDXMLDIR + prevmonth.strftime("%Y%m") + ".xml"
1000 db["logdisplay"] = prevmonth.strftime("%Y年%m月").gsub("年0", "年")
1005 db["loglist"] = LogList.readxml(LISTXMLPATH)
1009 case params["action"]
1012 file = FileUploader.new
1013 file.filelist().each { |fname|
1014 file.delete(fname) if File.ftype(XMLPATH + fname) == "file"
1019 loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}diary.xml"], LISTXMLPATH)
1022 db["loglist"] = LogList.readxml(LISTXMLPATH)
1025 Feed.to_xml(XMLPATH + db["loglist"].path[0].match(/[^\/]*?$/).to_a[0], Feed.new({}), [])
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"]))
1034 params["action"] = ""
1038 filelist = FileUploader.new.filelist()
1040 # タイプがファイルのみリストとして取得する
1042 filelist.each { |fname| db["filelist"] << fname if File.ftype(XMLPATH + fname) == "file"}
1047 # loglist.xmlが存在するかチェック
1048 if File.exist?(LISTXMLPATH) == false
1049 # なかった場合はloglist.xmlを自動生成
1050 loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}diary.xml"], LISTXMLPATH)
1052 Feed.to_xml(XMLPATH + File.basename(loglist.path[0]), Feed.new({}), [])
1055 db["loglist"] = LogList.readxml(LISTXMLPATH)
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({}), [])
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"]))
1071 # マルチパートフォームの場合の処理です
1072 def Controller.MultiForm(cgi, session, params, db)
1074 # loglist.xmlをロードします
1075 db["loglist"] = LogList.readxml(LISTXMLPATH)
1079 case params["action"]
1081 # この段階のエラーに対しては強度のエラーを発する
1083 db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
1084 db["loglist"].path.each do |val|
1085 if val == db["logpath"]
1086 # 重複していた場合エラーメッセージを表示し、処理を行わない
1088 params["action"] = ""
1089 params["mode"] = "error"
1090 db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1095 db["loginsertindex"] = cgi["loginsertindex"].read.to_i
1096 db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1099 if db["loginsertindex"] == 0
1100 params["action"] = ""
1101 params["mode"] = "error"
1102 db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1106 if File.basename(db["logpath"]).blank? || db["logdisplay"].blank?
1107 params["action"] = ""
1108 params["mode"] = "error"
1109 db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1114 file = FileUploader.new
1115 file.upload(db["logpath"], db["importxml"])
1118 db["loglist"].path[db["loginsertindex"], 0] = db["logpath"]
1119 db["loglist"].display[db["loginsertindex"], 0] = db["logdisplay"]
1120 db["loglist"].to_xml
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"]))
1129 # 入力されたログファイルパスが既に存在するかを確認
1131 db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
1132 db["loglist"].path.each do |val|
1133 if val == db["logpath"]
1134 # 重複していた場合エラーメッセージを表示し、処理を行わない
1136 params["action"] = ""
1137 db["error"] = "<br><span style='color: #ff0000'>同一のファイルが存在します!別の名前を指定してください。</span><br>"
1144 db["loginsertindex"] = cgi["loginsertindex"].read.to_i
1145 db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1146 db["importxml"] = cgi["updata"].read
1150 REXML::Document.new(Controller.fix_updata_enc(db["importxml"].to_s))
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"] = ""
1162 if db["loginsertindex"] == 0
1163 params["action"] = ""
1164 db["error"] = "<br><span style='color: #ff0000'>ラジオボックスでログの挿入位置を選択してください!</span><br>"
1168 if db["logpath"] == FEEDXMLDIR || db["logdisplay"].blank?
1169 params["action"] = ""
1170 db["error"] = "<br><span style='color: #ff0000'>インポートファイル、及び、ログの表示名は空欄にできません。</span><br>"
1179 case params["action"]
1181 # この段階のエラーに対しては強度のエラーを発する
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"] = "ログファイルの追加中に重大なエラーが発生しました。"
1194 db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1196 if File.basename(db["logpath"]).blank? || db["logdisplay"].blank? || db["importxml"].blank?
1197 params["action"] = ""
1198 params["mode"] = "error"
1199 db["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
1204 FileUtils.move(XMLPATH + "diary.xml", XMLPATH + File.basename(db["logpath"]))
1206 file = FileUploader.new
1207 file.upload("diary.xml", db["importxml"])
1210 db["loglist"].path[1, 0] = db["logpath"]
1211 db["loglist"].display[1, 0] = db["logdisplay"]
1212 db["loglist"].to_xml
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"]))
1221 # 入力されたログファイルパスが既に存在するかを確認
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>"
1234 if checkflag == true
1235 db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
1236 db["importxml"] = cgi["updata"].read
1240 REXML::Document.new(Controller.fix_updata_enc(db["importxml"].to_s))
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"] = ""
1251 if db["logpath"].blank? || db["logdisplay"].blank? || db["importxml"].blank?
1252 params["action"] = ""
1253 db["error"] = "<br><span style='color: #ff0000'>インポートファイル、及び、ログの表示名、ログのパスは空欄にできません。</span><br>"
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
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")
1286 # SESSION変数、パラメータなどを取得します
1288 session = CGI::Session.new(cgi)
1289 params = Hash[*cgi.params.to_a.map{|k, v| [k, v[0].to_s]}.flatten]
1293 if session["login"] != "true"
1294 if (cgi["loginid"] == LOGINID && cgi["password"] == PASSWORD)
1295 session["login"] = "true"
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"
1307 if params["mode"] == "logout"
1308 session["login"] = nil
1309 session["logini"] = nil
1310 session["password"] = nil
1316 # セッションが有効な場合のも実行します
1317 if session["login"] == "true"
1320 db = PStore.new("./work/#{session.session_id}.dat")
1326 File.delete("./work/#{session.session_id}.dat")
1327 # PStoreが破損していた場合はセッション情報を破棄する
1328 session["login"] = nil
1329 session["logini"] = nil
1330 session["password"] = nil
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)
1340 Controller.NormalForm(cgi, session, params, db)
1343 # エラー画面移行時はセッション情報を破棄する
1344 if params["mode"] == "error"
1345 session["login"] = nil
1346 session["logini"] = nil
1347 session["password"] = nil
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"]} "
1356 menu = "<div class=\"divstyle\" style=\"width: #{TABLEWIDTH}px;\"><div class=\"divstyle\" align=\"center\" style=\"margin-bottom: 5px;\">現在編集中のファイル : #{session["target_filepath"]} "
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>"
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>"
1365 # modeとactionの相関図は以下のようになります。
1370 if session["login"] != "true"
1371 # セッションが存在しない場合は強制的にトップページに遷移
1372 htmlwriter = HtmlWriter.new("./erbtemp/login.html.erb", binding)
1376 htmlwriter = HtmlWriter.new("./erbtemp/select.html.erb", binding)
1378 htmlwriter = HtmlWriter.new("./erbtemp/newentry.html.erb", binding)
1380 htmlwriter = HtmlWriter.new("./erbtemp/editentry.html.erb", binding)
1382 htmlwriter = HtmlWriter.new("./erbtemp/delentry.html.erb", binding)
1384 htmlwriter = HtmlWriter.new("./erbtemp/editfeed.html.erb", binding)
1386 htmlwriter = HtmlWriter.new("./erbtemp/log.html.erb", binding)
1388 htmlwriter = HtmlWriter.new("./erbtemp/insertfeed.html.erb", binding)
1390 htmlwriter = HtmlWriter.new("./erbtemp/replacefeed.html.erb", binding)
1392 htmlwriter = HtmlWriter.new("./erbtemp/indeximport.html.erb", binding)
1394 htmlwriter = HtmlWriter.new("./erbtemp/reset.html.erb", binding)
1396 htmlwriter = HtmlWriter.new("./erbtemp/error.html.erb", binding)
1398 htmlwriter = HtmlWriter.new("./erbtemp/index.html.erb", binding)
1402 # エラーが発生した場合、それを画面に表示します
1403 htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
1408 cgi.out{htmlwriter.to_code}
1410 # エラーが発生した場合、それを画面に表示します
1411 htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
1412 cgi.out{htmlwriter.to_code}
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