1 #!/usr/local/bin/ruby
\r
2 # -*- coding: utf-8 -*-
\r
4 #= Atom Feed 1.0を管理するWEBアプリケーション
\r
6 #Autohr:: Kureha Hisame (http://lunardial.sakura.ne.jp/) & Yui Naruse (http://airemix.com/)
\r
8 #Copyright:: Copyright 2009 FeedBlog Project (http://sourceforge.jp/projects/feedblog/)
\r
12 require "cgi/session"
\r
14 require "rexml/document"
\r
20 require "./common.rb"
\r
21 require "./define.rb"
\r
23 # = Feed/Entryのスーパークラス
\r
25 # 入出力用のメソッドなど、共通する機能を提供します
\r
29 # _hash_ :: 値を格納したhash配列。superfeed.new(CGI::Session.new(new CGI).params)のようにして使う。
\r
30 def initialize(hash)
\r
35 @paramlist.each do |key|
\r
36 val = hash[key.to_sym] || hash[key.to_s]
\r
39 val.gsub!(/\r\n|\r/, "\n")
\r
40 @attr[key.to_sym] = CGI.escapeHTML(val)
\r
43 @attr[key.to_sym] = ""
\r
47 # 可視・不可視を示すハッシュキーを格納する配列です
\r
50 # ハッシュキーの日本語説明を格納する配列です
\r
55 attr_reader :attr, :paramlist, :name, :display
\r
57 # 内部の@attrハッシュにアクセスする手段を提供するメソッドです
\r
59 # AbstractEntry.attr[:title]にアクセスしたい場合はAbstractEntry.titleで可能になります。
\r
60 # AbstractEntry.send("title")という方法でもアクセス可能です。
\r
61 def method_missing(methname, *args)
\r
62 methname = methname.to_s
\r
64 if methname[-1] == ?=
\r
66 raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" unless args.length == 1
\r
68 methname = @paramlist.find{|par|par == methname}
\r
69 return @attr[methname.to_sym] = args[0] if methname
\r
72 raise ArgumentError, "wrong number of arguments (#{args.length} for 0)" unless args.empty?
\r
73 return @attr[methname.to_sym] if @attr.key?(methname.to_sym)
\r
75 # attr上にキーがない値を入れようとした場合はNoMethodError
\r
84 @attr[key.to_sym] = value
\r
90 # Feedの基礎情報を保有するクラスです
\r
91 class Feed < AbstractEntry
\r
94 # _hash_ :: 値を格納したhash配列。feed.new(CGI::Session.new(new CGI).params)のようにして使います。
\r
95 def initialize(hash)
\r
99 # 内部データの項目名を格納する配列です
\r
100 @paramlist = ["feedattr", "title", "subtitle", "self", "url", "updated", "feedid", "rights", "aname", "amail", "others"]
\r
102 # AbstractEntryのinitializeメソッドを呼び出し、値を@attr配列に格納します
\r
105 # 可視・不可視を示すハッシュキーを格納する配列です
\r
106 @display = {"feedattr" => "none", "title" => "", "subtitle" => "",
\r
107 "self" => "", "url" => "",
\r
108 "updated" => "none", "feedid" => "",
\r
109 "rights" => "", "aname" => "",
\r
110 "amail" => "", "others" => "none"}
\r
112 # デバッグモードの場合、全ての入力要素を表示します
\r
114 @display.each do |key, val|
\r
119 # ハッシュキーの日本語説明を格納する配列です
\r
120 @name = {"feedattr" => "feedの保持している属性", "title" => "ウェブページのタイトル", "subtitle" => "ウェブページの簡単な説明",
\r
121 "self" => "このXMLファイルのURL", "url" => "ウェブページのURL",
\r
122 "updated" => "XMLファイルの最終更新日", "feedid" => "あなたのページ独自のID",
\r
123 "rights" => "ページの著作権表記", "aname" => "ページの製作者",
\r
124 "amail" => "ページ製作者のメールアドレス", "others" => "その他の要素"}
\r
128 # Atom XMLファイルを読み込んで解析し、等価なFeedオブジェクトを返却するメソッドです
\r
130 # _path_ :: Atom XMLファイルのパス
\r
131 def self.readxml(path)
\r
134 doc = REXML::Document.new(myopen(path, "r:utf-8"){|f|f.read})
\r
140 doc.elements["feed"].attributes.each_attribute do |attr|
\r
141 xmlattr.push("#{attr.to_string} ")
\r
143 xml[:feedattr] = xmlattr.join("\n").gsub(/"/, "'")
\r
145 # XML解析部分です。各element毎に判定を行います
\r
146 doc.elements.each("feed") do |elm|
\r
147 elm.elements.each { |child|
\r
151 xml[:feedid] = child.text
\r
152 when "title", "subtitle", "updated", "rights"
\r
153 xml[child.name.to_sym] = child.text
\r
155 child.elements.each do |gchild|
\r
158 xml[:aname] = gchild.text
\r
160 xml[:amail] = gchild.text
\r
164 child.attributes.each do |k, v|
\r
167 xml[:self] = child.attributes["href"]
\r
168 elsif v == "alternate"
\r
169 xml[:url] = child.attributes["href"]
\r
176 # 上記判定以外の全要素は配列に格納します
\r
177 others.push(child.to_s)
\r
179 rescue NoMethodError
\r
185 # Others要素を結合して代入します
\r
186 xml[:others] = others.join("\n")
\r
187 feed = Feed.new(xml)
\r
191 # 内部に保持している情報を、Atom Feed1.0形式の文字列に出力するメソッドです
\r
195 # buf.push("<feed #{@attr[:feedattr]}>")
\r
196 buf.push("<feed xml:lang='ja-jp' xmlns='http://www.w3.org/2005/Atom'>");
\r
197 buf.push("<title type=\"text\">#{@attr[:title]}</title>")
\r
198 buf.push("<subtitle type=\"text\">#{@attr[:subtitle]}</subtitle>")
\r
199 buf.push("<link rel=\"self\" type=\"application/atom+xml\" href=\"#{@attr[:self]}\" />")
\r
200 buf.push("<link rel=\"alternate\" type=\"text/html\" href=\"#{@attr[:url]}\" />")
\r
201 buf.push("<updated>#{Time.now.iso8601}</updated>")
\r
202 buf.push("<id>#{@attr[:feedid]}</id>")
\r
203 buf.push("<rights type=\"text\">#{@attr[:rights]}</rights>")
\r
204 buf.push("<author>")
\r
205 buf.push("\t<name>#{@attr[:aname]}</name>")
\r
206 buf.push("\t<email>#{@attr[:amail]}</email>")
\r
207 buf.push("</author>")
\r
208 buf.push("#{CGI.unescapeHTML(@attr[:others])}") if @attr[:others] != ""
\r
210 return buf.join("\n")
\r
214 def self.update(path, feed)
\r
215 entrylist = Entry.readxml(path)
\r
216 Feed.to_xml(path, feed, entrylist)
\r
223 # _feed_ :: Feedオブジェクト
\r
224 # _entry_ :: Entryオブジェクトの配列
\r
225 def self.to_xml(path, feed, entrylist_tmp)
\r
227 entrylist = entrylist_tmp.dup
\r
228 buf.push("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
\r
229 buf.push("#{feed.to_s}\n")
\r
230 entrylist.each { |entry|
\r
231 if REPLACEENTRYIDANDURL
\r
232 entry.entryid.gsub!(/^[^\?]*\?/, "")
\r
233 entry.entryid = feed.url + "?" + entry.entryid
\r
234 entry.url = feed.url + "#" + entry.entryid
\r
236 buf.push("#{entry.to_s}\n")
\r
238 buf.push("</feed>")
\r
240 myopen(path, "w") do |f|
\r
241 f.print buf.join("\n")
\r
247 # _feed_ :: Feedオブジェクト
\r
248 # _entry_ :: Entryオブジェクトの配列
\r
249 def self.to_xml_plain(path, feed, entrylist)
\r
251 buf.push("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
\r
252 buf.push("#{feed.to_s}\n")
\r
253 entrylist.each { |entry|
\r
254 buf.push("#{entry.to_s}\n")
\r
256 buf.push("</feed>")
\r
258 myopen(path, "w") do |f|
\r
259 f.print buf.join("\n")
\r
267 # Entryの基礎情報を保有するクラスです
\r
268 class Entry < AbstractEntry
\r
271 # _hash_ :: 値を格納したhash配列。entry.new(CGI::Session.new(new CGI).params)のようにして使います。
\r
272 def initialize(hash)
\r
273 # 内部データ保持用のハッシュ配列です
\r
276 # 内部データの項目名を格納する配列です
\r
277 @paramlist = ["entryid", "title", "summary", "published", "updated", "url", "content", "category", "others"]
\r
279 # AbstractEntryのinitializeメソッドを呼び出し、値を@attr配列に格納します
\r
282 # 可視・不可視を示すハッシュキーを格納する配列です
\r
283 @display = {"entryid" => "none", "title" => "",
\r
284 "summary" => "", "published" => "none",
\r
285 "updated" => "none", "url" => "",
\r
286 "content" => "", "category" => "",
\r
289 # デバッグモードの場合、全ての入力要素を表示します
\r
291 @display.each do |key, val|
\r
296 # ハッシュキーの日本語説明を格納する配列です
\r
297 @name = {"entryid" => "記事固有のID", "title" => "記事のタイトル",
\r
298 "summary" => "記事の簡単な説明", "published" => "記事の出版時刻",
\r
299 "updated" => "記事の更新時刻", "url" => "記事へのURLアドレス",
\r
300 "content" => "記事の本文", "category" => "タグ", "others"=>"その他の項目"}
\r
303 # Atom XMLファイルを読み込んで解析し、等価なEntryオブジェクト配列を返却するメソッドです
\r
305 # _path_ :: Atom XMLファイルのパス
\r
306 def self.readxml(path)
\r
309 doc = REXML::Document.new(myopen(path, "r:utf-8"){|f|f.read})
\r
313 # XML解析部分です。各element毎に判定を行います
\r
314 doc.elements.each("feed/entry") do |elm|
\r
318 elm.elements.each do |child|
\r
322 xml[:entryid] = child.text
\r
324 xml[:url] = child.attributes["href"]
\r
325 when "title", "summary", "summary", "published", "updated", "content"
\r
326 xml[child.name.to_sym] = child.text
\r
328 category << child.to_s
\r
330 # 上記判定以外の全要素は配列に格納します
\r
331 others.push(child.to_s)
\r
333 rescue NoMethodError
\r
337 xml[:category] = category.join("\n")
\r
338 # Others要素を結合して代入します
\r
339 xml[:others] = others.join("\n")
\r
340 entrylist.push(Entry.new(xml))
\r
346 # Atom XMLファイルを読み込んで解析し、テンプレートファイルにしたがってHTMLに変換するメソッドです
\r
347 def self.to_html(xmlpath, destpath, entry_temppath, html_temppath)
\r
349 if xmlpath.empty? or destpath.empty? or entry_temppath.empty? or html_temppath.empty?
\r
350 raise ArgumentError
\r
354 unless File.exist?(xmlpath) and File.exist?(entry_temppath) and File.exist?(html_temppath)
\r
359 entrylist = Entry.readxml(xmlpath)
\r
362 entrylist.each { |e|
\r
363 # Entry毎のHTML表示部分を生成
\r
364 body << e.to_template(entry_temppath)
\r
368 html_temp = HtmlWriter.new(html_temppath, binding)
\r
371 myopen(destpath, 'w:utf-8') { |f|
\r
372 f.write(CGI.pretty(html_temp.to_code))
\r
376 # Entryをテンプレートに沿って変形するメソッド
\r
377 def to_template(temppath)
\r
378 erb = HtmlWriter.new(temppath, binding)
\r
379 title = CGI.unescapeHTML(@attr[:title])
\r
380 date = @attr[:published]
\r
381 content = CGI.unescapeHTML(@attr[:content])
\r
386 def self.insert(path, entry)
\r
387 feed = Feed.readxml(path)
\r
388 entrylist = Entry.readxml(path)
\r
389 entrylist.unshift entry
\r
391 Feed.to_xml(path, feed, entrylist)
\r
397 def self.update(path, entry)
\r
398 feed = Feed.readxml(path)
\r
399 entrylist = Entry.readxml(path)
\r
402 entrylist.each_with_index { |e, i|
\r
403 if e.entryid == entry.entryid
\r
404 entrylist[i] = entry
\r
408 Feed.to_xml(path, feed, entrylist) if successed
\r
414 def self.select(path, entryid)
\r
415 feed = Feed.readxml(path)
\r
416 entrylist = Entry.readxml(path)
\r
419 entrylist.each_with_index { |e, i|
\r
420 entry = entrylist[i].dup if e.entryid == entryid
\r
427 def self.delete(path, entryid)
\r
428 feed = Feed.readxml(path)
\r
429 entrylist = Entry.readxml(path)
\r
433 entrylist.each_with_index { |e, i|
\r
434 if e.entryid == entryid
\r
441 entrylist.delete_at delete_index
\r
442 Feed.to_xml(path, feed, entrylist)
\r
448 # データソースから読み取ったHTMLを、エディタで編集可能な形式に変換するメソッドです
\r
449 def content_for_generator
\r
450 str = @attr[:content].dup
\r
452 str.gsub!(/(<\/(?:p|h\d|div)(?:>|>))\n/i, '\1')
\r
453 str.gsub!(/\n/, '<br>') if REPLACEBRTAG
\r
454 str.gsub!(/(<(?:(?!>).)*?)#{Regexp.escape(RELAYPATH)}/) { "#$1#{HOMEBASE}" }
\r
458 # エディタで編集されたCONTENT要素を、データソースに書き込める形式に変換するメソッドです
\r
459 def content_for_blog
\r
460 str = @attr[:content].dup
\r
461 str = CGI.unescapeHTML(str)
\r
463 str.gsub!(/(\r\n|\n)/, "")
\r
464 str.gsub!(/<br>|<br[ ]*\/>/i, "\n") if REPLACEBRTAG
\r
465 str.gsub!(/(<br>|<br[ ]*\/>|<\/p>|<\/h\d>|<\/div>)(?=[^\n])/i) { "#$1\n" } unless REPLACEBRTAG
\r
466 str.gsub!(/(<[^>]*?)#{Regexp.escape(HOMEBASE)}/) { "#$1#{RELAYPATH}" }
\r
467 CGI.escapeHTML(str)
\r
470 # 確認画面で表示されるCONTENT要素を生成するメソッドです
\r
471 def content_for_view
\r
472 str = @attr[:content].dup
\r
473 str = CGI.unescapeHTML(str)
\r
475 str.gsub!(/<br>|<br[ ]*\/>/i, "\n") if REPLACEBRTAG
\r
476 str.gsub!(/(<[^>]*?)#{Regexp.escape(RELAYPATH)}/) { "#$1#{HOMEBASE}" }
\r
480 # 内部に保持している情報を、Atom Feed1.0形式の文字列に出力するメソッドです
\r
483 buf.push("<entry>")
\r
484 buf.push("<id>#{@attr[:entryid]}</id>")
\r
485 buf.push("<title>#{@attr[:title]}</title>")
\r
486 buf.push("<summary>#{@attr[:summary]}</summary>")
\r
487 buf.push("<published>#{@attr[:published]}</published>")
\r
488 buf.push("<updated>#{@attr[:updated]}</updated>")
\r
489 buf.push("<link href=\"#{@attr[:url]}\" />")
\r
490 buf.push("<content type=\"html\">#{@attr[:content]}</content>")
\r
491 buf.push("#{CGI.unescapeHTML(@attr[:category])}") if @attr[:category] != ""
\r
492 buf.push("#{CGI.unescapeHTML(@attr[:others])}") if @attr[:others] != ""
\r
493 buf.push("</entry>")
\r
495 return buf.join("\n")
\r
501 # loglist.xmlにかかわる入出力を行います
\r
505 # _display_ :: 画面表示用文字の配列
\r
506 # _path_ :: XMLファイルパスの配列
\r
507 # _logpath_ :: loglist.xmlのパス
\r
508 def initialize(display, path, logpath)
\r
514 attr_accessor :display, :path, :logpath
\r
516 # loglist.xmlファイルを読み込んで解析し、LogListオブジェクトを返却するメソッドです
\r
518 # _logpath_ :: loglist.xmlへのパス
\r
519 def LogList.readxml(logpath)
\r
523 myopen(logpath, "r:utf-8") { |f|
\r
524 lines = f.readlines
\r
527 doc = REXML::Document.new(lines.join("\n"))
\r
531 doc.elements.each("list/file") { |elm|
\r
532 elm.elements.each {|child|
\r
535 @display.push(child.text)
\r
537 @path.push(child.text)
\r
544 return LogList.new(@display, @path, logpath)
\r
547 # loglist.xmlにオブジェクトの内容を反映するメソッドです
\r
551 path.each_with_index do |path, i|
\r
553 buf.push("<display>#{@display[i]}</display>")
\r
554 buf.push("<path>#{@path[i]}</path>")
\r
555 buf.push("</file>")
\r
557 buf.push("</list>")
\r
559 myopen(@logpath, "w") do |f|
\r
560 f.print buf.join("\n")
\r
566 # = FileUploaderクラス
\r
574 # ファイルの一覧を取得し、ファイル名称を格納した配列を返却します
\r
576 arr = Dir::entries(XMLPATH).sort
\r
585 # _name_ :: 消去するファイル名
\r
587 File.delete(XMLPATH + name.match(/[^\/]*?$/).to_a[0])
\r
590 # ファイルのアップロードを実行します
\r
592 # _name_ :: アップロードファイルの名称
\r
593 # _img_ :: アップロードファイル
\r
594 def upload(name, img)
\r
595 open(XMLPATH + File.basename(name), "w") do |f|
\r
605 class FeedGenPluginManager
\r
606 def self.exec(mode, filepath)
\r
607 feed = Feed.readxml(XMLPATH + filepath)
\r
608 entries = Entry.readxml(XMLPATH + filepath)
\r
611 Dir.foreach(PLUGINDIR) do |fn|
\r
612 next unless File.extname(fn) == '.rb'
\r
613 require File.join(PLUGINDIR, fn)
\r
614 plugin_name = "FeedGenPlugins::"
\r
615 plugin_name << File.basename(fn).gsub(/\.rb\Z/, "")
\r
616 plugin_ins = plugin_name.split(/::/).inject(Object) { |c,name| c.const_get(name) }
\r
617 plugin_ins.new.exec(mode, feed, entries)
\r
624 # コントローラ部分に相当する処理を受け持つクラスです
\r
626 def Controller.NormalForm(cgi, session, params, db)
\r
628 # modeとactionの相関図は以下のようになります。
\r
629 # [mode] + [action]
\r
633 case params["mode"]
\r
636 session["filepath"] = params["logpath"]
\r
637 db["loglist"] = LogList.readxml(LISTXMLPATH)
\r
638 # 初期状態で選択されるログは「loglist.xml」の最上に位置するログになります
\r
639 session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
\r
640 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
\r
641 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
\r
645 case params["action"]
\r
648 session["target_filepath"] = params["target_filepath"]
\r
649 db["newentry"] = Entry.new(params)
\r
650 db["newentry"].content = db["newentry"].content_for_blog
\r
653 session["target_filepath"] = params["target_filepath"]
\r
654 successed = Entry.insert(XMLPATH + File.basename(params["target_filepath"]), Entry.new(params))
\r
656 session["error"] = "記事の新規追加に失敗しました。"
\r
657 params["mode"] = "error"
\r
660 FeedGenPluginManager.exec("newentry", File.basename(session["filepath"]))
\r
661 session["info"] = "記事の新規作成が成功しました。"
\r
665 session["target_filepath"] = params["target_filepath"]
\r
666 db["newentry"] = Entry.new(params)
\r
668 # New Diary - Default
\r
669 session["target_filepath"] = session["filepath"]
\r
670 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
\r
671 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
\r
676 case params["action"]
\r
679 session["target_filepath"] = params["target_filepath"]
\r
680 session["editid"] = cgi["editid"].to_s
\r
681 db["editentry"] = Entry.select(XMLPATH + File.basename(session["target_filepath"]), session["editid"])
\r
684 session["target_filepath"] = params["target_filepath"]
\r
685 session["editid"] = cgi["editid"].to_s
\r
686 db["editentry"] = Entry.new(params)
\r
687 db["editentry"].content = db["editentry"].content_for_blog
\r
690 session["target_filepath"] = params["target_filepath"]
\r
691 successed = Entry.update(XMLPATH + File.basename(params["target_filepath"]), Entry.new(params))
\r
693 session["error"] = "記事の編集処理に失敗しました。該当の日記が既に存在しない可能性があります。"
\r
694 params["mode"] = "error"
\r
697 FeedGenPluginManager.exec("editentry", File.basename(session["filepath"]))
\r
698 session["info"] = "記事の編集が完了しました。"
\r
701 session["target_filepath"] = session["filepath"]
\r
702 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
\r
703 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
\r
706 session["target_filepath"] = params["target_filepath"]
\r
707 db["editentry"] = Entry.new(params)
\r
709 # Edit Diary - Default
\r
710 session["target_filepath"] = session["filepath"]
\r
711 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
\r
712 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
\r
717 case params["action"]
\r
720 session["target_filepath"] = params["target_filepath"]
\r
721 session["delid"] = cgi["delid"].to_s
\r
722 db["delentry"] = Entry.select(XMLPATH + File.basename(session["target_filepath"]), session["delid"])
\r
725 session["target_filepath"] = params["target_filepath"]
\r
726 successed = Entry.delete(XMLPATH + File.basename(params["target_filepath"]), cgi["delid"].to_s)
\r
728 session["error"] = "日記の編集処理に失敗しました。該当の日記が既に存在しない可能性があります。"
\r
729 params["mode"] = "error"
\r
732 FeedGenPluginManager.exec("delentry", File.basename(session["filepath"]))
\r
733 session["info"] = "記事の削除に成功しました。"
\r
736 session["target_filepath"] = session["filepath"]
\r
737 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
\r
738 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
\r
741 session["target_filepath"] = params["target_filepath"]
\r
742 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["target_filepath"]))
\r
743 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["target_filepath"]))
\r
745 # Delete Diary - Default
\r
746 session["target_filepath"] = session["filepath"]
\r
747 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
\r
748 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
\r
753 case params["action"]
\r
756 session["target_filepath"] = params["target_filepath"]
\r
757 db["feed"] = Feed.new(params)
\r
758 # 実際にFeed情報の変更をファイルに反映
\r
760 session["target_filepath"] = params["target_filepath"]
\r
761 db["feed"] = Feed.new(params)
\r
763 session["target_filepath"] = params["target_filepath"]
\r
764 Feed.update(XMLPATH + File.basename(params["target_filepath"]), Feed.new(params))
\r
765 session["info"] = "基本情報の更新が完了しました。"
\r
767 session["target_filepath"] = session["filepath"]
\r
768 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
\r
774 db["loglist"] = LogList.readxml(LISTXMLPATH)
\r
775 case params["action"]
\r
776 # ログファイルの編集を実際にファイルに反映
\r
778 # エラーチェック。この段階のエラーは強度のエラーを発する。
\r
779 db["loglist"].path.each do |val|
\r
780 if val == cgi["logpath"]
\r
781 # 重複していた場合エラーメッセージを表示し、処理を行わない
\r
782 params["action"] = ""
\r
783 params["mode"] = "error"
\r
784 session["error"] = "ログファイルの追加中に重大なエラーが発生しました。環境を見直してください。"
\r
789 # 入力された内容を保持する配列に追加
\r
790 db["loglist"].path[1, 0] = cgi["logpath"]
\r
791 db["loglist"].display[1, 0] = cgi["logdisplay"]
\r
792 db["loglist"].to_xml
\r
793 # 既存のdiary.xmlを指定された名称でコピーして保存
\r
794 FileUtils.copy_file(XMLPATH + File.basename(db["loglist"].path[0]), XMLPATH + File.basename(db["logpath"]), true)
\r
796 db["loglist"] = LogList.readxml(LISTXMLPATH)
\r
798 # 新たなdiary.xmlを生成。この際保持する情報は同一のFeedオブジェクトnew()
\r
799 Feed.to_xml(XMLPATH + File.basename(db["loglist"].path[0]), db["feed"], [])
\r
801 session["info"] = "ログファイルの追加が完了しました。"
\r
803 # 現在編集中のデータを強制的に最上のファイルパスに変更
\r
804 session["filepath"] = db["loglist"].path[0]
\r
807 prevmonth = (DateTime.now << 1)
\r
809 # 前月の時刻を元に、ディフォルト書式を生成します
\r
810 db["logpath"] = FEEDXMLDIR + prevmonth.strftime("%Y%m") + ".xml"
\r
811 db["logdisplay"] = prevmonth.strftime("%Y年%m月").gsub("年0", "年")
\r
815 # 入力されたログが既に存在するかを確認
\r
816 db["loglist"].path.each do |val|
\r
817 if val == cgi["logpath"]
\r
818 # 重複していた場合エラーメッセージを表示し、処理を行わない
\r
819 params["action"] = ""
\r
820 session["error"] = "同一のファイルが存在します!別の名前を指定してください。"
\r
825 db["logpath"] = cgi["logpath"]
\r
826 db["logdisplay"] = cgi["logdisplay"]
\r
828 if db["logpath"].blank? || db["logdisplay"].blank?
\r
829 params["action"] = ""
\r
836 db["logdelindex"] = params["logdelindex"].to_i
\r
838 if db["logdelindex"] < 1
\r
839 session["error"] = "ログファイルの削除パラメタが不正です。"
\r
840 params["action"] = ""
\r
845 if cgi["logdelindex"].to_i < 1
\r
846 params["action"] = ""
\r
847 params["mode"] = "error"
\r
848 session["error"] = "ログファイルの削除中に重大なエラーが発生しました。環境を見直してください。"
\r
852 File.delete(XMLPATH + File.basename(db["loglist"].path[db["logdelindex"]]))
\r
854 db["loglist"].path.delete_at(cgi["logdelindex"].to_i)
\r
855 db["loglist"].display.delete_at(cgi["logdelindex"].to_i)
\r
856 db["loglist"].to_xml
\r
858 db["loglist"] = LogList.readxml(LISTXMLPATH)
\r
861 session["info"] = "ログファイルの削除が完了しました。"
\r
863 # 現在編集中のデータを強制的に最上のファイルパスに変更
\r
864 session["filepath"] = db["loglist"].path[0]
\r
867 prevmonth = (DateTime.now << 1)
\r
869 # 前月の時刻を元に、ディフォルト書式を生成します
\r
870 db["logpath"] = FEEDXMLDIR + prevmonth.strftime("%Y%m") + ".xml"
\r
871 db["logdisplay"] = prevmonth.strftime("%Y年%m月").gsub("年0", "年")
\r
876 db["logeditindex"] = params["logdelindex"].to_i
\r
878 db["logpath"] = db["loglist"].path[db["logeditindex"]]
\r
879 db["logdisplay"] = db["loglist"].display[db["logeditindex"]]
\r
881 if db["logeditindex"] == 0
\r
882 session["error"] = "ログファイルの編集パラメタが不正です。"
\r
883 params["action"] = ""
\r
889 db["loglist"].path.each_with_index do |val, i|
\r
890 if db["logeditindex"] != i
\r
891 if params["logpath"].to_s == db["loglist"].path[i].to_s
\r
897 if checkflag == false
\r
898 params["action"] = "edit"
\r
899 session["error"] = "同一のファイルが存在します!別の名前を指定してください。"
\r
901 db["loginsertindex"] = params["loginsertindex"].to_i
\r
903 db["logpath"] = params["logpath"].to_s
\r
904 db["logdisplay"] = params["logdisplay"].to_s
\r
910 db["loglist"].path.each_with_index do |val, i|
\r
911 if db["logeditindex"] != i
\r
912 if params["logpath"].to_s == db["loglist"].path[i].to_s
\r
918 if checkflag == false
\r
919 params["action"] = ""
\r
920 params["mode"] = "error"
\r
921 session["error"] = "ログファイルの編集中に重大なエラーが発生しました。環境を見直してください。"
\r
924 db["loginsertindex"] = params["loginsertindex"].to_i
\r
926 db["logpath"] = params["logpath"].to_s
\r
927 db["logdisplay"] = params["logdisplay"].to_s
\r
930 if XMLPATH + File.basename(db["loglist"].path[db["logeditindex"]]) != XMLPATH + File.basename(db["logpath"])
\r
931 FileUtils.move(XMLPATH + File.basename(db["loglist"].path[db["logeditindex"]]), XMLPATH + File.basename(db["logpath"]))
\r
933 db["loglist"].path.delete_at(db["logeditindex"])
\r
934 db["loglist"].display.delete_at(db["logeditindex"])
\r
935 db["loglist"].path.insert(db["loginsertindex"] + 1, db["logpath"])
\r
936 db["loglist"].display.insert(db["loginsertindex"] + 1, db["logdisplay"])
\r
937 db["loglist"].to_xml
\r
940 session["info"] = "ログファイルの編集が完了しました。"
\r
942 # 現在編集中のデータを強制的に最上のファイルパスに変更
\r
943 session["filepath"] = db["loglist"].path[0]
\r
946 prevmonth = (DateTime.now << 1)
\r
948 # 前月の時刻を元に、ディフォルト書式を生成します
\r
949 db["logpath"] = FEEDXMLDIR + prevmonth.strftime("%Y%m") + ".xml"
\r
950 db["logdisplay"] = prevmonth.strftime("%Y年%m月").gsub("年0", "年")
\r
955 # 現在編集中のデータを強制的に最上のファイルパスに変更
\r
956 session["filepath"] = db["loglist"].path[0]
\r
959 prevmonth = (DateTime.now << 1)
\r
961 # 前月の時刻を元に、ディフォルト書式を生成します
\r
962 db["logpath"] = FEEDXMLDIR + prevmonth.strftime("%Y%m") + ".xml"
\r
963 db["logdisplay"] = prevmonth.strftime("%Y年%m月").gsub("年0", "年")
\r
968 db["loglist"] = LogList.readxml(LISTXMLPATH)
\r
972 case params["action"]
\r
975 file = FileUploader.new
\r
976 file.filelist().each { |fname|
\r
977 file.delete(fname) if File.ftype(XMLPATH + fname) == "file"
\r
982 loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}#{INITIALXML}"], LISTXMLPATH)
\r
985 db["loglist"] = LogList.readxml(LISTXMLPATH)
\r
988 Feed.to_xml(XMLPATH + db["loglist"].path[0].match(/[^\/]*?$/).to_a[0], Feed.new({}), [])
\r
990 # 初期の編集ファイルはloglist.xml上の最も上のファイル
\r
991 session["filepath"] = db["loglist"].path[0]
\r
992 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
\r
993 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
\r
996 params["mode"] = ""
\r
997 params["action"] = ""
\r
1001 filelist = FileUploader.new.filelist()
\r
1003 # タイプがファイルのみリストとして取得する
\r
1004 db["filelist"] = []
\r
1005 filelist.each { |fname| db["filelist"] << fname if File.ftype(XMLPATH + fname) == "file"}
\r
1010 # loglist.xmlが存在するかチェック
\r
1011 if File.exist?(LISTXMLPATH) == false
\r
1012 # なかった場合はloglist.xmlを自動生成
\r
1013 loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}#{INITIALXML}"], LISTXMLPATH)
\r
1015 Feed.to_xml(XMLPATH + File.basename(loglist.path[0]), Feed.new({}), [])
\r
1018 db["loglist"] = LogList.readxml(LISTXMLPATH)
\r
1020 # diary.xmlが存在するかチェック
\r
1021 if File.exist?(XMLPATH + File.basename(db["loglist"].path[0])) == false
\r
1022 # なかった場合はdiary.xmlを自動生成
\r
1023 Feed.to_xml(XMLPATH + File.basename(db["loglist"].path[0]), Feed.new({}), [])
\r
1026 # 初期の編集ファイルはloglist.xml上の最も上のファイル
\r
1027 session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
\r
1028 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
\r
1029 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
\r
1034 # マルチパートフォームの場合の処理です
\r
1035 def Controller.MultiForm(cgi, session, params, db)
\r
1037 # loglist.xmlをロードします
\r
1038 db["loglist"] = LogList.readxml(LISTXMLPATH)
\r
1039 case params["mode"]
\r
1042 case params["action"]
\r
1044 # この段階のエラーに対しては強度のエラーを発する
\r
1046 db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
\r
1047 db["loglist"].path.each do |val|
\r
1048 if val == db["logpath"]
\r
1049 # 重複していた場合エラーメッセージを表示し、処理を行わない
\r
1050 db["logpath"] = ""
\r
1051 params["action"] = ""
\r
1052 params["mode"] = "error"
\r
1053 session["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
\r
1058 db["loginsertindex"] = cgi["loginsertindex"].read.to_i
\r
1059 db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
\r
1062 if db["loginsertindex"] == 0
\r
1063 params["action"] = ""
\r
1064 params["mode"] = "error"
\r
1065 session["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
\r
1069 if File.basename(db["logpath"]).blank? || db["logdisplay"].blank?
\r
1070 params["action"] = ""
\r
1071 params["mode"] = "error"
\r
1072 session["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
\r
1076 # ファイルを実際にアップロードします
\r
1077 file = FileUploader.new
\r
1078 file.upload(db["logpath"], db["importxml"])
\r
1080 # loglist.xmlを更新します
\r
1081 db["loglist"].path[db["loginsertindex"], 0] = db["logpath"]
\r
1082 db["loglist"].display[db["loginsertindex"], 0] = db["logdisplay"]
\r
1083 db["loglist"].to_xml
\r
1086 db["loglist"] = LogList.readxml(LISTXMLPATH)
\r
1087 session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
\r
1088 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
\r
1089 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
\r
1091 session["info"] = "ログファイルのインポート処理が完了しました。"
\r
1094 # 入力されたログファイルパスが既に存在するかを確認
\r
1096 db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
\r
1097 db["loglist"].path.each do |val|
\r
1098 if val == db["logpath"]
\r
1099 # 重複していた場合エラーメッセージを表示し、処理を行わない
\r
1100 db["logpath"] = ""
\r
1101 params["action"] = ""
\r
1102 session["error"] = "同一のファイルが存在します!別の名前を指定してください。"
\r
1109 db["loginsertindex"] = cgi["loginsertindex"].read.to_i
\r
1110 db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
\r
1111 db["importxml"] = cgi["updata"].read
\r
1115 REXML::Document.new(Controller.fix_updata_enc(db["importxml"].to_s))
\r
1116 rescue => exception
\r
1117 session["error"] = "不正な整形のXMLです!ファイルを見直してください。<br>"
\r
1118 session["error"] << CGI.escapeHTML(exception.to_s).gsub("\n", "<br>")
\r
1119 params["action"] = ""
\r
1125 if db["loginsertindex"] == 0
\r
1126 params["action"] = ""
\r
1127 session["error"] = "ラジオボックスでログの挿入位置を選択してください!"
\r
1131 if db["logpath"] == FEEDXMLDIR || db["logdisplay"].blank?
\r
1132 params["action"] = ""
\r
1133 session["error"] = "インポートファイル、及び、ログの表示名は空欄にできません。"
\r
1140 # diary.xmlを入れ替える処理
\r
1142 case params["action"]
\r
1144 # この段階のエラーに対しては強度のエラーを発する
\r
1146 db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
\r
1147 db["loglist"].path.each do |val|
\r
1148 if val == db["logpath"]
\r
1149 # 重複していた場合エラーメッセージを表示し、処理を行わない
\r
1150 params["action"] = ""
\r
1151 params["mode"] = "error"
\r
1152 session["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
\r
1157 db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
\r
1159 if File.basename(db["logpath"]).blank? || db["logdisplay"].blank? || db["importxml"].blank?
\r
1160 params["action"] = ""
\r
1161 params["mode"] = "error"
\r
1162 session["error"] = "ログファイルの追加中に重大なエラーが発生しました。"
\r
1167 FileUtils.move(XMLPATH + INITIALXML, XMLPATH + File.basename(db["logpath"]))
\r
1169 file = FileUploader.new
\r
1170 file.upload(INITIALXML, db["importxml"])
\r
1172 # loglist.xmlを更新します
\r
1173 db["loglist"].path[1, 0] = db["logpath"]
\r
1174 db["loglist"].display[1, 0] = db["logdisplay"]
\r
1175 db["loglist"].to_xml
\r
1178 db["loglist"] = LogList.readxml(LISTXMLPATH)
\r
1179 session["filepath"] = db["loglist"].path[0] if session["filepath"] == nil
\r
1180 db["feed"] = Feed.readxml(XMLPATH + File.basename(session["filepath"]))
\r
1181 db["entry"] = Entry.readxml(XMLPATH + File.basename(session["filepath"]))
\r
1183 session["info"] = "ログファイルのインポート処理が完了しました。"
\r
1186 # 入力されたログファイルパスが既に存在するかを確認
\r
1188 db["logpath"] = Controller.get_mpart_value(cgi["logpath"])
\r
1189 db["loglist"].path.each do |val|
\r
1190 if val == db["logpath"]
\r
1191 # 重複していた場合エラーメッセージを表示し、処理を行わない
\r
1192 params["action"] = ""
\r
1193 session["error"] = "同一のファイルが存在します!別の名前を指定してください。"
\r
1199 if checkflag == true
\r
1200 db["logdisplay"] = Controller.get_mpart_value(cgi["logdisplay"])
\r
1201 db["importxml"] = cgi["updata"].read
\r
1205 REXML::Document.new(Controller.fix_updata_enc(db["importxml"].to_s))
\r
1206 rescue => exception
\r
1207 session["error"] = "不正な整形のXMLです!ファイルを見直してください。<br>"
\r
1208 session["error"] << CGI.escapeHTML(exception.to_s).gsub("\n", "<br>")
\r
1209 params["action"] = ""
\r
1214 if db["logpath"].blank? || db["logdisplay"].blank? || db["importxml"].blank?
\r
1215 params["action"] = ""
\r
1216 session["error"] = "インポートファイル、及び、ログの表示名、ログのパスは空欄にできません。"
\r
1228 # Formのenctypeがmultypartだった場合、入力された値をrubyバージョンに左右されずに読み取るメソッドです。
\r
1229 def Controller.get_mpart_value(cgi_param)
\r
1230 if RUBY_VERSION >= "1.9.0"
\r
1231 cgi_param[0..cgi_param.length].to_s
\r
1233 cgi_param.read.to_s
\r
1237 # アップロードされたXMLをバージョンに左右されずに読み取るために、ruby-1.9.1以上ではエンコーディングを強制指定します
\r
1238 def Controller.fix_updata_enc(file_to_s)
\r
1239 if RUBY_VERSION >= "1.9.0"
\r
1240 file_to_s.force_encoding("UTF-8")
\r
1249 # SESSION変数、パラメータなどを取得します
\r
1251 session = CGI::Session.new(cgi)
\r
1252 params = Hash[*cgi.params.to_a.map{|k, v| [k, v[0].to_s]}.flatten]
\r
1255 logger = WebLogger.get_logger(cgi.script_name, LOG_DIR, LOG_RELEASE_MODE)
\r
1259 session["info"] = ""
\r
1260 session["error"] = ""
\r
1261 if session["login"] != "true"
\r
1264 LOGININFO.each {|h|
\r
1265 if (cgi["loginid"] == h[:id] && cgi["password"] == h[:password])
\r
1266 session["login"] = "true"
\r
1267 session["name"] = h[:name]
\r
1270 logger.info "ログインユーザ:#{h[:id]}, IPアドレス:#{cgi.remote_addr}"
\r
1276 if (session["login"] == "true")
\r
1277 # ワークフォルダの中をクリーンアップします
\r
1278 filelist = Dir::entries("./work")
\r
1279 # 削除条件 : 最終変更日時から1日(60*60*24sec)かつ、ファイルタイプがファイルの場合
\r
1280 filelist.each do |file|
\r
1281 File.delete("./work/#{file}") if Time.now - File.ctime("./work/#{file}") > 86400 && File.ftype("./work/#{file}") == "file"
\r
1286 if (session["login"] != "true" and (cgi["loginid"] != "" or cgi["password"] != ""))
\r
1287 logger.info "次のアクセスがログインに失敗しました。ログインユーザ:#{cgi["loginid"]}, IPアドレス:#{cgi.remote_addr}"
\r
1292 if params["mode"] == "logout"
\r
1293 session["login"] = nil
\r
1299 # セッションが有効な場合のも実行します
\r
1300 if session["login"] == "true"
\r
1303 db = PStore.new("./work/#{session.session_id}.dat")
\r
1305 session["error"] = ""
\r
1309 File.delete("./work/#{session.session_id}.dat")
\r
1310 # PStoreが破損していた場合はセッション情報を破棄する
\r
1311 session["login"] = nil
\r
1315 # フォームによって挙動を変更します
\r
1316 if cgi["mode"].respond_to?(:read) && cgi["action"].respond_to?(:read)
\r
1317 params["mode"] = cgi["mode"].read
\r
1318 params["action"] = cgi["action"].read
\r
1319 Controller.MultiForm(cgi, session, params, db)
\r
1321 Controller.NormalForm(cgi, session, params, db)
\r
1324 # エラー画面移行時はセッション情報を破棄する
\r
1325 if params["mode"] == "error"
\r
1326 session["login"] = nil
\r
1332 if (session["login"] != "true" and (cgi["loginid"] != "" or cgi["password"] != ""))
\r
1333 session["error"] = "ログインに失敗しました。ユーザIDまたはパスワードを確認して下さい。"
\r
1334 logger.info "次のアクセスがログインに失敗しました。ログインID:#{cgi["loginid"]}, IPアドレス:#{cgi.remote_addr}"
\r
1338 # modeとactionの相関図は以下のようになります。
\r
1339 # [mode] + [action]
\r
1343 if session["login"] != "true"
\r
1344 # セッションが存在しない場合は強制的にトップページに遷移
\r
1345 htmlwriter = HtmlWriter.new("./erbtemp/index.html.erb", binding)
\r
1347 case params["mode"]
\r
1349 htmlwriter = HtmlWriter.new("./erbtemp/select.html.erb", binding)
\r
1351 htmlwriter = HtmlWriter.new("./erbtemp/newentry.html.erb", binding)
\r
1353 htmlwriter = HtmlWriter.new("./erbtemp/editentry.html.erb", binding)
\r
1355 htmlwriter = HtmlWriter.new("./erbtemp/delentry.html.erb", binding)
\r
1357 htmlwriter = HtmlWriter.new("./erbtemp/editfeed.html.erb", binding)
\r
1359 htmlwriter = HtmlWriter.new("./erbtemp/log.html.erb", binding)
\r
1361 htmlwriter = HtmlWriter.new("./erbtemp/insertfeed.html.erb", binding)
\r
1363 htmlwriter = HtmlWriter.new("./erbtemp/replacefeed.html.erb", binding)
\r
1365 htmlwriter = HtmlWriter.new("./erbtemp/indeximport.html.erb", binding)
\r
1367 htmlwriter = HtmlWriter.new("./erbtemp/reset.html.erb", binding)
\r
1369 htmlwriter = HtmlWriter.new("./erbtemp/error.html.erb", binding)
\r
1371 htmlwriter = HtmlWriter.new("./erbtemp/menu.html.erb", binding)
\r
1375 # ビュー部分(インクルード用ファイル読み込み)
\r
1378 htmlparts['headtag'] = HtmlWriter.new("./erbtemp/parts_headtag.html.erb", binding).to_code
\r
1379 htmlparts['headjs_switchpost'] = HtmlWriter.new("./erbtemp/parts_headjs_switchpost.html.erb", binding).to_code
\r
1380 htmlparts['headjs_tag'] = HtmlWriter.new("./erbtemp/parts_headjs_tag.html.erb", binding).to_code
\r
1381 htmlparts['menu'] = HtmlWriter.new("./erbtemp/parts_menu.html.erb", binding).to_code
\r
1382 htmlparts['selectfile'] = HtmlWriter.new("./erbtemp/parts_selectfile.html.erb", binding).to_code
\r
1384 htmlparts['infoarea'] = HtmlWriter.new("./erbtemp/parts_infoarea.html.erb", binding).to_code
\r
1387 cgi.out{htmlwriter.to_code}
\r
1389 rescue => exception
\r
1391 logger.error(exception.to_s)
\r
1392 logger.error(exception.backtrace.join "\n")
\r
1394 # エラーが発生した場合、それを画面に表示します
\r
1395 htmlwriter = HtmlWriter.new("./erbtemp/exception.html.erb", binding)
\r
1396 cgi.out{htmlwriter.to_code}
\r
1403 # エラーが発生した場合、それを画面に表示します
\r
1404 detail = ("%s: %s (%s)\n" %
\r
1405 [evar.backtrace[0], evar.message, evar.send('class')]) +
\r
1406 evar.backtrace[1..-1].join("\n")
\r
1407 puts "content-type: text/html\n\n<plaintext>\n" + detail
\r