sintonia/airtime_mvc/library/propel/contrib/pat/patForms.php

2785 lines
68 KiB
PHP

<?php
/**
* patForms form manager class - serialize form elements into any given output format
* using element classes, and build the output via renderer classes.
*
* $Id: patForms.php 1347 2009-12-03 21:06:36Z francois $
*
* @package patForms
* @author Sebastian Mordziol <argh@php-tools.net>
* @author gERD Schaufelberger <gerd@php-tools.net>
* @author Stephan Schmidt <schst@php-tools.net>
* @copyright 2003-2004 PHP Application Tools
* @license LGPL
* @link http://www.php-tools.net
*/
/**
* set the include path
*/
if ( !defined( 'PATFORMS_INCLUDE_PATH' ) ) {
define( 'PATFORMS_INCLUDE_PATH', dirname( __FILE__ ). '/patForms' );
}
/**
* needs helper methods of patForms_Element
*/
include_once PATFORMS_INCLUDE_PATH . "/Element.php";
/**
* error definition: renderer base class file (renderers/_base.php) could not
* be found.
*
* @see patForms::_createModule()
*/
define( "PATFORMS_ERROR_NO_MODULE_BASE_FILE", 1001 );
/**
* error definition: the specified renderer could not be found.
*
* @see patForms::_createModule()
*/
define( "PATFORMS_ERROR_MODULE_NOT_FOUND", 1002 );
/**
* error definition: the element added via the {@link patForms::addElement()}
* is not an object. Use the {@link patForms::createElement()} method to
* create an element object.
*
* @see patForms::addElement()
* @see patForms::createElement()
*/
define( "PATFORMS_ERROR_ELEMENT_IS_NO_OBJECT", 1003 );
/**
* error definition: generic unexpected error.
*/
define( "PATFORMS_ERROR_UNEXPECTED_ERROR", 1004 );
/**
* element does not exist
*/
define( "PATFORMS_ERROR_ELEMENT_NOT_FOUND", 1012 );
/**
* renderer object has not been set - if you want to render the form, you have to
* set a renderer object via the {@link patForms::setRenderer()} method. To create
* a renderer, use the {@link patForms::createRenderer()} method.
*
* @see patForms::setRenderer()
* @see patForms::createRenderer()
*/
define( "PATFORMS_ERROR_NO_RENDERER_SET", 1013 );
/**
* invalid renderer
*
* @see createRenderer()
*/
define( "PATFORMS_ERROR_INVALID_RENDERER", 1014 );
/**
* invalid method
*
* @see setMethod()
*/
define( "PATFORMS_ERROR_INVALID_METHOD", 1015 );
/**
* Given parameter is not a boolean value
*/
define( "PATFORMS_ERROR_PARAMETER_NO_BOOL", 1016 );
/**
* Given Static property does not exist
*/
define( "PATFORMS_ERROR_NO_STATIC_PROPERTY", 1017 );
/**
* Unknown event
*/
define( "PATFORMS_ERROR_UNKNOWN_EVENT", 1018 );
/**
* Invalid event handler
*/
define( "PATFORMS_ERROR_INVALID_HANDLER", 1019 );
/**
* Event exists
*/
define( 'PATFORMS_NOTICE_EVENT_ALREADY_REGISTERED', 1020 );
/**
* Invalid storage container
*/
define( 'PATFORMS_ERROR_INVALID_STORAGE', 1021 );
define( 'PATFORMS_NOTICE_ARRAY_EXPECTED', 1022 );
define( 'PATFORMS_NOTICE_ATTRIBUTE_NOT_SUPPORTED', 1023 );
define( 'PATFORMS_NOTICE_INVALID_OPTION', 1024 );
define( 'PATFORMS_ERROR_ATTRIBUTE_REQUIRED', 1025 );
define( 'PATFORMS_ERROR_CAN_NOT_VERIFY_FORMAT', 1026 );
define( 'PATFORMS_ERROR_METHOD_FOR_MODE_NOT_AVAILABLE', 1027 );
/**
* errors apply on translating errors matching current locale settings
*/
define( 'PATFORMS_NOTICE_VALIDATOR_ERROR_LOCALE_UNDEFINED', 1028 );
define( 'PATFORMS_WARNING_VALIDATOR_ERROR_UNDEFINED', 1029 );
/**
* apply the rule before the built-in validation
*/
define( 'PATFORMS_RULE_BEFORE_VALIDATION', 1 );
/**
* apply the rule after the built-in validation
*/
define( 'PATFORMS_RULE_AFTER_VALIDATION', 2 );
/**
* apply the rule before AND after the built-in validation
*/
define( 'PATFORMS_RULE_BOTH', 3 );
/**
* attach the observer to the elements
*/
define( 'PATFORMS_OBSERVER_ATTACH_TO_ELEMENTS', 1 );
/**
* attach the observer to the form
*/
define( 'PATFORMS_OBSERVER_ATTACH_TO_FORM', 2 );
/**
* attach the observer to the form and the elements
*/
define( 'PATFORMS_OBSERVER_ATTACH_TO_BOTH', 3 );
/**
* group values should stay nested
*/
define('PATFORMS_VALUES_NESTED', 0);
/**
* group values should be flattened
*/
define('PATFORMS_VALUES_FLATTENED', 1);
/**
* group values should be prefixed
*/
define('PATFORMS_VALUES_PREFIXED', 2);
/**
* Static patForms properties - used to emulate pre-PHP5 static properties.
*
* @see setStaticProperty()
* @see getStaticProperty()
*/
$GLOBALS['_patForms'] = array(
'format' => 'html',
'locale' => 'C',
'customLocales' => array(),
'autoFinalize' => true,
'defaultAttributes' => array(),
);
/**
* patForms form manager class - serialize form elements into any given output format
* using element classes, and build the output via renderer classes.
*
* @package patForms
* @author Sebastian Mordziol <argh@php-tools.net>
* @author gERD Schaufelberger <gerd@php-tools.net>
* @author Stephan Schmidt <schst@php-tools.net>
* @copyright 2003-2004 PHP Application Tools
* @license LGPL
* @link http://www.php-tools.net
* @version 0.9.0alpha
* @todo check the clientside functionality, as that can lead to broken pages
*/
class patForms
{
/**
* javascript that will displayed only once
*
* @access private
* @var array
*/
var $globalJavascript = array();
/**
* javascript that will be displayed once per instance
*
* @access private
* @var array
*/
var $instanceJavascript = array();
/**
* stores the mode for the form. It defaults to 'default', and is only overwritten if
* set specifically. It is passed on to any elements you create.
*
* @access private
* @see setMode()
*/
var $mode = 'default';
/**
* XML entities
*
* @access private
* @see toXML()
* @todo This is redundant to the Element's xmlEntities property - find a way to keep this in one place
*/
var $xmlEntities = array(
"<" => "&lt;",
">" => "&gt;",
"&" => "&amp;",
"'" => "&apos;",
'"' => "&quot;"
);
/**
* stores the format for the element. It defaults to 'html', and is only overwritten if
* set specifically. It is passed on to any elements you create.
*
* @access private
* @see setFormat()
*/
var $format = 'html';
/**
* stores the flag telling the form whether it has been submitted - this is passed on to any
* elements you create.
*
* @access private
* @see setSubmitted()
*/
var $submitted = false;
/**
* stores the element objects of this form.
* @access private
* @see addElement()
*/
var $elements = array();
/**
* stores the current element count for this form, used to generate the ids for each element
* @access private
* @see getElementId()
*/
var $elementCounter = 0;
/**
* stores a renderer
* @access private
* @see setRenderer(), renderForm()
*/
var $renderer = null;
/**
* stores the locale to use when adding validation errors for the whole form.
*
* @access private
* @var string $locale
* @see setLocale()
*/
var $locale = 'C';
/**
* stores custom locale
*
* @access private
* @var array
* @see setLocale()
*/
var $customLocales = array();
/**
* stores the element name
* @access private
* @see getElementName()
*/
var $elementName = 'Form';
/**
* flag to indicate, whether form should be validated automatically
* by renderForm()
*
* @access private
* @var string
* @see setAutoValidate(), renderForm()
*/
var $autoValidate = false;
/**
* name of the variable that indicates, whether the form has
* been submitted.
*
* @access private
* @var string
* @see setAutoValidate()
*/
var $submitVar = null;
/**
* event handlers
*
* @access private
* @var array
* @see registerEventHandler()
* @see registerEvent()
*/
var $_eventHandler = array();
/**
* events that can be triggered
*
* @access private
* @var array
* @see registerEventHandler()
* @see triggerEvent()
* @see registerEvent()
*/
var $_validEvents = array( 'onInit', 'onValidate', 'onSubmit', 'onError', 'onSuccess' );
/**
* Stores whether the current form has been validated
*
* @access private
*/
var $validated = false;
/**
* Stores whether the current form is valid or not (after the
* validation process)
*
* @access private
*/
var $valid = null;
/**
* Stores the names of all static properties that patForms will use as defaults
* for the properties with the same name on startup.
*
* @access private
*/
var $staticProperties = array(
'format' => 'setFormat',
'autoFinalize' => 'setAutoFinalize',
'locale' => 'setLocale',
);
/**
* Stores the flag for the autoFinalize feature
*
* @access private
*/
var $autoFinalize = true;
/**
* custom validation rules
*
* @access private
* @var array
*/
var $_rules = array();
/**
* define error codes an messages for the form
*
* Will be set by validation rules that have been
* added to the form.
*
* @access private
* @var array $validatorErrorCodes
*/
var $validatorErrorCodes = array();
/**
* stores any validation errors that can occurr during the
* form's validation process.
*
* @access private
* @var array $validationErrors
*/
var $validationErrors = array();
/**
* next error offset for rules
* @access private
* @var integer
*/
var $nextErrorOffset = 1000;
/**
* Attributes of the form - needed to generate the form tag
*
* @access private
* @var array $attributes
* @see setAttribute()
*/
var $attributes = array();
/**
* Attribute definition for the form - defines which attribute the form
* itself supports.
*
* @access public
*/
var $attributeDefinition = array(
'id' => array(
'required' => false,
'format' => 'string',
'outputFormats' => array( 'html' ),
),
'name' => array(
'required' => true,
'format' => 'string',
'outputFormats' => array( 'html' ),
),
'method' => array(
'required' => true,
'format' => 'string',
'default' => 'post',
'outputFormats' => array( 'html' ),
),
'action' => array(
'required' => true,
'format' => 'string',
'outputFormats' => array( 'html' ),
),
'accept' => array(
'required' => false,
'format' => 'string',
'outputFormats' => array( 'html' ),
),
'accept-charset' => array(
'required' => false,
'format' => 'string',
'outputFormats' => array( 'html' ),
),
'enctype' => array(
'required' => false,
'format' => 'string',
'outputFormats' => array( 'html' ),
),
'onreset' => array(
'required' => false,
'format' => 'string',
'outputFormats' => array( 'html' ),
),
'onsubmit' => array(
'required' => false,
'format' => 'string',
'outputFormats' => array( 'html' ),
),
'target' => array(
'required' => false,
'format' => 'string',
'outputFormats' => array( 'html' ),
),
);
/**
* Stores all available patForms options - these are inherited by all elements
* and their dependencies, like rules.
*
* Short option overview:
*
* - scripts: enable client script integration
*
* @access public
*/
var $options = array(
'scripts' => array(
'enabled' => true,
'params' => array(),
),
);
/**
* observers of the form
*
* @access private
* @var array
*/
var $observers = array();
/**
* Sets the default attributes that will be inherited by any elements you add to the form.
*
* <b>Note:</b> You have to call this method statically before creating a new form if you use
* patForm's automatic element creation feature via the {@link createForm()} method, as the
* default attributes cannot be set after an element has been created.
*
* @static
* @access public
* @param array $attributes The list of attributes to set with key => value pairs.
*/
function setDefaultAttributes( $attributes )
{
patForms::setStaticProperty( 'defaultAttributes', $attributes );
}
/**
* sets the locale (language) to use for the validation error messages of all elements
* in the form.
*
* @access public
* @param string language code
* @param string optional language file
* @return bool True on success
*/
function setLocale( $locale, $languageFile = null )
{
if (!is_null($languageFile)) {
$languageData = patForms::parseLocaleFile($languageFile);
$customLocales = patForms::getStaticProperty('customLocales');
$customLocales[$locale] = $languageData;
patForms::setStaticProperty('customLocales', $customLocales);
}
if ( isset( $this ) && is_a( $this, 'patForms' ) ) {
$this->locale = $locale;
if ( !empty( $this->elements ) ) {
$cnt = count( $this->elements );
for ( $i=0; $i < $cnt; $i++ ) {
$this->elements[$i]->setLocale( $locale );
}
}
} else {
patForms::setStaticProperty('locale', $locale);
}
return true;
}
/**
* checks, whether a locale is a custom locale
*
* @static
* @access public
* @param string locale name
* @return boolean
*/
function isCustomLocale($locale)
{
$customLocales = patForms::getStaticProperty('customLocales');
if (isset($customLocales[$locale])) {
return true;
}
return false;
}
/**
* get the custom locale for an element or a rule
*
* @static
* @access public
* @param string locale
* @param string key
* @return array
*/
function getCustomLocale($locale, $key)
{
$customLocales = patForms::getStaticProperty('customLocales');
if (!isset($customLocales[$locale])) {
return false;
}
if (!isset($customLocales[$locale][$key])) {
return false;
}
return $customLocales[$locale][$key];
}
/**
* parses a locale file
*
* @access private
* @param string filename
* @return array locale information
* @todo add some file checks
*/
function parseLocaleFile($filename)
{
return parse_ini_file($filename, true);
}
/**
* sets the format of the element - this will be passed on to any elements you create. If you
* have already added some elements when you call this method, it will be passed on to them too.
*
* @access public
* @param string $format The name of the format you have implemented in your element(s).
* @return bool $result True on success
* @see setMode()
* @see format
* @see serialize()
*/
function setFormat( $format )
{
if ( isset( $this ) && is_a( $this, 'patForms' ) )
{
$this->format = strtolower( $format );
if ( !empty( $this->elements ) )
{
$cnt = count( $this->elements );
for ( $i=0; $i < $cnt; $i++ )
{
$this->elements[$i]->setFormat( $format );
}
}
}
else
{
patForms::setStaticProperty( 'format', $format );
}
return true;
}
/**
* sets the mode of the form - If you have already added some elements when you call this
* method, it will be passed on to them too.
*
* @access public
* @param string $mode The mode to set the form to: default|readonly or any other mode you have implemented in your element class(es). Default is 'default'.
* @see setMode()
* @see mode
* @see serialize()
*/
function setMode( $mode )
{
$this->mode = strtolower( $mode );
if ( !empty( $this->elements ) )
{
$cnt = count( $this->elements );
for ( $i=0; $i < $cnt; $i++ )
{
$this->elements[$i]->setMode( $mode );
}
}
}
/**
* sets the current submitted state of the form. Set this to true if you want the form
* to pick up its submitted data. It will pass on this information to all elements that
* have been added so far, and new ones inherit it too.
*
* @access public
* @param bool $state True if it has been submitted, false otherwise (default).
* @see isSubmitted()
* @see submitted
*/
function setSubmitted( $state )
{
if ( $state == true )
{
$eventState = $this->triggerEvent( 'Submit' );
if ( $eventState === false )
return false;
}
$this->submitted = $state;
if ( !empty( $this->elements ) )
{
$cnt = count( $this->elements );
for ( $i=0; $i < $cnt; $i++ )
{
$this->elements[$i]->setSubmitted( $state );
}
}
return $state;
}
/**
* sets the method for the request
*
* @access public
* @param string $method GET or POST
* @see method
* @uses setAttribute()
*/
function setMethod( $method )
{
$method = strtolower( $method );
if ( $method != 'get' && $method != 'post' )
{
return patErrorManager::raiseError(
PATFORMS_ERROR_INVALID_METHOD,
'Unknown method "'.$method.'". Currently only GET and POST are supported as patForms methods.'
);
}
$this->setAttribute( 'method', $method );
return true;
}
/**
* sets the action for the form
*
* This is a only a wrapper for setAttribute()
*
* @access public
* @param string $action
* @see setAttribute()
*/
function setAction( $action )
{
return $this->setAttribute( 'action', $action );
}
/**
* Sets the AutoFinalize mode for the form. The AutoFinalize mode will tell patForms to
* finalize all elements after the form has been validated successfully.
*
* @access public
* @param boolean $mode Whether to activate the AutoFinalize mode (true) or not (false).
* @return boolean $success True if okay, a patError object otherwise.
* @see finalizeForm()
*/
function setAutoFinalize( $mode )
{
if ( !is_bool( $mode ) )
{
return patErrorManager::raiseError(
PATFORMS_ERROR_PARAMETER_NO_BOOL,
'The setAutoFinalize() method requires a boolean value ( true or false ) as parameter.'
);
}
if ( isset( $this ) && is_a( $this, 'patForms' ) )
{
$this->autoFinalize = $mode;
}
else
{
patForms::setStaticProperty( 'autoFinalize', $mode );
}
return true;
}
/**
* Wrapper method that adds a filter to all elements
* of the form at once instead of having to do it for
* each element.
*
* @access public
* @param object &$filter The filter object to apply
* @see patForms_Element::applyFilter()
* @todo add error management and docs once the element's applyFilter method has too
*/
function applyFilter( &$filter )
{
if ( empty( $this->elements ) )
return true;
$cnt = count( $this->elements );
for ( $i = 0; $i < $cnt; $i++ )
{
$this->elements[$i]->applyFilter( $filter );
}
}
/**
* creates a new patForms object and returns it; this method is made to be called statically
* to be able to create a new patForms object from anywhere.
*
* @access public
* @param array $formDefinition Optional form definition for elements that will be added to the form
* @param array $attributes The attributes to set for the form itself
* @return object patForms $form The new patForms object.
* @todo it should be possible to pass Rule definitions, so they can be loaded and added automatically.
*/
function &createForm( $formDefinition = null, $attributes = null )
{
$form = &new patForms();
if ( $attributes != null )
{
$form->setAttributes( $attributes );
}
if ( $formDefinition === null )
return $form;
foreach ( $formDefinition as $name => $element )
{
if ( !isset( $element["filters"] ) )
{
$element["filters"] = null;
}
if ( !isset( $element["children"] ) )
{
$element["children"] = null;
}
$el = &$form->createElement( $name, $element["type"], $element["attributes"], $element["filters"], $element["children"] );
if ( isset( $element["renderer"] ) ) {
$el->setRenderer( $element["renderer"] );
}
$result = $form->addElement( $el );
if (patErrorManager::isError( $result )) {
return $result;
}
}
return $form;
}
/**
* add a custom validation rule
*
* @access public
* @param object patForms_Rule validation rule
* @param integer time to apply rule (before or after built-in validation)
* @param boolean apply the rule, even if the form is invalid
* @param boolean should form get revalidated (not implemented yet)
* @return boolean currently always true
*/
function addRule( &$rule, $time = PATFORMS_RULE_AFTER_VALIDATION, $invalid = false, $revalidate = false )
{
$rule->prepareRule( $this );
$this->_rules[] = array(
'rule' => &$rule,
'time' => $time,
'invalid' => $invalid,
'revalidate' => $revalidate
);
}
/**
* patForms PHP5 constructor - processes some intitialization tasks like merging the currently
* set static properties with the internal properties.
*
* @access public
*/
function __construct()
{
foreach ( $this->staticProperties as $staticProperty => $setMethod )
{
$propValue = patForms::getStaticProperty( $staticProperty );
if ( patErrorManager::isError( $propValue ) )
continue;
$this->$setMethod( $propValue );
}
// initialize patForms internal attribute collection
$this->loadAttributeDefaults();
}
/**
* patForms pre-PHP5 constructor - does nothing for the moment except being a wrapper
* for the PHP5 contructor for older PHP versions support.
*
* @access public
*/
function patForms()
{
patForms::__construct();
}
/**
* sets a renderer object that will be used to render
* the form.
*
* @access public
* @param object &$renderer The renderer object
* @return mixed $success True on success, patError object otherwise.
* @see createRenderer()
* @see renderForm()
*/
function setRenderer( &$renderer, $args = array() )
{
if ( !is_object( $renderer ) )
{
return patErrorManager::raiseError(
PATFORMS_ERROR_INVALID_RENDERER,
'You can only set a patForms_Renderer object with the setRenderer() method, "'.gettype( $renderer ).'" given.'
);
}
$this->renderer = &$renderer;
if ( isset( $args['includeElements'] ) && $args['includeElements'] === true )
{
// check all elements - there may be some that need
// renderers too, so we give them the same renderer if
// they don't already have one.
$cnt = count( $this->elements );
for ( $i = 0; $i < $cnt; $i++ )
{
if ( $this->elements[$i]->usesRenderer && !is_object( $this->elements[$i]->renderer ) )
{
$this->elements[$i]->setRenderer( $renderer );
}
}
}
return true;
}
/**
* sets a storage container object that will be used to store data
*
* @access public
* @param object patForms_Storage
* @see createStorage()
*/
function setStorage( &$storage )
{
if ( !is_object( $storage ) )
{
return patErrorManager::raiseError(
PATFORMS_ERROR_INVALID_STORAGE,
'You can only set a patForms_Storage object with the setStorage() method, "'.gettype( $storage ).'" given.'
);
}
$this->registerEventHandlerObject( $storage,
array(
'onInit' => 'loadEntry',
'onValidate' => 'validateEntry',
'onSuccess' => 'storeEntry'
)
);
}
/**
* renders the form with the renderer that was set via the {@link setRenderer()}
* method.
*
* WARNING: This is still in alpha state!
*
* Should this method return a reference??
* The return value could contain large blocks of HTML or large arrays!
* Do we want to copy these?
*
* @access public
* @param mixed $args arguments that will be passed to the renderer
* @return mixed $form The rendered form, or false if failed.
*/
function renderForm( $args = null )
{
if ( $this->renderer === null )
{
return patErrorManager::raiseError(
PATFORMS_ERROR_NO_RENDERER_SET,
'Form cannot be rendered, you have to set a renderer first via the setRenderer() method.'
);
}
// form is not submitted, or auto-validation is disabled => render it
if ( !$this->isSubmitted() || $this->autoValidate !== true )
{
$this->triggerEvent( 'Init' );
return $this->renderer->render( $this, $args );
}
$this->validateForm();
return $this->renderer->render( $this, $args );
}
/**
* Validates all elements of the form.
*
* @access public
* @param boolean Flag to indicate, whether form should be validated again, if it already has been validated.
* @return boolean True if all elements could be validated, false otherwise.
* @see finishForm()
*/
function validateForm( $revalidate = false )
{
if ( $this->validated && !$revalidate )
return $this->valid;
$valid = true;
/**
* validate custom rules
*/
if ( !$this->_applyRules( PATFORMS_RULE_BEFORE_VALIDATION ) )
{
$valid = false;
}
/**
* validate elements
*/
if ( $valid === true )
{
$cnt = count( $this->elements );
for ( $i = 0; $i < $cnt; ++$i )
{
if ( !$this->elements[$i]->validate() )
{
$valid = false;
}
}
}
if ($valid === true) {
$result = $this->triggerEvent('Validate');
if ($result === false) {
$valid = false;
}
}
/**
* validate custom rules
*/
if ( !$this->_applyRules( PATFORMS_RULE_AFTER_VALIDATION, $valid ) )
{
$valid = false;
}
if ( $valid === true && $this->autoFinalize === true )
$this->finalizeForm();
$this->valid = $valid;
$this->validated = true;
if ( $valid === true )
{
$this->_announce( 'status', 'validated' );
$event = 'Success';
}
else
{
$this->_announce( 'status', 'error' );
$event = 'Error';
}
$this->triggerEvent( $event );
return $this->valid;
}
/**
* apply rules
*
* @access private
* @param integer time of validation
* @param boolean form is valid
* @return boolean rules are valid or not
* @todo add documentation
*/
function _applyRules( $time, $isValid = true )
{
$valid = true;
$cnt = count( $this->_rules );
for ($i = 0; $i < $cnt; $i++) {
// wrong time
if (( $this->_rules[$i]['time'] & $time ) != $time) {
continue;
}
if (!$isValid && !$this->_rules[$i]['invalid']) {
continue;
}
$result = $this->_rules[$i]['rule']->applyRule( $this, PATFORMS_RULE_AFTER_VALIDATION );
if ( $result === false ) {
$valid = false;
}
}
return $valid;
}
/**
* Finalizes the form by telling each fom element to finalize - finalizing means to
* process any tasks that need to be done after the form has been validated, like
* deleting any temporary files or whatever an element needs to do at that point.
*
* @access public
* @return bool $success Wether all elements could be finalized
* @see validateForm()
*/
function finalizeForm()
{
$success = true;
$cnt = count( $this->elements );
for ( $i = 0; $i < $cnt; ++$i )
{
if ( !$this->elements[$i]->finalize() )
{
patErrorManager::raiseWarning(
PATFORMS_ERROR_ELEMENT_NOT_FINALIZED,
'Element "'.$this->elements[$i]->elementName.'" could not be finalized. See the element error messages for more details.'
);
$success = false;
}
}
return $success;
}
/**
* creates a new renderer from the patForms renderer collection and returns it.
*
* @access public
* @param string The name of the renderer to create - have a look at the Renderer/ subfolder for a list of available renderers.
* @return object patForms_Renderer The renderer object, or error object
*/
function &createRenderer( $name )
{
return patForms::_createModule( 'Renderer', $name );
}
/**
* creates a new storage container and returns it.
*
* @access public
* @param string The name of the storage to create - have a look at the Storage/ subfolder for a list of available storage containers.
* @return object patForms_Storage The storage container, or error object
*/
function &createStorage( $name )
{
return patForms::_createModule( 'Storage', $name );
}
/**
* Creates a new filter and returns it.
*
* You may pass an array as second parameter that contains
* parameters for the filter. patForms will check for setter methods
* for all keys and set the corresponding values.
*
* This eases the creating of simple filter objects.
*
* @access public
* @param string The name of the filter to create - have a look at the Filter/ subfolder for a list of available filters.
* @param array Optional parameters for the filter, if you provide a parameter, make sure the filter implements a set[Paramname]() method.
* This will be automated with interceptors in the PHP5 version of patForms
* @return object patForms_Filter The filter, or error object
*/
function &createFilter( $name, $params = null )
{
$filter = &patForms::_createModule( 'Filter', $name );
if ( !is_array( $params ) )
{
return $filter;
}
foreach ( $params as $param => $value )
{
$setter = 'set' . ucfirst( $param );
if ( method_exists( $filter, $setter ) )
{
$filter->$setter( $value );
}
}
return $filter;
}
/**
* creates a new rule from the patForms rule collection and returns it.
*
* If your rules are not located in patForms/Rule you have to load and
* instantiate them on your own.
*
* @access public
* @param string The name of the rule to create - have a look at the Rule/ subfolder for a list of available rules.
* @param string The id of the rule, needed if the rule uses client side actions.
* @return object patForms_Rule The rule object, or error object
*/
function &createRule( $name, $id = null )
{
$rule = &patForms::_createModule( 'Rule', $name );
if ( $id != null )
{
$rule->setId( $id );
}
return $rule;
}
/**
* creates a new observer from the patForms observer collection and returns it.
*
* If your observers are not located in patForms/Observer you have to load and
* instantiate them on your own.
*
* @access public
* @param string The name of the observer to create - have a look at the Observer/ subfolder for a list of available observers.
* @return object patForms_Observer The observer object, or error object
*/
function &createObserver( $name )
{
$observer = &patForms::_createModule( 'Observer', $name );
return $observer;
}
/**
* creates a new module for patForms
*
* @access private
* @param string $type type of the module. Possible values are 'Renderer', 'Rule'
* @param string $name The name of the renderer to create - have a look at the renderers/ subfolder for a list of available renderers.
* @return object $module The module object, or an error object
*/
function &_createModule( $type, $name )
{
$baseFile = PATFORMS_INCLUDE_PATH . '/'.$type.'.php';
$baseClass = 'patForms_'.$type;
// if there is an underscore in the module name, we want
// to load the module from a subfolder, so we transform
// all underscores to slashes.
$pathName = $name;
if ( strstr( $pathName, '_' ) )
{
$pathName = str_replace( '_', '/', $name );
}
$moduleFile = PATFORMS_INCLUDE_PATH . '/'.$type.'/'.$pathName.'.php';
$moduleClass = 'patForms_'.$type.'_'.$name;
if ( !class_exists( $baseClass ) )
{
if ( !file_exists( $baseFile ) )
{
return patErrorManager::raiseError(
PATFORMS_ERROR_NO_MODULE_BASE_FILE,
$type .' base file could not be found',
'Tried to load base file in path "'.$baseFile.'"'
);
}
include_once $baseFile;
}
if ( !class_exists( $moduleClass ) )
{
if ( !file_exists( $moduleFile ) )
{
return patErrorManager::raiseError(
PATFORMS_ERROR_MODULE_NOT_FOUND,
$type.' "'.$name.'" file "'.$moduleFile. '" could not be found.'
);
}
include_once $moduleFile;
}
$module = &new $moduleClass();
return $module;
}
/**
* adds an element to the form - has to be a patForms_Element object. Use the {@link createElement()}
* method to create a new element object. Also takes care of passing on the form's configuration
* including the mode, format and submitted flags to the element.
*
* @access public
* @param object &$element The patForms_Element object to add to this form.
* @return bool $success True if everything went well, false otherwise.
* @see patForms_Element
* @see createElement()
*/
function addElement( &$element )
{
if ( !is_object( $element ) )
{
return patErrorManager::raiseError(
PATFORMS_ERROR_ELEMENT_IS_NO_OBJECT,
'The addElement() method expects an element object, "'.gettype( $element ).'" given.'
);
}
if ( patErrorManager::isError( $element ) )
{
return patErrorManager::raiseError(
PATFORMS_ERROR_UNEXPECTED_ERROR,
'The element you are trying to add is a patError object, and not a patForms element object.'
);
}
if ( !$element->getId() ) {
$element->setId( $this->getElementId() );
}
$element->setMode( $this->getMode() );
$element->setFormat( $this->getFormat() );
$element->setSubmitted( $this->isSubmitted() );
$element->setLocale( $this->getLocale() );
$this->elements[] =& $element;
return true;
}
/**
* replaces an element in the form
*
* @access public
* @param object $element The patForms_Element object to be replaced
* @param object &$replace The element that will replace the old element
* @return bool $success True if everything went well, false otherwise.
* @see patForms_Element
* @see addElement()
*/
function replaceElement( $element, &$replace )
{
if ( !is_object( $replace ) ) {
return patErrorManager::raiseError(
PATFORMS_ERROR_ELEMENT_IS_NO_OBJECT,
'The addElement() method expects an element object, "'.gettype( $replace ).'" given.'
);
}
if ( patErrorManager::isError( $replace ) ) {
return patErrorManager::raiseError(
PATFORMS_ERROR_UNEXPECTED_ERROR,
'The element you are trying to add is a patError object, and not a patForms element object.'
);
}
if (is_object($element)) {
$element = $element->getId();
}
$cnt = count($this->elements);
for ($i = 0; $i < $cnt; $i++) {
if ($this->elements[$i]->getId() === $element) {
if ( !$replace->getId() ) {
$replace->setId( $this->getElementId() );
}
$replace->setMode( $this->getMode() );
$replace->setFormat( $this->getFormat() );
$replace->setSubmitted( $this->isSubmitted() );
$replace->setLocale( $this->getLocale() );
$this->elements[$i] = &$replace;
return true;
}
// the current element is a container
if (method_exists($this->elements[$i], 'replaceElement')) {
$result = $this->elements[$i]->replaceElement($element, $replace);
if ($result === true) {
return $result;
}
}
}
return false;
}
/**
* Get an element by its name.
*
* @access public
* @param string $name name of the element
* @return object patForms element
* @deprecated please use patForms::getElementByName() instead
*/
function &getElement( $name )
{
return $this->getElementByName( $name );
}
/**
* Get an element by its name.
*
* @access public
* @param string $name name of the element
* @return mixed either a patForms element or an array containing patForms elements
* @see getElementById()
*/
function &getElementByName( $name )
{
if ( $name == '__form' ) {
return $this;
}
$elements = array();
$cnt = count( $this->elements );
for ($i = 0; $i < $cnt; $i++) {
if ($this->elements[$i]->getName() == $name) {
$elements[] = &$this->elements[$i];
continue;
}
if (method_exists($this->elements[$i], 'getElementById')) {
patErrorManager::pushExpect(PATFORMS_ERROR_ELEMENT_NOT_FOUND);
$result = &$this->elements[$i]->getElementByName($name);
patErrorManager::popExpect();
if (!patErrorManager::isError($result)) {
if (is_array($result)) {
$cnt2 = count( $result );
for ($j = 0; $j < $cnt2; $j++) {
$elements[] = &$result[$j];
}
} else {
$elements[] = &$result;
}
}
}
}
switch( count( $elements ) )
{
case 0:
return patErrorManager::raiseError(
PATFORMS_ERROR_ELEMENT_NOT_FOUND,
'Element '.$name.' could not be found.'
);
break;
case 1:
return $elements[0];
break;
default:
return $elements;
break;
}
}
/**
* Get an element by its id.
*
* @access public
* @param string $id id of the element
* @return object patForms element
*/
function &getElementById( $id )
{
$cnt = count( $this->elements );
for ( $i = 0; $i < $cnt; $i++ )
{
if ( $this->elements[$i]->getId() == $id ) {
return $this->elements[$i];
}
if (method_exists($this->elements[$i], 'getElementById')) {
patErrorManager::pushExpect(PATFORMS_ERROR_ELEMENT_NOT_FOUND);
$result = &$this->elements[$i]->getElementById($id);
patErrorManager::popExpect();
if (!patErrorManager::isError($result)) {
return $result;
}
}
}
return patErrorManager::raiseError(
PATFORMS_ERROR_ELEMENT_NOT_FOUND,
'Element '.$name.' could not be found.'
);
}
/**
* Get all elements of the form
*
* @access public
* @return array all elements of the form
*/
function &getElements()
{
return $this->elements;
}
/**
* Creates a new form element and returns a reference to it.
*
* The optional $filters array has to be in the following format:
*
* <pre>
* array(
* array(
* 'filter' => 'Multiplier',
* 'params' => array( 'multiplier' => 6 )
* )
* )
* </pre>
*
* @access public
* @param string $name The name of the element
* @param string $type The type of the element; for a list of possible elements, have a look at the elements/ subfolder of the patForms package.
* @param array $attributes Attributes for the element
* @param array $filters Optional filters that will be applied
* @return object patForms_Element $element The element object, or patError if failed.
*/
function &createElement( $name, $type, $attributes, $filters = null, $children = null )
{
$element =& patForms::_createModule( 'Element', $type );
if ( patErrorManager::isError( $element ) )
{
return $element;
}
$attributes['name'] = $name;
if ( !isset( $attributes['id'] ) ) {
$attributes['id'] = $this->getElementId();
}
// add default attributes - do this the 'silent' way be checking whether
// the element supports the given attribute, as the element throws a notice
// if it does not support it - this is not expected from default attributes.
foreach ( patForms::getStaticProperty( 'defaultAttributes' ) as $attributeName => $attributeValue )
{
if ( !$element->hasAttribute( $attributeName ) )
{
continue;
}
$element->setAttribute( $attributeName, $attributeValue );
}
// set the given attributes normally
$success = $element->setAttributes( $attributes );
if ( patErrorManager::isError( $success ) )
{
return $success;
}
if (is_array($children)) {
foreach ($children as $child) {
$childName = $child['attributes']['name'];
$childEl = &patForms::createElement($childName, $child['type'], $child['attributes']);
if ( isset( $child["renderer"] ) ) {
$childEl->setRenderer( $child["renderer"] );
}
$element->addElement($childEl);
}
}
$success = $element->_init();
if ( patErrorManager::isError( $success ) ) {
return $success;
}
// if we don't have any filters to add, we're done
if ( !is_array( $filters ) )
{
return $element;
}
$cnt = count( $filters );
for ( $i = 0; $i < $cnt; $i++ )
{
$params = isset( $filters[$i]['params'] ) ? $filters[$i]['params'] : null;
$filter = &patForms::createFilter( $filters[$i]['filter'], $params );
if ( patErrorManager::isError( $filter ) )
{
continue;
}
$element->applyFilter( $filter );
}
return $element;
}
/**
* retrieves the validation errors from all elements in the form. Use this if the validateForm()
* method returned false.
*
* @access public
* q
* @return array $errors Array containing an array with validation errors for each element in the form.
* @todo replace __form with the name of the form, once attributes are implemented
*/
function getValidationErrors($withElements = true)
{
$found = false;
$errors = array();
if ( !empty( $this->validationErrors ) )
{
$errors['__form'] = $this->validationErrors;
$found = true;
}
if ($withElements === false) {
return $errors;
}
$cnt = count( $this->elements );
for ( $i = 0; $i < $cnt; ++$i )
{
$name = $this->elements[$i]->getAttribute( 'name' );
if ( $name === false )
{
continue;
}
$elementErrors = $this->elements[$i]->getValidationErrors();
if ( empty( $elementErrors ) )
continue;
$errors[$name] = $elementErrors;
$found = true;
}
if ( $found )
return $errors;
return false;
}
/**
* retrieves the values for all elements in the form.
*
* @access public
* @param array desired fields
* @param integer Mode that should be used to return values in groups
* @return array The values for all elements, as elementname => elementvalue.
*
* @todo remove the ugly Group check and replace with something better
* @todo implement something similar for getValidation errors
*/
function getValues( $fields = null, $type = PATFORMS_VALUES_NESTED )
{
$values = array();
$cnt = count( $this->elements );
for ( $i = 0; $i < $cnt; ++$i )
{
$name = $this->elements[$i]->getAttribute( 'name' );
if ( $name === false ) {
continue;
}
if ( is_array( $fields ) && !in_array( $name, $fields ) ) {
continue;
}
$tmpVal = $this->elements[$i]->getValue();
if (!is_array($tmpVal) || $this->elements[$i]->elementName != 'Group') {
$values[$name] = $tmpVal;
continue;
}
switch ($type) {
case PATFORMS_VALUES_FLATTENED:
$values = array_merge($values, $tmpVal);
break;
case PATFORMS_VALUES_PREFIXED:
foreach ($tmpVal as $key => $val) {
$values[$name.'_'.$key] = $val;
}
break;
case PATFORMS_VALUES_NESTED:
default:
$values[$name] = $tmpVal;
break;
}
}
return $values;
}
/**
* sets the values for all elements in the form. Use this to fill your form with external
* data, like a db query. Caution: if you do this and set the form to submitted, the values
* will be overwritten by any values present in the $_GET or $_POST variables.
*
* @access public
* @param array $values The values for all elements, as elementname => elementvalue.
*/
function setValues( $values, $overrideUserInput = false )
{
patErrorManager::pushExpect(PATFORMS_ERROR_ELEMENT_NOT_FOUND);
foreach ($values as $elName => $value) {
$el = &$this->getElementByName($elName);
if (patErrorManager::isError($el)) {
continue;
}
if ($overrideUserInput === true) {
$el->setValue($value);
} else {
$el->setDefaultValue($value);
}
}
patErrorManager::popExpect();
return true;
}
/**
* retrieves the current mode of the form
*
* @access public
* @return string $mode The current form mode
* @see setMode()
* @see $mode
*/
function getMode()
{
return $this->mode;
}
/**
* returns the locale that is currently set for the form.
*
* @access public
* @return string $locale The locale.
* @see setLocale()
* @see $locale
*/
function getLocale()
{
return $this->locale;
}
/**
* retrieves the current format of the form
*
* @access public
* @return string $format The current form format
* @see setFormat()
* @see format
*/
function getFormat()
{
return $this->format;
}
/**
* retrieves the current method of the form
*
* @access public
* @return string $method The request method
* @see setMethod()
*/
function getMethod()
{
return $this->getAttribute( 'method' );
}
/**
* retrieves the current action of the form
*
* @access public
* @return string $action Action of the form
* @see setAction()
*/
function getAction()
{
$action = $this->getAttribute( 'action' );
if ( !empty( $action ) )
return $action;
return $_SERVER['PHP_SELF'];
}
/**
* adds an atribute to the form's attribute collection. If the attribute
* already exists, it is overwritten.
*
* @access public
* @param string $attributeName The name of the attribute to add
* @param string $atributeValue The value of the attribute
*/
function setAttribute( $attributeName, $attributeValue )
{
if ( !isset( $this->attributeDefinition[$attributeName] ) )
{
patErrorManager::raiseNotice(
PATFORMS_NOTICE_ATTRIBUTE_NOT_SUPPORTED,
"The attribute '".$attributeName."' is not supported by the form, skipped it. [".get_class( $this )."]"
);
return true;
}
$this->attributes[$attributeName] = $attributeValue;
return true;
}
/**
* adds several attributes at once to the form's attribute collection.
* Any existing attributes will be overwritten.
*
* @access public
* @param array $attributes The attributes to add
* @see setAttribute()
*/
function setAttributes( $attributes )
{
if ( !is_array( $attributes ) )
{
return patErrorManager::raiseError(
PATFORMS_NOTICE_ARRAY_EXPECTED,
"setAttributes: array expected"
);
}
foreach ( $attributes as $attributeName => $attributeValue )
{
$this->setAttribute( $attributeName, $attributeValue );
}
return true;
}
/**
* retrieves the value of a form attribute.
*
* @access public
* @param string $attribute The name of the attribute to retrieve
* @return mixed $attributeValue The value of the attribute, or false if it does not exist in the attributes collection.
* @see setAttribute()
*/
function getAttribute( $attribute )
{
if ( !isset( $this->attributes[$attribute] ) )
{
return false;
}
return $this->attributes[$attribute];
}
/**
* retrieves all attributes of the form, or only the specified attributes.
*
* @access public
* @param array $attributes Optional: The names of the attributes to retrieve. Only the attributes that exist will be returned.
* @return array $result The attributes
* @see getAttribute()
*/
function getAttributes( $attributes = array() )
{
if ( empty( $attributes ) )
{
return $this->attributes;
}
$result = array();
foreach ( $attributes as $attribute )
{
if ( $attributeValue = $this->getAttribute( $attribute ) )
{
$result[$attribute] = $attributeValue;
}
}
return $result;
}
/**
* Loads the default attribute values into the attributes collection. Done directly
* on startup (in the consructor).
*
* The action defaults to the path of the current script, with session
* ID appended automatically, if SID has been defined.
*
* @access public
* @return bool $success Always returns true.
* @see $attributeDefaults
*/
function loadAttributeDefaults()
{
foreach ( $this->attributeDefinition as $attributeName => $attributeDef )
{
if ( isset( $attributeDef['default'] ) )
{
$this->attributes[$attributeName] = $attributeDef['default'];
}
if ( $attributeName == 'action' )
{
$this->attributes[$attributeName] = $_SERVER['PHP_SELF'];
/**
* session has been started, append session ID
*/
if ( defined( 'SID' ) )
$this->attributes[$attributeName] .= '?' . SID;
}
}
return true;
}
/**
* retrieves the form's current submitted state.
*
* If autoValidate is used, it will check for the submitVar and
* set the submitted flag accordingly
*
* @access public
* @return bool $state True if it has been submitted, false otherwise.
* @see setSubmitted(), setAutoValidate()
* @see submitted
*/
function isSubmitted()
{
if ( $this->submitted === true )
{
return true;
}
if ( !isset( $this->submitVar ) )
{
return false;
}
if ( !$this->autoValidate )
{
return false;
}
if ( isset( $_GET[$this->submitVar] ) || isset( $_POST[$this->submitVar] ) )
{
$this->setSubmitted( true );
}
return $this->submitted;
}
/**
* Creates a new patForms_Creator object
*
* @static
* @access public
* @return object $creator The creator object, or a patError object on failure
*/
function createCreator( $type )
{
return patForms::_createModule( 'Creator', $type );
}
/**
* get the element name of the form
*
* @access public
* @return string name of the form
*/
function getElementName()
{
return $this->elementName;
}
/**
* get next error offset
*
* @access public
* @return integer
*/
function getErrorOffset( $requiredCodes = 100 )
{
$offset = $this->nextErrorOffset;
$this->nextErrorOffset = $this->nextErrorOffset + $requiredCodes;
return $offset;
}
/**
* add error codes and messages for validator method
*
* @access public
* @param array defintions
* @param integer offset for the error codes
*/
function addValidatorErrorCodes( $defs, $offset = 1000 )
{
foreach ( $defs as $lang => $codes )
{
if ( !isset( $this->validatorErrorCodes[$lang] ) )
{
$this->validatorErrorCodes[$lang] = array();
}
foreach ( $codes as $code => $message )
{
$this->validatorErrorCodes[$lang][($offset+$code)] = $message;
}
}
}
/**
* add a validation error to the whole form
*
* This can be achieved by adding a validation rule to the form.
*
* @access public
* @param integer $code
* @param array $vars fill named placeholder with values
* @return boolean $result true on success
* @see addRule()
*/
function addValidationError( $code, $vars = array() )
{
$error = false;
$lang = $this->locale;
$element = $this->getElementName();
// find error message for selected language
while ( true )
{
// error message matches language code
if ( isset( $this->validatorErrorCodes[$lang][$code] ) )
{
$error = array( "element" => $element, "code" => $code, "message" => $this->validatorErrorCodes[$lang][$code] );
break;
}
// no message found and no fallback-langauage available
else if ( $lang == "C" )
{
break;
}
$lang_old = $lang;
// look for other languages
if ( strlen( $lang ) > 5 )
{
list( $lang, $trash ) = explode( ".", $lang );
}
else if ( strlen( $lang ) > 2 )
{
list( $lang, $trash ) = explode( "_", $lang );
}
else
{
$lang = "C";
}
// inform developer about missing language
patErrorManager::raiseNotice(
PATFORMS_NOTICE_VALIDATOR_ERROR_LOCALE_UNDEFINED,
"Required Validation Error-Code for language: $lang_old not available. Now trying language: $lang",
"Add language definition in used element or choose other language"
);
}
// get default Error!
if ( !$error )
{
patErrorManager::raiseWarning(
PATFORMS_WARNING_VALIDATOR_ERROR_UNDEFINED,
"No Error Message for this validation Error was defined",
"Review the error-definition for validation-errors in your element '$element'."
);
$error = array( "element" => $element, "code" => 0, "message" => "Unknown validation Error" );
}
// insert values to placeholders
if ( !empty( $vars ) )
{
foreach ( $vars as $key => $value )
{
$error["message"] = str_replace( "[". strtoupper( $key ) ."]", $value, $error["message"] );
}
}
array_push( $this->validationErrors, $error );
$this->valid = false;
return true;
}
/**
* retreives a new element id, used to give each added element a unique id for this
* form (id can be overwritten by setting the id attribute specifically).
*
* @access private
* @return int $elementId The new element id.
*/
function getElementId()
{
$this->elementCounter++;
return 'pfo'.$this->elementCounter;
}
/**
* attach an observer
*
* @access public
* @param object patForms_Observer
* @see createObserver()
* @uses patForms_Element::createObserver()
*/
function attachObserver( &$observer, $where = PATFORMS_OBSERVER_ATTACH_TO_ELEMENTS )
{
/**
* attach the observer to all elements
*/
if ( ( $where & PATFORMS_OBSERVER_ATTACH_TO_ELEMENTS ) == PATFORMS_OBSERVER_ATTACH_TO_ELEMENTS )
{
$cnt = count( $this->elements );
for ( $i = 0; $i < $cnt; ++$i )
{
$this->elements[$i]->attachObserver( $observer );
}
}
/**
* attach the observer to the form
*/
if ( ( $where & PATFORMS_OBSERVER_ATTACH_TO_FORM ) == PATFORMS_OBSERVER_ATTACH_TO_FORM )
{
$this->observers[] = &$observer;
}
return true;
}
/**
* Retrieve the content for the start of the form, including any
* additional content, e.g. global scripts if the scripts option
* is enabled.
*
* @access public
* @return string $formStart The form start content
* @todo use format to build a dynamic method
*/
function serializeStart()
{
$methodName = "serializeStart".ucfirst( $this->getFormat() ).ucfirst( $this->getMode() );
if ( !method_exists( $this, $methodName ) )
{
return patErrorManager::raiseError(
PATFORMS_ERROR_METHOD_FOR_MODE_NOT_AVAILABLE,
"Method for patForms mode '".$this->getMode()."' (".$methodName.") is not available."
);
}
return $this->$methodName();
}
/**
* Serializes the form's start element for html format, in default mode.
*
* @access private
* @return mixed $formStart The serialized start content, or a patError object.
*/
function serializeStartHtmlDefault()
{
$attributes = $this->getAttributesFor( $this->format );
if ( patErrorManager::isError( $attributes ) )
{
return $attributes;
}
$content = patForms_Element::createTag( 'form', 'opening', $attributes );
if ( $this->optionEnabled( 'scripts' ) )
{
$content .= $this->getScripts();
}
return $content;
}
/**
* Serializes the form's start element for html format, in readonly mode.
*
* @access private
* @return mixed $formStart The serialized start content, or a patError object.
*/
function serializeStartHtmlReadonly()
{
$attributes = $this->getAttributesFor( $this->format );
if ( patErrorManager::isError( $attributes ) )
{
return $attributes;
}
return patForms_Element::createTag( 'form', 'opening', $attributes );
}
/**
* Retrieve the content for the end of the form.
*
* @access public
* @return string $formEnd The form end content
*/
function serializeEnd()
{
$methodName = "serializeEnd".ucfirst( $this->getFormat() ).ucfirst( $this->getMode() );
if ( !method_exists( $this, $methodName ) )
{
return patErrorManager::raiseError(
PATFORMS_ERROR_METHOD_FOR_MODE_NOT_AVAILABLE,
"Method for patForms mode '".$this->getMode()."' (".$methodName.") is not available."
);
}
return $this->$methodName();
}
/**
* Retrieves the content for the end of the form for html format,
* in default mode.
*
* @access private
* @return string $formEnd The form end content
*/
function serializeEndHtmlDefault()
{
return patForms_Element::createTag( 'form', 'closing' );
}
/**
* Retrieves the content for the end of the form for html format,
* in readonly mode.
*
* @access private
* @return string $formEnd The form end content
*/
function serializeEndHtmlReadonly()
{
return $this->serializeEndHtmlDefault();
}
/**
* validates the current attribute collection according to the attributes definition
* and the given output format, and returns the list of valid attributes.
*
* @access private
* @param string $format The output format to retrieve the attributes for.
* @return mixed $attributes The list of attributes, or false if failed.
*/
function getAttributesFor( $format )
{
$attributes = array();
foreach ( $this->attributeDefinition as $attributeName => $attributeDef )
{
if ( !isset( $this->attributes[$attributeName] ) )
{
if ( $attributeDef["required"] )
{
return patErrorManager::raiseError(
PATFORMS_ERROR_ATTRIBUTE_REQUIRED,
'patForms needs the attribute "'.$attributeName.'" to be set.',
'See the patForms attribute definition of patForms for a complete attribute reference.'
);
}
continue;
}
$attributeValue = $this->attributes[$attributeName];
if ( !in_array( $format, $attributeDef["outputFormats"] ) )
{
continue;
}
if ( isset( $attributeDef["format"] ) )
{
if ( !$this->_checkAttributeFormat( $attributeValue, $attributeDef["format"] ) )
{
return patErrorManager::raiseError(
PATFORMS_ERROR_CAN_NOT_VERIFY_FORMAT,
"Format '".$attributeDef["format"]."' could not be verified for patForms attribute '".$attributeName."' => '".$attributeValue."'"
);
}
}
$attributes[$attributeName] = $attributeValue;
}
return $attributes;
}
/**
* checks the format of an attribute value according to the given format.
*
* @access private
* @param mixed $attributeValue The attribute value to check
* @param string $format The format to check the attribute value against
* @return bool $result True if format check succeeded, false otherwise.
* @see createAttributes()
* @todo Implement this method sometime
*/
function _checkAttributeFormat( $attributeValue, $format )
{
return true;
}
/**
* Enables a patForms option.
*
* See the {@link $options} property for an exhaustive list of available options.
*
* @access public
* @param string $option The option to enable
* @param array $params Optional parameters for the option
* @return mixed $result True on success, patError object otherwise.
* @see disableOption()
* @see optionEnabled()
* @see $options
*/
function enableOption( $option, $params = array() )
{
if ( !in_array( $option, array_keys( $this->options ) ) )
{
return patErrorManager::raiseNotice(
PATFORMS_NOTICE_INVALID_OPTION,
'The option "'.$option.'" is not a valid patForms option.'
);
}
$this->options[$option]['enabled'] = true;
$this->options[$option]['params'] = $params;
// now update all available elements too
$cnt = count( $this->elements );
for ( $i=0; $i < $cnt; $i++ )
{
$this->elements[$i]->enableOption( $option, $params );
}
return true;
}
/**
* Disables a patForms option
*
* See the {@link $options} property for an exhaustive list of available options.
*
* @access public
* @param string $option The option to disable
* @return mixed $result True on success, patError object otherwise.
* @see enableOption()
* @see optionEnabled()
* @see $options
*/
function disableOption( $option )
{
if ( !in_array( $option, array_keys( $this->options ) ) )
{
return patErrorManager::raiseNotice(
PATFORMS_NOTICE_INVALID_OPTION,
'The option "'.$option.'" is not a valid patForms option.'
);
}
$this->options[$option]['enabled'] = false;
// now update all available elements too
$cnt = count( $this->elements );
for ( $i=0; $i < $cnt; $i++ )
{
$this->elements[$i]->disableOption( $option );
}
return true;
}
/**
* Checks whether the given option is enabled.
*
* @access public
* @param string $option The option to check
* @return bool $enabled True if enabled, false otherwise.
* @see enableOption()
* @see disableOption()
* @see $options
*/
function optionEnabled( $option )
{
if ( !isset( $this->options[$option] ) )
return false;
return $this->options[$option]['enabled'];
}
/**
* Set the form to auto validate
*
* If you use this method, patForms will check the _GET and _POST variables
* for the variable you specified. If it is set, patForms assumes, that
* the form has been submitted.
*
* When creating a start tag for the form, the value will be inserted automatically.
*
* @access public
* @param string $submitVar
*/
function setAutoValidate( $submitVar )
{
$this->autoValidate = true;
$this->submitVar = $submitVar;
}
/**
* register a new event
*
* After registering an event, you may register one or more
* event handlers for this event an then trigger the event.
*
* This lets you extend the functionality of patForms.
*
* @access public
* @param string event name
* @return boolean true, if event could be registered
* @see registerEventHandler()
* @see triggerEvent()
*/
function registerEvent( $name )
{
$event = 'on' . $name;
if ( in_array( $event, $this->_validEvents ) )
{
return patErrorManager::raiseNotice(
PATFORMS_NOTICE_EVENT_ALREADY_REGISTERED,
'Event "'.$event.'" already has been registered or is built-in event'
);
}
array_push( $this->_validEvents, $event );
return true;
}
/**
* Register an event handler
*
* An event handler can be any valid PHP callback. You may pass
* one of the following values:
* - string functionname to call a globally declared function
* - array( string classname, string methodname) to call a static method
* - array( object obj, string methodname) to call a method of an object
*
* When the handler is called, two parameters will be passed:
* - object form : a patForms object
* - string event : the name of the event has should be handled.
*
* An event handler should always return true. If false is returned,
* the event will be cancelled.
*
* Currently handlers for the following events can be registered:
* - onSubmit
* - onSuccess
* - onError
*
* @access public
* @param string event name
* @param mixed event handler
* @return boolean true, if the handler could be registered
* @see triggerEvent()
* @see $_validEvents
*/
function registerEventHandler( $event, $handler )
{
if ( !in_array( $event, $this->_validEvents ) )
{
return patErrorManager::raiseError(
PATFORMS_ERROR_UNKNOWN_EVENT,
'Cannot register event handler for unknown event "' . $event .'".'
);
}
if ( !is_callable( $handler ) )
{
return patErrorManager::raiseError(
PATFORMS_ERROR_INVALID_HANDLER,
'Event handler is not callable.'
);
}
if ( !isset( $this->_eventHandler[$event] ) )
{
$this->_eventHandler[$event] = array();
}
$this->_eventHandler[$event][] = &$handler;
return true;
}
/**
* set event handler object.
*
* An event handler object is used to handle all
* registered events. The object has to provide methods
* for all events it should handle, the names of the methods
* have to be the same as the names of the events.
*
* @access public
* @param object event handler object
* @param array method names, used to change the names of the methods
* @return boolean
*/
function registerEventHandlerObject( &$obj, $methods = array() )
{
if ( empty( $methods ) )
{
foreach ( $this->_validEvents as $event )
{
if ( !method_exists( $obj, $event ) )
continue;
$methods[$event] = $event;
}
}
foreach ( $methods as $event => $method )
{
if ( !isset( $this->_eventHandler[$event] ) )
{
$this->_eventHandler[$event] = array();
}
$this->_eventHandler[$event][] = array( &$obj, $method );
}
return true;
}
/**
* Trigger an event
*
* In most cases there's no need to call this event
* from outside the class. The method is declared public
* to allow you to trigger custom events.
*
* @access public
* @param string Event name. The event name must not contain 'on', as this will be
* prefixed automatically.
*/
function triggerEvent( $event )
{
$handlerName = 'on' . $event;
if ( !isset( $this->_eventHandler[$handlerName] ) || empty( $this->_eventHandler[$handlerName] ) )
{
return true;
}
$cnt = count( $this->_eventHandler[$handlerName] );
for ( $i = 0; $i < $cnt; $i++ )
{
$result = call_user_func( $this->_eventHandler[$handlerName][$i], $this, $event );
if ( $result == false )
{
return $result;
}
}
return true;
}
/**
* Serializes the entire form to XML, all elements included
*
* @access public
* @param string $namespace Optional namespace to use for the tags
* @return string $xml The XML representation of the form
* @see patForms_Element::toXML()
* @todo needs patForms_Element, maybe switch to PEAR::XML_Util
*/
function toXML( $namespace = null )
{
$tagName = 'Form';
// prepend Namespace
if ( $namespace != null )
{
$tagName = $namespace.':'.$tagName;
}
// get all attributes
$attributes = $this->getAttributes();
// create valid XML attributes
foreach ( $attributes as $key => $value )
{
$attributes[$key] = strtr( $value, $this->xmlEntities );
}
$elements = '';
for ( $i = 0; $i < $this->elementCounter; $i++ )
{
$elements .= $this->elements[$i]->toXML( $namespace );
}
return patForms_Element::createTag( $tagName, "full", $attributes, $elements );
}
/**
* Set a static property.
*
* Static properties are stored in an array in a global variable,
* until PHP5 is ready to use.
*
* @static
* @param string property name
* @param mixed property value
* @see getStaticProperty()
*/
function setStaticProperty( $property, &$value )
{
$GLOBALS["_patForms"][$property] = &$value;
}
/**
* Get a static property.
*
* Static properties are stored in an array in a global variable,
* until PHP5 is ready to use.
*
* @static
* @param string property name
* @return mixed property value
* @see setStaticProperty()
*/
function &getStaticProperty( $property )
{
if ( isset( $GLOBALS["_patForms"][$property] ) )
{
return $GLOBALS["_patForms"][$property];
}
return patErrorManager::raiseWarning(
PATFORMS_ERROR_NO_STATIC_PROPERTY,
'Static property "'.$property.'" could not be retreived, it does not exist.'
);
}
/**
* Retrieves the form's name
*
* If no name is set, it will use 'patForms' as name.
*
* @access public
* @return string $name The name of the form.
*/
function getName()
{
if ( isset( $this->attributes['name'] ) )
return $this->attributes['name'];
return 'patForms';
}
/**
* get the javascript for the form
*
* This is still in alpha state. It will later
* allow client side validation if the element
* provides this feature.
*
* @access public
* @return string javascript needed by the form
* @todo make this dependent on the format
* @todo add changeable linebreaks
*/
function getScripts()
{
foreach ($this->elements as $element) {
$element->registerJavascripts($this);
}
$globalJavascript = implode ("", $this->javascripts['global']);
$instances = implode ("", $this->javascripts['instance']);
$script = '<script type="text/javascript" language="Javascript1.3">' . "\n"
. $globalJavascript . "\n\n" . $instances . "\n"
. '</script>';
return $script;
/*
$globalJavascript = '';
$instances = '';
$displayedTypes = array();
$cnt = count( $this->elements );
for ( $i = 0; $i < $cnt; ++$i )
{
$instances .= $this->elements[$i]->getInstanceJavascript();
$type = $this->elements[$i]->getElementName();
if ( in_array( $type, $displayedTypes ) )
continue;
array_push( $displayedTypes, $type );
$globalJavascript .= $this->elements[$i]->getGlobalJavascript();
}
$cnt = count( $this->_rules );
for ( $i = 0; $i < $cnt; ++$i )
{
$instances .= $this->_rules[$i]['rule']->getInstanceJavascript();
$type = $this->_rules[$i]['rule']->getRuleName();
if ( in_array( $type, $displayedTypes ) )
continue;
array_push( $displayedTypes, $type );
$globalJavascript .= $this->_rules[$i]['rule']->getGlobalJavascript();
}
$script = '<script type="text/javascript" language="Javascript1.3">' . "\n"
. $globalJavascript . "\n\n" . $instances . "\n"
. '</script>';
return $script;
*/
}
private $javascripts = array(
'global' => array(),
'instance' => array()
);
function registerGlobalJavascript($type, $script) {
$this->javascripts['global'][$type] = $script;
}
function registerInstanceJavascript($script) {
$this->javascripts['instance'][] = $script;
}
/**
* anounce a change in the element to all observers
*
* @access private
* @param string property that changed
* @param mixed new value of the property
*/
function _announce( $property, $value )
{
$cnt = count( $this->observers );
for ( $i = 0; $i < $cnt; $i++ )
{
$this->observers[$i]->notify( $this, $property, $value );
}
return true;
}
}