<?php
/**
 * -----------------------------------------------------------------------------
 *
 * SyL - Web Application Framework for PHP
 *
 * PHP version 4 (>= 4.3.x) or 5
 *
 * Copyright (C) 2006 k.watanabe
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * -----------------------------------------------------------------------------
 * @package   SyL
 * @author    k.watanabe <k.watanabe@syl.jp>
 * @copyright 2006 k.watanabe
 * @license   http://www.opensource.org/licenses/lgpl-license.php
 * @version   CVS: $Id: SyL_Form.php,v 1.7 2006/12/23 11:13:09 seasonstream Exp $
 * @link      http://www.syl.jp/
 * -----------------------------------------------------------------------------
 */

// セッション使用判定
define( 'SYL_FORM_SESSION', false );
// JSエラーメッセージデフォルトヘッダ
define( 'SYL_FORM_JS_ERROR_HEADER', '※以下のエラーが発生しました。\n\n' );
// JSエラーメッセージデフォルトフッタ
define( 'SYL_FORM_JS_ERROR_FOOTER', '' );
// JS外部スクリプト
define( 'SYL_FORM_JS_SCRIPT', '/form/SyL_Validator.js' );

/**
 * 検証個別クラスの基底クラス
 */
require_once 'SyL_FormElement.php';

/**
 * フォームクラス
 * 
 * @package   SyL
 * @author    k.watanabe <k.watanabe@syl.jp>
 * @copyright 2006 k.watanabe
 * @license   http://www.opensource.org/licenses/lgpl-license.php
 * @version   CVS: $Id: SyL_Form.php,v 1.7 2006/12/23 11:13:09 seasonstream Exp $
 * @link      http://www.syl.jp/
 */
class SyL_Form
{
    /**
     * フォーム属性配列
     *
     * @access private
     * @var array
     */
    var $attributes = array();
    /**
     * フォーム要素の基本オブジェクト格納配列
     *
     * @access private
     * @var array
     */
    var $elements = array();
    /**
     * 結果表示用配列
     *
     * @access private
     * @var array
     */
    var $results = array();
    /**
     * セッション可能判定
     *
     * @access private
     * @var bool
     */
    var $session = false;
    /**
     * JavaScript入力チェック使用判定
     *
     * @access private
     * @var bool
     */
    var $js = false;
    /**
     * JavaScript入力チェック関数名
     *
     * @access private
     * @var string
     */
    var $js_func_name = 'SyLFormCheck';
    /**
     * JavaScriptエラーメッセージのヘッダ
     *
     * @access private
     * @var string
     */
    var $js_error_header = SYL_FORM_JS_ERROR_HEADER;
    /**
     * JavaScriptエラーメッセージのフッタ
     *
     * @access private
     * @var string
     */
    var $js_error_footer = SYL_FORM_JS_ERROR_FOOTER;
    /**
     * JavaScriptライブラリURL
     *
     * @access private
     * @var string
     */
    var $js_url = SYL_FORM_JS_SCRIPT;

    /**
     * コンストラクタ
     *
     * @access public
     * @param string フォーム要素HTMLタグ名
     * @param string フォーム要素表示名（チェックボックスの場合のみ配列）
     * @param string フォーム要素の値
     * @param array フォーム要素の部品配列（radio, select, checkboxの場合のみ）
     * @param string フォーム要素の追加属性
     */
    function SyL_Form($form_name="syl_form", $action=null, $method='post')
    {
        // セッションが使用可能か判定
        if (SYL_FORM_SESSION) {
            if (!session_id()) {
                session_start();
            }
            if (isset($_SESSION['__SyL_Form__'])) {
                $this->session = true;
            } else {
                $_SESSION['__SyL_Form__'] = true;
            }
        }

        $this->attributes['name']   = $form_name;
        $this->attributes['method'] = $method;
        $this->attributes['action'] = ($action == null) ? $_SERVER["PHP_SELF"] : $action;

        // フォームステータスを初期化
        $this->results['syl_form']       = '';
        $this->results['syl_javascript'] = '';
        $this->results['syl_read_only']  = false;
        $this->results['syl_error']      = false;
        // エラーメッセージを初期化
        $this->results['syl_error_message'] = array();
    }

    /**
     * 個別フォーム要素オブジェクトの取得
     *
     * @access public
     * @param string フォーム要素タイプ
     * @param string フォーム要素HTMLタグ名
     * @param string フォーム要素表示名
     * @param array フォーム要素の部品配列（radio, select, checkboxの場合のみ）
     * @param mixed フォーム要素の値（checkboxの場合は配列、それ以外はstring）
     * @param string フォーム要素の追加属性
     */
    function &createElement($type, $element_name, $display_name, $parts=array(), $value=null, $attributes=array())
    {
        return SyL_FormElement::create($type, $element_name, $display_name, $parts, $value, $attributes);
    }

    /**
     * フォーム要素オブジェクトをセット
     *
     * @access public
     * @param object フォーム要素オブジェクト
     */
    function addElement(&$element)
    {
        $name = $element->getName();
        $element->setFormName($this->attributes['name']);
        $element->setFormSession($this->session);
        // 要素オブジェクト格納
        $this->elements[$name] =& $element;
        // 結果配列初期化
        $this->results[$name] = array(
          'label'         => '',
          'html'          => '',
          'require'       => false,
          'error'         => false,
          'error_message' => '',
          'read_only'     => false
        );
      }

    /**
     * JavaScript使用判定
     *
     * @access public
     * @param bool JavaScript使用判定
     */
    function setJavaScript( $js )
    {
        $this->js = $js;
        $this->attributes['onSubmit'] = 'return ' . $this->js_func_name . '(this);';
    }

    /**
     * JavaScriptエラーメッセージヘッダ、フッタの設定
     *
     * @access public
     * @param string JavaScriptエラーメッセージヘッダ
     * @param string JavaScriptエラーメッセージフッタ
     */
    function setJavaScriptHeaderFooter($js_header, $js_footer)
    {
        $this->js_error_header = $js_header;
        $this->js_error_footer = $js_footer;
    }

    /**
     * 全ての要素の検証を実行する
     *
     * @access public
     * @return true: エラーなし、false: エラーあり
     */
    function validate()
    {
        $result = true;
        foreach (array_keys($this->elements) as $key) {
            if (!$this->elements[$key]->validate()) {
                $name          = $this->elements[$key]->getName();
                $error_message = $this->elements[$key]->getErrorMessage();
                $this->results[$name]['error'] = true;
                $this->results[$name]['error_message'] = $error_message;
                $this->results['syl_error'] = true;
                $this->results['syl_error_message'][] = $error_message;
                $result = false;
            }
        }
        return $result;
    }

    /**
     * フォーム要素表示名を取得する
     *
     * @access public
     * @param string フォーム要素名
     * @return string フォーム要素表示名
     */
    function getName($name)
    {
        return $this->elements[$name]->getDisplayName();
    }

    /**
     * フォーム要素のHTMLを取得
     *
     * @access public
     * @param string フォーム要素名
     * @param bool true: htmlエンコードを行う、false: htmlエンコードを行わない
     * @return string フォーム要素のHTML
     */
    function getHtml($name)
    {
        return $this->elements[$name]->getHtml();
    }

    /**
     * 要素の値を取得
     *
     * @access public
     * @param string フォーム要素名
     * @return mixed 要素の値
     */
    function getValue($name)
    {
        return $this->elements[$name]->getValue();
    }

    /**
     * 全ての要素の値を取得
     *
     * @access public
     * @return array 全ての要素の値
     */
    function getValueAll()
    {
        $values = array();
        foreach (array_keys($this->elements) as $name) {
            $values[$name] = $this->getValue($name);
        }
        return $values;
    }

    /**
     * フォーム要素が必須か判定する
     *
     * @access public
     * @param string フォーム要素名
     * @return bool true: 必須、false: 任意
     */
    function isRequire($name)
    {
        if ($this->results['syl_read_only']) {
            // 表示時は、任意状態とする
            return false;
        } else {
            return $this->elements[$name]->isRequire();
        }
    }

    /**
     * フォーム要素が表示のみか判定する
     *
     * @access public
     * @param string フォーム要素名
     * @return bool true: 必須、false: 任意
     */
    function isReadOnly($name)
    {
        return $this->elements[$name]->isReadOnly();
    }

    /**
     * フォームの全要素を表示のみにするか
     *
     * @access public
     * @param bool フォームの全要素を表示のみフラグ
     */
    function readOnly($read_only)
    {
        foreach (array_keys($this->elements) as $key) {
            $this->elements[$key]->readOnly($read_only);
        }

        $this->results['syl_read_only'] = $read_only;
    }

    /**
     * フォーム要素のエラーを判定する
     *
     * @access public
     * @param string フォーム要素名
     * @return bool true: 必須、false: 任意
     */
    function isError($name='')
    {
        if ($name == '') {
            foreach ($this->results as $name => $value) {
                if ((substr($name, 0, 4) != 'syl_') && $value['error']) {
                    return true;
                }
          }
          return false;
        } else {
            return $this->results[$name]['error'];
        }
    }

    /**
     * エラーメッセージをセットする
     *
     * @access public
     * @param string フォーム要素名
     * @param string エラーメッセージ
     */
    function setErrorMessage($name, $error_message)
    {
        $this->results[$name]['error'] = true;
        $this->results[$name]['error_message'] = $error_message;
        $this->results['syl_error'] = true;
        $this->results['syl_error_message'][] = $error_message;
    }

    /**
     * フォーム要素のエラーを取得する
     *
     * @access public
     * @param string フォーム要素名
     * @return string エラーメッセージ
     */
    function getErrorMessage($name)
    {
        return $this->results[$name]['error_message'];
    }

    /**
     * フォーム要素の全エラーを取得する
     *
     * @access public
     * @param string フォーム要素名
     * @return array エラーメッセージ配列
     */
    function getErrorMessageAll()
    {
        return $this->results['syl_error_message'];
    }

    /**
     * フォームタグの属性をセット
     *
     * @access public
     * @param string フォームタグの属性
     * @param string フォームタグの属性値
     */
    function setAttribute($key, $value)
    {
        $this->attributes[$key] = $value;
    }

    /**
     * フォームタグの属性を取得
     *
     * @access public
     * @return string フォームタグの属性
     */
    function getAttributes()
    {
        $attributes = '';
        foreach ($this->attributes as $name => $value) {
            $attributes .= $name . '="' . $value . '" ';
        }
        return $attributes;
    }

    /**
     * JavaScript入力チェックタグを取得
     *
     * @access public
     * @return string JavaScript入力チェックタグ
     */
    function getJavaScript()
    {
        static $js_include = false;
        if (!$this->js) {
            return '';
        }

        $js  = '';
        if (!$js_include) {
            $js  = '<script language="JavaScript" src="' . $this->js_url . '"></script>' . "\r\n";
            $js_include = true;
        }
        $js .= '<script language="javascript">' . "\r\n";
        $js .= '<!--' . "\r\n";
        $js .= 'var ' . $this->js_func_name . '_submit = false;' . "\r\n";
        $js .= 'function ' . $this->js_func_name . '(f)' . "\r\n";
        $js .= '{' . "\r\n";
        $js .= 'if (' . $this->js_func_name . '_submit) {' . "\r\n";
        $js .= '  return false;' . "\r\n";
        $js .= '}' . "\r\n";
        $js .= 'var validator = new SyL_Validator(f);' . "\r\n";
        $js .= "\r\n";
        foreach (array_keys($this->elements) as $key) {
            $js .= $this->elements[$key]->getJavaScript();
        }
        $js .= 'if (!' . $this->js_func_name . '_submit) {' . "\r\n";
        $js .= '  if (validator.isError()) {' . "\r\n";
        $js .= '    validator.displayErrorMessage(true, "' . $this->js_error_header . '", "' . $this->js_error_footer . '");' . "\r\n";
        $js .= '    return false;' . "\r\n";
        $js .= '  } else {' . "\r\n";
        $js .= '    ' . $this->js_func_name . '_submit = true;' . "\r\n";
        $js .= '    return true;' . "\r\n";
        $js .= '  }' . "\r\n";
        $js .= '} else {'         . "\r\n";
        $js .= '  return false;'  . "\r\n";
        $js .= '}'                . "\r\n";
        $js .= "\r\n";
        $js .= '}'         . "\r\n";
        $js .= '//-->'     . "\r\n";
        $js .= '</script>' . "\r\n";

        return $js;
    }

    /**
     * フォーム全要素を配列で取得する
     *
     * @access public
     * @return array フォーム全要素を配列
     */
    function &getResultArray()
    {
        foreach (array_keys($this->elements) as $name) {
            $this->results[$name]['label']     = $this->getName($name);
            $this->results[$name]['html']      = $this->getHtml($name);
            $this->results[$name]['require']   = $this->isRequire($name);
            $this->results[$name]['read_only'] = $this->isReadOnly($name);
        }
        // フォームタグの属性
        $this->results['syl_javascript'] = $this->getJavaScript();
        $this->results['syl_form']       = $this->getAttributes();
        return $this->results;
    }
}

?>
