drupal-civicrm/sites/all/modules/civicrm/packages/HTML/QuickForm.php
2018-01-14 13:10:16 +00:00

2103 lines
81 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Create, validate and process HTML forms
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.01 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_01.txt If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category HTML
* @package HTML_QuickForm
* @author Adam Daniel <adaniel1@eesus.jnj.com>
* @author Bertrand Mansion <bmansion@mamasam.com>
* @author Alexey Borzov <avb@php.net>
* @copyright 2001-2009 The PHP Group
* @license http://www.php.net/license/3_01.txt PHP License 3.01
* @version CVS: $Id: QuickForm.php,v 1.166 2009/04/04 21:34:02 avb Exp $
* @link http://pear.php.net/package/HTML_QuickForm
*/
/**
* PEAR and PEAR_Error classes, for error handling
*/
require_once 'PEAR.php';
/**
* Base class for all HTML classes
*/
require_once 'HTML/Common.php';
/**
* Element types known to HTML_QuickForm
* @see HTML_QuickForm::registerElementType(), HTML_QuickForm::getRegisteredTypes(),
* HTML_QuickForm::isTypeRegistered()
* @global array $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']
*/
$GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'] =
array(
'group' =>array('HTML/QuickForm/group.php','HTML_QuickForm_group'),
'hidden' =>array('HTML/QuickForm/hidden.php','HTML_QuickForm_hidden'),
'reset' =>array('HTML/QuickForm/reset.php','HTML_QuickForm_reset'),
'checkbox' =>array('HTML/QuickForm/checkbox.php','HTML_QuickForm_checkbox'),
'file' =>array('HTML/QuickForm/file.php','HTML_QuickForm_file'),
'image' =>array('HTML/QuickForm/image.php','HTML_QuickForm_image'),
'password' =>array('HTML/QuickForm/password.php','HTML_QuickForm_password'),
'radio' =>array('HTML/QuickForm/radio.php','HTML_QuickForm_radio'),
'button' =>array('HTML/QuickForm/button.php','HTML_QuickForm_button'),
'submit' =>array('HTML/QuickForm/submit.php','HTML_QuickForm_submit'),
'select' =>array('HTML/QuickForm/select.php','HTML_QuickForm_select'),
'hiddenselect' =>array('HTML/QuickForm/hiddenselect.php','HTML_QuickForm_hiddenselect'),
'text' =>array('HTML/QuickForm/text.php','HTML_QuickForm_text'),
'textarea' =>array('HTML/QuickForm/textarea.php','HTML_QuickForm_textarea'),
'link' =>array('HTML/QuickForm/link.php','HTML_QuickForm_link'),
'advcheckbox' =>array('HTML/QuickForm/advcheckbox.php','HTML_QuickForm_advcheckbox'),
'date' =>array('HTML/QuickForm/date.php','HTML_QuickForm_date'),
'static' =>array('HTML/QuickForm/static.php','HTML_QuickForm_static'),
'header' =>array('HTML/QuickForm/header.php', 'HTML_QuickForm_header'),
'html' =>array('HTML/QuickForm/html.php', 'HTML_QuickForm_html'),
'hierselect' =>array('HTML/QuickForm/hierselect.php', 'HTML_QuickForm_hierselect'),
'autocomplete' =>array('HTML/QuickForm/autocomplete.php', 'HTML_QuickForm_autocomplete'),
'xbutton' =>array('HTML/QuickForm/xbutton.php','HTML_QuickForm_xbutton'),
'advmultiselect'=>array('HTML/QuickForm/advmultiselect.php','HTML_QuickForm_advmultiselect'),
);
/**
* Validation rules known to HTML_QuickForm
* @see HTML_QuickForm::registerRule(), HTML_QuickForm::getRegisteredRules(),
* HTML_QuickForm::isRuleRegistered()
* @global array $GLOBALS['_HTML_QuickForm_registered_rules']
*/
$GLOBALS['_HTML_QuickForm_registered_rules'] = array(
'required' => array('html_quickform_rule_required', 'HTML/QuickForm/Rule/Required.php'),
'maxlength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'),
'minlength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'),
'rangelength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'),
'email' => array('html_quickform_rule_email', 'HTML/QuickForm/Rule/Email.php'),
'regex' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
'lettersonly' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
'alphanumeric' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
'numeric' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
'nopunctuation' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
'nonzero' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
'callback' => array('html_quickform_rule_callback', 'HTML/QuickForm/Rule/Callback.php'),
'compare' => array('html_quickform_rule_compare', 'HTML/QuickForm/Rule/Compare.php')
);
// {{{ error codes
/**#@+
* Error codes for HTML_QuickForm
*
* Codes are mapped to textual messages by errorMessage() method, if you add a
* new code be sure to add a new message for it to errorMessage()
*
* @see HTML_QuickForm::errorMessage()
*/
define('QUICKFORM_OK', 1);
define('QUICKFORM_ERROR', -1);
define('QUICKFORM_INVALID_RULE', -2);
define('QUICKFORM_NONEXIST_ELEMENT', -3);
define('QUICKFORM_INVALID_FILTER', -4);
define('QUICKFORM_UNREGISTERED_ELEMENT', -5);
define('QUICKFORM_INVALID_ELEMENT_NAME', -6);
define('QUICKFORM_INVALID_PROCESS', -7);
define('QUICKFORM_DEPRECATED', -8);
define('QUICKFORM_INVALID_DATASOURCE', -9);
/**#@-*/
// }}}
/**
* Create, validate and process HTML forms
*
* @category HTML
* @package HTML_QuickForm
* @author Adam Daniel <adaniel1@eesus.jnj.com>
* @author Bertrand Mansion <bmansion@mamasam.com>
* @author Alexey Borzov <avb@php.net>
* @version Release: 3.2.11
*/
class HTML_QuickForm extends HTML_Common
{
// {{{ properties
/**
* Array containing the form fields
* @since 1.0
* @var array
* @access private
*/
var $_elements = array();
/**
* Array containing element name to index map
* @since 1.1
* @var array
* @access private
*/
var $_elementIndex = array();
/**
* Array containing indexes of duplicate elements
* @since 2.10
* @var array
* @access private
*/
var $_duplicateIndex = array();
/**
* Array containing required field IDs
* @since 1.0
* @var array
* @access private
*/
var $_required = array();
/**
* Prefix message in javascript alert if error
* @since 1.0
* @var string
* @access public
*/
var $_jsPrefix = 'Invalid information entered.';
/**
* Postfix message in javascript alert if error
* @since 1.0
* @var string
* @access public
*/
var $_jsPostfix = 'Please correct these fields.';
/**
* Datasource object implementing the informal
* datasource protocol
* @since 3.3
* @var object
* @access private
*/
var $_datasource;
/**
* Array of default form values
* @since 2.0
* @var array
* @access private
*/
var $_defaultValues = array();
/**
* Array of constant form values
* @since 2.0
* @var array
* @access private
*/
var $_constantValues = array();
/**
* Array of submitted form values
* @since 1.0
* @var array
* @access private
*/
var $_submitValues = array();
/**
* Array of submitted form files
* @since 1.0
* @var integer
* @access public
*/
var $_submitFiles = array();
/**
* Value for maxfilesize hidden element if form contains file input
* @since 1.0
* @var integer
* @access public
*/
var $_maxFileSize = 1048576; // 1 Mb = 1048576
/**
* Flag to know if all fields are frozen
* @since 1.0
* @var boolean
* @access private
*/
var $_freezeAll = false;
/**
* Array containing the form rules
* @since 1.0
* @var array
* @access private
*/
var $_rules = array();
/**
* Form rules, global variety
* @var array
* @access private
*/
var $_formRules = array();
/**
* Array containing the validation errors
* @since 1.0
* @var array
* @access private
*/
var $_errors = array();
/**
* Note for required fields in the form
* @var string
* @since 1.0
* @access private
*/
var $_requiredNote = '<span style="font-size:80%; color:#ff0000;">*</span><span style="font-size:80%;"> denotes required field</span>';
/**
* Whether the form was submitted
* @var boolean
* @access private
*/
var $_flagSubmitted = false;
// }}}
// {{{ constructor
/**
* Class constructor
* @param string $formName Form's name.
* @param string $method (optional)Form's method defaults to 'POST'
* @param string $action (optional)Form's action
* @param string $target (optional)Form's target defaults to '_self'
* @param mixed $attributes (optional)Extra attributes for <form> tag
* @param bool $trackSubmit (optional)Whether to track if the form was submitted by adding a special hidden field
* @access public
*/
function __construct($formName='', $method='post', $action='', $target='', $attributes=null, $trackSubmit = false)
{
parent::__construct($attributes);
$method = (strtoupper($method) == 'GET') ? 'get' : 'post';
$action = CRM_Utils_System::postURL( $action );
// $action = ($action == '') ? $_SERVER['PHP_SELF'] : $action;
$target = empty($target) ? array() : array('target' => $target);
$attributes = array('action'=>$action, 'method'=>$method, 'name'=>$formName, 'id'=>$formName) + $target;
$this->updateAttributes($attributes);
if (!$trackSubmit || isset($_REQUEST['_qf__' . $formName])) {
if (1 == get_magic_quotes_gpc()) {
$this->_submitValues = $this->_recursiveFilter('stripslashes', 'get' == $method? $_GET: $_POST);
foreach ($_FILES as $keyFirst => $valFirst) {
foreach ($valFirst as $keySecond => $valSecond) {
if ('name' == $keySecond) {
$this->_submitFiles[$keyFirst][$keySecond] = $this->_recursiveFilter('stripslashes', $valSecond);
} else {
$this->_submitFiles[$keyFirst][$keySecond] = $valSecond;
}
}
}
} else {
$this->_submitValues = 'get' == $method? $_GET: $_POST;
$this->_submitFiles = $_FILES;
}
$this->_flagSubmitted = count($this->_submitValues) > 0 || count($this->_submitFiles) > 0;
}
if ($trackSubmit) {
unset($this->_submitValues['_qf__' . $formName]);
$this->addElement('hidden', '_qf__' . $formName, null);
}
if (preg_match('/^([0-9]+)([a-zA-Z]*)$/', ini_get('upload_max_filesize'), $matches)) {
// see http://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
switch (strtoupper($matches['2'])) {
case 'G':
$this->_maxFileSize = $matches['1'] * 1073741824;
break;
case 'M':
$this->_maxFileSize = $matches['1'] * 1048576;
break;
case 'K':
$this->_maxFileSize = $matches['1'] * 1024;
break;
default:
$this->_maxFileSize = $matches['1'];
}
}
} // end constructor
// }}}
// {{{ apiVersion()
/**
* Returns the current API version
*
* @since 1.0
* @access public
* @return float
*/
function apiVersion()
{
return 3.2;
} // end func apiVersion
// }}}
// {{{ registerElementType()
/**
* Registers a new element type
*
* @param string $typeName Name of element type
* @param string $include Include path for element type
* @param string $className Element class name
* @since 1.0
* @access public
* @return void
*/
function registerElementType($typeName, $include, $className)
{
$GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($typeName)] = array($include, $className);
} // end func registerElementType
// }}}
// {{{ registerRule()
/**
* Registers a new validation rule
*
* @param string $ruleName Name of validation rule
* @param string $type Either: 'regex', 'function' or 'rule' for an HTML_QuickForm_Rule object
* @param string $data1 Name of function, regular expression or HTML_QuickForm_Rule classname
* @param string $data2 Object parent of above function or HTML_QuickForm_Rule file path
* @since 1.0
* @access public
* @return void
*/
function registerRule($ruleName, $type, $data1, $data2 = null)
{
include_once('HTML/QuickForm/RuleRegistry.php');
$registry = HTML_QuickForm_RuleRegistry::singleton();
$registry->registerRule($ruleName, $type, $data1, $data2);
} // end func registerRule
// }}}
// {{{ elementExists()
/**
* Returns true if element is in the form
*
* @param string $element form name of element to check
* @since 1.0
* @access public
* @return boolean
*/
function elementExists($element=null)
{
return isset($this->_elementIndex[$element]);
} // end func elementExists
// }}}
// {{{ setDatasource()
/**
* Sets a datasource object for this form object
*
* Datasource default and constant values will feed the QuickForm object if
* the datasource implements defaultValues() and constantValues() methods.
*
* @param object $datasource datasource object implementing the informal datasource protocol
* @param mixed $defaultsFilter string or array of filter(s) to apply to default values
* @param mixed $constantsFilter string or array of filter(s) to apply to constants values
* @since 3.3
* @access public
* @return void
* @throws HTML_QuickForm_Error
*/
function setDatasource(&$datasource, $defaultsFilter = null, $constantsFilter = null)
{
if (is_object($datasource)) {
$this->_datasource =& $datasource;
if (is_callable(array($datasource, 'defaultValues'))) {
$this->setDefaults($datasource->defaultValues($this), $defaultsFilter);
}
if (is_callable(array($datasource, 'constantValues'))) {
$this->setConstants($datasource->constantValues($this), $constantsFilter);
}
} else {
return PEAR::raiseError(null, QUICKFORM_INVALID_DATASOURCE, null, E_USER_WARNING, "Datasource is not an object in QuickForm::setDatasource()", 'HTML_QuickForm_Error', true);
}
} // end func setDatasource
// }}}
// {{{ setDefaults()
/**
* Initializes default form values
*
* @param array $defaultValues values used to fill the form
* @param mixed $filter (optional) filter(s) to apply to all default values
* @since 1.0
* @access public
* @return void
* @throws HTML_QuickForm_Error
*/
function setDefaults($defaultValues = null, $filter = null)
{
if (is_array($defaultValues)) {
if (isset($filter)) {
if (is_array($filter) && (2 != count($filter) || !is_callable($filter))) {
foreach ($filter as $val) {
if (!is_callable($val)) {
return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true);
} else {
$defaultValues = $this->_recursiveFilter($val, $defaultValues);
}
}
} elseif (!is_callable($filter)) {
return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true);
} else {
$defaultValues = $this->_recursiveFilter($filter, $defaultValues);
}
}
$this->_defaultValues = HTML_QuickForm::arrayMerge($this->_defaultValues, $defaultValues);
foreach (array_keys($this->_elements) as $key) {
$this->_elements[$key]->onQuickFormEvent('updateValue', null, $this);
}
}
} // end func setDefaults
// }}}
// {{{ setConstants()
/**
* Initializes constant form values.
* These values won't get overridden by POST or GET vars
*
* @param array $constantValues values used to fill the form
* @param mixed $filter (optional) filter(s) to apply to all default values
*
* @since 2.0
* @access public
* @return void
* @throws HTML_QuickForm_Error
*/
function setConstants($constantValues = null, $filter = null)
{
if (is_array($constantValues)) {
if (isset($filter)) {
if (is_array($filter) && (2 != count($filter) || !is_callable($filter))) {
foreach ($filter as $val) {
if (!is_callable($val)) {
return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true);
} else {
$constantValues = $this->_recursiveFilter($val, $constantValues);
}
}
} elseif (!is_callable($filter)) {
return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true);
} else {
$constantValues = $this->_recursiveFilter($filter, $constantValues);
}
}
$this->_constantValues = HTML_QuickForm::arrayMerge($this->_constantValues, $constantValues);
foreach (array_keys($this->_elements) as $key) {
$this->_elements[$key]->onQuickFormEvent('updateValue', null, $this);
}
}
} // end func setConstants
// }}}
// {{{ setMaxFileSize()
/**
* Sets the value of MAX_FILE_SIZE hidden element
*
* @param int $bytes Size in bytes
* @since 3.0
* @access public
* @return void
*/
function setMaxFileSize($bytes = 0)
{
if ($bytes > 0) {
$this->_maxFileSize = $bytes;
}
if (!$this->elementExists('MAX_FILE_SIZE')) {
$this->addElement('hidden', 'MAX_FILE_SIZE', $this->_maxFileSize);
} else {
$el =& $this->getElement('MAX_FILE_SIZE');
$el->updateAttributes(array('value' => $this->_maxFileSize));
}
} // end func setMaxFileSize
// }}}
// {{{ getMaxFileSize()
/**
* Returns the value of MAX_FILE_SIZE hidden element
*
* @since 3.0
* @access public
* @return int max file size in bytes
*/
function getMaxFileSize()
{
return $this->_maxFileSize;
} // end func getMaxFileSize
// }}}
// {{{ &createElement()
/**
* Creates a new form element of the given type.
*
* This method accepts variable number of parameters, their
* meaning and count depending on $elementType
*
* @param string $elementType type of element to add (text, textarea, file...)
* @since 1.0
* @access public
* @return HTML_QuickForm_Element
* @throws HTML_QuickForm_Error
*/
function &createElement($elementType)
{
$args = func_get_args();
$element = HTML_QuickForm::_loadElement('createElement', $elementType, array_slice($args, 1));
return $element;
} // end func createElement
// }}}
// {{{ _loadElement()
/**
* Returns a form element of the given type
*
* @param string $event event to send to newly created element ('createElement' or 'addElement')
* @param string $type element type
* @param array $args arguments for event
* @since 2.0
* @access private
* @return HTML_QuickForm_Element
* @throws HTML_QuickForm_Error
*/
function &_loadElement($event, $type, $args)
{
$type = strtolower($type);
if (!HTML_QuickForm::isTypeRegistered($type)) {
$error = PEAR::raiseError(null, QUICKFORM_UNREGISTERED_ELEMENT, null, E_USER_WARNING, "Element '$type' does not exist in HTML_QuickForm::_loadElement()", 'HTML_QuickForm_Error', true);
return $error;
}
$className = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][1];
$includeFile = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][0];
include_once($includeFile);
$elementObject = new $className();
for ($i = 0; $i < 5; $i++) {
if (!isset($args[$i])) {
$args[$i] = null;
}
}
$err = $elementObject->onQuickFormEvent($event, $args, $this);
if ($err !== true) {
return $err;
}
return $elementObject;
} // end func _loadElement
// }}}
// {{{ addElement()
/**
* Adds an element into the form
*
* If $element is a string representing element type, then this
* method accepts variable number of parameters, their meaning
* and count depending on $element
*
* @param mixed $element element object or type of element to add (text, textarea, file...)
* @since 1.0
* @return HTML_QuickForm_Element a reference to newly added element
* @access public
* @throws HTML_QuickForm_Error
*/
function &addElement($element)
{
if (is_object($element) && is_subclass_of($element, 'html_quickform_element')) {
$elementObject = &$element;
$elementObject->onQuickFormEvent('updateValue', null, $this);
} else {
$args = func_get_args();
$elementObject =& $this->_loadElement('addElement', $element, array_slice($args, 1));
if (PEAR::isError($elementObject)) {
return $elementObject;
}
}
$elementName = $elementObject->getName();
// Add the element if it is not an incompatible duplicate
if (!empty($elementName) && isset($this->_elementIndex[$elementName])) {
if ($this->_elements[$this->_elementIndex[$elementName]]->getType() ==
$elementObject->getType()) {
$this->_elements[] =& $elementObject;
$elKeys = array_keys($this->_elements);
$this->_duplicateIndex[$elementName][] = end($elKeys);
} else {
$error = PEAR::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, "Element '$elementName' already exists in HTML_QuickForm::addElement()", 'HTML_QuickForm_Error', true);
return $error;
}
} else {
$this->_elements[] =& $elementObject;
$elKeys = array_keys($this->_elements);
$this->_elementIndex[$elementName] = end($elKeys);
}
if ($this->_freezeAll) {
$elementObject->freeze();
}
if ( $elementObject->getType( ) == 'text' ||
$elementObject->getType( ) == 'textarea' ) {
$this->addRule( $elementName,
ts( 'Illegal characters in input (potential scripting attack)' ),
'xssString' );
}
return $elementObject;
} // end func addElement
// }}}
// {{{ insertElementBefore()
/**
* Inserts a new element right before the other element
*
* Warning: it is not possible to check whether the $element is already
* added to the form, therefore if you want to move the existing form
* element to a new position, you'll have to use removeElement():
* $form->insertElementBefore($form->removeElement('foo', false), 'bar');
*
* @access public
* @since 3.2.4
* @param HTML_QuickForm_element Element to insert
* @param string Name of the element before which the new
* one is inserted
* @return HTML_QuickForm_element reference to inserted element
* @throws HTML_QuickForm_Error
*/
function &insertElementBefore(&$element, $nameAfter)
{
if (!empty($this->_duplicateIndex[$nameAfter])) {
$error = PEAR::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, 'Several elements named "' . $nameAfter . '" exist in HTML_QuickForm::insertElementBefore().', 'HTML_QuickForm_Error', true);
return $error;
} elseif (!$this->elementExists($nameAfter)) {
$error = PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$nameAfter' does not exist in HTML_QuickForm::insertElementBefore()", 'HTML_QuickForm_Error', true);
return $error;
}
$elementName = $element->getName();
$targetIdx = $this->_elementIndex[$nameAfter];
$duplicate = false;
// Like in addElement(), check that it's not an incompatible duplicate
if (!empty($elementName) && isset($this->_elementIndex[$elementName])) {
if ($this->_elements[$this->_elementIndex[$elementName]]->getType() != $element->getType()) {
$error = PEAR::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, "Element '$elementName' already exists in HTML_QuickForm::insertElementBefore()", 'HTML_QuickForm_Error', true);
return $error;
}
$duplicate = true;
}
// Move all the elements after added back one place, reindex _elementIndex and/or _duplicateIndex
$elKeys = array_keys($this->_elements);
for ($i = end($elKeys); $i >= $targetIdx; $i--) {
if (isset($this->_elements[$i])) {
$currentName = $this->_elements[$i]->getName();
$this->_elements[$i + 1] =& $this->_elements[$i];
if ($this->_elementIndex[$currentName] == $i) {
$this->_elementIndex[$currentName] = $i + 1;
} else {
$dupIdx = array_search($i, $this->_duplicateIndex[$currentName]);
$this->_duplicateIndex[$currentName][$dupIdx] = $i + 1;
}
unset($this->_elements[$i]);
}
}
// Put the element in place finally
$this->_elements[$targetIdx] =& $element;
if (!$duplicate) {
$this->_elementIndex[$elementName] = $targetIdx;
} else {
$this->_duplicateIndex[$elementName][] = $targetIdx;
}
$element->onQuickFormEvent('updateValue', null, $this);
if ($this->_freezeAll) {
$element->freeze();
}
// If not done, the elements will appear in reverse order
ksort($this->_elements);
return $element;
}
// }}}
// {{{ addGroup()
/**
* Adds an element group
* @param array $elements array of elements composing the group
* @param string $name (optional)group name
* @param string $groupLabel (optional)group label
* @param string $separator (optional)string to separate elements
* @param string $appendName (optional)specify whether the group name should be
* used in the form element name ex: group[element]
* @return HTML_QuickForm_group reference to a newly added group
* @since 2.8
* @access public
* @throws HTML_QuickForm_Error
*/
function &addGroup($elements, $name=null, $groupLabel='', $separator=null, $appendName = true)
{
static $anonGroups = 1;
if (0 == strlen($name)) {
$name = 'qf_group_' . $anonGroups++;
$appendName = false;
}
$group =& $this->addElement('group', $name, $groupLabel, $elements, $separator, $appendName);
return $group;
} // end func addGroup
// }}}
// {{{ &getElement()
/**
* Returns a reference to the element
*
* @param string $element Element name
* @since 2.0
* @access public
* @return HTML_QuickForm_element reference to element
* @throws HTML_QuickForm_Error
*/
function &getElement($element)
{
if (isset($this->_elementIndex[$element])) {
return $this->_elements[$this->_elementIndex[$element]];
} else {
$error = PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElement()", 'HTML_QuickForm_Error', true);
return $error;
}
} // end func getElement
// }}}
// {{{ &getElementValue()
/**
* Returns the element's raw value
*
* This returns the value as submitted by the form (not filtered)
* or set via setDefaults() or setConstants()
*
* @param string $element Element name
* @since 2.0
* @access public
* @return mixed element value
* @throws HTML_QuickForm_Error
*/
function &getElementValue($element)
{
if (!isset($this->_elementIndex[$element])) {
$error = PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true);
return $error;
}
$value = $this->_elements[$this->_elementIndex[$element]]->getValue();
if (isset($this->_duplicateIndex[$element])) {
foreach ($this->_duplicateIndex[$element] as $index) {
if (null !== ($v = $this->_elements[$index]->getValue())) {
if (is_array($value)) {
$value[] = $v;
} else {
$value = (null === $value)? $v: array($value, $v);
}
}
}
}
return $value;
} // end func getElementValue
// }}}
// {{{ getSubmitValue()
/**
* Returns the elements value after submit and filter
*
* @param string Element name
* @since 2.0
* @access public
* @return mixed submitted element value or null if not set
*/
function getSubmitValue($elementName)
{
$value = null;
if (isset($this->_submitValues[$elementName]) || isset($this->_submitFiles[$elementName])) {
$value = isset($this->_submitValues[$elementName])? $this->_submitValues[$elementName]: array();
if (is_array($value) && isset($this->_submitFiles[$elementName])) {
foreach ($this->_submitFiles[$elementName] as $k => $v) {
$value = HTML_QuickForm::arrayMerge($value, $this->_reindexFiles($this->_submitFiles[$elementName][$k], $k));
}
}
} elseif ('file' == $this->getElementType($elementName)) {
return $this->getElementValue($elementName);
} elseif (false !== ($pos = strpos($elementName, '['))) {
$base = str_replace(
array('\\', '\''), array('\\\\', '\\\''),
substr($elementName, 0, $pos)
);
$idx = "['" . str_replace(
array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"),
substr($elementName, $pos + 1, -1)
) . "']";
if (isset($this->_submitValues[$base])) {
$value = eval("return (isset(\$this->_submitValues['{$base}']{$idx})) ? \$this->_submitValues['{$base}']{$idx} : null;");
}
if ((is_array($value) || null === $value) && isset($this->_submitFiles[$base])) {
$props = array('name', 'type', 'size', 'tmp_name', 'error');
$code = "if (!isset(\$this->_submitFiles['{$base}']['name']{$idx})) {\n" .
" return null;\n" .
"} else {\n" .
" \$v = array();\n";
foreach ($props as $prop) {
$code .= " \$v = HTML_QuickForm::arrayMerge(\$v, \$this->_reindexFiles(\$this->_submitFiles['{$base}']['{$prop}']{$idx}, '{$prop}'));\n";
}
$fileValue = eval($code . " return \$v;\n}\n");
if (null !== $fileValue) {
$value = null === $value? $fileValue: HTML_QuickForm::arrayMerge($value, $fileValue);
}
}
}
// This is only supposed to work for groups with appendName = false
if (null === $value && 'group' == $this->getElementType($elementName)) {
$group =& $this->getElement($elementName);
$elements =& $group->getElements();
foreach (array_keys($elements) as $key) {
$name = $group->getElementName($key);
// prevent endless recursion in case of radios and such
if ($name != $elementName) {
if (null !== ($v = $this->getSubmitValue($name))) {
$value[$name] = $v;
}
}
}
}
return $value;
} // end func getSubmitValue
// }}}
// {{{ _reindexFiles()
/**
* A helper function to change the indexes in $_FILES array
*
* @param mixed Some value from the $_FILES array
* @param string The key from the $_FILES array that should be appended
* @return array
*/
function _reindexFiles($value, $key)
{
if (!is_array($value)) {
return array($key => $value);
} else {
$ret = array();
foreach ($value as $k => $v) {
$ret[$k] = $this->_reindexFiles($v, $key);
}
return $ret;
}
}
// }}}
// {{{ getElementError()
/**
* Returns error corresponding to validated element
*
* @param string $element Name of form element to check
* @since 1.0
* @access public
* @return string error message corresponding to checked element
*/
function getElementError($element)
{
if (isset($this->_errors[$element])) {
return $this->_errors[$element];
}
} // end func getElementError
// }}}
// {{{ setElementError()
/**
* Set error message for a form element
*
* @param string $element Name of form element to set error for
* @param string $message Error message, if empty then removes the current error message
* @since 1.0
* @access public
* @return void
*/
function setElementError($element, $message = null)
{
if (!empty($message)) {
$this->_errors[$element] = $message;
} else {
unset($this->_errors[$element]);
}
} // end func setElementError
// }}}
// {{{ getElementType()
/**
* Returns the type of the given element
*
* @param string $element Name of form element
* @since 1.1
* @access public
* @return string Type of the element, false if the element is not found
*/
function getElementType($element)
{
if (isset($this->_elementIndex[$element])) {
return $this->_elements[$this->_elementIndex[$element]]->getType();
}
return false;
} // end func getElementType
// }}}
// {{{ updateElementAttr()
/**
* Updates Attributes for one or more elements
*
* @param mixed $elements Array of element names/objects or string of elements to be updated
* @param mixed $attrs Array or sting of html attributes
* @since 2.10
* @access public
* @return void
*/
function updateElementAttr($elements, $attrs)
{
if (is_string($elements)) {
$elements = preg_split('/[ ]?,[ ]?/', $elements);
}
foreach (array_keys($elements) as $key) {
if (is_object($elements[$key]) && is_a($elements[$key], 'HTML_QuickForm_element')) {
$elements[$key]->updateAttributes($attrs);
} elseif (isset($this->_elementIndex[$elements[$key]])) {
$this->_elements[$this->_elementIndex[$elements[$key]]]->updateAttributes($attrs);
if (isset($this->_duplicateIndex[$elements[$key]])) {
foreach ($this->_duplicateIndex[$elements[$key]] as $index) {
$this->_elements[$index]->updateAttributes($attrs);
}
}
}
}
} // end func updateElementAttr
// }}}
// {{{ removeElement()
/**
* Removes an element
*
* The method "unlinks" an element from the form, returning the reference
* to the element object. If several elements named $elementName exist,
* it removes the first one, leaving the others intact.
*
* @param string $elementName The element name
* @param boolean $removeRules True if rules for this element are to be removed too
* @access public
* @since 2.0
* @return HTML_QuickForm_element a reference to the removed element
* @throws HTML_QuickForm_Error
*/
function &removeElement($elementName, $removeRules = true)
{
if (!isset($this->_elementIndex[$elementName])) {
$error = PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$elementName' does not exist in HTML_QuickForm::removeElement()", 'HTML_QuickForm_Error', true);
return $error;
}
$el =& $this->_elements[$this->_elementIndex[$elementName]];
unset($this->_elements[$this->_elementIndex[$elementName]]);
if (empty($this->_duplicateIndex[$elementName])) {
unset($this->_elementIndex[$elementName]);
} else {
$this->_elementIndex[$elementName] = array_shift($this->_duplicateIndex[$elementName]);
}
if ($removeRules) {
$this->_required = array_diff($this->_required, array($elementName));
unset($this->_rules[$elementName], $this->_errors[$elementName]);
if ('group' == $el->getType()) {
foreach (array_keys($el->getElements()) as $key) {
unset($this->_rules[$el->getElementName($key)]);
}
}
}
return $el;
} // end func removeElement
// }}}
// {{{ addRule()
/**
* Adds a validation rule for the given field
*
* If the element is in fact a group, it will be considered as a whole.
* To validate grouped elements as separated entities,
* use addGroupRule instead of addRule.
*
* @param string $element Form element name
* @param string $message Message to display for invalid data
* @param string $type Rule type, use getRegisteredRules() to get types
* @param string $format (optional)Required for extra rule data
* @param string $validation (optional)Where to perform validation: "server", "client"
* @param boolean $reset Client-side validation: reset the form element to its original value if there is an error?
* @param boolean $force Force the rule to be applied, even if the target form element does not exist
* @since 1.0
* @access public
* @throws HTML_QuickForm_Error
*/
function addRule($element, $message, $type, $format=null, $validation='server', $reset = false, $force = false)
{
if (!$force) {
if (!is_array($element) && !$this->elementExists($element)) {
return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
} elseif (is_array($element)) {
foreach ($element as $el) {
if (!$this->elementExists($el)) {
return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$el' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
}
}
}
}
if (false === ($newName = $this->isRuleRegistered($type, true))) {
return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
} elseif (is_string($newName)) {
$type = $newName;
}
if (is_array($element)) {
$dependent = $element;
$element = array_shift($dependent);
} else {
$dependent = null;
}
if ($type == 'required' || $type == 'uploadedfile') {
$this->_required[] = $element;
}
if (!isset($this->_rules[$element])) {
$this->_rules[$element] = array();
}
if ($validation == 'client') {
$this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);'));
}
$this->_rules[$element][] = array(
'type' => $type,
'format' => $format,
'message' => $message,
'validation' => $validation,
'reset' => $reset,
'dependent' => $dependent
);
} // end func addRule
// }}}
// {{{ addGroupRule()
/**
* Adds a validation rule for the given group of elements
*
* Only groups with a name can be assigned a validation rule
* Use addGroupRule when you need to validate elements inside the group.
* Use addRule if you need to validate the group as a whole. In this case,
* the same rule will be applied to all elements in the group.
* Use addRule if you need to validate the group against a function.
*
* @param string $group Form group name
* @param mixed $arg1 Array for multiple elements or error message string for one element
* @param string $type (optional)Rule type use getRegisteredRules() to get types
* @param string $format (optional)Required for extra rule data
* @param int $howmany (optional)How many valid elements should be in the group
* @param string $validation (optional)Where to perform validation: "server", "client"
* @param bool $reset Client-side: whether to reset the element's value to its original state if validation failed.
* @since 2.5
* @access public
* @throws HTML_QuickForm_Error
*/
function addGroupRule($group, $arg1, $type='', $format=null, $howmany=0, $validation = 'server', $reset = false)
{
if (!$this->elementExists($group)) {
return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Group '$group' does not exist in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
}
$groupObj =& $this->getElement($group);
if (is_array($arg1)) {
$required = 0;
foreach ($arg1 as $elementIndex => $rules) {
$elementName = $groupObj->getElementName($elementIndex);
foreach ($rules as $rule) {
$format = (isset($rule[2])) ? $rule[2] : null;
$validation = (isset($rule[3]) && 'client' == $rule[3])? 'client': 'server';
$reset = isset($rule[4]) && $rule[4];
$type = $rule[1];
if (false === ($newName = $this->isRuleRegistered($type, true))) {
return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
} elseif (is_string($newName)) {
$type = $newName;
}
$this->_rules[$elementName][] = array(
'type' => $type,
'format' => $format,
'message' => $rule[0],
'validation' => $validation,
'reset' => $reset,
'group' => $group);
if ('required' == $type || 'uploadedfile' == $type) {
$groupObj->_required[] = $elementName;
$this->_required[] = $elementName;
$required++;
}
if ('client' == $validation) {
$this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);'));
}
}
}
if ($required > 0 && count($groupObj->getElements()) == $required) {
$this->_required[] = $group;
}
} elseif (is_string($arg1)) {
if (false === ($newName = $this->isRuleRegistered($type, true))) {
return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
} elseif (is_string($newName)) {
$type = $newName;
}
// addGroupRule() should also handle <select multiple>
if (is_a($groupObj, 'html_quickform_group')) {
// Radios need to be handled differently when required
if ($type == 'required' && $groupObj->getGroupType() == 'radio') {
$howmany = ($howmany == 0) ? 1 : $howmany;
} else {
$howmany = ($howmany == 0) ? count($groupObj->getElements()) : $howmany;
}
}
$this->_rules[$group][] = array('type' => $type,
'format' => $format,
'message' => $arg1,
'validation' => $validation,
'howmany' => $howmany,
'reset' => $reset);
if ($type == 'required') {
$this->_required[] = $group;
}
if ($validation == 'client') {
$this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);'));
}
}
} // end func addGroupRule
// }}}
// {{{ addFormRule()
/**
* Adds a global validation rule
*
* This should be used when for a rule involving several fields or if
* you want to use some completely custom validation for your form.
* The rule function/method should return true in case of successful
* validation and array('element name' => 'error') when there were errors.
*
* @access public
* @param mixed Callback, either function name or array(&$object, 'method')
* @param string $format (optional)Required for extra rule data
* @throws HTML_QuickForm_Error
*/
function addFormRule($rule, $format=null)
{
if (!is_callable($rule)) {
return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, 'Callback function does not exist in HTML_QuickForm::addFormRule()', 'HTML_QuickForm_Error', true);
}
$this->_formRules[] = array($rule,$format);
}
// }}}
// {{{ applyFilter()
/**
* Applies a data filter for the given field(s)
*
* @param mixed $element Form element name or array of such names
* @param mixed $filter Callback, either function name or array(&$object, 'method')
* @since 2.0
* @access public
* @throws HTML_QuickForm_Error
*/
function applyFilter($element, $filter)
{
if (!is_callable($filter)) {
return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::applyFilter()", 'HTML_QuickForm_Error', true);
}
if ($element == '__ALL__') {
$this->_submitValues = $this->_recursiveFilter($filter, $this->_submitValues);
} else {
if (!is_array($element)) {
$element = array($element);
}
foreach ($element as $elName) {
$value = $this->getSubmitValue($elName);
if (null !== $value) {
if (false === strpos($elName, '[')) {
$this->_submitValues[$elName] = $this->_recursiveFilter($filter, $value);
} else {
$idx = "['" . str_replace(
array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"),
$elName
) . "']";
eval("\$this->_submitValues{$idx} = \$this->_recursiveFilter(\$filter, \$value);");
}
}
}
}
} // end func applyFilter
// }}}
// {{{ _recursiveFilter()
/**
* Recursively apply a filter function
*
* @param string $filter filter to apply
* @param mixed $value submitted values
* @since 2.0
* @access private
* @return cleaned values
*/
function _recursiveFilter($filter, $value)
{
if (is_array($value)) {
$cleanValues = array();
foreach ($value as $k => $v) {
$cleanValues[$k] = $this->_recursiveFilter($filter, $v);
}
return $cleanValues;
} else {
return call_user_func($filter, $value);
}
} // end func _recursiveFilter
// }}}
// {{{ arrayMerge()
/**
* Merges two arrays
*
* Merges two array like the PHP function array_merge but recursively.
* The main difference is that existing keys will not be renumbered
* if they are integers.
*
* @access public
* @param array $a original array
* @param array $b array which will be merged into first one
* @return array merged array
*/
static function arrayMerge($a, $b)
{
foreach ($b as $k => $v) {
if (is_array($v)) {
if (isset($a[$k]) && !is_array($a[$k])) {
$a[$k] = $v;
} else {
if (!isset($a[$k])) {
$a[$k] = array();
}
$a[$k] = HTML_QuickForm::arrayMerge($a[$k], $v);
}
} else {
$a[$k] = $v;
}
}
return $a;
} // end func arrayMerge
// }}}
// {{{ isTypeRegistered()
/**
* Returns whether or not the form element type is supported
*
* @param string $type Form element type
* @since 1.0
* @access public
* @return boolean
*/
function isTypeRegistered($type)
{
return isset($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($type)]);
} // end func isTypeRegistered
// }}}
// {{{ getRegisteredTypes()
/**
* Returns an array of registered element types
*
* @since 1.0
* @access public
* @return array
*/
function getRegisteredTypes()
{
return array_keys($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']);
} // end func getRegisteredTypes
// }}}
// {{{ isRuleRegistered()
/**
* Returns whether or not the given rule is supported
*
* @param string $name Validation rule name
* @param bool Whether to automatically register subclasses of HTML_QuickForm_Rule
* @since 1.0
* @access public
* @return mixed true if previously registered, false if not, new rule name if auto-registering worked
*/
function isRuleRegistered($name, $autoRegister = false)
{
if (is_scalar($name) && isset($GLOBALS['_HTML_QuickForm_registered_rules'][$name])) {
return true;
} elseif (!$autoRegister) {
return false;
}
// automatically register the rule if requested
include_once 'HTML/QuickForm/RuleRegistry.php';
$ruleName = false;
if (is_object($name) && is_a($name, 'html_quickform_rule')) {
$ruleName = !empty($name->name)? $name->name: strtolower(get_class($name));
} elseif (is_string($name) && class_exists($name)) {
$parent = strtolower($name);
do {
if ('html_quickform_rule' == strtolower($parent)) {
$ruleName = strtolower($name);
break;
}
} while ($parent = get_parent_class($parent));
}
if ($ruleName) {
$registry = HTML_QuickForm_RuleRegistry::singleton();
$registry->registerRule($ruleName, null, $name);
}
return $ruleName;
} // end func isRuleRegistered
// }}}
// {{{ getRegisteredRules()
/**
* Returns an array of registered validation rules
*
* @since 1.0
* @access public
* @return array
*/
function getRegisteredRules()
{
return array_keys($GLOBALS['_HTML_QuickForm_registered_rules']);
} // end func getRegisteredRules
// }}}
// {{{ isElementRequired()
/**
* Returns whether or not the form element is required
*
* @param string $element Form element name
* @since 1.0
* @access public
* @return boolean
*/
function isElementRequired($element)
{
return in_array($element, $this->_required, true);
} // end func isElementRequired
// }}}
// {{{ isElementFrozen()
/**
* Returns whether or not the form element is frozen
*
* @param string $element Form element name
* @since 1.0
* @access public
* @return boolean
*/
function isElementFrozen($element)
{
if (isset($this->_elementIndex[$element])) {
return $this->_elements[$this->_elementIndex[$element]]->isFrozen();
}
return false;
} // end func isElementFrozen
// }}}
// {{{ setJsWarnings()
/**
* Sets JavaScript warning messages
*
* @param string $pref Prefix warning
* @param string $post Postfix warning
* @since 1.1
* @access public
* @return void
*/
function setJsWarnings($pref, $post)
{
$this->_jsPrefix = $pref;
$this->_jsPostfix = $post;
} // end func setJsWarnings
// }}}
// {{{ setRequiredNote()
/**
* Sets required-note
*
* @param string $note Message indicating some elements are required
* @since 1.1
* @access public
* @return void
*/
function setRequiredNote($note)
{
$this->_requiredNote = $note;
} // end func setRequiredNote
// }}}
// {{{ getRequiredNote()
/**
* Returns the required note
*
* @since 2.0
* @access public
* @return string
*/
function getRequiredNote()
{
return $this->_requiredNote;
} // end func getRequiredNote
// }}}
// {{{ validate()
/**
* Performs the server side validation
* @access public
* @since 1.0
* @return boolean true if no error found
* @throws HTML_QuickForm_Error
*/
function validate()
{
if (count($this->_rules) == 0 && count($this->_formRules) == 0 &&
$this->isSubmitted()) {
return (0 == count($this->_errors));
} elseif (!$this->isSubmitted()) {
return false;
}
include_once('HTML/QuickForm/RuleRegistry.php');
$registry = HTML_QuickForm_RuleRegistry::singleton();
foreach ($this->_rules as $target => $rules) {
$submitValue = $this->getSubmitValue($target);
foreach ($rules as $rule) {
if ((isset($rule['group']) && isset($this->_errors[$rule['group']])) ||
isset($this->_errors[$target])) {
continue 2;
}
// If element is not required and is empty, we shouldn't validate it
if (!$this->isElementRequired($target)) {
if (!isset($submitValue) || '' == $submitValue) {
continue 2;
// Fix for bug #3501: we shouldn't validate not uploaded files, either.
// Unfortunately, we can't just use $element->isUploadedFile() since
// the element in question can be buried in group. Thus this hack.
// See also bug #12014, we should only consider a file that has
// status UPLOAD_ERR_NO_FILE as not uploaded, in all other cases
// validation should be performed, so that e.g. 'maxfilesize' rule
// will display an error if status is UPLOAD_ERR_INI_SIZE
// or UPLOAD_ERR_FORM_SIZE
} elseif (is_array($submitValue)) {
if (false === ($pos = strpos($target, '['))) {
$isUpload = !empty($this->_submitFiles[$target]);
} else {
$base = str_replace(
array('\\', '\''), array('\\\\', '\\\''),
substr($target, 0, $pos)
);
$idx = "['" . str_replace(
array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"),
substr($target, $pos + 1, -1)
) . "']";
eval("\$isUpload = isset(\$this->_submitFiles['{$base}']['name']{$idx});");
}
if ($isUpload && (!isset($submitValue['error']) || UPLOAD_ERR_NO_FILE == $submitValue['error'])) {
continue 2;
}
}
}
if (isset($rule['dependent']) && is_array($rule['dependent'])) {
$values = array($submitValue);
foreach ($rule['dependent'] as $elName) {
$values[] = $this->getSubmitValue($elName);
}
$result = $registry->validate($rule['type'], $values, $rule['format'], true);
} elseif (is_array($submitValue) && !isset($rule['howmany'])) {
$result = $registry->validate($rule['type'], $submitValue, $rule['format'], true);
} else {
$result = $registry->validate($rule['type'], $submitValue, $rule['format'], false);
}
if (!$result || (!empty($rule['howmany']) && $rule['howmany'] > (int)$result)) {
if (isset($rule['group'])) {
$this->_errors[$rule['group']] = $rule['message'];
} else {
$this->_errors[$target] = $rule['message'];
}
}
}
}
// process the global rules now
foreach ($this->_formRules as $value) {
list($rule, $options) = $value;
if (true !== ($res = call_user_func($rule, $this->_submitValues, $this->_submitFiles, $options))) {
if (is_array($res)) {
$this->_errors += $res;
} else {
return PEAR::raiseError(null, QUICKFORM_ERROR, null, E_USER_WARNING, 'Form rule callback returned invalid value in HTML_QuickForm::validate()', 'HTML_QuickForm_Error', true);
}
}
}
return (0 == count($this->_errors));
} // end func validate
// }}}
// {{{ freeze()
/**
* Displays elements without HTML input tags
*
* @param mixed $elementList array or string of element(s) to be frozen
* @since 1.0
* @access public
* @throws HTML_QuickForm_Error
*/
function freeze($elementList=null)
{
if (!isset($elementList)) {
$this->_freezeAll = true;
$elementList = array();
} else {
if (!is_array($elementList)) {
$elementList = preg_split('/[ ]*,[ ]*/', $elementList);
}
$elementList = array_flip($elementList);
}
foreach (array_keys($this->_elements) as $key) {
$name = $this->_elements[$key]->getName();
if ($this->_freezeAll || isset($elementList[$name])) {
$this->_elements[$key]->freeze();
unset($elementList[$name]);
}
}
if (!empty($elementList)) {
return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Nonexistant element(s): '" . implode("', '", array_keys($elementList)) . "' in HTML_QuickForm::freeze()", 'HTML_QuickForm_Error', true);
}
return true;
} // end func freeze
// }}}
// {{{ isFrozen()
/**
* Returns whether or not the whole form is frozen
*
* @since 3.0
* @access public
* @return boolean
*/
function isFrozen()
{
return $this->_freezeAll;
} // end func isFrozen
// }}}
// {{{ process()
/**
* Performs the form data processing
*
* @param mixed $callback Callback, either function name or array(&$object, 'method')
* @param bool $mergeFiles Whether uploaded files should be processed too
* @since 1.0
* @access public
* @throws HTML_QuickForm_Error
* @return mixed Whatever value the $callback function returns
*/
function process($callback, $mergeFiles = true)
{
if (!is_callable($callback)) {
return PEAR::raiseError(null, QUICKFORM_INVALID_PROCESS, null, E_USER_WARNING, "Callback function does not exist in QuickForm::process()", 'HTML_QuickForm_Error', true);
}
$values = ($mergeFiles === true) ? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles) : $this->_submitValues;
return call_user_func($callback, $values);
} // end func process
// }}}
// {{{ accept()
/**
* Accepts a renderer
*
* @param object An HTML_QuickForm_Renderer object
* @since 3.0
* @access public
* @return void
*/
function accept(&$renderer)
{
$renderer->startForm($this);
foreach (array_keys($this->_elements) as $key) {
$element =& $this->_elements[$key];
$elementName = $element->getName();
$required = ($this->isElementRequired($elementName) && !$element->isFrozen());
$error = $this->getElementError($elementName);
$element->accept($renderer, $required, $error);
}
$renderer->finishForm($this);
} // end func accept
// }}}
// {{{ defaultRenderer()
/**
* Returns a reference to default renderer object
*
* @access public
* @since 3.0
* @return object a default renderer object
*/
function &defaultRenderer()
{
if (!isset($GLOBALS['_HTML_QuickForm_default_renderer'])) {
include_once('HTML/QuickForm/Renderer/Default.php');
$GLOBALS['_HTML_QuickForm_default_renderer'] = new HTML_QuickForm_Renderer_Default();
}
return $GLOBALS['_HTML_QuickForm_default_renderer'];
} // end func defaultRenderer
// }}}
// {{{ toHtml ()
/**
* Returns an HTML version of the form
*
* @param string $in_data (optional) Any extra data to insert right
* before form is rendered. Useful when using templates.
*
* @return string Html version of the form
* @since 1.0
* @access public
*/
function toHtml ($in_data = null)
{
if (!is_null($in_data)) {
$this->addElement('html', $in_data);
}
$renderer =& $this->defaultRenderer();
$this->accept($renderer);
return $renderer->toHtml();
} // end func toHtml
// }}}
// {{{ getValidationScript()
/**
* Returns the client side validation script
*
* @since 2.0
* @access public
* @return string Javascript to perform validation, empty string if no 'client' rules were added
*/
function getValidationScript()
{
if (empty($this->_rules) || empty($this->_attributes['onsubmit'])) {
return '';
}
include_once('HTML/QuickForm/RuleRegistry.php');
$registry = HTML_QuickForm_RuleRegistry::singleton();
$test = array();
$js_escape = array(
"\r" => '\r',
"\n" => '\n',
"\t" => '\t',
"'" => "\\'",
'"' => '\"',
'\\' => '\\\\'
);
foreach ($this->_rules as $elementName => $rules) {
foreach ($rules as $rule) {
if ('client' == $rule['validation']) {
unset($element);
$dependent = isset($rule['dependent']) && is_array($rule['dependent']);
$rule['message'] = strtr($rule['message'], $js_escape);
if (isset($rule['group'])) {
$group =& $this->getElement($rule['group']);
// No JavaScript validation for frozen elements
if ($group->isFrozen()) {
continue 2;
}
$elements =& $group->getElements();
foreach (array_keys($elements) as $key) {
if ($elementName == $group->getElementName($key)) {
$element =& $elements[$key];
break;
}
}
} elseif ($dependent) {
$element = array();
$element[] =& $this->getElement($elementName);
foreach ($rule['dependent'] as $elName) {
$element[] =& $this->getElement($elName);
}
} else {
$element =& $this->getElement($elementName);
}
// No JavaScript validation for frozen elements
if (is_object($element) && $element->isFrozen()) {
continue 2;
} elseif (is_array($element)) {
foreach (array_keys($element) as $key) {
if ($element[$key]->isFrozen()) {
continue 3;
}
}
}
$test[] = $registry->getValidationScript($element, $elementName, $rule);
}
}
}
if (count($test) > 0) {
return
"\n<script type=\"text/javascript\">\n" .
"//<![CDATA[\n" .
"function validate_" . $this->_attributes['id'] . "(frm) {\n" .
" var value = '';\n" .
" var errFlag = new Array();\n" .
" var _qfGroups = {};\n" .
" _qfMsg = '';\n\n" .
join("\n", $test) .
"\n if (_qfMsg != '') {\n" .
" _qfMsg = '" . strtr($this->_jsPrefix, $js_escape) . "' + _qfMsg;\n" .
" _qfMsg = _qfMsg + '\\n" . strtr($this->_jsPostfix, $js_escape) . "';\n" .
" alert(_qfMsg);\n" .
" return false;\n" .
" }\n" .
" return true;\n" .
"}\n" .
"//]]>\n" .
"</script>";
}
return '';
} // end func getValidationScript
// }}}
// {{{ getSubmitValues()
/**
* Returns the values submitted by the form
*
* @since 2.0
* @access public
* @param bool Whether uploaded files should be returned too
* @return array
*/
function getSubmitValues($mergeFiles = false)
{
return $mergeFiles? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles): $this->_submitValues;
} // end func getSubmitValues
// }}}
// {{{ toArray()
/**
* Returns the form's contents in an array.
*
* The description of the array structure is in HTML_QuickForm_Renderer_Array docs
*
* @since 2.0
* @access public
* @param bool Whether to collect hidden elements (passed to the Renderer's constructor)
* @return array of form contents
*/
function toArray($collectHidden = false)
{
include_once 'HTML/QuickForm/Renderer/Array.php';
$renderer = new HTML_QuickForm_Renderer_Array($collectHidden);
$this->accept($renderer);
return $renderer->toArray();
} // end func toArray
// }}}
// {{{ exportValue()
/**
* Returns a 'safe' element's value
*
* This method first tries to find a cleaned-up submitted value,
* it will return a value set by setValue()/setDefaults()/setConstants()
* if submitted value does not exist for the given element.
*
* @param string Name of an element
* @access public
* @return mixed
* @throws HTML_QuickForm_Error
*/
function exportValue($element)
{
if (!isset($this->_elementIndex[$element])) {
return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true);
}
$value = $this->_elements[$this->_elementIndex[$element]]->exportValue($this->_submitValues, false);
if (isset($this->_duplicateIndex[$element])) {
foreach ($this->_duplicateIndex[$element] as $index) {
if (null !== ($v = $this->_elements[$index]->exportValue($this->_submitValues, false))) {
if (is_array($value)) {
$value[] = $v;
} else {
$value = (null === $value)? $v: array($value, $v);
}
}
}
}
return $value;
}
// }}}
// {{{ exportValues()
/**
* Returns 'safe' elements' values
*
* Unlike getSubmitValues(), this will return only the values
* corresponding to the elements present in the form.
*
* @param mixed Array/string of element names, whose values we want. If not set then return all elements.
* @access public
* @return array An assoc array of elements' values
* @throws HTML_QuickForm_Error
*/
function exportValues($elementList = null)
{
$values = array();
if (null === $elementList) {
// iterate over all elements, calling their exportValue() methods
foreach (array_keys($this->_elements) as $key) {
$value = $this->_elements[$key]->exportValue($this->_submitValues, true);
$fldName = null;
if ( isset( $this->_elements[$key]->_attributes['name'] ) ) {
//filter the value across XSS vulnerability issues.
$fldName = $this->_elements[$key]->_attributes['name'];
}
if (
// if not a text/textarea…
!in_array($this->_elements[$key]->_type, array('text', 'textarea'))
// …or should be skipped…
or CRM_Core_HTMLInputCoder::isSkippedField($fldName)
//CRM-17962 - do not encode if element is a wysiwyg editor
or CRM_Utils_Array::value('class', $this->_elements[$key]->_attributes) == 'crm-form-wysiwyg'
) {
// …dont filter, otherwise filter (else clause below)
} else {
//here value might be array or single value.
//so we should iterate and get filtered value.
CRM_Core_HTMLInputCoder::encodeInput( $value );
}
if (is_array($value)) {
// This shit throws a bogus warning in PHP 4.3.x
$values = HTML_QuickForm::arrayMerge($values, $value);
}
}
} else {
if (!is_array($elementList)) {
$elementList = array_map('trim', explode(',', $elementList));
}
foreach ($elementList as $elementName) {
$value = $this->exportValue($elementName);
//filter the value across XSS vulnerability issues.
if ( ! CRM_Core_HTMLInputCoder::isSkippedField($elementName)) {
CRM_Core_HTMLInputCoder::encodeInput( $value );
}
if (PEAR::isError($value)) {
return $value;
}
$values[$elementName] = $value;
}
}
return $values;
}
// }}}
// {{{ isSubmitted()
/**
* Tells whether the form was already submitted
*
* This is useful since the _submitFiles and _submitValues arrays
* may be completely empty after the trackSubmit value is removed.
*
* @access public
* @return bool
*/
function isSubmitted()
{
return $this->_flagSubmitted;
}
// }}}
// {{{ isError()
/**
* Tell whether a result from a QuickForm method is an error (an instance of HTML_QuickForm_Error)
*
* @access public
* @param mixed result code
* @return bool whether $value is an error
* @static
*/
public static function isError($value)
{
return (is_object($value) && is_a($value, 'html_quickform_error'));
} // end func isError
// }}}
// {{{ errorMessage()
/**
* Return a textual error message for an QuickForm error code
*
* @access public
* @param int error code
* @return string error message
* @static
*/
static function errorMessage($value)
{
// make the variable static so that it only has to do the defining on the first call
static $errorMessages;
// define the varies error messages
if (!isset($errorMessages)) {
$errorMessages = array(
QUICKFORM_OK => 'no error',
QUICKFORM_ERROR => 'unknown error',
QUICKFORM_INVALID_RULE => 'the rule does not exist as a registered rule',
QUICKFORM_NONEXIST_ELEMENT => 'nonexistent html element',
QUICKFORM_INVALID_FILTER => 'invalid filter',
QUICKFORM_UNREGISTERED_ELEMENT => 'unregistered element',
QUICKFORM_INVALID_ELEMENT_NAME => 'element already exists',
QUICKFORM_INVALID_PROCESS => 'process callback does not exist',
QUICKFORM_DEPRECATED => 'method is deprecated',
QUICKFORM_INVALID_DATASOURCE => 'datasource is not an object'
);
}
// If this is an error object, then grab the corresponding error code
if (HTML_QuickForm::isError($value)) {
$value = $value->getCode();
}
// return the textual error message corresponding to the code
return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[QUICKFORM_ERROR];
} // end func errorMessage
// }}}
} // end class HTML_QuickForm
/**
* Class for errors thrown by HTML_QuickForm package
*
* @category HTML
* @package HTML_QuickForm
* @author Adam Daniel <adaniel1@eesus.jnj.com>
* @author Bertrand Mansion <bmansion@mamasam.com>
* @version Release: 3.2.11
*/
class HTML_QuickForm_Error extends PEAR_Error {
// {{{ properties
/**
* Prefix for all error messages
* @var string
*/
var $error_message_prefix = 'QuickForm Error: ';
// }}}
// {{{ constructor
/**
* Creates a quickform error object, extending the PEAR_Error class
*
* @param int $code the error code
* @param int $mode the reaction to the error, either return, die or trigger/callback
* @param int $level intensity of the error (PHP error code)
* @param mixed $debuginfo any information that can inform user as to nature of the error
*/
function __construct($code = QUICKFORM_ERROR, $mode = PEAR_ERROR_RETURN,
$level = E_USER_NOTICE, $debuginfo = null)
{
if (is_int($code)) {
parent::__construct(HTML_QuickForm::errorMessage($code), $code, $mode, $level, $debuginfo);
} else {
parent::__construct("Invalid error code: $code", QUICKFORM_ERROR, $mode, $level, $debuginfo);
}
}
// }}}
} // end class HTML_QuickForm_Error
?>