OSDN Git Service

- Plugin function added.
[feedblog/feedgenerator.git] / feedgenerator.rb
index 8b403e1..837a061 100644 (file)
@@ -17,14 +17,18 @@ require 'time'
 require "date"
 require "fileutils"
 
-# ログインID
-LOGINID = "login"
-# ログインパスワード
-PASSWORD = "password"
+# ログイン情報配列
+LOGININFO = [
+{:id => "login", :password => "password", :name => "テストユーザ"}
+]
 # インターフェースのテーブルの幅
-TABLEWIDTH = 800
+TABLEWIDTH = 1000
 # XMLファイル格納先までの相対パス
 XMLPATH = "./../lunardial/xml/"
+# FeedBlogを設置したディレクトリのURL
+HOMEBASE = "https://lunardial.sakura.ne.jp/"
+# 入力されたフルパスURL(HOMEBASE)を置換する文字列
+RELAYPATH = "./"
 # loglist.xmlファイルの定義
 LISTXMLPATH = "#{XMLPATH}loglist.xml"
 # FeedBlog上の表示ページからログ格納ディレクトリまでのパス
@@ -37,6 +41,12 @@ USEFILEMANAGER = true
 FILEMANAGER = "./filemanager.rb"
 # XMLに書き込む際、改行部分を<br>のまま保持するか、改行記号に直すか
 REPLACEBRTAG = false
+# ファイルの書き込み時にENTRYのIDおよびURLを、FEEDオブジェクトから自動生成した値に置換するか否か
+REPLACEENTRYIDANDURL = false
+# プラグインディレクトリ
+PLUGINDIR = "./plugins/"
+# 記事用初期ファイル名
+INITIALXML = "diary.xml"
 
 # バージョン情報を示す文字列です
 APPVERSION = "- FeedGenerator for Ruby version 2.0.0.0 -<br>Copyright(c) 2009 Kureha.H (<a href=\"http://lunardial.sakura.ne.jp/\" target=\"_blank\">http://lunardial.sakura.ne.jp/</a>) & Yui Naruse (<a href=\"http://airemix.com/\" target=\"_blank\">http://airemix.com/</a>)"
@@ -56,9 +66,16 @@ class Object
   # _arg[1]_ :: モードの指定。例 : w:utf-8(書き込みモード・UTF-8エンコードでファイルを開く)
   def myopen(*arg)
     mode = arg[1]
-    arg[1] = mode[/[^:]+/] if mode && RUBY_VERSION < "1.8.7" && mode.include?(':')
+    rdonly_p = true
+    case mode
+      when String
+      arg[1] = mode[/[^:]+/] if RUBY_VERSION < "1.8.7" && mode.include?(':')
+      rdonly_p = /\A[^:]*[wa+]/ !~ mode
+      when Numeric
+      rdonly_p = !(mode & (IO::WRONY | IO::RDWR))
+    end
     open(*arg) do |f|
-      f.flock(/\A\w+r/ =~ mode ? File::LOCK_SH : File::LOCK_EX)
+      f.flock(rdonly_p ? File::LOCK_SH : File::LOCK_EX)
       return yield(f)
     end
   end
@@ -254,7 +271,8 @@ class Feed < AbstractEntry
   def to_s
     buf = []
     
-    buf.push("<feed #{@attr[:feedattr]}>")
+    # buf.push("<feed #{@attr[:feedattr]}>")
+    buf.push("<feed xml:lang='ja-jp' xmlns='http://www.w3.org/2005/Atom'>");
     buf.push("<title type=\"text\">#{@attr[:title]}</title>")
     buf.push("<subtitle type=\"text\">#{@attr[:subtitle]}</subtitle>")
     buf.push("<link rel=\"self\" type=\"application/atom+xml\" href=\"#{@attr[:self]}\" />")
@@ -283,7 +301,31 @@ class Feed < AbstractEntry
   #
   # _feed_ :: Feedオブジェクト
   # _entry_ :: Entryオブジェクトの配列
-  def self.to_xml(path, feed, entrylist)
+  def self.to_xml(path, feed, entrylist_tmp)
+    buf = []
+    entrylist = entrylist_tmp.dup
+    buf.push("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
+    buf.push("#{feed.to_s}\n")
+    entrylist.each { |entry|
+      if REPLACEENTRYIDANDURL
+        entry.entryid.gsub!(/^[^\?]*\?/, "")
+        entry.entryid = feed.url + "?" + entry.entryid
+        entry.url = feed.url + "#" + entry.entryid
+      end
+      buf.push("#{entry.to_s}\n")
+    }
+    buf.push("</feed>")
+    
+    myopen(path, "w") do |f|
+      f.print buf.join("\n")
+    end
+  end
+  
+  # XMLファイル出力用メソッドです
+  #
+  # _feed_ :: Feedオブジェクト
+  # _entry_ :: Entryオブジェクトの配列
+  def self.to_xml_plain(path, feed, entrylist)
     buf = []
     buf.push("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
     buf.push("#{feed.to_s}\n")
@@ -319,7 +361,7 @@ class Entry < AbstractEntry
     # 可視・不可視を示すハッシュキーを格納する配列です
     @display = {"entryid" => "none", "title" => "",
       "summary" => "", "published" => "none",
-      "updated" => "none", "url" => "none",
+      "updated" => "none", "url" => "",
       "content" => "", "others"=>"none"}
     
     # デバッグモードの場合、全ての入力要素を表示します
@@ -374,6 +416,45 @@ class Entry < AbstractEntry
     return entrylist
   end
   
+  # Atom XMLファイルを読み込んで解析し、テンプレートファイルにしたがってHTMLに変換するメソッドです
+  def self.to_html(xmlpath, destpath, entry_temppath, html_temppath)
+    # 引数チェック - 全必須
+    if xmlpath.empty? or destpath.empty? or entry_temppath.empty? or html_temppath.empty?
+      raise ArgumentError
+    end
+    
+    # 必須ファイル存在チェック
+    unless File.exist?(xmlpath) and File.exist?(entry_temppath) and File.exist?(html_temppath)
+      raise IOError
+    end
+    
+    # XML読み込み
+    entrylist = Entry.readxml(xmlpath)
+    
+    body = ''
+    entrylist.each { |e|
+      # Entry毎のHTML表示部分を生成
+      body << e.to_template(entry_temppath)
+    }
+    
+    # HTML全体のテンプレートを生成
+    html_temp = HtmlWriter.new(html_temppath, binding)
+    
+    # HTMLに書き込み
+    myopen(destpath, 'w:utf-8') { |f|
+      f.write(CGI.pretty(html_temp.to_code))
+    }
+  end
+  
+  # Entryをテンプレートに沿って変形するメソッド
+  def to_template(temppath)
+    erb = HtmlWriter.new(temppath, binding)
+    title = CGI.unescapeHTML(@attr[:title])
+    date = @attr[:published]
+    content = CGI.unescapeHTML(@attr[:content])
+    erb.to_code
+  end
+  
   # Entry挿入メソッド
   def self.insert(path, entry)
     feed = Feed.readxml(path)
@@ -443,7 +524,7 @@ class Entry < AbstractEntry
     str.strip!
     str.gsub!(/(&lt;\/(?:p|h\d|div)(?:&gt;|>))\n/i, '\1')
     str.gsub!(/\n/, '&lt;br&gt;') if REPLACEBRTAG
-    str.gsub!(/(&lt;(?:(?!&gt;).)*?)#{Regexp.escape(FEEDXMLDIR)}/) { "#$1#{XMLPATH}" }
+    str.gsub!(/(&lt;(?:(?!&gt;).)*?)#{Regexp.escape(RELAYPATH)}/) { "#$1#{HOMEBASE}" }
     str
   end
   
@@ -453,9 +534,9 @@ class Entry < AbstractEntry
     str = CGI.unescapeHTML(str)
     str.strip!
     str.gsub!(/(\r\n|\n)/, "")
-    str.gsub!(/<br>/i, "\n") if REPLACEBRTAG
-    str.gsub!(/(<br>|<\/p>|<\/h\d>|<\/div>)(?=[^\n])/i) { "#$1\n" } unless REPLACEBRTAG
-    str.gsub!(/(<[^>]*?)#{Regexp.escape(XMLPATH)}/) { "#$1#{FEEDXMLDIR}" }
+    str.gsub!(/<br>|<br[ ]*\/>/i, "\n") if REPLACEBRTAG
+    str.gsub!(/(<br>|<br[ ]*\/>|<\/p>|<\/h\d>|<\/div>)(?=[^\n])/i) { "#$1\n" } unless REPLACEBRTAG
+    str.gsub!(/(<[^>]*?)#{Regexp.escape(HOMEBASE)}/) { "#$1#{RELAYPATH}" }
     CGI.escapeHTML(str)
   end
   
@@ -464,8 +545,8 @@ class Entry < AbstractEntry
     str = @attr[:content].dup
     str = CGI.unescapeHTML(str)
     str.strip!
-    str.gsub!(/<br>/i, "\n") if REPLACEBRTAG
-    str.gsub!(/(<[^>]*?)#{Regexp.escape(FEEDXMLDIR)}/) { "#$1#{XMLPATH}" }
+    str.gsub!(/<br>|<br[ ]*\/>/i, "\n") if REPLACEBRTAG
+    str.gsub!(/(<[^>]*?)#{Regexp.escape(RELAYPATH)}/) { "#$1#{HOMEBASE}" }
     str
   end
   
@@ -609,6 +690,26 @@ class FileUploader
   end
 end
 
+# = Pluginクラス
+#
+# プラグインの処理を行うクラスです
+class FeedGenPluginManager
+  def self.exec(mode, filepath)
+    feed = Feed.readxml(XMLPATH + filepath)
+    entries = Entry.readxml(XMLPATH + filepath)
+    feed.freeze
+    entries.freeze
+    Dir.foreach(PLUGINDIR) do |fn|
+      next unless File.extname(fn) == '.rb'
+      require File.join(PLUGINDIR, fn)
+      plugin_name = "FeedGenPlugins::"
+      plugin_name << File.basename(fn).gsub(/\.rb\Z/, "")
+      plugin_ins = plugin_name.split(/::/).inject(Object) { |c,name| c.const_get(name) }
+      plugin_ins.new.exec(mode, feed, entries)
+    end
+  end
+end
+
 # = Controllerクラス
 #
 # コントローラ部分に相当する処理を受け持つクラスです
@@ -645,6 +746,9 @@ class Controller
           unless successed
             db["error"] = "日記の新規追加に失敗しました。" 
             params["mode"] = "error"
+          else
+            # 成功時はプラグイン処理を実施する
+            FeedGenPluginManager.exec("newentry", File.basename(session["filepath"]))
           end
           # 画面を戻った際の処理
           when "back"
@@ -678,6 +782,9 @@ class Controller
           unless successed
             db["error"] = "日記の編集処理に失敗しました。<br>該当の日記が既に存在しない可能性があります。" 
             params["mode"] = "error"
+          else
+            # 成功時はプラグイン処理を実施する
+            FeedGenPluginManager.exec("editentry", File.basename(session["filepath"]))
           end
           when "back"
           session["target_filepath"] = params["target_filepath"]
@@ -704,6 +811,9 @@ class Controller
           unless successed
             db["error"] = "日記の編集処理に失敗しました。<br>該当の日記が既に存在しない可能性があります。" 
             params["mode"] = "error"
+          else
+            # 成功時はプラグイン処理を実施する
+            FeedGenPluginManager.exec("delentry", File.basename(session["filepath"]))
           end
           when "back"
           session["target_filepath"] = params["target_filepath"]
@@ -905,7 +1015,7 @@ class Controller
           
           # 全ファイルの初期化を実行する
           # loglist.xmlの初期化
-          loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}diary.xml"], LISTXMLPATH)
+          loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}#{INITIALXML}"], LISTXMLPATH)
           loglist.to_xml
           
           db["loglist"] = LogList.readxml(LISTXMLPATH)
@@ -936,7 +1046,7 @@ class Controller
         # loglist.xmlが存在するかチェック
         if File.exist?(LISTXMLPATH) == false
           # なかった場合はloglist.xmlを自動生成
-          loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}diary.xml"], LISTXMLPATH)
+          loglist = LogList.new(["最新の記事"], ["#{FEEDXMLDIR}#{INITIALXML}"], LISTXMLPATH)
           loglist.to_xml
           Feed.to_xml(XMLPATH + File.basename(loglist.path[0]), Feed.new({}), [])
         end
@@ -960,10 +1070,8 @@ class Controller
   # マルチパートフォームの場合の処理です
   def Controller.MultiForm(cgi, session, params, db)
     db.transaction do
-      
       # loglist.xmlをロードします
       db["loglist"] = LogList.readxml(LISTXMLPATH)
-      
       case params["mode"]
         # 特定位置に挿入する場合の処理
         when "insert"
@@ -1092,10 +1200,10 @@ class Controller
           end
           
           # diary.xmlを移動します
-          FileUtils.move(XMLPATH + "diary.xml", XMLPATH + File.basename(db["logpath"]))
+          FileUtils.move(XMLPATH + INITIALXML, XMLPATH + File.basename(db["logpath"]))
           # ファイルをアップロードします
           file = FileUploader.new
-          file.upload("diary.xml", db["importxml"])
+          file.upload(INITIALXML, db["importxml"])
           
           # loglist.xmlを更新します
           db["loglist"].path[1, 0] = db["logpath"]
@@ -1182,9 +1290,17 @@ def main
   # コントローラー部分
   # セッション管理
   if session["login"] != "true"
-    if (cgi["loginid"] == LOGINID && cgi["password"] == PASSWORD)
-      session["login"] = "true"
-      
+    
+    # ログイン情報を確認
+    LOGININFO.each {|h|
+      if (cgi["loginid"] == h[:id] && cgi["password"] == h[:password])
+        session["login"] = "true"
+        session["name"] = h[:name]
+        break
+      end
+    }
+    
+    if (session["login"] == "true")   
       # ワークフォルダの中をクリーンアップします
       filelist = Dir::entries("./work")
       # 削除条件 : 最終変更日時から1日(60*60*24sec)かつ、ファイルタイプがファイルの場合
@@ -1197,8 +1313,6 @@ def main
   # ログアウト処理
   if params["mode"] == "logout"
     session["login"] = nil
-    session["logini"] = nil
-    session["password"] = nil
     session.delete
   end
   
@@ -1217,8 +1331,6 @@ def main
         File.delete("./work/#{session.session_id}.dat")
         # PStoreが破損していた場合はセッション情報を破棄する
         session["login"] = nil
-        session["logini"] = nil
-        session["password"] = nil
         session.delete
       end
       
@@ -1234,8 +1346,6 @@ def main
       # エラー画面移行時はセッション情報を破棄する
       if params["mode"] == "error"
         session["login"] = nil
-        session["logini"] = nil
-        session["password"] = nil
         session.delete
       end
     end