PageRenderTime 46ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/joomla/form/form.php

https://github.com/J2MTecnologia/joomla-3.x
PHP | 2272 lines | 1340 code | 244 blank | 688 comment | 146 complexity | 8008bc24b3fc98dcac7413005e52fe40 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause

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

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