OSDN Git Service

- [re-merge] Idea_Extended_ViewClass because of merge failure on r818.
authormumumu-org <mumumu-org@2ef88817-412d-0410-a32c-8029a115e976>
Thu, 30 Apr 2009 21:26:30 +0000 (21:26 +0000)
committermumumu-org <mumumu-org@2ef88817-412d-0410-a32c-8029a115e976>
Thu, 30 Apr 2009 21:26:30 +0000 (21:26 +0000)
13 files changed:
CHANGES
Ethna.php
class/Ethna_Controller.php
class/Ethna_ViewClass.php
class/Plugin/Generator/Ethna_Plugin_Generator_Project.php
class/View/Ethna_View_404.php [new file with mode: 0644]
class/View/Ethna_View_500.php [new file with mode: 0644]
class/View/Ethna_View_Json.php [new file with mode: 0644]
class/View/Ethna_View_Redirect.php [new file with mode: 0644]
skel/template.404.tpl [new file with mode: 0644]
skel/template.500.tpl [new file with mode: 0644]
skel/template.index.tpl
skel/template.layout.tpl [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index 2e7321b..ae8a37e 100644 (file)
--- a/CHANGES
+++ b/CHANGES
 - 指定 Action が存在しない場合い、app/action 以下を全て include する仕様を変更
 - add-project -b オプションの挙動変更
 - controller での smarty_xx_plugin の機能を削除
+- ビューまわりの変更
+-- Ethna_ActionClass から、Ethna_ViewClass#preforward に引数を渡せるようにした
+--- return array('forward_name', $params); の形式で渡せば、$params が preforwardの引数として渡される
+-- 汎用ビュークラスを実装
+--- ビューへの出力時によく使われる処理を雛形として実装したもの
+--- Ethna_View_Json.php
+--- Ethna_View_404.php
+--- Ethna_View_500.php
+--- Ethna_View_Redirect.php
+---- アクションクラスで return array('redirect', 'http://example.com');
+     とすれば http://example.com にリダイレクトされる
+-- レイアウトテンプレートを実装
+--- HTMLの外側に当たる雛形のテンプレートを描くためのもの。各アクションの出力はこのテンプレートの出力でラップされる
+--- デフォルトは template/{locale_name/layout.tpl に置かれている。
+--- この機能はデフォルトで有効になっている。無効にしたければ、[appid]_ViewClass.php の $use_layout を false にする
+-- フォームヘルパのテキストエリアに value 属性を付加していた動きを修正。(thanks: syachi5150)
+---  http://sourceforge.jp/ticket/browse.php?group_id=1343&tid=16326
+- [Breaking B.C] ルールがユーザにとって直感的ではないとの理由から、フォーム定義の max と フォームヘルパの maxlength の連携機能を削除 (thanks: syachi5150)
+-- https://sourceforge.jp/ticket/browse.php?group_id=1343&tid=16325
 
 *** bug fix
 
index 102d9d5..e9ba355 100644 (file)
--- a/Ethna.php
+++ b/Ethna.php
@@ -80,6 +80,10 @@ require_once ETHNA_BASE . '/class/Ethna_Generator.php';
 require_once ETHNA_BASE . '/class/Ethna_UrlHandler.php';
 require_once ETHNA_BASE . '/class/Ethna_Util.php';
 require_once ETHNA_BASE . '/class/Ethna_ViewClass.php';
+require_once ETHNA_BASE . '/class/View/Ethna_View_Json.php';
+require_once ETHNA_BASE . '/class/View/Ethna_View_Redirect.php';
+require_once ETHNA_BASE . '/class/View/Ethna_View_404.php';
+require_once ETHNA_BASE . '/class/View/Ethna_View_500.php';
 require_once ETHNA_BASE . '/class/View/Ethna_View_List.php';
 require_once ETHNA_BASE . '/class/Ethna_Plugin.php';
 require_once ETHNA_BASE . '/class/Ethna_Renderer.php';
index 5d591c4..e5da981 100644 (file)
@@ -119,6 +119,14 @@ class Ethna_Controller
     /** @var    array   forward定義 */
     var $forward = array();
 
+    /** @var    array   デフォルトのforward定義 */
+    var $forward_default = array(
+        '404' => array( 'view_name' => 'Ethna_View_404',),
+        '500' => array( 'view_name' => 'Ethna_View_500',), 
+        'json' => array( 'view_name' => 'Ethna_View_Json',),
+        'redirect' => array( 'view_name' => 'Ethna_View_Redirect',),
+    );
+
     /** @var    array   action定義 */
     var $action = array();
 
@@ -217,6 +225,10 @@ class Ethna_Controller
             }
         }
 
+        // 遷移先設定をマージ
+        // 但し、キーは完全に維持する
+        $this->forward = $this->forward + $this->forward_default;
+
         // 初期設定
         // フレームワークとしての内部エンコーディングはクライアント
         // エンコーディング(=ブラウザからのエンコーディング)
@@ -967,12 +979,22 @@ class Ethna_Controller
         }
 
         // コントローラで遷移先を決定する(オプション)
-        $forward_name = $this->_sortForward($action_name, $forward_name);
+        $forward_name_params = $this->_sortForward($action_name, $forward_name);
+
+        // Viewへの引数があれば取り出す
+        $preforward_params = array();
+        if (is_array($forward_name_params)) {
+            $forward_name = array_shift($forward_name_params);
+            $preforward_params = $forward_name_params;
+        }
+        else {
+            $forward_name = $forward_name_params;
+        }
 
         if ($forward_name != null) {
             $view_class_name = $this->getViewClassName($forward_name);
             $this->view =& new $view_class_name($backend, $forward_name, $this->_getForwardPath($forward_name));
-            $this->view->preforward();
+            call_user_func_array(array($this->view, 'preforward'), $preforward_params);
             $this->view->forward();
         }
 
index 3aa4964..04082cb 100644 (file)
@@ -56,7 +56,7 @@ class Ethna_ViewClass
     /** @var    array   アクションフォームオブジェクト(helper) */
     var $helper_action_form = array();
 
-    /** @var    array   helperでhtmlのattributeにはしなパラメータの一覧 */
+    /** @var    array   helperã\81§htmlã\81®attributeã\81«ã\81¯ã\81\97ã\81ªã\81\84ã\83\91ã\83©ã\83¡ã\83¼ã\82¿ã\81®ä¸\80覧 */
     var $helper_parameter_keys = array('default', 'option', 'separator');
 
     /** @var    object  Ethna_Session       セッションオブジェクト */
@@ -73,6 +73,33 @@ class Ethna_ViewClass
 
     /**#@-*/
 
+    /**#@+
+     *  @access protected
+     */
+
+    /** @var  string レイアウト(HTMLの外枠を記述するファイル)のテンプレートファイルを指定   */
+    var $_layout_file = 'layout.tpl';
+
+    /**#@-*/
+
+    /**#@+
+     *  @access public
+     */
+
+    /** @var boolean  レイアウトテンプレートの使用フラグ       */
+    var $use_layout = true;
+
+    /** @var  boolean  デフォルトのヘッダ出力を使用するか否か  */
+    /**                ヘッダ出力を改造する場合はfalseにする   */
+    var $has_default_header = true;
+
+    /** @var  array    デフォルトのヘッダ出力を使用するか否か  */
+    /**                ヘッダ出力を改造する場合はfalseにする   */
+    var $default_header = array(
+        'Pragma' => 'no-cache',
+        'Cache-Control' => 'no-cache, no-store, must-revalidate',
+    );
+
     // {{{ Ethna_ViewClass
     /**
      *  Ethna_ViewClassのコンストラクタ
@@ -117,8 +144,11 @@ class Ethna_ViewClass
      *  ここで設定する(例:セレクトボックス等)
      *
      *  @access public
+     *  @param  mixed  $params  アクションクラスから返された引数 
+     *                          array('forward_name', $param) の形でアクション
+     *                          から値を返すことで、$params に値が渡されます。
      */
-    function preforward()
+    function preforward($params = NULL)
     {
     }
     // }}}
@@ -136,7 +166,203 @@ class Ethna_ViewClass
     {
         $renderer =& $this->_getRenderer();
         $this->_setDefault($renderer);
-        $renderer->perform($this->forward_path);
+
+        if ($this->has_default_header) {
+            $this->default_header['Content-Type'] = 'text/html; charset=' . $this->ctl->getClientEncoding();
+            $this->header($this->default_header);
+        }
+
+        // using layout.tpl flag
+        if ($this->use_layout) {
+
+            // check : layout file existance
+            $layout = $this->getLayout();
+            if ($this->templateExists($layout)) {
+                $content = $renderer->perform($this->forward_path, true);
+
+                if (Ethna::isError($content)) {
+                    if ($content->getCode() == E_GENERAL) {
+                        $error = 404;
+                    }
+                    else {
+                        $error = 500;
+                    }
+
+                    $this->error($error);
+                    $content = $renderer->perform($this->forward_path, true);
+                }
+
+                $renderer->assign('content', $content);
+                $renderer->display($layout, serialize($_SERVER['REQUEST_URI']));
+            } else {
+                return Ethna::raiseWarning('file "'.$layout.'" not found');
+            }
+        } else {
+            $renderer->perform($this->forward_path);
+        }
+    }
+    // }}}
+
+    // {{{ header
+    /**
+     *  HTTPヘッダを送信します。
+     *
+     *  @param  mixed   ヘッダを設定する値
+     *                  配列指定の場合、header => value の形式
+     *                  整数指定の場合は、HTTPステータスコード
+     *                  文字列で指定する場合は、ヘッダ出力をそのまま指定
+     *  @access public
+     */
+    function header($status)
+    {
+        if (is_array($status)) {
+            foreach ($status as $key => $status) {
+                header ($key . ": " . $status);
+            }
+        } else if (is_int($status)) {
+            $codes = array(
+                100 => "Continue",
+                101 => "Switching Protocols",
+                200 => "OK",
+                201 => "Created",
+                202 => "Accepted",
+                203 => "Non-Authoritative Information",
+                204 => "No Content",
+                205 => "Reset Content",
+                206 => "Partial Content",
+                300 => "Multiple Choices",
+                301 => "Moved Permanently",
+                302 => "Found",
+                303 => "See Other",
+                304 => "Not Modified",
+                305 => "Use Proxy",
+                307 => "Temporary Redirect",
+                400 => "Bad Request",
+                401 => "Unauthorized",
+                402 => "Payment Required",
+                403 => "Forbidden",
+                404 => "Not Found",
+                405 => "Method Not Allowed",
+                406 => "Not Acceptable",
+                407 => "Proxy Authentication Required",
+                408 => "Request Time-out",
+                409 => "Conflict",
+                410 => "Gone",
+                411 => "Length Required",
+                412 => "Precondition Failed",
+                413 => "Request Entity Too Large",
+                414 => "Request-URI Too Large",
+                415 => "Unsupported Media Type",
+                416 => "Requested range not satisfiable",
+                417 => "Expectation Failed",
+                500 => "Internal Server Error",
+                501 => "Not Implemented",
+                502 => "Bad Gateway",
+                503 => "Service Unavailable",
+                504 => "Gateway Time-out"
+            );
+
+            if (array_key_exists($status, $codes)) {
+                header("HTTP/1.1: {$status} {$codes[$status]}");
+            }
+        } else {
+            // check valid header
+            if (preg_match("/^.+\:\s.+$/", $status)) {
+                header($status);
+            }
+        }
+    }
+    // }}}
+
+    // {{{ redirect
+    /**
+     *  HTTPヘッダを送信します。
+     *
+     *  @param  mixed   ヘッダを設定する値
+     *                  配列指定の場合、header => value の形式
+     *                  整数指定の場合は、HTTPステータスコード
+     *                  文字列で指定する場合は、ヘッダ出力をそのまま指定
+     *  @access public
+     */
+    function redirect($url)
+    {
+        $this->has_default_header = false;
+        $this->use_layout = false;
+
+        $this->header(302);
+        $this->header(array('Location' => $url));
+    }
+    // }}}
+
+    // {{{ setLayout
+    /**
+     *  レイアウトテンプレートのファイル名を設定します。
+     *  レイアウトテンプレートは、HTML の外枠を設定するのに使用します。
+     *  
+     *  @param string $filename  レイアウトファイル名
+     *  @access public
+     */
+    function setLayout($filename)
+    {
+        // check layout file existance
+        if ($this->templateExists($filename)) {
+            $this->_layout_file = $filename;
+            return true;
+        } else {
+            return Ethna::raiseWarning('file "'.$filename.'" not found');
+        }
+    }
+    // }}}
+
+    // {{{ getLayout
+    /**
+     *  レイアウトテンプレートファイル名を取得します。
+     *  
+     *  @return string  レイアウトテンプレートのファイル名
+     *  @access public
+     */
+    function getLayout()
+    {
+        return $this->_layout_file;
+    }
+    // }}}
+
+    // {{{ templateExists
+    /**
+     *  テンプレートファイルが存在するか否かを返します。
+     *
+     * @param   string  $filename  チェック対象のテンプレートファイル
+     * @access  public
+     * @return  boolean 指定したテンプレートファイルが存在すればtrue
+     *                  存在しなければfalse
+     */
+    function templateExists($filename)
+    {
+        $renderer = $this->_getRenderer();
+        if ($renderer->templateExists($filename)) {
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+    // }}}
+
+    // {{{ error 
+    /**
+     *  エラーページ出力用のHTTPステータスコードを指定します。
+     *
+     *  @param  int  HTTPステータスコード
+     *  @access public
+     */
+    function error($code)
+    {
+        $this->has_default_header = false;
+        $this->header($code);
+
+        // template 以下に error404.tpl とかがあれば, 
+        // preforward で $this->error(404); とかすればいい
+        $this->forward_path = "error{$code}.tpl";
     }
     // }}}
 
@@ -642,10 +868,8 @@ class Ethna_ViewClass
             $params['value'] = $value;
         }
 
-        // maxlength
-        if (isset($def['max']) && $def['max']) {
-            $params['maxlength'] = $def['max'];
-        }
+        //   maxlength と フォーム定義のmax連携はサポートしない
+        //   @see http://sourceforge.jp/ticket/browse.php?group_id=1343&tid=16325
 
         return $this->_getFormInput_Html('input', $params);
     }
@@ -831,7 +1055,6 @@ class Ethna_ViewClass
         $element = '';
         if (isset($params['value'])) {
             $element = $params['value'];
-            unset($params['value']);
         } else if (isset($params['default'])) {
             $element = $params['default'];
         } else if (isset($def['default'])) {
@@ -843,8 +1066,6 @@ class Ethna_ViewClass
             } else {
                 $element = '';
             }
-        } else {
-            $params['value'] = $element;
         }
 
         return $this->_getFormInput_Html('textarea', $params, $element);
@@ -888,10 +1109,8 @@ class Ethna_ViewClass
             $params['value'] = $value;
         }
 
-        // maxlength
-        if (isset($def['max']) && $def['max']) {
-            $params['maxlength'] = $def['max'];
-        }
+        //   maxlength と フォーム定義のmax連携はサポートしない
+        //   @see http://sourceforge.jp/ticket/browse.php?group_id=1343&tid=16325
 
         return $this->_getFormInput_Html('input', $params);
     }
index 4454469..2e03160 100644 (file)
@@ -151,6 +151,9 @@ class Ethna_Plugin_Generator_Project extends Ethna_Plugin_Generator
             "app.url_handler.php" => sprintf("$basedir/app/%s_UrlHandler.php", $macro['project_id']),
             "etc.ini.php" => sprintf("$basedir/etc/%s-ini.php", $macro['project_prefix']),
             "template.index.tpl" => sprintf("$basedir/template/$locale/index.tpl"),
+            "template.layout.tpl" => sprintf("$basedir/template/$locale/layout.tpl"),
+            "template.404.tpl" => sprintf("$basedir/template/$locale/error404.tpl"),
+            "template.500.tpl" => sprintf("$basedir/template/$locale/error500.tpl"),
         );
 
         $skelfile_maps = array(
diff --git a/class/View/Ethna_View_404.php b/class/View/Ethna_View_404.php
new file mode 100644 (file)
index 0000000..8266885
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+// vim: foldmethod=marker
+/**
+ *  Ethna_View_404.php
+ *
+ *  @author     Yoshinari Takaoka <takaoka@beatcraft.com>
+ *  @license    http://www.opensource.org/licenses/bsd-license.php The BSD License
+ *  @package    Ethna
+ *  @version    $Id$
+ */
+
+// {{{ Ethna_View_404
+/**
+ *  404ページ(リソースが存在しない場合のエラーページ)
+ *  を出力するビューの実装
+ *
+ *  @author     Yoshinari Takaoka <takaoka@beatcraft.com>
+ *  @access     public
+ *  @package    Ethna
+ */
+class Ethna_View_404 extends Ethna_ViewClass
+{
+    /**#@+
+     *  @access private
+     */
+
+    /**#@-*/
+
+    /**
+     *  404 ページを出力するための前処理を行う
+     *
+     *  @access public
+     *  @param  array  $param  出力に必要なユーザー定義パラメータ
+     */
+    function preforward($param = array())
+    {
+        $this->error(404);
+    }
+
+}
+// }}}
+?>
diff --git a/class/View/Ethna_View_500.php b/class/View/Ethna_View_500.php
new file mode 100644 (file)
index 0000000..8a9a469
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+// vim: foldmethod=marker
+/**
+ *  Ethna_View_500.php
+ *
+ *  @author     Yoshinari Takaoka <takaoka@beatcraft.com>
+ *  @license    http://www.opensource.org/licenses/bsd-license.php The BSD License
+ *  @package    Ethna
+ *  @version    $Id$
+ */
+
+// {{{ Ethna_View_500
+/**
+ *  500ページ(内部エラーが起きた場合のエラーページ)
+ *  を出力するビューの実装
+ *
+ *  @author     Yoshinari Takaoka <takaoka@beatcraft.com>
+ *  @access     public
+ *  @package    Ethna
+ */
+class Ethna_View_500 extends Ethna_ViewClass
+{
+    /**#@+
+     *  @access private
+     */
+
+    /**#@-*/
+
+    /**
+     *  500 ページを出力するための前処理を行う
+     *
+     *  @access public
+     *  @param  array  $param  出力に必要なユーザー定義パラメータ
+     */
+    function preforward($param = array())
+    {
+        $this->error(500);
+    }
+
+}
+// }}}
+?>
diff --git a/class/View/Ethna_View_Json.php b/class/View/Ethna_View_Json.php
new file mode 100644 (file)
index 0000000..d5b93da
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+// vim: foldmethod=marker
+/**
+ *  Ethna_View_Json.php
+ *
+ *  @author     Yoshinari Takaoka <takaoka@beatcraft.com>
+ *  @license    http://www.opensource.org/licenses/bsd-license.php The BSD License
+ *  @package    Ethna
+ *  @version    $Id$
+ */
+
+// {{{ Ethna_View_Json
+/**
+ *  JSON を出力するビューの実装
+ *
+ *  @author     Yoshinari Takaoka <takaoka@beatcraft.com>
+ *  @access     public
+ *  @package    Ethna
+ */
+class Ethna_View_Json extends Ethna_ViewClass
+{
+    /**#@+
+     *  @access private
+     */
+
+    /**#@-*/
+
+    /**
+     *  Jsonを出力する
+     *
+     *  @access public
+     *  @param  array  $encode_param  出力するJSONにエンコードする値
+     */
+    function preforward($encode_param = array())
+    {
+        $client_enc = $this->ctl->getClientEncoding();
+        if (mb_enabled() && strcasecmp('UTF-8', $client_enc) != 0) {
+            mb_convert_variables('UTF-8', $client_enc, $encode_param);
+        }
+        $encoded_param = json_encode($encode_param);
+
+        header('Content-Type: application/json; charset=UTF-8');
+        echo $encoded_param;
+    }
+
+    function forward()
+    {
+        // do nothing.
+    }
+}
+// }}}
+?>
diff --git a/class/View/Ethna_View_Redirect.php b/class/View/Ethna_View_Redirect.php
new file mode 100644 (file)
index 0000000..e0a6668
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+// vim: foldmethod=marker
+/**
+ *  Ethna_View_Redirect.php
+ *
+ *  @author     Yoshinari Takaoka <takaoka@beatcraft.com>
+ *  @license    http://www.opensource.org/licenses/bsd-license.php The BSD License
+ *  @package    Ethna
+ *  @version    $Id$
+ */
+
+// {{{ Ethna_View_Redirect
+/**
+ *  別のURLへリダイレクトするためのビューの実装
+ *
+ *  @author     Yoshinari Takaoka <takaoka@beatcraft.com>
+ *  @access     public
+ *  @package    Ethna
+ */
+class Ethna_View_Redirect extends Ethna_ViewClass
+{
+    /**#@+
+     *  @access private
+     */
+
+    /**#@-*/
+
+    /**
+     *  別のURLへリダイレクトするための前処理を行う
+     *
+     *  @access public
+     *  @param  string  $url  リダイレクト先のURL
+     */
+    function preforward($url = NULL)
+    {
+        if (is_null($url)) {
+            Ethna::raiseWarning(
+                "URL is not set! use array('redirect', $url); on ActionClass."
+            );
+        }
+        $this->redirect($url);
+    }
+
+    /**
+     *  遷移名に対応する画面を出力する
+     *
+     *  @access public
+     */
+    function forward()
+    {
+         // do nothing.
+    }
+}
+// }}}
+?>
diff --git a/skel/template.404.tpl b/skel/template.404.tpl
new file mode 100644 (file)
index 0000000..b290e05
--- /dev/null
@@ -0,0 +1 @@
+404 Not Found.
diff --git a/skel/template.500.tpl b/skel/template.500.tpl
new file mode 100644 (file)
index 0000000..4cb023e
--- /dev/null
@@ -0,0 +1 @@
+500 Internal Server Error.
index cece182..b6410c1 100644 (file)
@@ -1,11 +1,3 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset={$client_enc}" />
-<link rel="stylesheet" href="{$config.url}css/ethna.css" type="text/css" />
-</head>
-<body>
-
 <div id="header">
     <h1>{$project_id}</h1>
 </div>
@@ -18,6 +10,3 @@
 <div id="footer">
     Powered By <a href="http://ethna.jp">Ethna</a>-{$smarty.const.ETHNA_VERSION}.
 </div>
-
-</body>
-</html>
diff --git a/skel/template.layout.tpl b/skel/template.layout.tpl
new file mode 100644 (file)
index 0000000..64ec999
--- /dev/null
@@ -0,0 +1,10 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset={$client_enc}" />
+<link rel="stylesheet" href="{$config.url}css/ethna.css" type="text/css" />
+</head>
+<body>
+{$content}
+</body>
+</html>