?
Path : /home/admin/public_html/old/libraries/rokcommon/RokCommon/ |
Current File : /home/admin/public_html/old/libraries/rokcommon/RokCommon/Form.php |
<?php /** * @version $Id: Form.php 19098 2014-02-25 21:03:54Z btowles $ * @author RocketTheme http://www.rockettheme.com * @copyright Copyright (C) 2007 - 2015 RocketTheme, LLC * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPLv2 only * * Derived From: * * @package Joomla.Platform * @subpackage Form * * @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ class RokCommon_Form_Exception extends Exception { } /** * Form Class for the Joomla Platform. * * This class implements a robust API for constructing, populating, filtering, and validating forms. * It uses XML definitions to construct form fields and a variety of field and rule classes to * render and validate the form. * * @package Joomla.Platform * @subpackage Form * @link http://www.w3.org/TR/html4/interact/forms.html * @link http://www.w3.org/TR/html5/forms.html * @since 11.1 */ class RokCommon_Form { protected $container; const FORM_PATH_PACKAGE = 'rokcommon_formpath'; const FORM_FIELD_PREFIX = 'RokCommon_Form_Field_'; const FORM_FIELD_OVERRIDEPATH_PRIORITY = 50; const FORM_GROUP_PREFIX = 'RokCommon_Form_Group_'; const FORM_GROUP_OVERRIDEPATH_PRIORITY = 50; const FORM_VALIDATOR_PREFIX = 'RokCommon_Form_Validator_'; const FORM_VALIDATOR_OVERRIDEPATH_PRIORITY = 50; /** * The RokCommon_Registry data store for form fields during display. * @var object * @since 11.1 */ protected $data; /** * The form object errors array. * @var array * @since 11.1 */ protected $errors = array(); /** * The name of the form instance. * @var string * @since 11.1 */ protected $name; /** * The form object options for use in rendering and validation. * @var array * @since 11.1 */ protected $options = array(); /** * The form XML definition. * @var object * @since 11.1 */ protected $xml; /** * @var RokCommon_ClassLoader_PrefixedLoader */ protected $fieldLoader; /** * @var RokCommon_ClassLoader_PrefixedLoader */ protected $groupLoader; /** * @var RokCommon_ClassLoader_PrefixedLoader */ protected $validatorLoader; /** * @var RokCommon_Form_IItemNameHandler */ protected $namehandler; /** * Form instances. * @var array * @since 11.1 */ protected static $forms = array(); var $control = null; /** * Method to instantiate the form object. * * @param string $name The name of the form. * @param array $options An array of form options. * * @since 11.1 */ public function __construct($name, array $options = array()) { $this->container = RokCommon_Service::getContainer(); // TODO move this initialization someplace else not cool here // Force the loaders for the parts to load up $this->container->getService('form.field.loader'); $this->container->getService('form.group.loader'); $this->container->getService('form.validator.loader'); // Set the name for the form. $this->name = $name; // Initialise the RokCommon_Registry data. $this->data = new RokCommon_Registry; // Set the options if specified. $this->options['control'] = isset($options['control']) ? $options['control'] : false; } /** * @param \RokCommon_Form_IItemNameHandler $namehandler */ public function setNamehandler($namehandler) { $this->namehandler = $namehandler; } /** * @return \RokCommon_Form_IItemNameHandler */ public function getNamehandler() { return $this->namehandler; } /** * Method to bind data to the form. * * @param mixed $data An array or object of data to bind to the form. * * @return boolean True on success. * * @since 11.1 */ public function bind($data) { // Make sure there is a valid RokCommon_Form XML document. if (!($this->xml instanceof SimpleXMLElement)) { return false; } // The data must be an object or array. if (!is_object($data) && !is_array($data)) { return false; } // Convert the input to an array. if (is_object($data)) { if ($data instanceof RokCommon_Registry) { // Handle a RokCommon_Registry. $data = $data->toArray(); } elseif ($data instanceof JObject) { // Handle a JObject. $data = $data->getProperties(); } else { // Handle other types of objects. $data = (array)$data; } } // Process the input data. foreach ($data as $k => $v) { if ($this->findField($k)) { // If the field exists set the value. $this->data->set($k, $v); } elseif (is_object($v) || RokCommon_Utils_ArrayHelper::isAssociative($v)) { // If the value is an object or an associative array hand it off to the recursive bind level method. $this->bindLevel($k, $v); } } return true; } /** * Method to bind data to the form for the group level. * * @param string $group The dot-separated form group path on which to bind the data. * @param mixed $data An array or object of data to bind to the form for the group level. * * @return void * * @since 11.1 */ protected function bindLevel($group, $data) { // Ensure the input data is an array. settype($data, 'array'); // Process the input data. foreach ($data as $k => $v) { if ($this->findField($k, $group)) { // If the field exists set the value. $this->data->set($group . '.' . $k, $v); } elseif (is_object($v) || RokCommon_Utils_ArrayHelper::isAssociative($v)) { // If the value is an object or an associative array, hand it off to the recursive bind level method $this->bindLevel($group . '.' . $k, $v); } } } /** * Method to filter the form data. * * @param array $data An array of field values to filter. * @param string $group The dot-separated form group path on which to filter the fields. * * @return mixed Array or false. * * @since 11.1 */ public function filter($data, $group = null) { // Make sure there is a valid RokCommon_Form XML document. if (!($this->xml instanceof SimpleXMLElement)) { return false; } // Initialise variables. $input = new RokCommon_Registry($data); $output = new RokCommon_Registry; // Get the fields for which to filter the data. $fields = $this->findFieldsByGroup($group); if (!$fields) { // PANIC! return false; } // Filter the fields. foreach ($fields as $field) { // Initialise variables. $name = (string)$field['name']; // Get the field groups for the element. $attrs = $field->xpath('ancestor::fields[@name]/@name'); $groups = array_map('strval', $attrs ? $attrs : array()); $group = implode('.', $groups); // Get the field value from the data input. if ($group) { // Filter the value if it exists. if ($input->exists($group . '.' . $name)) { $output->set($group . '.' . $name, $this->filterField($field, $input->get($group . '.' . $name, (string)$field['default']))); } } else { // Filter the value if it exists. if ($input->exists($name)) { $output->set($name, $this->filterField($field, $input->get($name, (string)$field['default']))); } } } return $output->toArray(); } /** * Return all errors, if any. * * @return array Array of error messages or RokCommon_Form_Exception objects. * * @since 11.1 */ public function getErrors() { return $this->errors; } /** * Method to get a form field represented as a RokCommon_Form_IField object. * * @param string $name The name of the form field. * @param string $group The optional dot-separated form group path on which to find the field. * @param mixed $value The optional value to use as the default for the field. * * @return mixed The RokCommon_Form_AbstractField object for the field or boolean false on error. * * @since 11.1 */ public function getField($name, $group = null, $value = null) { // Make sure there is a valid RokCommon_Form XML document. if (!($this->xml instanceof SimpleXMLElement)) { return false; } // Attempt to find the field by name and group. $element = $this->findField($name, $group); // If the field element was not found return false. if (!$element) { return false; } return $this->loadField($element, $group, $value); } /** * Method to get an attribute value from a field XML element. If the attribute doesn't exist or * is null then the optional default value will be used. * * @param string $name The name of the form field for which to get the attribute value. * @param string $attribute The name of the attribute for which to get a value. * @param mixed $default The optional default value to use if no attribute value exists. * @param string $group The optional dot-separated form group path on which to find the field. * * @return mixed The attribute value for the field. * * @since 11.1 */ public function getFieldAttribute($name, $attribute, $default = null, $group = null) { // Make sure there is a valid RokCommon_Form XML document. if (!($this->xml instanceof SimpleXMLElement)) { // TODO: throw exception. return $default; } // Find the form field element from the definition. $element = $this->findField($name, $group); // If the element exists and the attribute exists for the field return the attribute value. if (($element instanceof SimpleXMLElement) && ((string)$element[$attribute])) { return (string)$element[$attribute]; } // Otherwise return the given default value. else { return $default; } } /** * Method to get an array of RokCommon_Form_AbstractField objects in a given fieldset by name. If no name is * given then all fields are returned. * * @param string $set The optional name of the fieldset. * * @return array The array of RokCommon_Form_IField objects in the fieldset. * * @since 11.1 */ public function getFieldset($set = null, $forcegroup = array()) { // Initialise variables. $fields = array(); // Get all of the field elements in the fieldset. if ($set) { $elements = $this->findFieldsByFieldset($set); } // Get all fields. else { $elements = $this->findFieldsByGroup(); } // If no field elements were found return empty. if (empty($elements)) { return $fields; } // Build the result array from the found field elements. foreach ($elements as $element) { // Get the field groups for the element. $attrs = $element->xpath('ancestor::fields[@name]/@name'); $groups = array_map('strval', $attrs ? $attrs : array()); if (!empty($forcegroup)) { array_shift($groups); foreach (array_reverse($forcegroup) as $fgroup) { array_unshift($groups, $fgroup); } } $group = implode('.', $groups); // If the field is successfully loaded add it to the result array. if ($field = $this->loadField($element, $group)) { $fields[$field->id] = $field; } } return $fields; } /** * Method to get an array of fieldset objects optionally filtered over a given field group. * * @param string $group The dot-separated form group path on which to filter the fieldsets. * * @return array The array of fieldset objects. * * @since 11.1 */ public function getFieldsets($group = null) { // Initialise variables. $fieldsets = array(); $sets = array(); // Make sure there is a valid RokCommon_Form XML document. if (!($this->xml instanceof SimpleXMLElement)) { return $fieldsets; } if ($group) { // Get the fields elements for a given group. $elements = &$this->findGroup($group); foreach ($elements as &$element) { // Get an array of <fieldset /> elements and fieldset attributes within the fields element. if ($tmp = $element->xpath('descendant::fieldset[@name] | descendant::field[@fieldset]/@fieldset')) { $sets = array_merge($sets, (array)$tmp); } } } else { // Get an array of <fieldset /> elements and fieldset attributes. $sets = $this->xml->xpath('//fieldset[@name] | //field[@fieldset]/@fieldset'); } // If no fieldsets are found return empty. if (empty($sets)) { return $fieldsets; } // Process each found fieldset. foreach ($sets as $set) { // Are we dealing with a fieldset element? if ((string)$set['name']) { // Only create it if it doesn't already exist. if (empty($fieldsets[(string)$set['name']])) { // Build the fieldset object. $fieldset = (object)array( 'name' => '', 'label' => '', 'description' => '' ); foreach ($set->attributes() as $name => $value) { $fieldset->$name = (string)$value; } // Add the fieldset object to the list. $fieldsets[$fieldset->name] = $fieldset; } } // Must be dealing with a fieldset attribute. else { // Only create it if it doesn't already exist. if (empty($fieldsets[(string)$set])) { // Attempt to get the fieldset element for data (throughout the entire form document). $tmp = $this->xml->xpath('//fieldset[@name="' . (string)$set . '"]'); // If no element was found, build a very simple fieldset object. if (empty($tmp)) { $fieldset = (object)array( 'name' => (string)$set, 'label' => '', 'description' => '' ); } // Build the fieldset object from the element. else { $fieldset = (object)array( 'name' => '', 'label' => '', 'description' => '' ); foreach ($tmp[0]->attributes() as $name => $value) { $fieldset->$name = (string)$value; } } // Add the fieldset object to the list. $fieldsets[$fieldset->name] = $fieldset; } } } return $fieldsets; } /** * Method to get the form control. This string serves as a container for all form fields. For * example, if there is a field named 'foo' and a field named 'bar' and the form control is * empty the fields will be rendered like: <input name="foo" /> and <input name="bar" />. If * the form control is set to 'joomla' however, the fields would be rendered like: * <input name="joomla[foo]" /> and <input name="joomla[bar]" />. * * @return string The form control string. * * @since 11.1 */ public function getFormControl() { return (string)$this->options['control']; } /** * Method to get an array of RokCommon_Form_AbstractField objects in a given field group by name. * * @param string $group The dot-separated form group path for which to get the form fields. * @param boolean $nested True to also include fields in nested groups that are inside of the * group for which to find fields. * * @return array The array of RokCommon_Form_AbstractField objects in the field group. * * @since 11.1 */ public function getGroup($group, $nested = false) { // Initialise variables. $fields = array(); // Get all of the field elements in the field group. $elements = $this->findFieldsByGroup($group, $nested); // If no field elements were found return empty. if (empty($elements)) { return $fields; } // Build the result array from the found field elements. foreach ($elements as $element) { // If the field is successfully loaded add it to the result array. if ($field = $this->loadField($element, $group)) { $fields[$field->id] = $field; } } return $fields; } /** * Method to get a form field markup for the field input. * * @param string $name The name of the form field. * @param string $group The optional dot-separated form group path on which to find the field. * @param mixed $value The optional value to use as the default for the field. * * @return string The form field markup. * * @since 11.1 */ public function getInput($name, $group = null, $value = null) { // Attempt to get the form field. if ($field = $this->getField($name, $group, $value)) { return $field->input; } return ''; } /** * Method to get the label for a field input. * * @param string $name The name of the form field. * @param string $group The optional dot-separated form group path on which to find the field. * * @return string The form field label. * * @since 11.1 */ public function getLabel($name, $group = null) { // Attempt to get the form field. if ($field = $this->getField($name, $group)) { return $field->label; } return ''; } /** * Method to get the form name. * * @return string The name of the form. * * @since 11.1 */ public function getName() { return $this->name; } /** * Method to get the value of a field. * * @param string $name The name of the field for which to get the value. * @param string $group The optional dot-separated form group path on which to get the value. * @param mixed $default The optional default value of the field value is empty. * * @return mixed The value of the field or the default value if empty. * * @since 11.1 */ public function getValue($name, $group = null, $default = null) { // If a group is set use it. if ($group) { $return = $this->data->get($group . '.' . $name, $default); } else { $return = $this->data->get($name, $default); } return $return; } /** * Method to load the form description from an XML string or object. * * The replace option works per field. If a field being loaded already exists in the current * form definition then the behavior or load will vary depending upon the replace flag. If it * is set to true, then the existing field will be replaced in its exact location by the new * field being loaded. If it is false, then the new field being loaded will be ignored and the * method will move on to the next field to load. * * @param string $data The name of an XML string or object. * @param string $replace Flag to toggle whether form fields should be replaced if a field * already exists with the same group/name. * @param string $xpath An optional xpath to search for the fields. * * @return boolean True on success, false otherwise. * * @since 11.1 */ public function load($data, $replace = true, $xpath = false) { // If the data to load isn't already an XML element or string return false. if ((!($data instanceof SimpleXMLElement)) && (!is_string($data))) { return false; } // Attempt to load the XML if a string. if (is_string($data)) { $data = RokCommon_Utils_XMLHelper::getXML($data, false); // Make sure the XML loaded correctly. if (!$data) { return false; } } // If we have no XML definition at this point let's make sure we get one. if (empty($this->xml)) { // If no XPath query is set to search for fields, and we have a <form />, set it and return. if (!$xpath && ($data->getName() == 'form')) { $this->xml = $data; // Synchronize any paths found in the load. $this->syncPaths(); return true; } // Create a root element for the form. else { $this->xml = new RokCommon_XMLElement('<form></form>'); } } // Get the XML elements to load. $elements = array(); if ($xpath) { $elements = $data->xpath($xpath); } elseif ($data->getName() == 'form') { $elements = $data->children(); } // If there is nothing to load return true. if (empty($elements)) { return true; } // Load the found form elements. foreach ($elements as $element) { // Get an array of fields with the correct name. $fields = $element->xpath('descendant-or-self::field'); foreach ($fields as $field) { // Get the group names as strings for ancestor fields elements. $attrs = $field->xpath('ancestor::fields[@name]/@name'); $groups = array_map('strval', $attrs ? $attrs : array()); // Check to see if the field exists in the current form. if ($current = $this->findField((string)$field['name'], implode('.', $groups))) { // If set to replace found fields remove it from the current definition. if ($replace) { $dom = dom_import_simplexml($current); $dom->parentNode->removeChild($dom); } else { unset($field); } } } // Merge the new field data into the existing XML document. RokCommon_Utils_XMLHelper::addNode($this->xml, $element); } // Synchronize any paths found in the load. $this->syncPaths(); return true; } /** * Method to load the form description from an XML file. * * The reset option works on a group basis. If the XML file references * groups that have already been created they will be replaced with the * fields in the new XML file unless the $reset parameter has been set * to false. * * @param string $file The filesystem path of an XML file. * @param string $reset Flag to toggle whether form fields should be replaced if a field * already exists with the same group/name. * @param string $xpath An optional xpath to search for the fields. * * @return boolean True on success, false otherwise. * * @since 11.1 */ public function loadFile($file, $reset = true, $xpath = false) { // Check to see if the path is an absolute path. if (!is_file($file)) { $file = RokCommon_Composite::get(self::FORM_PATH_PACKAGE)->get($file . '.xml'); // If unable to find the file return false. if (!$file) { return false; } } // Attempt to load the XML file. $xml = RokCommon_Utils_XMLHelper::getXML($file, true); return $this->load($xml, $reset, $xpath); } /** * Method to remove a field from the form definition. * * @param string $name The name of the form field for which remove. * @param string $group The optional dot-separated form group path on which to find the field. * * @return boolean True on success. * * @since 11.1 */ public function removeField($name, $group = null) { // Make sure there is a valid RokCommon_Form XML document. if (!($this->xml instanceof SimpleXMLElement)) { // TODO: throw exception. return false; } // Find the form field element from the definition. $element = $this->findField($name, $group); // If the element exists remove it from the form definition. if ($element instanceof SimpleXMLElement) { $dom = dom_import_simplexml($element); $dom->parentNode->removeChild($dom); } return true; } /** * Method to remove a group from the form definition. * * @param string $group The dot-separated form group path for the group to remove. * * @return boolean True on success. * * @since 11.1 */ public function removeGroup($group) { // Make sure there is a valid RokCommon_Form XML document. if (!($this->xml instanceof SimpleXMLElement)) { // TODO: throw exception. return false; } // Get the fields elements for a given group. $elements = &$this->findGroup($group); foreach ($elements as &$element) { $dom = dom_import_simplexml($element); $dom->parentNode->removeChild($dom); } return true; } /** * Method to reset the form data store and optionally the form XML definition. * * @param boolean $xml True to also reset the XML form definition. * * @return boolean True on success. * * @since 11.1 */ public function reset($xml = false) { unset($this->data); $this->data = new RokCommon_Registry; if ($xml) { unset($this->xml); $this->xml = new RokCommon_XMLElement('<form></form>'); } return true; } /** * Method to set a field XML element to the form definition. If the replace flag is set then * the field will be set whether it already exists or not. If it isn't set, then the field * will not be replaced if it already exists. * * @param object &$element The XML element object representation of the form field. * @param string $group The optional dot-separated form group path on which to set the field. * @param boolean $replace True to replace an existing field if one already exists. * * @return boolean True on success. * * @since 11.1 */ public function setField(&$element, $group = null, $replace = true) { // Make sure there is a valid RokCommon_Form XML document. if (!($this->xml instanceof SimpleXMLElement)) { // TODO: throw exception. return false; } // Make sure the element to set is valid. if (!($element instanceof SimpleXMLElement)) { // TODO: throw exception. return false; } // Find the form field element from the definition. $old = &$this->findField((string)$element['name'], $group); // If an existing field is found and replace flag is false do nothing and return true. if (!$replace && !empty($old)) { return true; } // If an existing field is found and replace flag is true remove the old field. if ($replace && !empty($old) && ($old instanceof SimpleXMLElement)) { $dom = dom_import_simplexml($old); $dom->parentNode->removeChild($dom); } // If no existing field is found find a group element and add the field as a child of it. if ($group) { // Get the fields elements for a given group. $fields = &$this->findGroup($group); // If an appropriate fields element was found for the group, add the element. if (isset($fields[0]) && ($fields[0] instanceof SimpleXMLElement)) { RokCommon_Utils_XMLHelper::addNode($fields[0], $element); } } else { // Set the new field to the form. RokCommon_Utils_XMLHelper::addNode($this->xml, $element); } // Synchronize any paths found in the load. $this->syncPaths(); return true; } /** * Method to set an attribute value for a field XML element. * * @param string $name The name of the form field for which to set the attribute value. * @param string $attribute The name of the attribute for which to set a value. * @param mixed $value The value to set for the attribute. * @param string $group The optional dot-separated form group path on which to find the field. * * @return boolean True on success. * * @since 11.1 */ public function setFieldAttribute($name, $attribute, $value, $group = null) { // Make sure there is a valid RokCommon_Form XML document. if (!($this->xml instanceof SimpleXMLElement)) { // TODO: throw exception. return false; } // Find the form field element from the definition. $element = & $this->findField($name, $group); // If the element doesn't exist return false. if (!($element instanceof SimpleXMLElement)) { return false; } // Otherwise set the attribute and return true. else { $element[$attribute] = $value; // Synchronize any paths found in the load. $this->syncPaths(); return true; } } /** * Method to set some field XML elements to the form definition. If the replace flag is set then * the fields will be set whether they already exists or not. If it isn't set, then the fields * will not be replaced if they already exist. * * @param object &$elements The array of XML element object representations of the form fields. * @param string $group The optional dot-separated form group path on which to set the fields. * @param boolean $replace True to replace existing fields if they already exist. * * @return boolean True on success. * * @since 11.1 */ public function setFields(&$elements, $group = null, $replace = true) { // Make sure there is a valid RokCommon_Form XML document. if (!($this->xml instanceof SimpleXMLElement)) { // TODO: throw exception. return false; } // Make sure the elements to set are valid. foreach ($elements as $element) { if (!($element instanceof SimpleXMLElement)) { // TODO: throw exception. return false; } } // Set the fields. $return = true; foreach ($elements as $element) { if (!$this->setField($element, $group, $replace)) { $return = false; } } // Synchronize any paths found in the load. $this->syncPaths(); return $return; } /** * Method to set the value of a field. If the field does not exist in the form then the method * will return false. * * @param string $name The name of the field for which to set the value. * @param string $group The optional dot-separated form group path on which to find the field. * @param mixed $value The value to set for the field. * * @return boolean True on success. * * @since 11.1 */ public function setValue($name, $group = null, $value = null) { // If the field does not exist return false. if (!$this->findField($name, $group)) { return false; } // If a group is set use it. if ($group) { $this->data->set($group . '.' . $name, $value); } else { $this->data->set($name, $value); } return true; } /** * Method to validate form data. * * Validation warnings will be pushed into RokCommon_Form::errors and should be * retrieved with RokCommon_Form::getErrors() when validate returns boolean false. * * @param array $data An array of field values to validate. * @param string $group The optional dot-separated form group path on which to filter the * fields to be validated. * * @return mixed True on sucess. * * @since 11.1 */ public function validate($data, $group = null) { // Make sure there is a valid RokCommon_Form XML document. if (!($this->xml instanceof SimpleXMLElement)) { return false; } // Initialise variables. $return = true; // Create an input registry object from the data to validate. $input = new RokCommon_Registry($data); // Get the fields for which to validate the data. $fields = $this->findFieldsByGroup($group); if (!$fields) { // PANIC! return false; } // Validate the fields. foreach ($fields as $field) { // Initialise variables. $value = null; $name = (string)$field['name']; // Get the group names as strings for ancestor fields elements. $attrs = $field->xpath('ancestor::fields[@name]/@name'); $groups = array_map('strval', $attrs ? $attrs : array()); $group = implode('.', $groups); // Get the value from the input data. if ($group) { $value = $input->get($group . '.' . $name); } else { $value = $input->get($name); } // Validate the field. $valid = $this->validateField($field, $group, $value, $input); // Check for an error. if ($valid instanceof Exception) { switch ($valid->get('level')) { case E_ERROR: JError::raiseWarning(0, $valid->getMessage()); return false; break; default: array_push($this->errors, $valid); $return = false; break; } } } return $return; } /** * Method to apply an input filter to a value based on field data. * * @param string $element The XML element object representation of the form field. * @param mixed $value The value to filter for the field. * * @return mixed The filtered value. * * @since 11.1 */ protected function filterField($element, $value) { // Make sure there is a valid RokCommon_XMLElement. if (!($element instanceof RokCommon_XMLElement)) { return false; } // Get the field filter type. $filter = (string)$element['filter']; // Process the input value based on the filter. $return = null; switch (strtoupper($filter)) { // Access Control Rules. case 'RULES': $return = array(); foreach ((array)$value as $action => $ids) { // Build the rules array. $return[$action] = array(); foreach ($ids as $id => $p) { if ($p !== '') { $return[$action][$id] = ($p == '1' || $p == 'true') ? true : false; } } } break; // Do nothing, thus leaving the return value as null. case 'UNSET': break; // No Filter. case 'RAW': $return = $value; break; // Filter the input as an array of integers. case 'INT_ARRAY': // Make sure the input is an array. if (is_object($value)) { $value = get_object_vars($value); } $value = is_array($value) ? $value : array($value); RokCommon_Utils_ArrayHelper::toInteger($value); $return = $value; break; // Filter safe HTML. case 'SAFEHTML': $return = JFilterInput::getInstance(null, null, 1, 1)->clean($value, 'string'); break; // Convert a date to UTC based on the server timezone offset. case 'SERVER_UTC': if (intval($value) > 0) { // Get the server timezone setting. $offset = JFactory::getConfig()->get('offset'); // Return an SQL formatted datetime string in UTC. $return = JFactory::getDate($value, $offset)->toSql(); } else { $return = ''; } break; // Convert a date to UTC based on the user timezone offset. case 'USER_UTC': if (intval($value) > 0) { // Get the user timezone setting defaulting to the server timezone setting. $offset = JFactory::getUser()->getParam('timezone', JFactory::getConfig()->get('offset')); // Return a MySQL formatted datetime string in UTC. $return = JFactory::getDate($value, $offset)->toSql(); } else { $return = ''; } break; case 'TEL': $value = trim($value); // Does it match the NANP pattern? if (preg_match('/^(?:\+?1[-. ]?)?\(?([2-9][0-8][0-9])\)?[-. ]?([2-9][0-9]{2})[-. ]?([0-9]{4})$/', $value) == 1) { $number = (string)preg_replace('/[^\d]/', '', $value); if (substr($number, 0, 1) == 1) { $number = substr($number, 1); } if (substr($number, 0, 2) == '+1') { $number = substr($number, 2); } $result = '1.' . $number; } // If not, does it match ITU-T? elseif (preg_match('/^\+(?:[0-9] ?){6,14}[0-9]$/', $value) == 1) { $countrycode = substr($value, 0, strpos($value, ' ')); $countrycode = (string)preg_replace('/[^\d]/', '', $countrycode); $number = strstr($value, ' '); $number = (string)preg_replace('/[^\d]/', '', $number); $result = $countrycode . '.' . $number; } // If not, does it match EPP? elseif (preg_match('/^\+[0-9]{1,3}\.[0-9]{4,14}(?:x.+)?$/', $value) == 1) { if (strstr($value, 'x')) { $xpos = strpos($value, 'x'); $value = substr($value, 0, $xpos); } $result = str_replace('+', '', $value); } // Maybe it is already ccc.nnnnnnn? elseif (preg_match('/[0-9]{1,3}\.[0-9]{4,14}$/', $value) == 1) { $result = $value; } // If not, can we make it a string of digits? else { $value = (string)preg_replace('/[^\d]/', '', $value); if ($value != null && strlen($value) <= 15) { $length = strlen($value); // if it is fewer than 13 digits assume it is a local number if ($length <= 12) { $result = '.' . $value; } else { // If it has 13 or more digits let's make a country code. $cclen = $length - 12; $result = substr($value, 0, $cclen) . '.' . substr($value, $cclen); } } // If not let's not save anything. else { $result = ''; } } $return = $result; break; default: // Check for a callback filter. if (strpos($filter, '::') !== false && is_callable(explode('::', $filter))) { $return = call_user_func(explode('::', $filter), $value); } // Filter using a callback function if specified. elseif (function_exists($filter)) { $return = call_user_func($filter, $value); } // Filter using JFilterInput. All HTML code is filtered by default. else { $return = JFilterInput::getInstance()->clean($value, $filter); } break; } return $return; } /** * Method to get a form field represented as an XML element object. * * @param string $name The name of the form field. * @param string $group The optional dot-separated form group path on which to find the field. * * @return mixed The XML element object for the field or boolean false on error. * * @since 11.1 */ protected function findField($name, $group = null) { // Initialise variables. $element = false; $fields = array(); // Make sure there is a valid RokCommon_Form XML document. if (!($this->xml instanceof RokCommon_XMLElement)) { return false; } // Let's get the appropriate field element based on the method arguments. if ($group) { // Get the fields elements for a given group. $elements = &$this->findGroup($group); // Get all of the field elements with the correct name for the fields elements. foreach ($elements as $element) { // If there are matching field elements add them to the fields array. if ($tmp = $element->xpath('descendant::field[@name="' . $name . '"]')) { $fields = array_merge($fields, $tmp); } } // Make sure something was found. if (!$fields) { return false; } // Use the first correct match in the given group. $groupNames = explode('.', $group); foreach ($fields as &$field) { // Get the group names as strings for ancestor fields elements. $attrs = $field->xpath('ancestor::fields[@name]/@name'); $names = array_map('strval', $attrs ? $attrs : array()); // If the field is in the exact group use it and break out of the loop. if ($names == (array)$groupNames) { $element = &$field; break; } } } else { // Get an array of fields with the correct name. $fields = $this->xml->xpath('//field[@name="' . $name . '"]'); // Make sure something was found. if (!$fields) { return false; } // Search through the fields for the right one. foreach ($fields as &$field) { // If we find an ancestor fields element with a group name then it isn't what we want. if ($field->xpath('ancestor::fields[@name]')) { continue; } // Found it! else { $element = &$field; break; } } } return $element; } /** * Method to get an array of <field /> elements from the form XML document which are * in a specified fieldset by name. * * @param string $name The name of the fieldset. * * @return mixed Boolean false on error or array of RokCommon_XMLElement objects. * * @since 11.1 */ protected function &findFieldsByFieldset($name) { // Initialise variables. $false = false; // Make sure there is a valid RokCommon_Form XML document. if (!($this->xml instanceof RokCommon_XMLElement)) { return $false; } /* * Get an array of <field /> elements that are underneath a <fieldset /> element * with the appropriate name attribute, and also any <field /> elements with * the appropriate fieldset attribute. */ $fields = $this->xml->xpath('//fieldset[@name="' . $name . '"]//field | //field[@fieldset="' . $name . '"]'); return $fields; } /** * Method to get an array of <field /> elements from the form XML document which are * in a control group by name. * * @param mixed $group The optional dot-separated form group path on which to find the fields. * Null will return all fields. False will return fields not in a group. * @param boolean $nested True to also include fields in nested groups that are inside of the * group for which to find fields. * * @return mixed Boolean false on error or array of RokCommon_XMLElement objects. * * @since 11.1 */ protected function &findFieldsByGroup($group = null, $nested = false) { // Initialise variables. $false = false; $fields = array(); // Make sure there is a valid RokCommon_Form XML document. if (!($this->xml instanceof RokCommon_XMLElement)) { return $false; } // Get only fields in a specific group? if ($group) { // Get the fields elements for a given group. $elements = &$this->findGroup($group); // Get all of the field elements for the fields elements. foreach ($elements as $element) { // If there are field elements add them to the return result. if ($tmp = $element->xpath('descendant::field')) { // If we also want fields in nested groups then just merge the arrays. if ($nested) { $fields = array_merge($fields, $tmp); } // If we want to exclude nested groups then we need to check each field. else { $groupNames = explode('.', $group); foreach ($tmp as $field) { // Get the names of the groups that the field is in. $attrs = $field->xpath('ancestor::fields[@name]/@name'); $names = array_map('strval', $attrs ? $attrs : array()); // If the field is in the specific group then add it to the return list. if ($names == (array)$groupNames) { $fields = array_merge($fields, array($field)); } } } } } } elseif ($group === false) { // Get only field elements not in a group. $fields = $this->xml->xpath('descendant::fields[not(@name)]/field | descendant::fields[not(@name)]/fieldset/field '); } else { // Get an array of all the <field /> elements. $fields = $this->xml->xpath('//field'); } return $fields; } /** * Method to get a form field group represented as an XML element object. * * @param string $group The dot-separated form group path on which to find the group. * * @return mixed An array of XML element objects for the group or boolean false on error. * * @since 11.1 */ protected function &findGroup($group) { // Initialise variables. $false = false; $groups = array(); $tmp = array(); // Make sure there is a valid RokCommon_Form XML document. if (!($this->xml instanceof RokCommon_XMLElement)) { return $false; } // Make sure there is actually a group to find. $group = explode('.', $group); if (!empty($group)) { // Get any fields elements with the correct group name. $elements = $this->xml->xpath('//fields[@name="' . (string)$group[0] . '"]'); // Check to make sure that there are no parent groups for each element. foreach ($elements as $element) { if (!$element->xpath('ancestor::fields[@name]')) { $tmp[] = $element; } } // Iterate through the nested groups to find any matching form field groups. for ($i = 1, $n = count($group); $i < $n; $i++) { // Initialise some loop variables. $validNames = array_slice($group, 0, $i + 1); $current = $tmp; $tmp = array(); // Check to make sure that there are no parent groups for each element. foreach ($current as $element) { // Get any fields elements with the correct group name. $children = $element->xpath('descendant::fields[@name="' . (string)$group[$i] . '"]'); // For the found fields elements validate that they are in the correct groups. foreach ($children as $fields) { // Get the group names as strings for ancestor fields elements. $attrs = $fields->xpath('ancestor-or-self::fields[@name]/@name'); $names = array_map('strval', $attrs ? $attrs : array()); // If the group names for the fields element match the valid names at this // level add the fields element. if ($validNames == $names) { $tmp[] = $fields; } } } } // Only include valid XML objects. foreach ($tmp as $element) { if ($element instanceof RokCommon_XMLElement) { $groups[] = $element; } } } return $groups; } /** * Method to load, setup and return a RokCommon_Form_AbstractField object based on field data. * * @param string $element The XML element object representation of the form field. * @param string $group The optional dot-separated form group path on which to find the field. * @param mixed $value The optional value to use as the default for the field. * * @return mixed The RokCommon_Form_AbstractField object for the field or boolean false on error. * * @since 11.1 */ protected function loadField($element, $group = null, $value = null) { // Make sure there is a valid RokCommon_XMLElement. if (!($element instanceof RokCommon_XMLElement)) { return false; } // Get the field type. $type = $element['type'] ? (string)$element['type'] : 'text'; // Load the RokCommon_Form_AbstractField object for the field. /** @var $field RokCommon_Form_IField */ $field = $this->loadFieldType($type); // If the object could not be loaded, get a text field object. if ($field === false) { $field = $this->loadFieldType('text'); } // Get the value for the form field if not set. // Default to the translated version of the 'default' attribute // if 'translate_default' attribute if set to 'true' or '1' // else the value of the 'default' attribute for the field. if ($value === null) { $default = (string)$element['default']; if (($translate = $element['translate_default']) && ((string)$translate == 'true' || (string)$translate == '1')) { $lang = JFactory::getLanguage(); if ($lang->hasKey($default)) { $debug = $lang->setDebug(false); $default = rc__($default); $lang->setDebug($debug); } else { $default = rc__($default); } } $value = $this->getValue((string)$element['name'], $group, $default); } // Setup the RokCommon_Form_AbstractField object. $field->setForm($this); if ($field->setup($element, $value, $group)) { return $field; } else { return false; } } /** * Proxy for {@link RokCommon_Form_Helper::loadFieldType()}. * * @param string $type The field type. * @param boolean $new Flag to toggle whether we should get a new instance of the object. * * @return mixed RokCommon_Form_AbstractField object on success, false otherwise. * * @since 11.1 */ protected function loadFieldType($type, $new = true) { return $this->loadType($type, self::FORM_FIELD_PREFIX); } /** * Proxy for RokCommon_Form_Helper::loadRuleType(). * * @param string $type The rule type. * @param boolean $new Flag to toggle whether we should get a new instance of the object. * * @return mixed RokCommon_Form_AbstractValidator object on success, false otherwise. * * @see JFormHelper::loadRuleType() * @since 11.1 */ protected function loadRuleType($type, $new = true) { return $this->loadType($type, self::FORM_VALIDATOR_PREFIX); } protected function loadType($type, $prefix) { $class = $prefix . ucfirst($type); if (!class_exists($class)) { return false; } $new_item = new $class(); return $new_item; } /** * Method to synchronize any field, form or rule paths contained in the XML document. * * @return boolean True on success. * * @since 11.1 * @todo Maybe we should receive all addXXXpaths attributes at once? */ protected function syncPaths() { // Make sure there is a valid RokCommon_Form XML document. if (!($this->xml instanceof RokCommon_XMLElement)) { return false; } // Get any addfieldpath attributes from the form definition. $paths = $this->xml->xpath('//*[@addfieldpath]/@addfieldpath'); $paths = array_map('strval', $paths ? $paths : array()); // Add the field paths. foreach ($paths as $path) { $path = JPATH_ROOT . '/' . ltrim($path, '/\\'); self::addFieldPath($path); } // Get any addformpath attributes from the form definition. $paths = $this->xml->xpath('//*[@addformpath]/@addformpath'); $paths = array_map('strval', $paths ? $paths : array()); // Add the form paths. foreach ($paths as $path) { $path = JPATH_ROOT . '/' . ltrim($path, '/\\'); self::addFormPath($path, 20); } // Get any addrulepath attributes from the form definition. $paths = $this->xml->xpath('//*[@addrulepath]/@addrulepath | //*[@addvalidatorpath]/@addvalidatorpath'); $paths = array_map('strval', $paths ? $paths : array()); // Add the rule paths. foreach ($paths as $path) { $path = JPATH_ROOT . '/' . ltrim($path, '/\\'); self::addRulePath($path); } return true; } /** * Method to validate a RokCommon_Form_AbstractField object based on field data. * * @param string $element The XML element object representation of the form field. * @param string $group The optional dot-separated form group path on which to find the field. * @param mixed $value The optional value to use as the default for the field. * @param object $input An optional RokCommon_Registry object with the entire data set to validate * against the entire form. * * @return mixed Boolean true if field value is valid, RokCommon_Form_Exception on failure. * * @since 11.1 */ protected function validateField($element, $group = null, $value = null, $input = null) { // Make sure there is a valid RokCommon_XMLElement. if (!$element instanceof RokCommon_XMLElement) { return new RokCommon_Form_Exception(rc__('JLIB_FORM_ERROR_VALIDATE_FIELD'), -1, E_ERROR); } // Initialise variables. $valid = true; // Check if the field is required. $required = ((string)$element['required'] == 'true' || (string)$element['required'] == 'required'); if ($required) { // If the field is required and the value is empty return an error message. if (($value === '') || ($value === null)) { // Does the field have a defined error message? if ($element['message']) { $message = $element['message']; } else { if ($element['label']) { $message = rc__($element['label']); } else { $message = rc__($element['name']); } $message = rc_n('JLIB_FORM_VALIDATE_FIELD_REQUIRED', $message); } return new RokCommon_Form_Exception($message, 2, E_WARNING); } } // Get the field validation rule. if ($type = (string)$element['validate']) { // Load the RokCommon_Form_AbstractValidator object for the field. $rule = $this->loadRuleType($type); // If the object could not be loaded return an error message. if ($rule === false) { return new RokCommon_Form_Exception(rc_n('JLIB_FORM_VALIDATE_FIELD_RULE_MISSING', $type), -2, E_ERROR); } // Run the field validation rule test. $valid = $rule->test($element, $value, $group, $input, $this); // Check for an error in the validation test. if ($valid instanceof Exception) { return $valid; } } // Check if the field is valid. if ($valid === false) { // Does the field have a defined error message? $message = (string)$element['message']; if ($message) { return new RokCommon_Form_Exception(rc__($message), 1, E_WARNING); } else { return new RokCommon_Form_Exception(rc_n('JLIB_FORM_VALIDATE_FIELD_INVALID', rc__((string)$element['label'])), 1, E_WARNING); } } return true; } /** * Proxy for {@link RokCommon_Form_Helper::addFieldPath()}. * * @param mixed $new A path or array of paths to add. * * @return array The list of paths that have been added. * * @since 11.1 */ public static function addFieldPath($new = null) { $container = RokCommon_Service::getContainer(); /** @var $loader RokCommon_ClassLoader_PrefixedLoader */ $loader = $container->getService('form.field.loader'); $loader->addPath($new, self::FORM_FIELD_PREFIX, self::FORM_FIELD_OVERRIDEPATH_PRIORITY); } /** * Proxy for RokCommon_Form_Helper::addFormPath(). * * @param string|string[] $path A path or array of paths to add. * @param int $priority the priority for the path */ public static function addFormPath($path, $priority = 10) { if (is_array($path)) { foreach ($path as $p) { self::addFormPath($p, $priority); } return; } RokCommon_Composite::addPackagePath(self::FORM_PATH_PACKAGE, $path, $priority); } /** * Proxy for RokCommon_Form_Helper::addRulePath(). * * @param mixed $new A path or array of paths to add. * * @return array The list of paths that have been added. * * @see RokCommon_Form_Helper::addRulePath() * @since 11.1 */ public static function addRulePath($new = null) { $container = RokCommon_Service::getContainer(); /** @var $loader RokCommon_ClassLoader_PrefixedLoader */ $loader = $container->getService('form.validator.loader'); $loader->addPath($new, self::FORM_VALIDATOR_PREFIX, self::FORM_VALIDATOR_OVERRIDEPATH_PRIORITY); } /** * Method to get an instance of a form. * * @param string $name The name of the form. * @param string $data The name of an XML file or string to load as the form definition. * @param array $options An array of form options. * @param bool|string $replace Flag to toggle whether form fields should be replaced if a field * already exists with the same group/name. * @param bool|string $xpath An optional xpath to search for the fields. * * @throws Exception * @return object RokCommon_Form instance. * * @since 11.1 */ public static function getInstance($name, $data = null, $options = array(), $replace = true, $xpath = false) { // Reference to array with form instances $forms = &self::$forms; // Only instantiate the form if it does not already exist. if (!isset($forms[$name])) { $data = trim($data); if (empty($data)) { throw new Exception(rc__('JLIB_FORM_ERROR_NO_DATA')); } // Instantiate the form. $forms[$name] = new RokCommon_Form($name, $options); // Load the data. if (substr(trim($data), 0, 1) == '<') { if ($forms[$name]->load($data, $replace, $xpath) == false) { throw new Exception(rc__('JLIB_FORM_ERROR_XML_FILE_DID_NOT_LOAD')); return false; } } else { if ($forms[$name]->loadFile($data, $replace, $xpath) == false) { throw new Exception(rc__('JLIB_FORM_ERROR_XML_FILE_DID_NOT_LOAD')); return false; } } } return $forms[$name]; } /** * Method to get an array of GantryFormField objects in a given fieldset by name. If no name is * given then all fields are returned. * * @param string $set The optional name of the fieldset. * * @return array The array of GantryFormField objects in the fieldset. * @since 1.6 */ //TODO Rename this to something better public function getFullFieldset($set = null, $xml = null) { // Initialize variables. $fields = array(); // Get all of the field elements in the fieldset. if ($set) { $elements = $this->findFullFieldsByFieldset($set, $xml); } // Get all fields. else { $elements = $this->findFieldsByGroup(); } // If no field elements were found return empty. if (empty($elements)) { return $fields; } // Build the result array from the found field elements. foreach ($elements as $element) { // Get the field groups for the element. $attrs = $element->xpath('ancestor::fields[@name]/@name'); $groups = array_map('strval', $attrs ? $attrs : array()); $group = implode('.', $groups); $type = $element->getName(); if ($type == 'field') { // If the field is successfully loaded add it to the result array. if ($field = $this->loadField($element, $group)) { $fields[$field->id] = $field; } } else if ($type == 'fields') { if ($field = $this->loadGroup($element, $group)) { $fields[$field->id] = $field; } } } return $fields; } /** * Method to get an array of <field /> elements from the form XML document which are * in a specified fieldset by name. * * @param string $name The name of the fieldset. * * @return mixed Boolean false on error or array of SimpleXMLElement objects. * @since 1.6 */ //TODO Rename this to something better protected function & findFullFieldsByFieldset($name, $xml = null) { // Initialize variables. $false = false; if (null == $xml) { $xml =& $this->xml; } // Make sure there is a valid GantryForm XML document. if (!$xml instanceof SimpleXMLElement) { return $false; } /* * Get an array of <field /> elements that are underneath a <fieldset /> element * with the appropriate name attribute, and also any <field /> elements with * the appropriate fieldset attribute. */ $fields = $xml->xpath('//fieldset[@name="' . $name . '"]/field | //fieldset[@name="' . $name . '"]/fields'); return $fields; } public function & getSubFields(&$xml, $groups_xpath = 'ancestor::fields[@name]/@name') { // Initialize variables. $fields = array(); $elements = $this->findSubFields($xml); // If no field elements were found return empty. if (empty($elements)) { return $fields; } // Build the result array from the found field elements. foreach ($elements as $element) { // Get the field groups for the element. $attrs = $element->xpath($groups_xpath); $groups = array_map('strval', $attrs ? $attrs : array()); $group = implode('.', $groups); $type = $element->getName(); if ($type == 'field') { // If the field is successfully loaded add it to the result array. if ($field = $this->loadField($element, $group)) { $fields[$field->id] = $field; } } else if ($type == 'fields') { if ($field = $this->loadGroup($element, $group)) { $fields[$field->id] = $field; } } } return $fields; } protected function &findSubFields(&$xml) { $fields = $xml->xpath('field | fields'); return $fields; } /** * Method to load, setup and return a GantryFormField object based on field data. * * @param string $element The XML element object representation of the form field. * @param string $group The optional dot-separated form group path on which to find the field. * @param mixed $value The optional value to use as the default for the field. * * @return mixed The GantryFormField object for the field or boolean false on error. * @since 1.6 */ protected function loadGroup($element, $group = null, $value = null) { // Make sure there is a valid GantrySimpleXMLElement. if (!$element instanceof SimpleXMLElement) { return false; } // Get the field type. $type = $element['type'] ? (string)$element['type'] : 'text'; // Load the GantryFormField object for the field. $field = $this->loadGroupType($type); // If the object could not be loaded, get a text field object. if ($field === false) { $field = $this->loadGroupType(''); } // Get the value for the form field if not set. Default to the 'default' attribute for the field. if ($value === null) { $value = $this->getValue((string)$element['name'], $group, (string)$element['default']); } // Setup the GantryFormField object. $field->setForm($this); if ($field->setup($element, $value, $group)) { return $field; } else { return false; } } /** * Method to load a form field object given a type. * * @param string $type The field type. * @param boolean $new Flag to toggle whether we should get a new instance of the object. * * @return mixed GantryFormField object on success, false otherwise. * @since 1.6z */ protected function loadGroupType($type, $new = true) { return $this->loadType($type, self::FORM_GROUP_PREFIX); } /** * Method to add a path to the list of field include paths. * * @param mixed $new A path or array of paths to add. * * @return array The list of paths that have been added. * @since 1.6 */ public static function addGroupPath($new = null) { $container = RokCommon_Service::getContainer(); /** @var $loader RokCommon_ClassLoader_PrefixedLoader */ $loader = $container->getService('form.group.loader'); $loader->addPath($new, self::FORM_GROUP_PREFIX, self::FORM_GROUP_OVERRIDEPATH_PRIORITY); } public function initialize() { $fields = $this->getFullFieldset(); foreach ($fields as $field) { $fieldTypes[] = get_class($field); } $fieldTypes = array_unique($fieldTypes); foreach ($fieldTypes as $fieldType) { call_user_func(array($fieldType, 'initialize')); } } public function finalize() { $fields = $this->getFullFieldset(); foreach ($fields as $field) { $fieldTypes[] = get_class($field); } $fieldTypes = array_unique($fieldTypes); foreach ($fieldTypes as $fieldType) { call_user_func(array($fieldType, 'finalize')); } } /** * @param string $name */ public function setName($name) { $this->name = $name; } /** * Getter for the form data * * @return RokCommon_Registry Object with the data * * @since 3.2 */ public function getData() { return $this->data; } /** * Method to get the XML form object * * @return SimpleXMLElement The form XML object * * @since 3.2 */ public function getXml() { return $this->xml; } }