PageRenderTime 73ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/libraries/joomla/form/form.php

http://github.com/joomla/joomla-platform
PHP | 2114 lines | 1252 code | 225 blank | 637 comment | 145 complexity | ff1f48e4ce6bfde95d176016c6eac1d0 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * @package Joomla.Platform
  4. * @subpackage Form
  5. *
  6. * @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  7. * @license GNU General Public License version 2 or later; see LICENSE
  8. */
  9. defined('JPATH_PLATFORM') or die;
  10. jimport('joomla.filesystem.path');
  11. jimport('joomla.utilities.arrayhelper');
  12. /**
  13. * Form Class for the Joomla Platform.
  14. *
  15. * This class implements a robust API for constructing, populating, filtering, and validating forms.
  16. * It uses XML definitions to construct form fields and a variety of field and rule classes to
  17. * render and validate the form.
  18. *
  19. * @package Joomla.Platform
  20. * @subpackage Form
  21. * @link http://www.w3.org/TR/html4/interact/forms.html
  22. * @link http://www.w3.org/TR/html5/forms.html
  23. * @since 11.1
  24. */
  25. class JForm
  26. {
  27. /**
  28. * The JRegistry data store for form fields during display.
  29. * @var object
  30. * @since 11.1
  31. */
  32. protected $data;
  33. /**
  34. * The form object errors array.
  35. * @var array
  36. * @since 11.1
  37. */
  38. protected $errors = array();
  39. /**
  40. * The name of the form instance.
  41. * @var string
  42. * @since 11.1
  43. */
  44. protected $name;
  45. /**
  46. * The form object options for use in rendering and validation.
  47. * @var array
  48. * @since 11.1
  49. */
  50. protected $options = array();
  51. /**
  52. * The form XML definition.
  53. * @var SimpleXMLElement
  54. * @since 11.1
  55. */
  56. protected $xml;
  57. /**
  58. * Form instances.
  59. * @var array
  60. * @since 11.1
  61. */
  62. protected static $forms = array();
  63. /**
  64. * Method to instantiate the form object.
  65. *
  66. * @param string $name The name of the form.
  67. * @param array $options An array of form options.
  68. *
  69. * @since 11.1
  70. */
  71. public function __construct($name, array $options = array())
  72. {
  73. // Set the name for the form.
  74. $this->name = $name;
  75. // Initialise the JRegistry data.
  76. $this->data = new JRegistry;
  77. // Set the options if specified.
  78. $this->options['control'] = isset($options['control']) ? $options['control'] : false;
  79. }
  80. /**
  81. * Method to bind data to the form.
  82. *
  83. * @param mixed $data An array or object of data to bind to the form.
  84. *
  85. * @return boolean True on success.
  86. *
  87. * @since 11.1
  88. */
  89. public function bind($data)
  90. {
  91. // Make sure there is a valid JForm XML document.
  92. if (!($this->xml instanceof SimpleXMLElement))
  93. {
  94. return false;
  95. }
  96. // The data must be an object or array.
  97. if (!is_object($data) && !is_array($data))
  98. {
  99. return false;
  100. }
  101. // Convert the input to an array.
  102. if (is_object($data))
  103. {
  104. if ($data instanceof JRegistry)
  105. {
  106. // Handle a JRegistry.
  107. $data = $data->toArray();
  108. }
  109. elseif ($data instanceof JObject)
  110. {
  111. // Handle a JObject.
  112. $data = $data->getProperties();
  113. }
  114. else
  115. {
  116. // Handle other types of objects.
  117. $data = (array) $data;
  118. }
  119. }
  120. // Process the input data.
  121. foreach ($data as $k => $v)
  122. {
  123. if ($this->findField($k))
  124. {
  125. // If the field exists set the value.
  126. $this->data->set($k, $v);
  127. }
  128. elseif (is_object($v) || JArrayHelper::isAssociative($v))
  129. {
  130. // If the value is an object or an associative array hand it off to the recursive bind level method.
  131. $this->bindLevel($k, $v);
  132. }
  133. }
  134. return true;
  135. }
  136. /**
  137. * Method to bind data to the form for the group level.
  138. *
  139. * @param string $group The dot-separated form group path on which to bind the data.
  140. * @param mixed $data An array or object of data to bind to the form for the group level.
  141. *
  142. * @return void
  143. *
  144. * @since 11.1
  145. */
  146. protected function bindLevel($group, $data)
  147. {
  148. // Ensure the input data is an array.
  149. settype($data, 'array');
  150. // Process the input data.
  151. foreach ($data as $k => $v)
  152. {
  153. if ($this->findField($k, $group))
  154. {
  155. // If the field exists set the value.
  156. $this->data->set($group . '.' . $k, $v);
  157. }
  158. elseif (is_object($v) || JArrayHelper::isAssociative($v))
  159. {
  160. // If the value is an object or an associative array, hand it off to the recursive bind level method
  161. $this->bindLevel($group . '.' . $k, $v);
  162. }
  163. }
  164. }
  165. /**
  166. * Method to filter the form data.
  167. *
  168. * @param array $data An array of field values to filter.
  169. * @param string $group The dot-separated form group path on which to filter the fields.
  170. *
  171. * @return mixed Array or false.
  172. *
  173. * @since 11.1
  174. */
  175. public function filter($data, $group = null)
  176. {
  177. // Make sure there is a valid JForm XML document.
  178. if (!($this->xml instanceof SimpleXMLElement))
  179. {
  180. return false;
  181. }
  182. $input = new JRegistry($data);
  183. $output = new JRegistry;
  184. // Get the fields for which to filter the data.
  185. $fields = $this->findFieldsByGroup($group);
  186. if (!$fields)
  187. {
  188. // PANIC!
  189. return false;
  190. }
  191. // Filter the fields.
  192. foreach ($fields as $field)
  193. {
  194. $name = (string) $field['name'];
  195. // Get the field groups for the element.
  196. $attrs = $field->xpath('ancestor::fields[@name]/@name');
  197. $groups = array_map('strval', $attrs ? $attrs : array());
  198. $group = implode('.', $groups);
  199. // Get the field value from the data input.
  200. if ($group)
  201. {
  202. // Filter the value if it exists.
  203. if ($input->exists($group . '.' . $name))
  204. {
  205. $output->set($group . '.' . $name, $this->filterField($field, $input->get($group . '.' . $name, (string) $field['default'])));
  206. }
  207. }
  208. else
  209. {
  210. // Filter the value if it exists.
  211. if ($input->exists($name))
  212. {
  213. $output->set($name, $this->filterField($field, $input->get($name, (string) $field['default'])));
  214. }
  215. }
  216. }
  217. return $output->toArray();
  218. }
  219. /**
  220. * Return all errors, if any.
  221. *
  222. * @return array Array of error messages or RuntimeException objects.
  223. *
  224. * @since 11.1
  225. */
  226. public function getErrors()
  227. {
  228. return $this->errors;
  229. }
  230. /**
  231. * Method to get a form field represented as a JFormField object.
  232. *
  233. * @param string $name The name of the form field.
  234. * @param string $group The optional dot-separated form group path on which to find the field.
  235. * @param mixed $value The optional value to use as the default for the field.
  236. *
  237. * @return mixed The JFormField object for the field or boolean false on error.
  238. *
  239. * @since 11.1
  240. */
  241. public function getField($name, $group = null, $value = null)
  242. {
  243. // Make sure there is a valid JForm XML document.
  244. if (!($this->xml instanceof SimpleXMLElement))
  245. {
  246. return false;
  247. }
  248. // Attempt to find the field by name and group.
  249. $element = $this->findField($name, $group);
  250. // If the field element was not found return false.
  251. if (!$element)
  252. {
  253. return false;
  254. }
  255. return $this->loadField($element, $group, $value);
  256. }
  257. /**
  258. * Method to get an attribute value from a field XML element. If the attribute doesn't exist or
  259. * is null then the optional default value will be used.
  260. *
  261. * @param string $name The name of the form field for which to get the attribute value.
  262. * @param string $attribute The name of the attribute for which to get a value.
  263. * @param mixed $default The optional default value to use if no attribute value exists.
  264. * @param string $group The optional dot-separated form group path on which to find the field.
  265. *
  266. * @return mixed The attribute value for the field.
  267. *
  268. * @since 11.1
  269. * @throws UnexpectedValueException
  270. */
  271. public function getFieldAttribute($name, $attribute, $default = null, $group = null)
  272. {
  273. // Make sure there is a valid JForm XML document.
  274. if (!($this->xml instanceof SimpleXMLElement))
  275. {
  276. throw new UnexpectedValueException(sprintf('%s::getFieldAttribute `xml` is not an instance of SimpleXMLElement', get_class($this)));
  277. }
  278. // Find the form field element from the definition.
  279. $element = $this->findField($name, $group);
  280. // If the element exists and the attribute exists for the field return the attribute value.
  281. if (($element instanceof SimpleXMLElement) && ((string) $element[$attribute]))
  282. {
  283. return (string) $element[$attribute];
  284. }
  285. // Otherwise return the given default value.
  286. else
  287. {
  288. return $default;
  289. }
  290. }
  291. /**
  292. * Method to get an array of JFormField objects in a given fieldset by name. If no name is
  293. * given then all fields are returned.
  294. *
  295. * @param string $set The optional name of the fieldset.
  296. *
  297. * @return array The array of JFormField objects in the fieldset.
  298. *
  299. * @since 11.1
  300. */
  301. public function getFieldset($set = null)
  302. {
  303. $fields = array();
  304. // Get all of the field elements in the fieldset.
  305. if ($set)
  306. {
  307. $elements = $this->findFieldsByFieldset($set);
  308. }
  309. // Get all fields.
  310. else
  311. {
  312. $elements = $this->findFieldsByGroup();
  313. }
  314. // If no field elements were found return empty.
  315. if (empty($elements))
  316. {
  317. return $fields;
  318. }
  319. // Build the result array from the found field elements.
  320. foreach ($elements as $element)
  321. {
  322. // Get the field groups for the element.
  323. $attrs = $element->xpath('ancestor::fields[@name]/@name');
  324. $groups = array_map('strval', $attrs ? $attrs : array());
  325. $group = implode('.', $groups);
  326. // If the field is successfully loaded add it to the result array.
  327. if ($field = $this->loadField($element, $group))
  328. {
  329. $fields[$field->id] = $field;
  330. }
  331. }
  332. return $fields;
  333. }
  334. /**
  335. * Method to get an array of fieldset objects optionally filtered over a given field group.
  336. *
  337. * @param string $group The dot-separated form group path on which to filter the fieldsets.
  338. *
  339. * @return array The array of fieldset objects.
  340. *
  341. * @since 11.1
  342. */
  343. public function getFieldsets($group = null)
  344. {
  345. $fieldsets = array();
  346. $sets = array();
  347. // Make sure there is a valid JForm XML document.
  348. if (!($this->xml instanceof SimpleXMLElement))
  349. {
  350. return $fieldsets;
  351. }
  352. if ($group)
  353. {
  354. // Get the fields elements for a given group.
  355. $elements = &$this->findGroup($group);
  356. foreach ($elements as &$element)
  357. {
  358. // Get an array of <fieldset /> elements and fieldset attributes within the fields element.
  359. if ($tmp = $element->xpath('descendant::fieldset[@name] | descendant::field[@fieldset]/@fieldset'))
  360. {
  361. $sets = array_merge($sets, (array) $tmp);
  362. }
  363. }
  364. }
  365. else
  366. {
  367. // Get an array of <fieldset /> elements and fieldset attributes.
  368. $sets = $this->xml->xpath('//fieldset[@name] | //field[@fieldset]/@fieldset');
  369. }
  370. // If no fieldsets are found return empty.
  371. if (empty($sets))
  372. {
  373. return $fieldsets;
  374. }
  375. // Process each found fieldset.
  376. foreach ($sets as $set)
  377. {
  378. // Are we dealing with a fieldset element?
  379. if ((string) $set['name'])
  380. {
  381. // Only create it if it doesn't already exist.
  382. if (empty($fieldsets[(string) $set['name']]))
  383. {
  384. // Build the fieldset object.
  385. $fieldset = (object) array('name' => '', 'label' => '', 'description' => '');
  386. foreach ($set->attributes() as $name => $value)
  387. {
  388. $fieldset->$name = (string) $value;
  389. }
  390. // Add the fieldset object to the list.
  391. $fieldsets[$fieldset->name] = $fieldset;
  392. }
  393. }
  394. // Must be dealing with a fieldset attribute.
  395. else
  396. {
  397. // Only create it if it doesn't already exist.
  398. if (empty($fieldsets[(string) $set]))
  399. {
  400. // Attempt to get the fieldset element for data (throughout the entire form document).
  401. $tmp = $this->xml->xpath('//fieldset[@name="' . (string) $set . '"]');
  402. // If no element was found, build a very simple fieldset object.
  403. if (empty($tmp))
  404. {
  405. $fieldset = (object) array('name' => (string) $set, 'label' => '', 'description' => '');
  406. }
  407. // Build the fieldset object from the element.
  408. else
  409. {
  410. $fieldset = (object) array('name' => '', 'label' => '', 'description' => '');
  411. foreach ($tmp[0]->attributes() as $name => $value)
  412. {
  413. $fieldset->$name = (string) $value;
  414. }
  415. }
  416. // Add the fieldset object to the list.
  417. $fieldsets[$fieldset->name] = $fieldset;
  418. }
  419. }
  420. }
  421. return $fieldsets;
  422. }
  423. /**
  424. * Method to get the form control. This string serves as a container for all form fields. For
  425. * example, if there is a field named 'foo' and a field named 'bar' and the form control is
  426. * empty the fields will be rendered like: <input name="foo" /> and <input name="bar" />. If
  427. * the form control is set to 'joomla' however, the fields would be rendered like:
  428. * <input name="joomla[foo]" /> and <input name="joomla[bar]" />.
  429. *
  430. * @return string The form control string.
  431. *
  432. * @since 11.1
  433. */
  434. public function getFormControl()
  435. {
  436. return (string) $this->options['control'];
  437. }
  438. /**
  439. * Method to get an array of JFormField objects in a given field group by name.
  440. *
  441. * @param string $group The dot-separated form group path for which to get the form fields.
  442. * @param boolean $nested True to also include fields in nested groups that are inside of the
  443. * group for which to find fields.
  444. *
  445. * @return array The array of JFormField objects in the field group.
  446. *
  447. * @since 11.1
  448. */
  449. public function getGroup($group, $nested = false)
  450. {
  451. $fields = array();
  452. // Get all of the field elements in the field group.
  453. $elements = $this->findFieldsByGroup($group, $nested);
  454. // If no field elements were found return empty.
  455. if (empty($elements))
  456. {
  457. return $fields;
  458. }
  459. // Build the result array from the found field elements.
  460. foreach ($elements as $element)
  461. {
  462. // Get the field groups for the element.
  463. $attrs = $element->xpath('ancestor::fields[@name]/@name');
  464. $groups = array_map('strval', $attrs ? $attrs : array());
  465. $group = implode('.', $groups);
  466. // If the field is successfully loaded add it to the result array.
  467. if ($field = $this->loadField($element, $group))
  468. {
  469. $fields[$field->id] = $field;
  470. }
  471. }
  472. return $fields;
  473. }
  474. /**
  475. * Method to get a form field markup for the field input.
  476. *
  477. * @param string $name The name of the form field.
  478. * @param string $group The optional dot-separated form group path on which to find the field.
  479. * @param mixed $value The optional value to use as the default for the field.
  480. *
  481. * @return string The form field markup.
  482. *
  483. * @since 11.1
  484. */
  485. public function getInput($name, $group = null, $value = null)
  486. {
  487. // Attempt to get the form field.
  488. if ($field = $this->getField($name, $group, $value))
  489. {
  490. return $field->input;
  491. }
  492. return '';
  493. }
  494. /**
  495. * Method to get the label for a field input.
  496. *
  497. * @param string $name The name of the form field.
  498. * @param string $group The optional dot-separated form group path on which to find the field.
  499. *
  500. * @return string The form field label.
  501. *
  502. * @since 11.1
  503. */
  504. public function getLabel($name, $group = null)
  505. {
  506. // Attempt to get the form field.
  507. if ($field = $this->getField($name, $group))
  508. {
  509. return $field->label;
  510. }
  511. return '';
  512. }
  513. /**
  514. * Method to get the form name.
  515. *
  516. * @return string The name of the form.
  517. *
  518. * @since 11.1
  519. */
  520. public function getName()
  521. {
  522. return $this->name;
  523. }
  524. /**
  525. * Method to get the value of a field.
  526. *
  527. * @param string $name The name of the field for which to get the value.
  528. * @param string $group The optional dot-separated form group path on which to get the value.
  529. * @param mixed $default The optional default value of the field value is empty.
  530. *
  531. * @return mixed The value of the field or the default value if empty.
  532. *
  533. * @since 11.1
  534. */
  535. public function getValue($name, $group = null, $default = null)
  536. {
  537. // If a group is set use it.
  538. if ($group)
  539. {
  540. $return = $this->data->get($group . '.' . $name, $default);
  541. }
  542. else
  543. {
  544. $return = $this->data->get($name, $default);
  545. }
  546. return $return;
  547. }
  548. /**
  549. * Method to load the form description from an XML string or object.
  550. *
  551. * The replace option works per field. If a field being loaded already exists in the current
  552. * form definition then the behavior or load will vary depending upon the replace flag. If it
  553. * is set to true, then the existing field will be replaced in its exact location by the new
  554. * field being loaded. If it is false, then the new field being loaded will be ignored and the
  555. * method will move on to the next field to load.
  556. *
  557. * @param string $data The name of an XML string or object.
  558. * @param string $replace Flag to toggle whether form fields should be replaced if a field
  559. * already exists with the same group/name.
  560. * @param string $xpath An optional xpath to search for the fields.
  561. *
  562. * @return boolean True on success, false otherwise.
  563. *
  564. * @since 11.1
  565. */
  566. public function load($data, $replace = true, $xpath = false)
  567. {
  568. // If the data to load isn't already an XML element or string return false.
  569. if ((!($data instanceof SimpleXMLElement)) && (!is_string($data)))
  570. {
  571. return false;
  572. }
  573. // Attempt to load the XML if a string.
  574. if (is_string($data))
  575. {
  576. try
  577. {
  578. $data = new SimpleXMLElement($data);
  579. }
  580. catch (Exception $e)
  581. {
  582. return false;
  583. }
  584. // Make sure the XML loaded correctly.
  585. if (!$data)
  586. {
  587. return false;
  588. }
  589. }
  590. // If we have no XML definition at this point let's make sure we get one.
  591. if (empty($this->xml))
  592. {
  593. // If no XPath query is set to search for fields, and we have a <form />, set it and return.
  594. if (!$xpath && ($data->getName() == 'form'))
  595. {
  596. $this->xml = $data;
  597. // Synchronize any paths found in the load.
  598. $this->syncPaths();
  599. return true;
  600. }
  601. // Create a root element for the form.
  602. else
  603. {
  604. $this->xml = new SimpleXMLElement('<form></form>');
  605. }
  606. }
  607. // Get the XML elements to load.
  608. $elements = array();
  609. if ($xpath)
  610. {
  611. $elements = $data->xpath($xpath);
  612. }
  613. elseif ($data->getName() == 'form')
  614. {
  615. $elements = $data->children();
  616. }
  617. // If there is nothing to load return true.
  618. if (empty($elements))
  619. {
  620. return true;
  621. }
  622. // Load the found form elements.
  623. foreach ($elements as $element)
  624. {
  625. // Get an array of fields with the correct name.
  626. $fields = $element->xpath('descendant-or-self::field');
  627. foreach ($fields as $field)
  628. {
  629. // Get the group names as strings for ancestor fields elements.
  630. $attrs = $field->xpath('ancestor::fields[@name]/@name');
  631. $groups = array_map('strval', $attrs ? $attrs : array());
  632. // Check to see if the field exists in the current form.
  633. if ($current = $this->findField((string) $field['name'], implode('.', $groups)))
  634. {
  635. // If set to replace found fields, replace the data and remove the field so we don't add it twice.
  636. if ($replace)
  637. {
  638. $olddom = dom_import_simplexml($current);
  639. $loadeddom = dom_import_simplexml($field);
  640. $addeddom = $olddom->ownerDocument->importNode($loadeddom);
  641. $olddom->parentNode->replaceChild($addeddom, $olddom);
  642. $loadeddom->parentNode->removeChild($loadeddom);
  643. }
  644. else
  645. {
  646. unset($field);
  647. }
  648. }
  649. }
  650. // Merge the new field data into the existing XML document.
  651. self::addNode($this->xml, $element);
  652. }
  653. // Synchronize any paths found in the load.
  654. $this->syncPaths();
  655. return true;
  656. }
  657. /**
  658. * Method to load the form description from an XML file.
  659. *
  660. * The reset option works on a group basis. If the XML file references
  661. * groups that have already been created they will be replaced with the
  662. * fields in the new XML file unless the $reset parameter has been set
  663. * to false.
  664. *
  665. * @param string $file The filesystem path of an XML file.
  666. * @param string $reset Flag to toggle whether form fields should be replaced if a field
  667. * already exists with the same group/name.
  668. * @param string $xpath An optional xpath to search for the fields.
  669. *
  670. * @return boolean True on success, false otherwise.
  671. *
  672. * @since 11.1
  673. */
  674. public function loadFile($file, $reset = true, $xpath = false)
  675. {
  676. // Check to see if the path is an absolute path.
  677. if (!is_file($file))
  678. {
  679. // Not an absolute path so let's attempt to find one using JPath.
  680. $file = JPath::find(self::addFormPath(), strtolower($file) . '.xml');
  681. // If unable to find the file return false.
  682. if (!$file)
  683. {
  684. return false;
  685. }
  686. }
  687. // Attempt to load the XML file.
  688. $xml = simplexml_load_file($file);
  689. return $this->load($xml, $reset, $xpath);
  690. }
  691. /**
  692. * Method to remove a field from the form definition.
  693. *
  694. * @param string $name The name of the form field for which remove.
  695. * @param string $group The optional dot-separated form group path on which to find the field.
  696. *
  697. * @return boolean True on success.
  698. *
  699. * @since 11.1
  700. * @throws UnexpectedValueException
  701. */
  702. public function removeField($name, $group = null)
  703. {
  704. // Make sure there is a valid JForm XML document.
  705. if (!($this->xml instanceof SimpleXMLElement))
  706. {
  707. throw new UnexpectedValueException(sprintf('%s::getFieldAttribute `xml` is not an instance of SimpleXMLElement', get_class($this)));
  708. }
  709. // Find the form field element from the definition.
  710. $element = $this->findField($name, $group);
  711. // If the element exists remove it from the form definition.
  712. if ($element instanceof SimpleXMLElement)
  713. {
  714. $dom = dom_import_simplexml($element);
  715. $dom->parentNode->removeChild($dom);
  716. }
  717. return true;
  718. }
  719. /**
  720. * Method to remove a group from the form definition.
  721. *
  722. * @param string $group The dot-separated form group path for the group to remove.
  723. *
  724. * @return boolean True on success.
  725. *
  726. * @since 11.1
  727. * @throws UnexpectedValueException
  728. */
  729. public function removeGroup($group)
  730. {
  731. // Make sure there is a valid JForm XML document.
  732. if (!($this->xml instanceof SimpleXMLElement))
  733. {
  734. throw new UnexpectedValueException(sprintf('%s::getFieldAttribute `xml` is not an instance of SimpleXMLElement', get_class($this)));
  735. }
  736. // Get the fields elements for a given group.
  737. $elements = &$this->findGroup($group);
  738. foreach ($elements as &$element)
  739. {
  740. $dom = dom_import_simplexml($element);
  741. $dom->parentNode->removeChild($dom);
  742. }
  743. return true;
  744. }
  745. /**
  746. * Method to reset the form data store and optionally the form XML definition.
  747. *
  748. * @param boolean $xml True to also reset the XML form definition.
  749. *
  750. * @return boolean True on success.
  751. *
  752. * @since 11.1
  753. */
  754. public function reset($xml = false)
  755. {
  756. unset($this->data);
  757. $this->data = new JRegistry;
  758. if ($xml)
  759. {
  760. unset($this->xml);
  761. $this->xml = new SimpleXMLElement('<form></form>');
  762. }
  763. return true;
  764. }
  765. /**
  766. * Method to set a field XML element to the form definition. If the replace flag is set then
  767. * the field will be set whether it already exists or not. If it isn't set, then the field
  768. * will not be replaced if it already exists.
  769. *
  770. * @param SimpleXMLElement $element The XML element object representation of the form field.
  771. * @param string $group The optional dot-separated form group path on which to set the field.
  772. * @param boolean $replace True to replace an existing field if one already exists.
  773. *
  774. * @return boolean True on success.
  775. *
  776. * @since 11.1
  777. * @throws UnexpectedValueException
  778. */
  779. public function setField(SimpleXMLElement $element, $group = null, $replace = true)
  780. {
  781. // Make sure there is a valid JForm XML document.
  782. if (!($this->xml instanceof SimpleXMLElement))
  783. {
  784. throw new UnexpectedValueException(sprintf('%s::getFieldAttribute `xml` is not an instance of SimpleXMLElement', get_class($this)));
  785. }
  786. // Find the form field element from the definition.
  787. $old = &$this->findField((string) $element['name'], $group);
  788. // If an existing field is found and replace flag is false do nothing and return true.
  789. if (!$replace && !empty($old))
  790. {
  791. return true;
  792. }
  793. // If an existing field is found and replace flag is true remove the old field.
  794. if ($replace && !empty($old) && ($old instanceof SimpleXMLElement))
  795. {
  796. $dom = dom_import_simplexml($old);
  797. $dom->parentNode->removeChild($dom);
  798. }
  799. // If no existing field is found find a group element and add the field as a child of it.
  800. if ($group)
  801. {
  802. // Get the fields elements for a given group.
  803. $fields = &$this->findGroup($group);
  804. // If an appropriate fields element was found for the group, add the element.
  805. if (isset($fields[0]) && ($fields[0] instanceof SimpleXMLElement))
  806. {
  807. self::addNode($fields[0], $element);
  808. }
  809. }
  810. else
  811. {
  812. // Set the new field to the form.
  813. self::addNode($this->xml, $element);
  814. }
  815. // Synchronize any paths found in the load.
  816. $this->syncPaths();
  817. return true;
  818. }
  819. /**
  820. * Method to set an attribute value for a field XML element.
  821. *
  822. * @param string $name The name of the form field for which to set the attribute value.
  823. * @param string $attribute The name of the attribute for which to set a value.
  824. * @param mixed $value The value to set for the attribute.
  825. * @param string $group The optional dot-separated form group path on which to find the field.
  826. *
  827. * @return boolean True on success.
  828. *
  829. * @since 11.1
  830. * @throws UnexpectedValueException
  831. */
  832. public function setFieldAttribute($name, $attribute, $value, $group = null)
  833. {
  834. // Make sure there is a valid JForm XML document.
  835. if (!($this->xml instanceof SimpleXMLElement))
  836. {
  837. throw new UnexpectedValueException(sprintf('%s::getFieldAttribute `xml` is not an instance of SimpleXMLElement', get_class($this)));
  838. }
  839. // Find the form field element from the definition.
  840. $element = $this->findField($name, $group);
  841. // If the element doesn't exist return false.
  842. if (!($element instanceof SimpleXMLElement))
  843. {
  844. return false;
  845. }
  846. // Otherwise set the attribute and return true.
  847. else
  848. {
  849. $element[$attribute] = $value;
  850. // Synchronize any paths found in the load.
  851. $this->syncPaths();
  852. return true;
  853. }
  854. }
  855. /**
  856. * Method to set some field XML elements to the form definition. If the replace flag is set then
  857. * the fields will be set whether they already exists or not. If it isn't set, then the fields
  858. * will not be replaced if they already exist.
  859. *
  860. * @param array &$elements The array of XML element object representations of the form fields.
  861. * @param string $group The optional dot-separated form group path on which to set the fields.
  862. * @param boolean $replace True to replace existing fields if they already exist.
  863. *
  864. * @return boolean True on success.
  865. *
  866. * @since 11.1
  867. * @throws UnexpectedValueException
  868. */
  869. public function setFields(&$elements, $group = null, $replace = true)
  870. {
  871. // Make sure there is a valid JForm XML document.
  872. if (!($this->xml instanceof SimpleXMLElement))
  873. {
  874. throw new UnexpectedValueException(sprintf('%s::getFieldAttribute `xml` is not an instance of SimpleXMLElement', get_class($this)));
  875. }
  876. // Make sure the elements to set are valid.
  877. foreach ($elements as $element)
  878. {
  879. if (!($element instanceof SimpleXMLElement))
  880. {
  881. throw new UnexpectedValueException(sprintf('$element not SimpleXMLElement in %s::setFields', get_class($this)));
  882. }
  883. }
  884. // Set the fields.
  885. $return = true;
  886. foreach ($elements as $element)
  887. {
  888. if (!$this->setField($element, $group, $replace))
  889. {
  890. $return = false;
  891. }
  892. }
  893. // Synchronize any paths found in the load.
  894. $this->syncPaths();
  895. return $return;
  896. }
  897. /**
  898. * Method to set the value of a field. If the field does not exist in the form then the method
  899. * will return false.
  900. *
  901. * @param string $name The name of the field for which to set the value.
  902. * @param string $group The optional dot-separated form group path on which to find the field.
  903. * @param mixed $value The value to set for the field.
  904. *
  905. * @return boolean True on success.
  906. *
  907. * @since 11.1
  908. */
  909. public function setValue($name, $group = null, $value = null)
  910. {
  911. // If the field does not exist return false.
  912. if (!$this->findField($name, $group))
  913. {
  914. return false;
  915. }
  916. // If a group is set use it.
  917. if ($group)
  918. {
  919. $this->data->set($group . '.' . $name, $value);
  920. }
  921. else
  922. {
  923. $this->data->set($name, $value);
  924. }
  925. return true;
  926. }
  927. /**
  928. * Method to validate form data.
  929. *
  930. * Validation warnings will be pushed into JForm::errors and should be
  931. * retrieved with JForm::getErrors() when validate returns boolean false.
  932. *
  933. * @param array $data An array of field values to validate.
  934. * @param string $group The optional dot-separated form group path on which to filter the
  935. * fields to be validated.
  936. *
  937. * @return mixed True on sucess.
  938. *
  939. * @since 11.1
  940. */
  941. public function validate($data, $group = null)
  942. {
  943. // Make sure there is a valid JForm XML document.
  944. if (!($this->xml instanceof SimpleXMLElement))
  945. {
  946. return false;
  947. }
  948. $return = true;
  949. // Create an input registry object from the data to validate.
  950. $input = new JRegistry($data);
  951. // Get the fields for which to validate the data.
  952. $fields = $this->findFieldsByGroup($group);
  953. if (!$fields)
  954. {
  955. // PANIC!
  956. return false;
  957. }
  958. // Validate the fields.
  959. foreach ($fields as $field)
  960. {
  961. $value = null;
  962. $name = (string) $field['name'];
  963. // Get the group names as strings for ancestor fields elements.
  964. $attrs = $field->xpath('ancestor::fields[@name]/@name');
  965. $groups = array_map('strval', $attrs ? $attrs : array());
  966. $group = implode('.', $groups);
  967. // Get the value from the input data.
  968. if ($group)
  969. {
  970. $value = $input->get($group . '.' . $name);
  971. }
  972. else
  973. {
  974. $value = $input->get($name);
  975. }
  976. // Validate the field.
  977. $valid = $this->validateField($field, $group, $value, $input);
  978. // Check for an error.
  979. if ($valid instanceof Exception)
  980. {
  981. array_push($this->errors, $valid);
  982. $return = false;
  983. }
  984. }
  985. return $return;
  986. }
  987. /**
  988. * Method to apply an input filter to a value based on field data.
  989. *
  990. * @param string $element The XML element object representation of the form field.
  991. * @param mixed $value The value to filter for the field.
  992. *
  993. * @return mixed The filtered value.
  994. *
  995. * @since 11.1
  996. */
  997. protected function filterField($element, $value)
  998. {
  999. // Make sure there is a valid SimpleXMLElement.
  1000. if (!($element instanceof SimpleXMLElement))
  1001. {
  1002. return false;
  1003. }
  1004. // Get the field filter type.
  1005. $filter = (string) $element['filter'];
  1006. // Process the input value based on the filter.
  1007. $return = null;
  1008. switch (strtoupper($filter))
  1009. {
  1010. // Access Control Rules.
  1011. case 'RULES':
  1012. $return = array();
  1013. foreach ((array) $value as $action => $ids)
  1014. {
  1015. // Build the rules array.
  1016. $return[$action] = array();
  1017. foreach ($ids as $id => $p)
  1018. {
  1019. if ($p !== '')
  1020. {
  1021. $return[$action][$id] = ($p == '1' || $p == 'true') ? true : false;
  1022. }
  1023. }
  1024. }
  1025. break;
  1026. // Do nothing, thus leaving the return value as null.
  1027. case 'UNSET':
  1028. break;
  1029. // No Filter.
  1030. case 'RAW':
  1031. $return = $value;
  1032. break;
  1033. // Filter the input as an array of integers.
  1034. case 'INT_ARRAY':
  1035. // Make sure the input is an array.
  1036. if (is_object($value))
  1037. {
  1038. $value = get_object_vars($value);
  1039. }
  1040. $value = is_array($value) ? $value : array($value);
  1041. JArrayHelper::toInteger($value);
  1042. $return = $value;
  1043. break;
  1044. // Filter safe HTML.
  1045. case 'SAFEHTML':
  1046. $return = JFilterInput::getInstance(null, null, 1, 1)->clean($value, 'string');
  1047. break;
  1048. // Convert a date to UTC based on the server timezone offset.
  1049. case 'SERVER_UTC':
  1050. if ((int) $value > 0)
  1051. {
  1052. // Get the server timezone setting.
  1053. $offset = JFactory::getConfig()->get('offset');
  1054. // Return an SQL formatted datetime string in UTC.
  1055. $return = JFactory::getDate($value, $offset)->toSql();
  1056. }
  1057. else
  1058. {
  1059. $return = '';
  1060. }
  1061. break;
  1062. // Convert a date to UTC based on the user timezone offset.
  1063. case 'USER_UTC':
  1064. if ((int) $value > 0)
  1065. {
  1066. // Get the user timezone setting defaulting to the server timezone setting.
  1067. $offset = JFactory::getUser()->getParam('timezone', JFactory::getConfig()->get('offset'));
  1068. // Return a MySQL formatted datetime string in UTC.
  1069. $return = JFactory::getDate($value, $offset)->toSql();
  1070. }
  1071. else
  1072. {
  1073. $return = '';
  1074. }
  1075. break;
  1076. // Ensures a protocol is present in the saved field. Only use when
  1077. // the only permitted protocols requre '://'. See JFormRuleUrl for list of these.
  1078. case 'URL':
  1079. if (empty($value))
  1080. {
  1081. return;
  1082. }
  1083. $value = JFilterInput::getInstance()->clean($value, 'html');
  1084. $value = trim($value);
  1085. // Check for a protocol
  1086. $protocol = parse_url($value, PHP_URL_SCHEME);
  1087. // If there is no protocol and the relative option is not specified,
  1088. // we assume that it is an external URL and prepend http://.
  1089. if (($element['type'] == 'url' && !$protocol && !$element['relative'])
  1090. || (!$element['type'] == 'url' && !$protocol))
  1091. {
  1092. $protocol = 'http';
  1093. // If it looks like an internal link, then add the root.
  1094. if (substr($value, 0) == 'index.php')
  1095. {
  1096. $value = JURI::root() . $value;
  1097. }
  1098. // Otherwise we treat it is an external link.
  1099. // Put the url back together.
  1100. $value = $protocol . '://' . $value;
  1101. }
  1102. // If relative URLS are allowed we assume that URLs without protocols are internal.
  1103. elseif (!$protocol && $element['relative'])
  1104. {
  1105. $host = JURI::getInstance('SERVER')->gethost();
  1106. // If it starts with the host string, just prepend the protocol.
  1107. if (substr($value, 0) == $host)
  1108. {
  1109. $value = 'http://' . $value;
  1110. }
  1111. // Otherwise prepend the root.
  1112. else
  1113. {
  1114. $value = JURI::root() . $value;
  1115. }
  1116. }
  1117. $return = $value;
  1118. break;
  1119. case 'TEL':
  1120. $value = trim($value);
  1121. // Does it match the NANP pattern?
  1122. if (preg_match('/^(?:\+?1[-. ]?)?\(?([2-9][0-8][0-9])\)?[-. ]?([2-9][0-9]{2})[-. ]?([0-9]{4})$/', $value) == 1)
  1123. {
  1124. $number = (string) preg_replace('/[^\d]/', '', $value);
  1125. if (substr($number, 0, 1) == 1)
  1126. {
  1127. $number = substr($number, 1);
  1128. }
  1129. if (substr($number, 0, 2) == '+1')
  1130. {
  1131. $number = substr($number, 2);
  1132. }
  1133. $result = '1.' . $number;
  1134. }
  1135. // If not, does it match ITU-T?
  1136. elseif (preg_match('/^\+(?:[0-9] ?){6,14}[0-9]$/', $value) == 1)
  1137. {
  1138. $countrycode = substr($value, 0, strpos($value, ' '));
  1139. $countrycode = (string) preg_replace('/[^\d]/', '', $countrycode);
  1140. $number = strstr($value, ' ');
  1141. $number = (string) preg_replace('/[^\d]/', '', $number);
  1142. $result = $countrycode . '.' . $number;
  1143. }
  1144. // If not, does it match EPP?
  1145. elseif (preg_match('/^\+[0-9]{1,3}\.[0-9]{4,14}(?:x.+)?$/', $value) == 1)
  1146. {
  1147. if (strstr($value, 'x'))
  1148. {
  1149. $xpos = strpos($value, 'x');
  1150. $value = substr($value, 0, $xpos);
  1151. }
  1152. $result = str_replace('+', '', $value);
  1153. }
  1154. // Maybe it is already ccc.nnnnnnn?
  1155. elseif (preg_match('/[0-9]{1,3}\.[0-9]{4,14}$/', $value) == 1)
  1156. {
  1157. $result = $value;
  1158. }
  1159. // If not, can we make it a string of digits?
  1160. else
  1161. {
  1162. $value = (string) preg_replace('/[^\d]/', '', $value);
  1163. if ($value != null && strlen($value) <= 15)
  1164. {
  1165. $length = strlen($value);
  1166. // If it is fewer than 13 digits assume it is a local number
  1167. if ($length <= 12)
  1168. {
  1169. $result = '.' . $value;
  1170. }
  1171. else
  1172. {
  1173. // If it has 13 or more digits let's make a country code.
  1174. $cclen = $length - 12;
  1175. $result = substr($value, 0, $cclen) . '.' . substr($value, $cclen);
  1176. }
  1177. }
  1178. // If not let's not save anything.
  1179. else
  1180. {
  1181. $result = '';
  1182. }
  1183. }
  1184. $return = $result;
  1185. break;
  1186. default:
  1187. // Check for a callback filter.
  1188. if (strpos($filter, '::') !== false && is_callable(explode('::', $filter)))
  1189. {
  1190. $return = call_user_func(explode('::', $filter), $value);
  1191. }
  1192. // Filter using a callback function if specified.
  1193. elseif (function_exists($filter))
  1194. {
  1195. $return = call_user_func($filter, $value);
  1196. }
  1197. // Filter using JFilterInput. All HTML code is filtered by default.
  1198. else
  1199. {
  1200. $return = JFilterInput::getInstance()->clean($value, $filter);
  1201. }
  1202. break;
  1203. }
  1204. return $return;
  1205. }
  1206. /**
  1207. * Method to get a form field represented as an XML element object.
  1208. *
  1209. * @param string $name The name of the form field.
  1210. * @param string $group The optional dot-separated form group path on which to find the field.
  1211. *
  1212. * @return mixed The XML element object for the field or boolean false on error.
  1213. *
  1214. * @since 11.1
  1215. */
  1216. protected function findField($name, $group = null)
  1217. {
  1218. $element = false;
  1219. $fields = array();
  1220. // Make sure there is a valid JForm XML document.
  1221. if (!($this->xml instanceof SimpleXMLElement))
  1222. {
  1223. return false;
  1224. }
  1225. // Let's get the appropriate field element based on the method arguments.
  1226. if ($group)
  1227. {
  1228. // Get the fields elements for a given group.
  1229. $elements = &$this->findGroup($group);
  1230. // Get all of the field elements with the correct name for the fields elements.
  1231. foreach ($elements as $element)
  1232. {
  1233. // If there are matching field elements add them to the fields array.
  1234. if ($tmp = $element->xpath('descendant::field[@name="' . $name . '"]'))
  1235. {
  1236. $fields = array_merge($fields, $tmp);
  1237. }
  1238. }
  1239. // Make sure something was found.
  1240. if (!$fields)
  1241. {
  1242. return false;
  1243. }
  1244. // Use the first correct match in the given group.
  1245. $groupNames = explode('.', $group);
  1246. foreach ($fields as &$field)
  1247. {
  1248. // Get the group names as strings for ancestor fields elements.
  1249. $attrs = $field->xpath('ancestor::fields[@name]/@name');
  1250. $names = array_map('strval', $attrs ? $attrs : array());
  1251. // If the field is in the exact group use it and break out of the loop.
  1252. if ($names == (array) $groupNames)
  1253. {
  1254. $element = &$field;
  1255. break;
  1256. }
  1257. }
  1258. }
  1259. else
  1260. {
  1261. // Get an array of fields with the correct name.
  1262. $fields = $this->xml->xpath('//field[@name="' . $name . '"]');
  1263. // Make sure something was found.
  1264. if (!$fields)
  1265. {
  1266. return false;
  1267. }
  1268. // Search through the fields for the right one.
  1269. foreach ($fields as &$field)
  1270. {
  1271. // If we find an ancestor fields element with a group name then it isn't what we want.
  1272. if ($field->xpath('ancestor::fields[@name]'))
  1273. {
  1274. continue;
  1275. }
  1276. // Found it!
  1277. else
  1278. {
  1279. $element = &$field;
  1280. break;
  1281. }
  1282. }
  1283. }
  1284. return $element;
  1285. }
  1286. /**
  1287. * Method to get an array of <field /> elements from the form XML document which are
  1288. * in a specified fieldset by name.
  1289. *
  1290. * @param string $name The name of the fieldset.
  1291. *
  1292. * @return mixed Boolean false on error or array of SimpleXMLElement objects.
  1293. *
  1294. * @since 11.1
  1295. */
  1296. protected function &findFieldsByFieldset($name)
  1297. {
  1298. $false = false;
  1299. // Make sure there is a valid JForm XML document.
  1300. if (!($this->xml instanceof SimpleXMLElement))
  1301. {
  1302. return $false;
  1303. }
  1304. /*
  1305. * Get an array of <field /> elements that are underneath a <fieldset /> element
  1306. * with the appropriate name attribute (unless they are the decendents
  1307. * of another <field /> element for some reaon), and also any <field /> elements
  1308. * with the appropriate fieldset attribute.
  1309. */
  1310. $fields = $this->xml->xpath('//fieldset[@name="' . $name . '"]//field[not(ancestor::field)] | //field[@fieldset="' . $name . '"]');
  1311. return $fields;
  1312. }
  1313. /**
  1314. * Method to get an array of <field /> elements from the form XML document which are
  1315. * in a control group by name.
  1316. *
  1317. * @param mixed $group The optional dot-separated form group path on which to find the fields.
  1318. * Null will return all fields. False will return fields not in a group.
  1319. * @param boolean $nested True to also include fields in nested groups that are inside of the
  1320. * group for which to find fields.
  1321. *
  1322. * @return mixed Boolean false on error or array of SimpleXMLElement objects.
  1323. *
  1324. * @since 11.1
  1325. */
  1326. protected function &findFieldsByGroup($group = null, $nested = false)
  1327. {
  1328. $false = false;
  1329. $fields = array();
  1330. // Make sure there is a valid JForm XML document.
  1331. if (!($this->xml instanceof SimpleXMLElement))
  1332. {
  1333. return $false;
  1334. }
  1335. // Get only fields in a specific group?
  1336. if ($group)
  1337. {
  1338. // Get the fields elements for a given group.
  1339. $elements = &$this->findGroup($group);
  1340. // Get all of the field elements for the fields elements.
  1341. foreach ($elements as $element)
  1342. {
  1343. // If there are field elements add them to the return result.
  1344. if ($tmp = $element->xpath('descendant::field'))
  1345. {
  1346. // If we also want fields in nested groups then just merge the arrays.
  1347. if ($nested)
  1348. {
  1349. $fields = array_merge($fields, $tmp);
  1350. }
  1351. // If we want to exclude nested groups then we need to check each field.
  1352. else
  1353. {
  1354. $groupNames = explode('.', $group);
  1355. foreach ($tmp as $field)
  1356. {
  1357. // Get the names of the groups that the field is in.
  1358. $attrs = $field->xpath('ancestor::fields[@name]/@name');
  1359. $names = array_map('strval', $attrs ? $attrs : array());
  1360. // If the field is in the specific group then add it to the return list.
  1361. if ($names == (array) $groupNames)
  1362. {
  1363. $fields = array_merge($fields, array($field));
  1364. }
  1365. }
  1366. }
  1367. }
  1368. }
  1369. }
  1370. elseif ($group === false)
  1371. {
  1372. // Get only field elements not in a group.
  1373. $fields = $this->xml->xpath('descendant::fields[not(@name)]/field | descendant::fields[not(@name)]/fieldset/field ');
  1374. }
  1375. else
  1376. {
  1377. // Get an array of all the <field /> elements.
  1378. $fields = $this->xml->xpath('//field');
  1379. }
  1380. return $fields;
  1381. }
  1382. /**
  1383. * Method to get a form field group represented as an XML element object.
  1384. *
  1385. * @param string $group The dot-separated form group path on which to find the group.
  1386. *
  1387. * @return mixed An array of XML element objects for the group or boolean false on error.
  1388. *
  1389. * @since 11.1
  1390. */
  1391. protected function &findGroup($group)
  1392. {
  1393. $false = false;
  1394. $groups = array();
  1395. $tmp = array();
  1396. // Make sure there is a valid JForm XML document.
  1397. if (!($this->xml instanceof SimpleXMLElement))
  1398. {
  1399. return $false;
  1400. }
  1401. // Make sure there is actually a group to find.
  1402. $group = explode('.', $group);
  1403. if (!empty($group))
  1404. {
  1405. // Get any fields elements with the correct group name.
  1406. $elements = $this->xml->xpath('//fields[@name="' . (string) $group[0] . '"]');
  1407. // Check to make sure that there are no parent groups for each element.
  1408. foreach ($elements as $element)
  1409. {
  1410. if (!$element->xpath('ancestor::fields[@name]'))
  1411. {
  1412. $tmp[] = $element;
  1413. }
  1414. }
  1415. // Iterate through the nested groups to find any matching form field groups.
  1416. for ($i = 1, $n = count($group); $i < $n; $i++)
  1417. {
  1418. // Initialise some loop variables.
  1419. $validNames = array_slice($group, 0, $i + 1);
  1420. $current = $tmp;
  1421. $tmp = array();
  1422. // Check to make sure that there are no parent groups for each element.
  1423. foreach ($current as $element)
  1424. {
  1425. // Get any fields elements with the correct group name.
  1426. $children = $element->xpath('descendant::fields[@name="' . (string) $group[$i] . '"]');
  1427. // For the found fields elements validate that they are in the correct groups.
  1428. foreach ($children as $fields)
  1429. {
  1430. // Get the group names as strings for ancestor fields elements.
  1431. $attrs = $fields->xpath('ancestor-or-self::fields[@name]/@name');
  1432. $names = array_map('strval', $attrs ? $attrs : array());
  1433. // If the group names for the fields element match the valid names at this
  1434. // level add the fields element.
  1435. if ($validNames == $names)
  1436. {
  1437. $tmp[] = $fields;
  1438. }
  1439. }
  1440. }
  1441. }
  1442. // Only include valid XML objects.
  1443. foreach ($tmp as $element)
  1444. {
  1445. if ($element instanceof SimpleXMLElement)
  1446. {
  1447. $groups[] = $element;
  1448. }
  1449. }
  1450. }
  1451. return $groups;
  1452. }
  1453. /**
  1454. * Method to load, setup and return a JFormField object based on field data.
  1455. *
  1456. * @param string $element The XML element object representation of the form field.
  1457. * @param string $group The optional dot-separated form group path on which to find the field.
  1458. * @param mixed $value The optional value to use as the default for the field.
  1459. *
  1460. * @return mixed The JFormField object for the field or boolean false on error.
  1461. *
  1462. * @since 11.1
  1463. */
  1464. protected function loadField($element, $group = null, $value = null)
  1465. {
  1466. // Make sure there is a valid SimpleXMLElement.
  1467. if (!($element instanceof SimpleXMLElement))
  1468. {
  1469. return false;
  1470. }
  1471. // Get the field type.
  1472. $type = $element['type'] ? (string) $element['type'] : 'text';
  1473. // Load the JFormField object for the field.
  1474. $field = $this->loadFieldType($type);
  1475. // If the object could not be loaded, get a text field object.
  1476. if ($field === false)
  1477. {
  1478. $field = $this->loadFieldType('text');
  1479. }
  1480. /*
  1481. * Get the value for the form field if not set.
  1482. * Default to the translated version of the 'default' attribute
  1483. * if 'translate_default' attribute if set to 'true' or '1'
  1484. * else the value of the 'default' attribute for the field.
  1485. */
  1486. if ($value === null)
  1487. {
  1488. $default = (string) $element['default'];
  1489. if (($translate = $element['translate_default']) && ((string) $translate == 'true' || (string) $translate == '1'))
  1490. {
  1491. $lang = JFactory::getLanguage();
  1492. if ($lang->hasKey($default))
  1493. {
  1494. $debug = $lang->setDebug(false);
  1495. $default = JText::_($default);
  1496. $lang->setDebug($debug);
  1497. }
  1498. else
  1499. {
  1500. $default = JText::_($default);
  1501. }
  1502. }
  1503. $value = $this->getValue((string) $element['name'], $group, $default);
  1504. }
  1505. // Setup the JFormField object.
  1506. $field->setForm($this);
  1507. if ($field->setup($element, $value, $group))
  1508. {
  1509. return $field;
  1510. }
  1511. else
  1512. {
  1513. return false;
  1514. }
  1515. }
  1516. /**
  1517. * Proxy for {@link JFormHelper::loadFieldType()}.
  1518. *
  1519. * @param string $type The field type.
  1520. * @param boolean $new Flag to toggle whether we should get a new instance of the object.
  1521. *
  1522. * @return mixed JFormField object on success, false otherwise.
  1523. *
  1524. * @since 11.1
  1525. */
  1526. protected function loadFieldType($type, $new = true)
  1527. {
  1528. return JFormHelper::loadFieldType($type, $new);
  1529. }
  1530. /**
  1531. * Proxy for JFormHelper::loadRuleType().
  1532. *
  1533. * @param string $type The rule type.
  1534. * @param boolean $new Flag to toggle whether we should get a new instance of the object.
  1535. *
  1536. * @return mixed JFormRule object on success, false otherwise.
  1537. *
  1538. * @see JFormHelper::loadRuleType()
  1539. * @since 11.1
  1540. */
  1541. protected function loadRuleType($type, $new = true)
  1542. {
  1543. return JFormHelper::loadRuleType($type, $new);
  1544. }
  1545. /**
  1546. * Method to synchronize any field, form or rule paths contained in the XML document.
  1547. *
  1548. * @return boolean True on success.
  1549. *
  1550. * @since 11.1
  1551. * @todo Maybe we should receive all addXXXpaths attributes at once?
  1552. */
  1553. protected function syncPaths()
  1554. {
  1555. // Make sure there is a valid JForm XML document.
  1556. if (!($this->xml instanceof SimpleXMLElement))
  1557. {
  1558. return false;
  1559. }
  1560. // Get any addfieldpath attributes from the form definition.
  1561. $paths = $this->xml->xpath('//*[@addfieldpath]/@addfieldpath');
  1562. $paths = array_map('strval', $paths ? $paths : array());
  1563. // Add the field paths.
  1564. foreach ($paths as $path)
  1565. {
  1566. $path = JPATH_ROOT . '/' . ltrim($path, '/\\');
  1567. self::addFieldPath($path);
  1568. }
  1569. // Get any addformpath attributes from the form definition.
  1570. $paths = $this->xml->xpath('//*[@addformpath]/@addformpath');
  1571. $paths = array_map('strval', $paths ? $paths : array());
  1572. // Add the form paths.
  1573. foreach ($paths as $path)
  1574. {
  1575. $path = JPATH_ROOT . '/' . ltrim($path, '/\\');
  1576. self::addFormPath($path);
  1577. }
  1578. // Get any addrulepath attributes from the form definition.
  1579. $paths = $this->xml->xpath('//*[@addrulepath]/@addrulepath');
  1580. $paths = array_map('strval', $paths ? $paths : array());
  1581. // Add the rule paths.
  1582. foreach ($paths as $path)
  1583. {
  1584. $path = JPATH_ROOT . '/' . ltrim($path, '/\\');
  1585. self::addRulePath($path);
  1586. }
  1587. return true;
  1588. }
  1589. /**
  1590. * Method to validate a JFormField object based on field data.
  1591. *
  1592. * @param SimpleXMLElement $element The XML element object representation of the form field.
  1593. * @param string $group The optional dot-separated form group path on which to find the field.
  1594. * @param mixed $value The optional value to use as the default for the field.
  1595. * @param JRegistry $input An optional JRegistry object with the entire data set to validate
  1596. * against the entire form.
  1597. *
  1598. * @return mixed Boolean true if field value is valid, Exception on failure.
  1599. *
  1600. * @since 11.1
  1601. * @throws InvalidArgumentException
  1602. * @throws UnexpectedValueException
  1603. */
  1604. protected function validateField(SimpleXMLElement $element, $group = null, $value = null, JRegistry $input = null)
  1605. {
  1606. $valid = true;
  1607. // Check if the field is required.
  1608. $required = ((string) $element['required'] == 'true' || (string) $element['required'] == 'required');
  1609. if ($required)
  1610. {
  1611. // If the field is required and the value is empty return an error message.
  1612. if (($value === '') || ($value === null))
  1613. {
  1614. if ($element['label'])
  1615. {
  1616. $message = JText::_($element['label']);
  1617. }
  1618. else
  1619. {
  1620. $message = JText::_($element['name']);
  1621. }
  1622. $message = JText::sprintf('JLIB_FORM_VALIDATE_FIELD_REQUIRED', $message);
  1623. return new RuntimeException($message);
  1624. }
  1625. }
  1626. // Get the field validation rule.
  1627. if ($type = (string) $element['validate'])
  1628. {
  1629. // Load the JFormRule object for the field.
  1630. $rule = $this->loadRuleType($type);
  1631. // If the o…

Large files files are truncated, but you can click here to view the full file