<?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( "<" => "<", ">" => ">", "&" => "&", "'" => "'", '"' => """ ); /** * 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; } }