PageRenderTime 68ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/inc/patForms.php

https://github.com/chregu/fluxcms
PHP | 2819 lines | 1530 code | 301 blank | 988 comment | 211 complexity | cb9862037ff4832c2e8b6578c1e06cc5 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, Apache-2.0, LGPL-2.1
  1. <?php
  2. /**
  3. * patForms form manager class - serialize form elements into any given output format
  4. * using element classes, and build the output via renderer classes.
  5. *
  6. * $Id$
  7. *
  8. * @package patForms
  9. * @author Sebastian Mordziol <argh@php-tools.net>
  10. * @author gERD Schaufelberger <gerd@php-tools.net>
  11. * @author Stephan Schmidt <schst@php-tools.net>
  12. * @copyright 2003-2004 PHP Application Tools
  13. * @license LGPL
  14. * @link http://www.php-tools.net
  15. */
  16. /**
  17. * set the include path
  18. */
  19. if( !defined( 'PATFORMS_INCLUDE_PATH' ) ) {
  20. define( 'PATFORMS_INCLUDE_PATH', dirname( __FILE__ ). '/patForms' );
  21. }
  22. /**
  23. * location of global javascripts
  24. */
  25. define('PATFORMS_SCRIPT_PATH', PATFORMS_INCLUDE_PATH . '/Scripts');
  26. /**
  27. * needs helper methods of patForms_Element
  28. */
  29. include_once PATFORMS_INCLUDE_PATH . "/Element.php";
  30. /**
  31. * error definition: renderer base class file (renderers/_base.php) could not
  32. * be found.
  33. *
  34. * @see patForms::_createModule()
  35. */
  36. define( "PATFORMS_ERROR_NO_MODULE_BASE_FILE", 1001 );
  37. /**
  38. * error definition: the specified renderer could not be found.
  39. *
  40. * @see patForms::_createModule()
  41. */
  42. define( "PATFORMS_ERROR_MODULE_NOT_FOUND", 1002 );
  43. /**
  44. * error definition: the element added via the {@link patForms::addElement()}
  45. * is not an object. Use the {@link patForms::createElement()} method to
  46. * create an element object.
  47. *
  48. * @see patForms::addElement()
  49. * @see patForms::createElement()
  50. */
  51. define( "PATFORMS_ERROR_ELEMENT_IS_NO_OBJECT", 1003 );
  52. /**
  53. * error definition: generic unexpected error.
  54. */
  55. define( "PATFORMS_ERROR_UNEXPECTED_ERROR", 1004 );
  56. /**
  57. * element does not exist
  58. */
  59. define( "PATFORMS_ERROR_ELEMENT_NOT_FOUND", 1012 );
  60. /**
  61. * renderer object has not been set - if you want to render the form, you have to
  62. * set a renderer object via the {@link patForms::setRenderer()} method. To create
  63. * a renderer, use the {@link patForms::createRenderer()} method.
  64. *
  65. * @see patForms::setRenderer()
  66. * @see patForms::createRenderer()
  67. */
  68. define( "PATFORMS_ERROR_NO_RENDERER_SET", 1013 );
  69. /**
  70. * invalid renderer
  71. *
  72. * @see createRenderer()
  73. */
  74. define( "PATFORMS_ERROR_INVALID_RENDERER", 1014 );
  75. /**
  76. * invalid method
  77. *
  78. * @see setMethod()
  79. */
  80. define( "PATFORMS_ERROR_INVALID_METHOD", 1015 );
  81. /**
  82. * Given parameter is not a boolean value
  83. */
  84. define( "PATFORMS_ERROR_PARAMETER_NO_BOOL", 1016 );
  85. /**
  86. * Given Static property does not exist
  87. */
  88. define( "PATFORMS_ERROR_NO_STATIC_PROPERTY", 1017 );
  89. /**
  90. * Unknown event
  91. */
  92. define( "PATFORMS_ERROR_UNKNOWN_EVENT", 1018 );
  93. /**
  94. * Invalid event handler
  95. */
  96. define( "PATFORMS_ERROR_INVALID_HANDLER", 1019 );
  97. /**
  98. * Event exists
  99. */
  100. define( 'PATFORMS_NOTICE_EVENT_ALREADY_REGISTERED', 1020 );
  101. /**
  102. * Invalid storage container
  103. */
  104. define( 'PATFORMS_ERROR_INVALID_STORAGE', 1021 );
  105. define( 'PATFORMS_NOTICE_ARRAY_EXPECTED', 1022 );
  106. define( 'PATFORMS_NOTICE_ATTRIBUTE_NOT_SUPPORTED', 1023 );
  107. define( 'PATFORMS_NOTICE_INVALID_OPTION', 1024 );
  108. define( 'PATFORMS_ERROR_ATTRIBUTE_REQUIRED', 1025 );
  109. define( 'PATFORMS_ERROR_CAN_NOT_VERIFY_FORMAT', 1026 );
  110. define( 'PATFORMS_ERROR_METHOD_FOR_MODE_NOT_AVAILABLE', 1027 );
  111. /**
  112. * errors apply on translating errors matching current locale settings
  113. */
  114. define( 'PATFORMS_NOTICE_VALIDATOR_ERROR_LOCALE_UNDEFINED', 1028 );
  115. define( 'PATFORMS_WARNING_VALIDATOR_ERROR_UNDEFINED', 1029 );
  116. /**
  117. * Script file could not be loaded
  118. */
  119. define( 'PATFORMS_WARNING_SCRIPTFILE_NOT_FOUND', 1040 );
  120. /**
  121. * apply the rule before the built-in validation
  122. */
  123. define( 'PATFORMS_RULE_BEFORE_VALIDATION', 1 );
  124. /**
  125. * apply the rule after the built-in validation
  126. */
  127. define( 'PATFORMS_RULE_AFTER_VALIDATION', 2 );
  128. /**
  129. * apply the rule before AND after the built-in validation
  130. */
  131. define( 'PATFORMS_RULE_BOTH', 3 );
  132. /**
  133. * attach the observer to the elements
  134. */
  135. define( 'PATFORMS_OBSERVER_ATTACH_TO_ELEMENTS', 1 );
  136. /**
  137. * attach the observer to the form
  138. */
  139. define( 'PATFORMS_OBSERVER_ATTACH_TO_FORM', 2 );
  140. /**
  141. * attach the observer to the form and the elements
  142. */
  143. define( 'PATFORMS_OBSERVER_ATTACH_TO_BOTH', 3 );
  144. /**
  145. * group values should stay nested
  146. */
  147. define('PATFORMS_VALUES_NESTED', 0);
  148. /**
  149. * group values should be flattened
  150. */
  151. define('PATFORMS_VALUES_FLATTENED', 1);
  152. /**
  153. * group values should be prefixed
  154. */
  155. define('PATFORMS_VALUES_PREFIXED', 2);
  156. /**
  157. * Static patForms properties - used to emulate pre-PHP5 static properties.
  158. *
  159. * @see setStaticProperty()
  160. * @see getStaticProperty()
  161. */
  162. $GLOBALS['_patForms'] = array(
  163. 'format' => 'html',
  164. 'locale' => 'C',
  165. 'customLocales' => array(),
  166. 'autoFinalize' => true,
  167. 'defaultAttributes' => array(),
  168. 'elementCounter' => 0,
  169. );
  170. /**
  171. * patForms form manager class - serialize form elements into any given output format
  172. * using element classes, and build the output via renderer classes.
  173. *
  174. * @package patForms
  175. * @author Sebastian Mordziol <argh@php-tools.net>
  176. * @author gERD Schaufelberger <gerd@php-tools.net>
  177. * @author Stephan Schmidt <schst@php-tools.net>
  178. * @copyright 2003-2004 PHP Application Tools
  179. * @license LGPL
  180. * @link http://www.php-tools.net
  181. * @version 1.0
  182. * @todo check the clientside functionality, as that can lead to broken pages
  183. */
  184. class patForms
  185. {
  186. /**
  187. * javascript that will displayed only once
  188. *
  189. * @access private
  190. * @var array
  191. */
  192. var $globalJavascript = array();
  193. /**
  194. * javascript that will be displayed once per instance
  195. *
  196. * @access private
  197. * @var array
  198. */
  199. var $instanceJavascript = array();
  200. /**
  201. * stores the mode for the form. It defaults to 'default', and is only overwritten if
  202. * set specifically. It is passed on to any elements you create.
  203. *
  204. * @access private
  205. * @see setMode()
  206. */
  207. var $mode = 'default';
  208. /**
  209. * XML entities
  210. *
  211. * @access private
  212. * @see toXML()
  213. * @todo This is redundant to the Element's xmlEntities property - find a way to keep this in one place
  214. */
  215. var $xmlEntities = array(
  216. "<" => "&lt;",
  217. ">" => "&gt;",
  218. "&" => "&amp;",
  219. "'" => "&apos;",
  220. '"' => "&quot;"
  221. );
  222. /**
  223. * stores the format for the element. It defaults to 'html', and is only overwritten if
  224. * set specifically. It is passed on to any elements you create.
  225. *
  226. * @access private
  227. * @see setFormat()
  228. */
  229. var $format = 'html';
  230. /**
  231. * stores the flag telling the form whether it has been submitted - this is passed on to any
  232. * elements you create.
  233. *
  234. * @access private
  235. * @see setSubmitted()
  236. */
  237. var $submitted = false;
  238. /**
  239. * stores the element objects of this form.
  240. * @access private
  241. * @see addElement()
  242. */
  243. var $elements = array();
  244. /**
  245. * stores a renderer
  246. * @access private
  247. * @see setRenderer(), renderForm()
  248. */
  249. var $renderer = null;
  250. /**
  251. * stores the locale to use when adding validation errors for the whole form.
  252. *
  253. * @access private
  254. * @var string $locale
  255. * @see setLocale()
  256. */
  257. var $locale = 'C';
  258. /**
  259. * stores custom locale
  260. *
  261. * @access private
  262. * @var array
  263. * @see setLocale()
  264. */
  265. var $customLocales = array();
  266. /**
  267. * stores the element name
  268. * @access private
  269. * @see getElementName()
  270. */
  271. var $elementName = 'Form';
  272. /**
  273. * flag to indicate, whether form should be validated automatically
  274. * by renderForm()
  275. *
  276. * @access private
  277. * @var string
  278. * @see setAutoValidate(), renderForm()
  279. */
  280. var $autoValidate = false;
  281. /**
  282. * name of the variable that indicates, whether the form has
  283. * been submitted.
  284. *
  285. * @access private
  286. * @var string
  287. * @see setAutoValidate()
  288. */
  289. var $submitVar = null;
  290. /**
  291. * event handlers
  292. *
  293. * @access private
  294. * @var array
  295. * @see registerEventHandler()
  296. * @see registerEvent()
  297. */
  298. var $_eventHandler = array();
  299. /**
  300. * events that can be triggered
  301. *
  302. * @access private
  303. * @var array
  304. * @see registerEventHandler()
  305. * @see triggerEvent()
  306. * @see registerEvent()
  307. */
  308. var $_validEvents = array( 'onInit', 'onSubmit', 'onError', 'onSuccess' );
  309. /**
  310. * Stores whether the current form has been validated
  311. *
  312. * @access private
  313. */
  314. var $validated = false;
  315. /**
  316. * Stores whether the current form is valid or not (after the
  317. * validation process)
  318. *
  319. * @access private
  320. */
  321. var $valid = null;
  322. /**
  323. * Stores the names of all static properties that patForms will use as defaults
  324. * for the properties with the same name on startup.
  325. *
  326. * @access private
  327. */
  328. var $staticProperties = array(
  329. 'format' => 'setFormat',
  330. 'autoFinalize' => 'setAutoFinalize',
  331. 'locale' => 'setLocale',
  332. );
  333. /**
  334. * Stores the flag for the autoFinalize feature
  335. *
  336. * @access private
  337. */
  338. var $autoFinalize = true;
  339. /**
  340. * custom validation rules
  341. *
  342. * @access private
  343. * @var array
  344. */
  345. var $_rules = array();
  346. /**
  347. * define error codes an messages for the form
  348. *
  349. * Will be set by validation rules that have been
  350. * added to the form.
  351. *
  352. * @access private
  353. * @var array $validatorErrorCodes
  354. */
  355. var $validatorErrorCodes = array();
  356. /**
  357. * stores any validation errors that can occurr during the
  358. * form's validation process.
  359. *
  360. * @access private
  361. * @var array $validationErrors
  362. */
  363. var $validationErrors = array();
  364. /**
  365. * next error offset for rules
  366. * @access private
  367. * @var integer
  368. */
  369. var $nextErrorOffset = 1000;
  370. /**
  371. * Attributes of the form - needed to generate the form tag
  372. *
  373. * @access private
  374. * @var array $attributes
  375. * @see setAttribute()
  376. */
  377. var $attributes = array();
  378. /**
  379. * Attribute definition for the form - defines which attribute the form
  380. * itself supports.
  381. *
  382. * @access public
  383. */
  384. var $attributeDefinition = array(
  385. 'id' => array(
  386. 'required' => false,
  387. 'format' => 'string',
  388. 'outputFormats' => array( 'html' ),
  389. ),
  390. 'name' => array(
  391. 'required' => true,
  392. 'format' => 'string',
  393. 'outputFormats' => array( 'html' ),
  394. ),
  395. 'method' => array(
  396. 'required' => true,
  397. 'format' => 'string',
  398. 'default' => 'post',
  399. 'outputFormats' => array( 'html' ),
  400. ),
  401. 'action' => array(
  402. 'required' => true,
  403. 'format' => 'string',
  404. 'outputFormats' => array( 'html' ),
  405. ),
  406. 'accept' => array(
  407. 'required' => false,
  408. 'format' => 'string',
  409. 'outputFormats' => array( 'html' ),
  410. ),
  411. 'accept-charset' => array(
  412. 'required' => false,
  413. 'format' => 'string',
  414. 'outputFormats' => array( 'html' ),
  415. ),
  416. 'enctype' => array(
  417. 'required' => false,
  418. 'format' => 'string',
  419. 'outputFormats' => array( 'html' ),
  420. ),
  421. 'onreset' => array(
  422. 'required' => false,
  423. 'format' => 'string',
  424. 'outputFormats' => array( 'html' ),
  425. ),
  426. 'onsubmit' => array(
  427. 'required' => false,
  428. 'format' => 'string',
  429. 'outputFormats' => array( 'html' ),
  430. ),
  431. 'target' => array(
  432. 'required' => false,
  433. 'format' => 'string',
  434. 'outputFormats' => array( 'html' ),
  435. ),
  436. );
  437. /**
  438. * Stores all available patForms options - these are inherited by all elements
  439. * and their dependencies, like rules.
  440. *
  441. * Short option overview:
  442. *
  443. * - scripts: enable client script integration
  444. *
  445. * @access public
  446. */
  447. var $options = array(
  448. 'scripts' => array(
  449. 'enabled' => false,
  450. 'params' => array(
  451. 'folder' => PATFORMS_SCRIPT_PATH,
  452. 'jsInclude' => false
  453. ),
  454. ),
  455. );
  456. /**
  457. * observers of the form
  458. *
  459. * @access private
  460. * @var array
  461. */
  462. var $observers = array();
  463. /**
  464. * Sets the default attributes that will be inherited by any elements you add to the form.
  465. *
  466. * <b>Note:</b> You have to call this method statically before creating a new form if you use
  467. * patForm's automatic element creation feature via the {@link createForm()} method, as the
  468. * default attributes cannot be set after an element has been created.
  469. *
  470. * @static
  471. * @access public
  472. * @param array $attributes The list of attributes to set with key => value pairs.
  473. */
  474. function setDefaultAttributes( $attributes )
  475. {
  476. patForms::setStaticProperty( 'defaultAttributes', $attributes );
  477. }
  478. /**
  479. * sets the locale (language) to use for the validation error messages of all elements
  480. * in the form.
  481. *
  482. * @access public
  483. * @param string language code
  484. * @param string optional language file
  485. * @return bool True on success
  486. */
  487. function setLocale( $locale, $languageFile = null )
  488. {
  489. if (!is_null($languageFile)) {
  490. $languageData = patForms::parseLocaleFile($languageFile);
  491. $customLocales = patForms::getStaticProperty('customLocales');
  492. $customLocales[$locale] = $languageData;
  493. patForms::setStaticProperty('customLocales', $customLocales);
  494. }
  495. if( isset( $this ) && is_a( $this, 'patForms' ) ) {
  496. $this->locale = $locale;
  497. if( !empty( $this->elements ) ) {
  498. $cnt = count( $this->elements );
  499. for( $i=0; $i < $cnt; $i++ ) {
  500. $this->elements[$i]->setLocale( $locale );
  501. }
  502. }
  503. } else {
  504. patForms::setStaticProperty('locale', $locale);
  505. }
  506. return true;
  507. }
  508. /**
  509. * checks, whether a locale is a custom locale
  510. *
  511. * @static
  512. * @access public
  513. * @param string locale name
  514. * @return boolean
  515. */
  516. function isCustomLocale($locale)
  517. {
  518. $customLocales = patForms::getStaticProperty('customLocales');
  519. if (isset($customLocales[$locale])) {
  520. return true;
  521. }
  522. return false;
  523. }
  524. /**
  525. * get the custom locale for an element or a rule
  526. *
  527. * @static
  528. * @access public
  529. * @param string locale
  530. * @param string key
  531. * @return array
  532. */
  533. function getCustomLocale($locale, $key)
  534. {
  535. $customLocales = patForms::getStaticProperty('customLocales');
  536. if (!isset($customLocales[$locale])) {
  537. return false;
  538. }
  539. if (!isset($customLocales[$locale][$key])) {
  540. return false;
  541. }
  542. return $customLocales[$locale][$key];
  543. }
  544. /**
  545. * parses a locale file
  546. *
  547. * @access private
  548. * @param string filename
  549. * @return array locale information
  550. * @todo add some file checks
  551. */
  552. function parseLocaleFile($filename)
  553. {
  554. return parse_ini_file($filename, true);
  555. }
  556. /**
  557. * sets the format of the element - this will be passed on to any elements you create. If you
  558. * have already added some elements when you call this method, it will be passed on to them too.
  559. *
  560. * @access public
  561. * @param string $format The name of the format you have implemented in your element(s).
  562. * @return bool $result True on success
  563. * @see setMode()
  564. * @see format
  565. * @see serialize()
  566. */
  567. function setFormat( $format )
  568. {
  569. if( isset( $this ) && is_a( $this, 'patForms' ) )
  570. {
  571. $this->format = strtolower( $format );
  572. if( !empty( $this->elements ) )
  573. {
  574. $cnt = count( $this->elements );
  575. for( $i=0; $i < $cnt; $i++ )
  576. {
  577. $this->elements[$i]->setFormat( $format );
  578. }
  579. }
  580. }
  581. else
  582. {
  583. patForms::setStaticProperty( 'format', $format );
  584. }
  585. return true;
  586. }
  587. /**
  588. * sets the mode of the form - If you have already added some elements when you call this
  589. * method, it will be passed on to them too.
  590. *
  591. * @access public
  592. * @param string $mode The mode to set the form to: default|readonly or any other mode you have implemented in your element class(es). Default is 'default'.
  593. * @see setMode()
  594. * @see mode
  595. * @see serialize()
  596. */
  597. function setMode( $mode )
  598. {
  599. $this->mode = strtolower( $mode );
  600. if( !empty( $this->elements ) )
  601. {
  602. $cnt = count( $this->elements );
  603. for( $i=0; $i < $cnt; $i++ )
  604. {
  605. $this->elements[$i]->setMode( $mode );
  606. }
  607. }
  608. }
  609. /**
  610. * sets the current submitted state of the form. Set this to true if you want the form
  611. * to pick up its submitted data. It will pass on this information to all elements that
  612. * have been added so far, and new ones inherit it too.
  613. *
  614. * @access public
  615. * @param bool $state True if it has been submitted, false otherwise (default).
  616. * @see isSubmitted()
  617. * @see submitted
  618. */
  619. function setSubmitted( $state )
  620. {
  621. if( $state == true )
  622. {
  623. $eventState = $this->triggerEvent( 'Submit' );
  624. if( $eventState === false )
  625. return false;
  626. }
  627. $this->submitted = $state;
  628. if( !empty( $this->elements ) )
  629. {
  630. $cnt = count( $this->elements );
  631. for( $i=0; $i < $cnt; $i++ )
  632. {
  633. $this->elements[$i]->setSubmitted( $state );
  634. }
  635. }
  636. return $state;
  637. }
  638. /**
  639. * sets the method for the request
  640. *
  641. * @access public
  642. * @param string $method GET or POST
  643. * @see method
  644. * @uses setAttribute()
  645. */
  646. function setMethod( $method )
  647. {
  648. $method = strtolower( $method );
  649. if( $method != 'get' && $method != 'post' )
  650. {
  651. return patErrorManager::raiseError(
  652. PATFORMS_ERROR_INVALID_METHOD,
  653. 'Unknown method "'.$method.'". Currently only GET and POST are supported as patForms methods.'
  654. );
  655. }
  656. $this->setAttribute( 'method', $method );
  657. return true;
  658. }
  659. /**
  660. * sets the action for the form
  661. *
  662. * This is a only a wrapper for setAttribute()
  663. *
  664. * @access public
  665. * @param string $action
  666. * @see setAttribute()
  667. */
  668. function setAction( $action )
  669. {
  670. return $this->setAttribute( 'action', $action );
  671. }
  672. /**
  673. * Sets the AutoFinalize mode for the form. The AutoFinalize mode will tell patForms to
  674. * finalize all elements after the form has been validated successfully.
  675. *
  676. * @access public
  677. * @param boolean $mode Whether to activate the AutoFinalize mode (true) or not (false).
  678. * @return boolean $success True if okay, a patError object otherwise.
  679. * @see finalizeForm()
  680. */
  681. function setAutoFinalize( $mode )
  682. {
  683. if( !is_bool( $mode ) )
  684. {
  685. return patErrorManager::raiseError(
  686. PATFORMS_ERROR_PARAMETER_NO_BOOL,
  687. 'The setAutoFinalize() method requires a boolean value ( true or false ) as parameter.'
  688. );
  689. }
  690. if( isset( $this ) && is_a( $this, 'patForms' ) )
  691. {
  692. $this->autoFinalize = $mode;
  693. }
  694. else
  695. {
  696. patForms::setStaticProperty( 'autoFinalize', $mode );
  697. }
  698. return true;
  699. }
  700. /**
  701. * Wrapper method that adds a filter to all elements
  702. * of the form at once instead of having to do it for
  703. * each element.
  704. *
  705. * @access public
  706. * @param object &$filter The filter object to apply
  707. * @see patForms_Element::applyFilter()
  708. * @todo add error management and docs once the element's applyFilter method has too
  709. */
  710. function applyFilter( &$filter )
  711. {
  712. if( empty( $this->elements ) )
  713. return true;
  714. $cnt = count( $this->elements );
  715. for( $i = 0; $i < $cnt; $i++ )
  716. {
  717. $this->elements[$i]->applyFilter( $filter );
  718. }
  719. }
  720. /**
  721. * creates a new patForms object and returns it; this method is made to be called statically
  722. * to be able to create a new patForms object from anywhere.
  723. *
  724. * @access public
  725. * @param array $formDefinition Optional form definition for elements that will be added to the form
  726. * @param array $attributes The attributes to set for the form itself
  727. * @return object patForms $form The new patForms object.
  728. * @todo it should be possible to pass Rule definitions, so they can be loaded and added automatically.
  729. */
  730. function &createForm( $formDefinition = null, $attributes = null )
  731. {
  732. $form = &new patForms();
  733. if( $attributes != null )
  734. {
  735. $form->setAttributes( $attributes );
  736. }
  737. if( $formDefinition === null )
  738. return $form;
  739. foreach( $formDefinition as $name => $element )
  740. {
  741. if( !isset( $element["filters"] ) )
  742. {
  743. $element["filters"] = null;
  744. }
  745. if( !isset( $element["children"] ) )
  746. {
  747. $element["children"] = null;
  748. }
  749. $el = &$form->createElement( $name, $element["type"], $element["attributes"], $element["filters"], $element["children"] );
  750. if( isset( $element["renderer"] ) ) {
  751. $el->setRenderer( $element["renderer"] );
  752. }
  753. $result = $form->addElement( $el );
  754. if (patErrorManager::isError( $result )) {
  755. return $result;
  756. }
  757. }
  758. return $form;
  759. }
  760. /**
  761. * add a custom validation rule
  762. *
  763. * @access public
  764. * @param object patForms_Rule validation rule
  765. * @param integer time to apply rule (before or after built-in validation)
  766. * @param boolean apply the rule, even if the form is invalid
  767. * @param boolean should form get revalidated (not implemented yet)
  768. * @return boolean currently always true
  769. */
  770. function addRule( &$rule, $time = PATFORMS_RULE_AFTER_VALIDATION, $invalid = false, $revalidate = false )
  771. {
  772. $rule->prepareRule( $this );
  773. $this->_rules[] = array(
  774. 'rule' => &$rule,
  775. 'time' => $time,
  776. 'invalid' => $invalid,
  777. 'revalidate' => $revalidate
  778. );
  779. }
  780. /**
  781. * patForms PHP5 constructor - processes some intitialization tasks like merging the currently
  782. * set static properties with the internal properties.
  783. *
  784. * @access public
  785. */
  786. function __construct()
  787. {
  788. foreach( $this->staticProperties as $staticProperty => $setMethod )
  789. {
  790. $propValue = patForms::getStaticProperty( $staticProperty );
  791. if( patErrorManager::isError( $propValue ) )
  792. continue;
  793. $this->$setMethod( $propValue );
  794. }
  795. // initialize patForms internal attribute collection
  796. $this->loadAttributeDefaults();
  797. }
  798. /**
  799. * patForms pre-PHP5 constructor - does nothing for the moment except being a wrapper
  800. * for the PHP5 contructor for older PHP versions support.
  801. *
  802. * @access public
  803. */
  804. function patForms()
  805. {
  806. patForms::__construct();
  807. }
  808. /**
  809. * sets a renderer object that will be used to render
  810. * the form.
  811. *
  812. * @access public
  813. * @param object &$renderer The renderer object
  814. * @return mixed $success True on success, patError object otherwise.
  815. * @see createRenderer()
  816. * @see renderForm()
  817. */
  818. function setRenderer( &$renderer, $args = array() )
  819. {
  820. if( !is_object( $renderer ) )
  821. {
  822. return patErrorManager::raiseError(
  823. PATFORMS_ERROR_INVALID_RENDERER,
  824. 'You can only set a patForms_Renderer object with the setRenderer() method, "'.gettype( $renderer ).'" given.'
  825. );
  826. }
  827. $this->renderer = &$renderer;
  828. if( isset( $args['includeElements'] ) && $args['includeElements'] === true )
  829. {
  830. // check all elements - there may be some that need
  831. // renderers too, so we give them the same renderer if
  832. // they don't already have one.
  833. $cnt = count( $this->elements );
  834. for( $i = 0; $i < $cnt; $i++ )
  835. {
  836. if( $this->elements[$i]->usesRenderer && !is_object( $this->elements[$i]->renderer ) )
  837. {
  838. $this->elements[$i]->setRenderer( $renderer );
  839. }
  840. }
  841. }
  842. return true;
  843. }
  844. /**
  845. * sets a storage container object that will be used to store data
  846. *
  847. * @access public
  848. * @param object patForms_Storage
  849. * @see createStorage()
  850. */
  851. function setStorage( &$storage )
  852. {
  853. if( !is_object( $storage ) )
  854. {
  855. return patErrorManager::raiseError(
  856. PATFORMS_ERROR_INVALID_STORAGE,
  857. 'You can only set a patForms_Storage object with the setStorage() method, "'.gettype( $storage ).'" given.'
  858. );
  859. }
  860. $this->registerEventHandlerObject( $storage,
  861. array(
  862. 'onInit' => 'loadEntry',
  863. 'onSuccess' => 'storeEntry'
  864. )
  865. );
  866. }
  867. /**
  868. * renders the form with the renderer that was set via the {@link setRenderer()}
  869. * method.
  870. *
  871. * WARNING: This is still in alpha state!
  872. *
  873. * Should this method return a reference??
  874. * The return value could contain large blocks of HTML or large arrays!
  875. * Do we want to copy these?
  876. *
  877. * @access public
  878. * @param mixed $args arguments that will be passed to the renderer
  879. * @return mixed $form The rendered form, or false if failed.
  880. */
  881. function renderForm( $args = null )
  882. {
  883. if( $this->renderer === null )
  884. {
  885. return patErrorManager::raiseError(
  886. PATFORMS_ERROR_NO_RENDERER_SET,
  887. 'Form cannot be rendered, you have to set a renderer first via the setRenderer() method.'
  888. );
  889. }
  890. // form is not submitted, or auto-validation is disabled => render it
  891. if( !$this->isSubmitted() || $this->autoValidate !== true )
  892. {
  893. $this->triggerEvent( 'Init' );
  894. return $this->renderer->render( $this, $args );
  895. }
  896. $this->validateForm();
  897. return $this->renderer->render( $this, $args );
  898. }
  899. /**
  900. * Validates all elements of the form.
  901. *
  902. * @access public
  903. * @param boolean Flag to indicate, whether form should be validated again, if it already has been validated.
  904. * @return boolean True if all elements could be validated, false otherwise.
  905. * @see finishForm()
  906. */
  907. function validateForm( $revalidate = false )
  908. {
  909. if( $this->validated && !$revalidate )
  910. return $this->valid;
  911. $valid = true;
  912. /**
  913. * validate custom rules
  914. */
  915. if( !$this->_applyRules( PATFORMS_RULE_BEFORE_VALIDATION ) )
  916. {
  917. $valid = false;
  918. }
  919. /**
  920. * validate elements
  921. */
  922. if( $valid === true )
  923. {
  924. $cnt = count( $this->elements );
  925. for( $i = 0; $i < $cnt; ++$i )
  926. {
  927. if( !$this->elements[$i]->validate() )
  928. {
  929. $valid = false;
  930. }
  931. }
  932. }
  933. /**
  934. * validate custom rules
  935. */
  936. if( !$this->_applyRules( PATFORMS_RULE_AFTER_VALIDATION, $valid ) )
  937. {
  938. $valid = false;
  939. }
  940. if( $valid === true && $this->autoFinalize === true )
  941. $this->finalizeForm();
  942. $this->valid = $valid;
  943. $this->validated = true;
  944. if( $valid === true )
  945. {
  946. $this->_announce( 'status', 'validated' );
  947. $event = 'Success';
  948. }
  949. else
  950. {
  951. $this->_announce( 'status', 'error' );
  952. $event = 'Error';
  953. }
  954. $this->triggerEvent( $event );
  955. return $this->valid;
  956. }
  957. /**
  958. * apply rules
  959. *
  960. * @access private
  961. * @param integer time of validation
  962. * @param boolean form is valid
  963. * @return boolean rules are valid or not
  964. * @todo add documentation
  965. */
  966. function _applyRules( $time, $isValid = true )
  967. {
  968. $valid = true;
  969. $cnt = count( $this->_rules );
  970. for ($i = 0; $i < $cnt; $i++) {
  971. // wrong time
  972. if (( $this->_rules[$i]['time'] & $time ) != $time) {
  973. continue;
  974. }
  975. if (!$isValid && !$this->_rules[$i]['invalid']) {
  976. continue;
  977. }
  978. $result = $this->_rules[$i]['rule']->applyRule( $this, PATFORMS_RULE_AFTER_VALIDATION );
  979. if( $result === false ) {
  980. $valid = false;
  981. }
  982. }
  983. return $valid;
  984. }
  985. /**
  986. * Finalizes the form by telling each fom element to finalize - finalizing means to
  987. * process any tasks that need to be done after the form has been validated, like
  988. * deleting any temporary files or whatever an element needs to do at that point.
  989. *
  990. * @access public
  991. * @return bool $success Wether all elements could be finalized
  992. * @see validateForm()
  993. */
  994. function finalizeForm()
  995. {
  996. $success = true;
  997. $cnt = count( $this->elements );
  998. for( $i = 0; $i < $cnt; ++$i )
  999. {
  1000. if( !$this->elements[$i]->finalize() )
  1001. {
  1002. patErrorManager::raiseWarning(
  1003. PATFORMS_ERROR_ELEMENT_NOT_FINALIZED,
  1004. 'Element "'.$this->elements[$i]->elementName.'" could not be finalized. See the element error messages for more details.'
  1005. );
  1006. $success = false;
  1007. }
  1008. }
  1009. return $success;
  1010. }
  1011. /**
  1012. * creates a new renderer from the patForms renderer collection and returns it.
  1013. *
  1014. * @access public
  1015. * @param string The name of the renderer to create - have a look at the Renderer/ subfolder for a list of available renderers.
  1016. * @return object patForms_Renderer The renderer object, or error object
  1017. */
  1018. function &createRenderer( $name )
  1019. {
  1020. return patForms::_createModule( 'Renderer', $name );
  1021. }
  1022. /**
  1023. * creates a new storage container and returns it.
  1024. *
  1025. * @access public
  1026. * @param string The name of the storage to create - have a look at the Storage/ subfolder for a list of available storage containers.
  1027. * @return object patForms_Storage The storage container, or error object
  1028. */
  1029. function &createStorage( $name )
  1030. {
  1031. return patForms::_createModule( 'Storage', $name );
  1032. }
  1033. /**
  1034. * Creates a new filter and returns it.
  1035. *
  1036. * You may pass an array as second parameter that contains
  1037. * parameters for the filter. patForms will check for setter methods
  1038. * for all keys and set the corresponding values.
  1039. *
  1040. * This eases the creating of simple filter objects.
  1041. *
  1042. * @access public
  1043. * @param string The name of the filter to create - have a look at the Filter/ subfolder for a list of available filters.
  1044. * @param array Optional parameters for the filter, if you provide a parameter, make sure the filter implements a set[Paramname]() method.
  1045. * This will be automated with interceptors in the PHP5 version of patForms
  1046. * @return object patForms_Filter The filter, or error object
  1047. */
  1048. function &createFilter( $name, $params = null )
  1049. {
  1050. $filter = &patForms::_createModule( 'Filter', $name );
  1051. if( !is_array( $params ) )
  1052. {
  1053. return $filter;
  1054. }
  1055. foreach( $params as $param => $value )
  1056. {
  1057. $setter = 'set' . ucfirst( $param );
  1058. if( method_exists( $filter, $setter ) )
  1059. {
  1060. $filter->$setter( $value );
  1061. }
  1062. }
  1063. return $filter;
  1064. }
  1065. /**
  1066. * creates a new rule from the patForms rule collection and returns it.
  1067. *
  1068. * If your rules are not located in patForms/Rule you have to load and
  1069. * instantiate them on your own.
  1070. *
  1071. * @access public
  1072. * @param string The name of the rule to create - have a look at the Rule/ subfolder for a list of available rules.
  1073. * @param string The id of the rule, needed if the rule uses client side actions.
  1074. * @return object patForms_Rule The rule object, or error object
  1075. */
  1076. static function &createRule( $name, $id = null )
  1077. {
  1078. $rule = &patForms::_createModule( 'Rule', $name );
  1079. if( $id != null )
  1080. {
  1081. $rule->setId( $id );
  1082. }
  1083. return $rule;
  1084. }
  1085. /**
  1086. * creates a new observer from the patForms observer collection and returns it.
  1087. *
  1088. * If your observers are not located in patForms/Observer you have to load and
  1089. * instantiate them on your own.
  1090. *
  1091. * @access public
  1092. * @param string The name of the observer to create - have a look at the Observer/ subfolder for a list of available observers.
  1093. * @return object patForms_Observer The observer object, or error object
  1094. */
  1095. function &createObserver( $name )
  1096. {
  1097. $observer = &patForms::_createModule( 'Observer', $name );
  1098. return $observer;
  1099. }
  1100. /**
  1101. * creates a new module for patForms
  1102. *
  1103. * @access private
  1104. * @param string $type type of the module. Possible values are 'Renderer', 'Rule'
  1105. * @param string $name The name of the renderer to create - have a look at the renderers/ subfolder for a list of available renderers.
  1106. * @return object $module The module object, or an error object
  1107. */
  1108. function &_createModule( $type, $name )
  1109. {
  1110. $baseFile = PATFORMS_INCLUDE_PATH . '/'.$type.'.php';
  1111. $baseClass = 'patForms_'.$type;
  1112. // if there is an underscore in the module name, we want
  1113. // to load the module from a subfolder, so we transform
  1114. // all underscores to slashes.
  1115. $pathName = $name;
  1116. if( strstr( $pathName, '_' ) )
  1117. {
  1118. $pathName = str_replace( '_', '/', $name );
  1119. }
  1120. $moduleFile = PATFORMS_INCLUDE_PATH . '/'.$type.'/'.$pathName.'.php';
  1121. $moduleClass = 'patForms_'.$type.'_'.$name;
  1122. if( !class_exists( $baseClass, FALSE ) )
  1123. {
  1124. if( !file_exists( $baseFile ) )
  1125. {
  1126. return patErrorManager::raiseError(
  1127. PATFORMS_ERROR_NO_MODULE_BASE_FILE,
  1128. $type .' base file could not be found',
  1129. 'Tried to load base file in path "'.$baseFile.'"'
  1130. );
  1131. }
  1132. include_once $baseFile;
  1133. }
  1134. if( !class_exists( $moduleClass, true ) )
  1135. {
  1136. if( !file_exists( $moduleFile ) )
  1137. {
  1138. if (defined('PATFORMS_LOCAL_INCLUDE_PATH')) {
  1139. $localModuleClass = PATFORMS_LOCAL_INCLUDE_PATH."/".$type."/".$pathName.".php";
  1140. if (file_exists($localModuleClass)) {
  1141. include_once $localModuleClass;
  1142. $module = new $moduleClass();
  1143. return $module;
  1144. }
  1145. }
  1146. return patErrorManager::raiseError(
  1147. PATFORMS_ERROR_MODULE_NOT_FOUND,
  1148. $type.' "'.$name.'" file "'.$moduleFile. '" could not be found.'
  1149. );
  1150. }
  1151. include_once $moduleFile;
  1152. }
  1153. $module = new $moduleClass();
  1154. return $module;
  1155. }
  1156. /**
  1157. * adds an element to the form - has to be a patForms_Element object. Use the {@link createElement()}
  1158. * method to create a new element object. Also takes care of passing on the form's configuration
  1159. * including the mode, format and submitted flags to the element.
  1160. *
  1161. * @access public
  1162. * @param object &$element The patForms_Element object to add to this form.
  1163. * @return bool $success True if everything went well, false otherwise.
  1164. * @see patForms_Element
  1165. * @see createElement()
  1166. */
  1167. function addElement( &$element )
  1168. {
  1169. if( !is_object( $element ) )
  1170. {
  1171. return patErrorManager::raiseError(
  1172. PATFORMS_ERROR_ELEMENT_IS_NO_OBJECT,
  1173. 'The addElement() method expects an element object, "'.gettype( $element ).'" given.'
  1174. );
  1175. }
  1176. if( patErrorManager::isError( $element ) )
  1177. {
  1178. return patErrorManager::raiseError(
  1179. PATFORMS_ERROR_UNEXPECTED_ERROR,
  1180. 'The element you are trying to add is a patError object, and not a patForms element object.'
  1181. );
  1182. }
  1183. if( !$element->getId() ) {
  1184. $element->setId( $this->getElementId() );
  1185. }
  1186. $element->setMode( $this->getMode() );
  1187. $element->setFormat( $this->getFormat() );
  1188. $element->setSubmitted( $this->isSubmitted() );
  1189. $element->setLocale( $this->getLocale() );
  1190. $this->elements[] =& $element;
  1191. return true;
  1192. }
  1193. /**
  1194. * replaces an element in the form
  1195. *
  1196. * @access public
  1197. * @param object $element The patForms_Element object to be replaced
  1198. * @param object &$replace The element that will replace the old element
  1199. * @return bool $success True if everything went well, false otherwise.
  1200. * @see patForms_Element
  1201. * @see addElement()
  1202. */
  1203. function replaceElement( $element, &$replace )
  1204. {
  1205. if( !is_object( $replace ) ) {
  1206. return patErrorManager::raiseError(
  1207. PATFORMS_ERROR_ELEMENT_IS_NO_OBJECT,
  1208. 'The addElement() method expects an element object, "'.gettype( $replace ).'" given.'
  1209. );
  1210. }
  1211. if( patErrorManager::isError( $replace ) ) {
  1212. return patErrorManager::raiseError(
  1213. PATFORMS_ERROR_UNEXPECTED_ERROR,
  1214. 'The element you are trying to add is a patError object, and not a patForms element object.'
  1215. );
  1216. }
  1217. if (is_object($element)) {
  1218. $element = $element->getId();
  1219. }
  1220. $cnt = count($this->elements);
  1221. for ($i = 0; $i < $cnt; $i++) {
  1222. if ($this->elements[$i]->getId() === $element) {
  1223. if( !$replace->getId() ) {
  1224. $replace->setId( $this->getElementId() );
  1225. }
  1226. $replace->setMode( $this->getMode() );
  1227. $replace->setFormat( $this->getFormat() );
  1228. $replace->setSubmitted( $this->isSubmitted() );
  1229. $replace->setLocale( $this->getLocale() );
  1230. $this->elements[$i] = &$replace;
  1231. return true;
  1232. }
  1233. // the current element is a container
  1234. if (method_exists($this->elements[$i], 'replaceElement')) {
  1235. $result = $this->elements[$i]->replaceElement($element, $replace);
  1236. if ($result === true) {
  1237. return $result;
  1238. }
  1239. }
  1240. }
  1241. return false;
  1242. }
  1243. /**
  1244. * Get an element by its name.
  1245. *
  1246. * @access public
  1247. * @param string $name name of the element
  1248. * @return object patForms element
  1249. * @deprecated please use patForms::getElementByName() instead
  1250. */
  1251. function &getElement( $name )
  1252. {
  1253. return $this->getElementByName( $name );
  1254. }
  1255. /**
  1256. * Get an element by its name.
  1257. *
  1258. * @access public
  1259. * @param string $name name of the element
  1260. * @return mixed either a patForms element or an array containing patForms elements
  1261. * @see getElementById()
  1262. */
  1263. function &getElementByName( $name )
  1264. {
  1265. if( $name == '__form' ) {
  1266. return $this;
  1267. }
  1268. $elements = array();
  1269. $cnt = count( $this->elements );
  1270. for ($i = 0; $i < $cnt; $i++) {
  1271. if ($this->elements[$i]->getName() == $name) {
  1272. $elements[] = &$this->elements[$i];
  1273. continue;
  1274. }
  1275. if (method_exists($this->elements[$i], 'getElementById')) {
  1276. patErrorManager::pushExpect(PATFORMS_ERROR_ELEMENT_NOT_FOUND);
  1277. $result = &$this->elements[$i]->getElementByName($name);
  1278. patErrorManager::popExpect();
  1279. if (!patErrorManager::isError($result)) {
  1280. if (is_array($result)) {
  1281. $cnt2 = count( $result );
  1282. for ($j = 0; $j < $cnt2; $j++) {
  1283. $elements[] = &$result[$j];
  1284. }
  1285. } else {
  1286. $elements[] = &$result;
  1287. }
  1288. }
  1289. }
  1290. }
  1291. switch( count( $elements ) )
  1292. {
  1293. case 0:
  1294. $r = patErrorManager::raiseError(
  1295. PATFORMS_ERROR_ELEMENT_NOT_FOUND,
  1296. 'Element '.$name.' could not be found.'
  1297. );
  1298. return $r;
  1299. break;
  1300. case 1:
  1301. return $elements[0];
  1302. break;
  1303. default:
  1304. return $elements;
  1305. break;
  1306. }
  1307. }
  1308. /**
  1309. * Get an element by its id.
  1310. *
  1311. * @access public
  1312. * @param string $id id of the element
  1313. * @return object patForms element
  1314. */
  1315. function &getElementById( $id )
  1316. {
  1317. $cnt = count( $this->elements );
  1318. for( $i = 0; $i < $cnt; $i++ )
  1319. {
  1320. if( $this->elements[$i]->getId() == $id ) {
  1321. return $this->elements[$i];
  1322. }
  1323. if (method_exists($this->elements[$i], 'getElementById')) {
  1324. patErrorManager::pushExpect(PATFORMS_ERROR_ELEMENT_NOT_FOUND);
  1325. $result = &$this->elements[$i]->getElementById($id);
  1326. patErrorManager::popExpect();
  1327. if (!patErrorManager::isError($result)) {
  1328. return $result;
  1329. }
  1330. }
  1331. }
  1332. return patErrorManager::raiseError(
  1333. PATFORMS_ERROR_ELEMENT_NOT_FOUND,
  1334. 'Element '.$name.' could not be found.'
  1335. );
  1336. }
  1337. /**
  1338. * Get all elements of the form
  1339. *
  1340. * @access public
  1341. * @return array all elements of the form
  1342. */
  1343. function &getElements()
  1344. {
  1345. return $this->elements;
  1346. }
  1347. /**
  1348. * Creates a new form element and returns a reference to it.
  1349. *
  1350. * The optional $filters array has to be in the following format:
  1351. *
  1352. * <pre>
  1353. * array(
  1354. * array(
  1355. * 'filter' => 'Multiplier',
  1356. * 'params' => array( 'multiplier' => 6 )
  1357. * )
  1358. * )
  1359. * </pre>
  1360. *
  1361. * @access public
  1362. * @param string $name The name of the element
  1363. * @param string $type The type of the element; for a list of possible elements, have a look at the elements/ subfolder of the patForms package.
  1364. * @param array $attributes Attributes for the element
  1365. * @param array $filters Optional filters that will be applied
  1366. * @return object patForms_Element $element The element object, or patError if failed.
  1367. */
  1368. function &createElement( $name, $type, $attributes, $filters = null, $children = null )
  1369. {
  1370. $element =& patForms::_createModule( 'Element', $type );
  1371. if( patErrorManager::isError( $element ) )
  1372. {
  1373. return $element;
  1374. }
  1375. $attributes['name'] = $name;
  1376. if( !isset( $attributes['id'] ) ) {
  1377. $attributes['id'] = patForms::getElementId();
  1378. }
  1379. // add default attributes - do this the 'silent' way be checking whether
  1380. // the element supports the given attribute, as the element throws a notice
  1381. // if it does not support it - this is not expected from default attributes.
  1382. foreach( patForms::getStaticProperty( 'defaultAttributes' ) as $attributeName => $attributeValue )
  1383. {
  1384. if( !$element->hasAttribute( $attributeName ) )
  1385. {
  1386. continue;
  1387. }
  1388. $element->setAttribute( $attributeName, $attributeValue );
  1389. }
  1390. // set the given attributes normally
  1391. $success = $element->setAttributes( $attributes );
  1392. if( patErrorManager::isError( $success ) )
  1393. {
  1394. return $success;
  1395. }
  1396. if (is_array($children)) {
  1397. foreach ($children as $child) {
  1398. $childName = $child['attributes']['name'];
  1399. $childEl = &patForms::createElement($childName, $child['type'], $child['attributes']);
  1400. if( isset( $child["renderer"] ) ) {
  1401. $childEl->setRenderer( $child["renderer"] );
  1402. }
  1403. $element->addElement($childEl);
  1404. }
  1405. }
  1406. $success = $element->_init();
  1407. if( patErrorManager::isError( $success ) ) {
  1408. return $success;
  1409. }
  1410. // if we don't have any filters to add, we're done
  1411. if( !is_array( $filters ) )
  1412. {
  1413. return $element;
  1414. }
  1415. $cnt = count( $filters );
  1416. for( $i = 0; $i < $cnt; $i++ )
  1417. {
  1418. $params = isset( $filters[$i]['params'] ) ? $filters[$i]['params'] : null;
  1419. $filter = &patForms::createFilter( $filters[$i]['filter'], $params );
  1420. if( patErrorManager::isError( $filter ) )
  1421. {
  1422. continue;
  1423. }
  1424. $element->applyFilter( $filter );
  1425. }
  1426. return $element;
  1427. }
  1428. /**
  1429. * retrieves the validation errors from all elements in the form. Use this if the validateForm()
  1430. * method returned false.
  1431. *
  1432. * @access public
  1433. * q
  1434. * @return array $errors Array containing an array with validation errors for each element in the form.
  1435. * @todo replace __form with the name of the form, once attributes are implemented
  1436. */
  1437. function getValidationErrors($withElements = true)
  1438. {
  1439. $found = false;
  1440. $errors = array();
  1441. if( !empty( $this->validationErrors ) )
  1442. {
  1443. $errors['__form'] = $this->validationErrors;
  1444. $found = true;
  1445. }
  1446. if ($withElements === false) {
  1447. return $errors;
  1448. }
  1449. $cnt = count( $this->elements );
  1450. for( $i = 0; $i < $cnt; ++$i )
  1451. {
  1452. $name = $this->elements[$i]->getAttribute( 'name' );
  1453. if( $name === false )
  1454. {
  1455. continue;
  1456. }
  1457. $elementErrors = $this->elements[$i]->getValidationErrors();
  1458. if( empty( $elementErrors ) )
  1459. continue;
  1460. $errors[$name] = $elementErrors;
  1461. $found = true;
  1462. }
  1463. if( $found )
  1464. return $errors;
  1465. return false;
  1466. }
  1467. /**
  1468. * retrieves the values for all elements in the form.
  1469. *
  1470. * @access public
  1471. * @param array desired fields
  1472. * @param integer Mode that should be used to return values in groups
  1473. * @return array The values for all elements, as elementname => elementvalue.
  1474. *
  1475. * @todo remove the ugly Group check and replace with something better
  1476. * @todo implement something similar for getValidation errors
  1477. */
  1478. function getValues( $fields = null, $type = PATFORMS_VALUES_NESTED )
  1479. {
  1480. $values = array();
  1481. $cnt = count( $this->elements );
  1482. for( $i = 0; $i < $cnt; ++$i )
  1483. {
  1484. $name = $this->elements[$i]->getAttribute( 'name' );
  1485. if( $name === false ) {
  1486. continue;
  1487. }
  1488. if( is_array( $fields ) && !in_array( $name, $fields ) ) {
  1489. continue;
  1490. }
  1491. $tmpVal = $this->elements[$i]->getValue();
  1492. if (!is_array($tmpVal) || $this->elements[$i]->elementName != 'Group') {
  1493. $values[$name] = $tmpVal;
  1494. continue;
  1495. }
  1496. switch ($type) {
  1497. case PATFORMS_VALUES_FLATTENED:
  1498. $values = array_merge($values, $tmpVal);
  1499. break;
  1500. case PATFORMS_VALUES_PREFIXED:
  1501. foreach ($tmpVal as $key => $val) {
  1502. $values[$name.'_'.$key] = $val;
  1503. }
  1504. break;
  1505. case PATFORMS_VALUES_NESTED:
  1506. default:
  1507. $values[$name] = $tmpVal;
  1508. break;
  1509. }
  1510. }
  1511. return $values;
  1512. }
  1513. /**
  1514. * sets the values for all elements in the form. Use this to fill your form with external
  1515. * data, like a db query. Caution: if you do this and set the form to submitted, the values
  1516. * will be overwritten by any values present in the $_GET or $_POST variables.
  1517. *
  1518. * @access public
  1519. * @param array $values The values for all elements, as elementname => elementvalue.
  1520. */
  1521. function setValues( $values, $overrideUserInput = false )
  1522. {
  1523. patErrorManager::pushExpect(PATFORMS_ERROR_ELEMENT_NOT_FOUND);
  1524. if(is_array($values)) {
  1525. foreach ($values as $elName => $value) {
  1526. $el = &$this->getElementByName($elName);
  1527. if (patErrorManager::isError($el)) {
  1528. continue;
  1529. }
  1530. if ($overrideUserInput === true) {
  1531. $el->setValue($value);
  1532. } else {
  1533. $el->setDefaultValue($value);
  1534. }
  1535. }
  1536. }
  1537. patErrorManager::popExpect();
  1538. return true;
  1539. }
  1540. /**
  1541. * retrieves the current mode of the form
  1542. *
  1543. * @access public
  1544. * @return string $mode The current form mode
  1545. * @see setMode()
  1546. * @see $mode
  1547. */
  1548. function getMode()
  1549. {
  1550. return $this->mode;
  1551. }
  1552. /**
  1553. * returns the locale that is currently set for the form.
  1554. *
  1555. * @access public
  1556. * @return string $locale The locale.
  1557. * @see setLocale()
  1558. * @see $locale
  1559. */
  1560. function getLocale()
  1561. {
  1562. return $this->locale;
  1563. }
  1564. /**
  1565. * retrieves the current format of the form
  1566. *
  1567. * @access public
  1568. * @return string $format The current form format
  1569. * @see setFormat()
  1570. * @see format
  1571. */
  1572. function getFormat()
  1573. {
  1574. return $this->format;
  1575. }
  1576. /**
  1577. * retrieves the current method of the form
  1578. *
  1579. * @access public
  1580. * @return string $method The request method
  1581. * @see setMethod()
  1582. */
  1583. function getMethod()
  1584. {
  1585. return $this->getAttribute( 'method' );
  1586. }
  1587. /**
  1588. * retrieves the current action of the form
  1589. *
  1590. * @access public
  1591. * @return string $action Action of the form
  1592. * @see setAction()
  1593. */
  1594. function getAction()
  1595. {
  1596. $action = $this->getAttribute( 'action' );
  1597. if( !empty( $action ) )
  1598. return $action;
  1599. return $_SERVER['PHP_SELF'];
  1600. }
  1601. /**
  1602. * adds an atribute to the form's attribute collection. If the attribute
  1603. * already exists, it is overwritten.
  1604. *
  1605. * @access public
  1606. * @param string $attributeName The name of the attribute to add
  1607. * @param string $atributeValue The value of the attribute
  1608. */
  1609. function setAttribute( $attributeName, $attributeValue )
  1610. {
  1611. if( !isset( $this->attributeDefinition[$attributeName] ) )
  1612. {
  1613. patErrorManager::raiseNotice(
  1614. PATFORMS_NOTICE_ATTRIBUTE_NOT_SUPPORTED,
  1615. "The attribute '".$attributeName."' is not supported by the form, skipped it. [".get_class( $this )."]"
  1616. );
  1617. return true;
  1618. }
  1619. $this->attributes[$attributeName] = $attributeValue;
  1620. return true;
  1621. }
  1622. /**
  1623. * adds several attributes at once to the form's attribute collection.
  1624. * Any existing attributes will be overwritten.
  1625. *
  1626. * @access public
  1627. * @param array $attributes The attributes to add
  1628. * @see setAttribute()
  1629. */
  1630. function setAttributes( $attributes )
  1631. {
  1632. if( !is_array( $attributes ) )
  1633. {
  1634. return patErrorManager::raiseError(
  1635. PATFORMS_NOTICE_ARRAY_EXPECTED,
  1636. "setAttributes: array expected"
  1637. );
  1638. }
  1639. foreach( $attributes as $attributeName => $attributeValue )
  1640. {
  1641. $this->setAttribute( $attributeName, $attributeValue );
  1642. }
  1643. return true;
  1644. }
  1645. /**
  1646. * retrieves the value of a form attribute.
  1647. *
  1648. * @access public
  1649. * @param string $attribute The name of the attribute to retrieve
  1650. * @return mixed $attributeValue The value of the attribute, or false if it does not exist in the attributes collection.
  1651. * @see setAttribute()
  1652. */
  1653. function getAttribute( $attribute )
  1654. {
  1655. if( !isset( $this->attributes[$attribute] ) )
  1656. {
  1657. return false;
  1658. }
  1659. return $this->attributes[$attribute];
  1660. }
  1661. /**
  1662. * retrieves all attributes of the form, or only the specified attributes.
  1663. *
  1664. * @access public
  1665. * @param array $attributes Optional: The names of the attributes to retrieve. Only the attributes that exist will be returned.
  1666. * @return array $result The attributes
  1667. * @see getAttribute()
  1668. */
  1669. function getAttributes( $attributes = array() )
  1670. {
  1671. if( empty( $attributes ) )
  1672. {
  1673. return $this->attributes;
  1674. }
  1675. $result = array();
  1676. foreach( $attributes as $attribute )
  1677. {
  1678. if( $attributeValue = $this->getAttribute( $attribute ) )
  1679. {
  1680. $result[$attribute] = $attributeValue;
  1681. }
  1682. }
  1683. return $result;
  1684. }
  1685. /**
  1686. * Loads the default attribute values into the attributes collection. Done directly
  1687. * on startup (in the consructor).
  1688. *
  1689. * The action defaults to the path of the current script, with session
  1690. * ID appended automatically, if SID has been defined.
  1691. *
  1692. * @access public
  1693. * @return bool $success Always returns true.
  1694. * @see $attributeDefaults
  1695. */
  1696. function loadAttributeDefaults()
  1697. {
  1698. foreach( $this->attributeDefinition as $attributeName => $attributeDef )
  1699. {
  1700. if( isset( $attributeDef['default'] ) )
  1701. {
  1702. $this->attributes[$attributeName] = $attributeDef['default'];
  1703. }
  1704. if( $attributeName == 'action' )
  1705. {
  1706. $this->attributes[$attributeName] = $_SERVER['PHP_SELF'];
  1707. /**
  1708. * session has been started, append session ID
  1709. */
  1710. if( defined( 'SID' ) )
  1711. $this->attributes[$attributeName] .= '?' . SID;
  1712. }
  1713. }
  1714. return true;
  1715. }
  1716. /**
  1717. * retrieves the form's current submitted state.
  1718. *
  1719. * If autoValidate is used, it will check for the submitVar and
  1720. * set the submitted flag accordingly
  1721. *
  1722. * @access public
  1723. * @return bool $state True if it has been submitted, false otherwise.
  1724. * @see setSubmitted(), setAutoValidate()
  1725. * @see submitted
  1726. */
  1727. function isSubmitted()
  1728. {
  1729. if( $this->submitted === true )
  1730. {
  1731. return true;
  1732. }
  1733. if( !isset( $this->submitVar ) )
  1734. {
  1735. return false;
  1736. }
  1737. if( !$this->autoValidate )
  1738. {
  1739. return false;
  1740. }
  1741. if( isset( $_GET[$this->submitVar] ) || isset( $_POST[$this->submitVar] ) )
  1742. {
  1743. $this->setSubmitted( true );
  1744. }
  1745. return $this->submitted;
  1746. }
  1747. /**
  1748. * Creates a new patForms_Creator object
  1749. *
  1750. * @static
  1751. * @access public
  1752. * @return object $creator The creator object, or a patError object on failure
  1753. */
  1754. function createCreator( $type )
  1755. {
  1756. return patForms::_createModule( 'Creator', $type );
  1757. }
  1758. /**
  1759. * get the element name of the form
  1760. *
  1761. * @access public
  1762. * @return string name of the form
  1763. */
  1764. function getElementName()
  1765. {
  1766. return $this->elementName;
  1767. }
  1768. /**
  1769. * get next error offset
  1770. *
  1771. * @access public
  1772. * @return integer
  1773. */
  1774. function getErrorOffset( $requiredCodes = 100 )
  1775. {
  1776. $offset = $this->nextErrorOffset;
  1777. $this->nextErrorOffset = $this->nextErrorOffset + $requiredCodes;
  1778. return $offset;
  1779. }
  1780. /**
  1781. * add error codes and messages for validator method
  1782. *
  1783. * @access public
  1784. * @param array defintions
  1785. * @param integer offset for the error codes
  1786. */
  1787. function addValidatorErrorCodes( $defs, $offset = 1000 )
  1788. {
  1789. foreach( $defs as $lang => $codes )
  1790. {
  1791. if( !isset( $this->validatorErrorCodes[$lang] ) )
  1792. {
  1793. $this->validatorErrorCodes[$lang] = array();
  1794. }
  1795. foreach( $codes as $code => $message )
  1796. {
  1797. $this->validatorErrorCodes[$lang][($offset+$code)] = $message;
  1798. }
  1799. }
  1800. }
  1801. /**
  1802. * add a validation error to the whole form
  1803. *
  1804. * This can be achieved by adding a validation rule to the form.
  1805. *
  1806. * @access public
  1807. * @param integer $code
  1808. * @param array $vars fill named placeholder with values
  1809. * @return boolean $result true on success
  1810. * @see addRule()
  1811. */
  1812. function addValidationError( $code, $vars = array() )
  1813. {
  1814. $error = false;
  1815. $lang = $this->locale;
  1816. $element = $this->getElementName();
  1817. // find error message for selected language
  1818. while( true )
  1819. {
  1820. // error message matches language code
  1821. if( isset( $this->validatorErrorCodes[$lang][$code] ) )
  1822. {
  1823. $error = array( "element" => $element, "code" => $code, "message" => $this->validatorErrorCodes[$lang][$code] );
  1824. break;
  1825. }
  1826. // no message found and no fallback-langauage available
  1827. else if ( $lang == "C" )
  1828. {
  1829. break;
  1830. }
  1831. $lang_old = $lang;
  1832. // look for other languages
  1833. if( strlen( $lang ) > 5 )
  1834. {
  1835. list( $lang, $trash ) = explode( ".", $lang );
  1836. }
  1837. else if( strlen( $lang ) > 2 )
  1838. {
  1839. list( $lang, $trash ) = explode( "_", $lang );
  1840. }
  1841. else
  1842. {
  1843. $lang = "C";
  1844. }
  1845. // inform developer about missing language
  1846. patErrorManager::raiseNotice(
  1847. PATFORMS_NOTICE_VALIDATOR_ERROR_LOCALE_UNDEFINED,
  1848. "Required Validation Error-Code for language: $lang_old not available. Now trying language: $lang",
  1849. "Add language definition in used element or choose other language"
  1850. );
  1851. }
  1852. // get default Error!
  1853. if( !$error )
  1854. {
  1855. patErrorManager::raiseWarning(
  1856. PATFORMS_WARNING_VALIDATOR_ERROR_UNDEFINED,
  1857. "No Error Message for this validation Error was defined",
  1858. "Review the error-definition for validation-errors in your element '$element'."
  1859. );
  1860. $error = array( "element" => $element, "code" => 0, "message" => "Unknown validation Error" );
  1861. }
  1862. // insert values to placeholders
  1863. if( !empty( $vars ) )
  1864. {
  1865. foreach( $vars as $key => $value )
  1866. {
  1867. $error["message"] = str_replace( "[". strtoupper( $key ) ."]", $value, $error["message"] );
  1868. }
  1869. }
  1870. array_push( $this->validationErrors, $error );
  1871. $this->valid = false;
  1872. return true;
  1873. }
  1874. /**
  1875. * Retrieves a new element id, used to give each added element a unique id for this
  1876. * form (id can be overwritten by setting the id attribute specifically).
  1877. *
  1878. * @static
  1879. * @access public
  1880. * @return int $elementId The new element id.
  1881. */
  1882. function getElementId()
  1883. {
  1884. $GLOBALS['_patForms']['elementCounter']++;
  1885. return 'pfo'.$GLOBALS['_patForms']['elementCounter'];
  1886. }
  1887. /**
  1888. * attach an observer
  1889. *
  1890. * @access public
  1891. * @param object patForms_Observer
  1892. * @see createObserver()
  1893. * @uses patForms_Element::createObserver()
  1894. */
  1895. function attachObserver( &$observer, $where = PATFORMS_OBSERVER_ATTACH_TO_ELEMENTS )
  1896. {
  1897. /**
  1898. * attach the observer to all elements
  1899. */
  1900. if( ( $where & PATFORMS_OBSERVER_ATTACH_TO_ELEMENTS ) == PATFORMS_OBSERVER_ATTACH_TO_ELEMENTS )
  1901. {
  1902. $cnt = count( $this->elements );
  1903. for( $i = 0; $i < $cnt; ++$i )
  1904. {
  1905. $this->elements[$i]->attachObserver( $observer );
  1906. }
  1907. }
  1908. /**
  1909. * attach the observer to the form
  1910. */
  1911. if( ( $where & PATFORMS_OBSERVER_ATTACH_TO_FORM ) == PATFORMS_OBSERVER_ATTACH_TO_FORM )
  1912. {
  1913. $this->observers[] = &$observer;
  1914. }
  1915. return true;
  1916. }
  1917. /**
  1918. * Retrieve the content for the start of the form, including any
  1919. * additional content, e.g. global scripts if the scripts option
  1920. * is enabled.
  1921. *
  1922. * @access public
  1923. * @return string $formStart The form start content
  1924. * @todo use format to build a dynamic method
  1925. */
  1926. function serializeStart()
  1927. {
  1928. $methodName = "serializeStart".ucfirst( $this->getFormat() ).ucfirst( $this->getMode() );
  1929. if( !method_exists( $this, $methodName ) )
  1930. {
  1931. return patErrorManager::raiseError(
  1932. PATFORMS_ERROR_METHOD_FOR_MODE_NOT_AVAILABLE,
  1933. "Method for patForms mode '".$this->getMode()."' (".$methodName.") is not available."
  1934. );
  1935. }
  1936. return $this->$methodName();
  1937. }
  1938. /**
  1939. * Serializes the form's start element for html format, in default mode.
  1940. *
  1941. * @access private
  1942. * @return mixed $formStart The serialized start content, or a patError object.
  1943. */
  1944. function serializeStartHtmlDefault()
  1945. {
  1946. $attributes = $this->getAttributesFor( $this->format );
  1947. if( patErrorManager::isError( $attributes ) )
  1948. {
  1949. return $attributes;
  1950. }
  1951. $content = patForms_Element::createTag( 'form', 'opening', $attributes );
  1952. if ($this->optionEnabled( 'scripts' )) {
  1953. $content .= $this->getScripts();
  1954. }
  1955. return $content;
  1956. }
  1957. /**
  1958. * Serializes the form's start element for html format, in readonly mode.
  1959. *
  1960. * @access private
  1961. * @return mixed $formStart The serialized start content, or a patError object.
  1962. */
  1963. function serializeStartHtmlReadonly()
  1964. {
  1965. $attributes = $this->getAttributesFor( $this->format );
  1966. if( patErrorManager::isError( $attributes ) )
  1967. {
  1968. return $attributes;
  1969. }
  1970. return patForms_Element::createTag( 'form', 'opening', $attributes );
  1971. }
  1972. /**
  1973. * Retrieve the content for the end of the form.
  1974. *
  1975. * @access public
  1976. * @return string $formEnd The form end content
  1977. */
  1978. function serializeEnd()
  1979. {
  1980. $methodName = "serializeEnd".ucfirst( $this->getFormat() ).ucfirst( $this->getMode() );
  1981. if( !method_exists( $this, $methodName ) )
  1982. {
  1983. return patErrorManager::raiseError(
  1984. PATFORMS_ERROR_METHOD_FOR_MODE_NOT_AVAILABLE,
  1985. "Method for patForms mode '".$this->getMode()."' (".$methodName.") is not available."
  1986. );
  1987. }
  1988. return $this->$methodName();
  1989. }
  1990. /**
  1991. * Retrieves the content for the end of the form for html format,
  1992. * in default mode.
  1993. *
  1994. * @access private
  1995. * @return string $formEnd The form end content
  1996. */
  1997. function serializeEndHtmlDefault()
  1998. {
  1999. return patForms_Element::createTag( 'form', 'closing' );
  2000. }
  2001. /**
  2002. * Retrieves the content for the end of the form for html format,
  2003. * in readonly mode.
  2004. *
  2005. * @access private
  2006. * @return string $formEnd The form end content
  2007. */
  2008. function serializeEndHtmlReadonly()
  2009. {
  2010. return $this->serializeEndHtmlDefault();
  2011. }
  2012. /**
  2013. * validates the current attribute collection according to the attributes definition
  2014. * and the given output format, and returns the list of valid attributes.
  2015. *
  2016. * @access private
  2017. * @param string $format The output format to retrieve the attributes for.
  2018. * @return mixed $attributes The list of attributes, or false if failed.
  2019. */
  2020. function getAttributesFor( $format )
  2021. {
  2022. $attributes = array();
  2023. foreach( $this->attributeDefinition as $attributeName => $attributeDef )
  2024. {
  2025. if( !isset( $this->attributes[$attributeName] ) )
  2026. {
  2027. if( $attributeDef["required"] )
  2028. {
  2029. return patErrorManager::raiseError(
  2030. PATFORMS_ERROR_ATTRIBUTE_REQUIRED,
  2031. 'patForms needs the attribute "'.$attributeName.'" to be set.',
  2032. 'See the patForms attribute definition of patForms for a complete attribute reference.'
  2033. );
  2034. }
  2035. continue;
  2036. }
  2037. $attributeValue = $this->attributes[$attributeName];
  2038. if( !in_array( $format, $attributeDef["outputFormats"] ) )
  2039. {
  2040. continue;
  2041. }
  2042. if( isset( $attributeDef["format"] ) )
  2043. {
  2044. if( !$this->_checkAttributeFormat( $attributeValue, $attributeDef["format"] ) )
  2045. {
  2046. return patErrorManager::raiseError(
  2047. PATFORMS_ERROR_CAN_NOT_VERIFY_FORMAT,
  2048. "Format '".$attributeDef["format"]."' could not be verified for patForms attribute '".$attributeName."' => '".$attributeValue."'"
  2049. );
  2050. }
  2051. }
  2052. $attributes[$attributeName] = $attributeValue;
  2053. }
  2054. return $attributes;
  2055. }
  2056. /**
  2057. * checks the format of an attribute value according to the given format.
  2058. *
  2059. * @access private
  2060. * @param mixed $attributeValue The attribute value to check
  2061. * @param string $format The format to check the attribute value against
  2062. * @return bool $result True if format check succeeded, false otherwise.
  2063. * @see createAttributes()
  2064. * @todo Implement this method sometime
  2065. */
  2066. function _checkAttributeFormat( $attributeValue, $format )
  2067. {
  2068. return true;
  2069. }
  2070. /**
  2071. * Enables a patForms option.
  2072. *
  2073. * See the {@link $options} property for an exhaustive list of available options.
  2074. *
  2075. * @access public
  2076. * @param string $option The option to enable
  2077. * @param array $params Optional parameters for the option
  2078. * @return mixed $result True on success, patError object otherwise.
  2079. * @see disableOption()
  2080. * @see optionEnabled()
  2081. * @see $options
  2082. */
  2083. function enableOption( $option, $params = array() )
  2084. {
  2085. if( !in_array( $option, array_keys( $this->options ) ) )
  2086. {
  2087. return patErrorManager::raiseNotice(
  2088. PATFORMS_NOTICE_INVALID_OPTION,
  2089. 'The option "'.$option.'" is not a valid patForms option.'
  2090. );
  2091. }
  2092. $this->options[$option]['enabled'] = true;
  2093. $this->options[$option]['params'] = array_merge($this->options[$option]['params'], $params);
  2094. // now update all available elements too
  2095. $cnt = count( $this->elements );
  2096. for( $i=0; $i < $cnt; $i++ )
  2097. {
  2098. $this->elements[$i]->enableOption( $option, $params );
  2099. }
  2100. return true;
  2101. }
  2102. /**
  2103. * Disables a patForms option
  2104. *
  2105. * See the {@link $options} property for an exhaustive list of available options.
  2106. *
  2107. * @access public
  2108. * @param string $option The option to disable
  2109. * @return mixed $result True on success, patError object otherwise.
  2110. * @see enableOption()
  2111. * @see optionEnabled()
  2112. * @see $options
  2113. */
  2114. function disableOption( $option )
  2115. {
  2116. if( !in_array( $option, array_keys( $this->options ) ) )
  2117. {
  2118. return patErrorManager::raiseNotice(
  2119. PATFORMS_NOTICE_INVALID_OPTION,
  2120. 'The option "'.$option.'" is not a valid patForms option.'
  2121. );
  2122. }
  2123. $this->options[$option]['enabled'] = false;
  2124. // now update all available elements too
  2125. $cnt = count( $this->elements );
  2126. for( $i=0; $i < $cnt; $i++ )
  2127. {
  2128. $this->elements[$i]->disableOption( $option );
  2129. }
  2130. return true;
  2131. }
  2132. /**
  2133. * Checks whether the given option is enabled.
  2134. *
  2135. * @access public
  2136. * @param string $option The option to check
  2137. * @return bool $enabled True if enabled, false otherwise.
  2138. * @see enableOption()
  2139. * @see disableOption()
  2140. * @see getOptionParameters()
  2141. * @see $options
  2142. */
  2143. function optionEnabled( $option )
  2144. {
  2145. if( !isset( $this->options[$option] ) )
  2146. return false;
  2147. return $this->options[$option]['enabled'];
  2148. }
  2149. /**
  2150. * returns the parameters that have been supplied for an option
  2151. *
  2152. * @access public
  2153. * @param string $option The option to check
  2154. * @return array
  2155. * @see enableOption()
  2156. * @see disableOption()
  2157. * @see optionEnabled()
  2158. * @see $options
  2159. */
  2160. function getOptionParameters( $option )
  2161. {
  2162. if( !isset( $this->options[$option] ) )
  2163. return false;
  2164. return $this->options[$option]['params'];
  2165. }
  2166. /**
  2167. * Set the form to auto validate
  2168. *
  2169. * If you use this method, patForms will check the _GET and _POST variables
  2170. * for the variable you specified. If it is set, patForms assumes, that
  2171. * the form has been submitted.
  2172. *
  2173. * When creating a start tag for the form, the value will be inserted automatically.
  2174. *
  2175. * @access public
  2176. * @param string $submitVar
  2177. */
  2178. function setAutoValidate( $submitVar )
  2179. {
  2180. $this->autoValidate = true;
  2181. $this->submitVar = $submitVar;
  2182. }
  2183. /**
  2184. * register a new event
  2185. *
  2186. * After registering an event, you may register one or more
  2187. * event handlers for this event an then trigger the event.
  2188. *
  2189. * This lets you extend the functionality of patForms.
  2190. *
  2191. * @access public
  2192. * @param string event name
  2193. * @return boolean true, if event could be registered
  2194. * @see registerEventHandler()
  2195. * @see triggerEvent()
  2196. */
  2197. function registerEvent( $name )
  2198. {
  2199. $event = 'on' . $name;
  2200. if( in_array( $event, $this->_validEvents ) )
  2201. {
  2202. return patErrorManager::raiseNotice(
  2203. PATFORMS_NOTICE_EVENT_ALREADY_REGISTERED,
  2204. 'Event "'.$event.'" already has been registered or is built-in event'
  2205. );
  2206. }
  2207. array_push( $this->_validEvents, $event );
  2208. return true;
  2209. }
  2210. /**
  2211. * Register an event handler
  2212. *
  2213. * An event handler can be any valid PHP callback. You may pass
  2214. * one of the following values:
  2215. * - string functionname to call a globally declared function
  2216. * - array( string classname, string methodname) to call a static method
  2217. * - array( object obj, string methodname) to call a method of an object
  2218. *
  2219. * When the handler is called, two parameters will be passed:
  2220. * - object form : a patForms object
  2221. * - string event : the name of the event has should be handled.
  2222. *
  2223. * An event handler should always return true. If false is returned,
  2224. * the event will be cancelled.
  2225. *
  2226. * Currently handlers for the following events can be registered:
  2227. * - onSubmit
  2228. * - onSuccess
  2229. * - onError
  2230. *
  2231. * @access public
  2232. * @param string event name
  2233. * @param mixed event handler
  2234. * @return boolean true, if the handler could be registered
  2235. * @see triggerEvent()
  2236. * @see $_validEvents
  2237. */
  2238. function registerEventHandler( $event, $handler )
  2239. {
  2240. if( !in_array( $event, $this->_validEvents ) )
  2241. {
  2242. return patErrorManager::raiseError(
  2243. PATFORMS_ERROR_UNKNOWN_EVENT,
  2244. 'Cannot register event handler for unknown event "' . $event .'".'
  2245. );
  2246. }
  2247. if( !is_callable( $handler ) )
  2248. {
  2249. return patErrorManager::raiseError(
  2250. PATFORMS_ERROR_INVALID_HANDLER,
  2251. 'Event handler is not callable.'
  2252. );
  2253. }
  2254. if( !isset( $this->_eventHandler[$event] ) )
  2255. {
  2256. $this->_eventHandler[$event] = array();
  2257. }
  2258. $this->_eventHandler[$event][] = &$handler;
  2259. return true;
  2260. }
  2261. /**
  2262. * set event handler object.
  2263. *
  2264. * An event handler object is used to handle all
  2265. * registered events. The object has to provide methods
  2266. * for all events it should handle, the names of the methods
  2267. * have to be the same as the names of the events.
  2268. *
  2269. * @access public
  2270. * @param object event handler object
  2271. * @param array method names, used to change the names of the methods
  2272. * @return boolean
  2273. */
  2274. function registerEventHandlerObject( &$obj, $methods = array() )
  2275. {
  2276. if( empty( $methods ) )
  2277. {
  2278. foreach( $this->_validEvents as $event )
  2279. {
  2280. if( !method_exists( $obj, $event ) )
  2281. continue;
  2282. $methods[$event] = $event;
  2283. }
  2284. }
  2285. foreach( $methods as $event => $method )
  2286. {
  2287. if( !isset( $this->_eventHandler[$event] ) )
  2288. {
  2289. $this->_eventHandler[$event] = array();
  2290. }
  2291. $this->_eventHandler[$event][] = array( &$obj, $method );
  2292. }
  2293. return true;
  2294. }
  2295. /**
  2296. * Trigger an event
  2297. *
  2298. * In most cases there's no need to call this event
  2299. * from outside the class. The method is declared public
  2300. * to allow you to trigger custom events.
  2301. *
  2302. * @access public
  2303. * @param string Event name. The event name must not contain 'on', as this will be
  2304. * prefixed automatically.
  2305. */
  2306. function triggerEvent( $event )
  2307. {
  2308. $handlerName = 'on' . $event;
  2309. if( !isset( $this->_eventHandler[$handlerName] ) || empty( $this->_eventHandler[$handlerName] ) )
  2310. {
  2311. return true;
  2312. }
  2313. $cnt = count( $this->_eventHandler[$handlerName] );
  2314. for( $i = 0; $i < $cnt; $i++ )
  2315. {
  2316. $result = call_user_func( $this->_eventHandler[$handlerName][$i], $this, $event );
  2317. if( $result == false )
  2318. {
  2319. return $result;
  2320. }
  2321. }
  2322. return true;
  2323. }
  2324. /**
  2325. * Serializes the entire form to XML, all elements included
  2326. *
  2327. * @access public
  2328. * @param string $namespace Optional namespace to use for the tags
  2329. * @return string $xml The XML representation of the form
  2330. * @see patForms_Element::toXML()
  2331. * @todo needs patForms_Element, maybe switch to PEAR::XML_Util
  2332. */
  2333. function toXML( $namespace = null )
  2334. {
  2335. $tagName = 'Form';
  2336. // prepend Namespace
  2337. if( $namespace != null )
  2338. {
  2339. $tagName = $namespace.':'.$tagName;
  2340. }
  2341. // get all attributes
  2342. $attributes = $this->getAttributes();
  2343. // create valid XML attributes
  2344. foreach( $attributes as $key => $value )
  2345. {
  2346. $attributes[$key] = strtr( $value, $this->xmlEntities );
  2347. }
  2348. $elements = '';
  2349. for( $i = 0; $i < $this->elementCounter; $i++ )
  2350. {
  2351. $elements .= $this->elements[$i]->toXML( $namespace );
  2352. }
  2353. return patForms_Element::createTag( $tagName, "full", $attributes, $elements );
  2354. }
  2355. /**
  2356. * Set a static property.
  2357. *
  2358. * Static properties are stored in an array in a global variable,
  2359. * until PHP5 is ready to use.
  2360. *
  2361. * @static
  2362. * @param string property name
  2363. * @param mixed property value
  2364. * @see getStaticProperty()
  2365. */
  2366. function setStaticProperty( $property, &$value )
  2367. {
  2368. $GLOBALS["_patForms"][$property] = &$value;
  2369. }
  2370. /**
  2371. * Get a static property.
  2372. *
  2373. * Static properties are stored in an array in a global variable,
  2374. * until PHP5 is ready to use.
  2375. *
  2376. * @static
  2377. * @param string property name
  2378. * @return mixed property value
  2379. * @see setStaticProperty()
  2380. */
  2381. function &getStaticProperty( $property )
  2382. {
  2383. if( isset( $GLOBALS["_patForms"][$property] ) )
  2384. {
  2385. return $GLOBALS["_patForms"][$property];
  2386. }
  2387. return patErrorManager::raiseWarning(
  2388. PATFORMS_ERROR_NO_STATIC_PROPERTY,
  2389. 'Static property "'.$property.'" could not be retreived, it does not exist.'
  2390. );
  2391. }
  2392. /**
  2393. * Retrieves the form's name
  2394. *
  2395. * If no name is set, it will use 'patForms' as name.
  2396. *
  2397. * @access public
  2398. * @return string $name The name of the form.
  2399. */
  2400. function getName()
  2401. {
  2402. if( isset( $this->attributes['name'] ) )
  2403. return $this->attributes['name'];
  2404. return 'patForms';
  2405. }
  2406. /**
  2407. * get the javascript for the form
  2408. *
  2409. * This is still in alpha state. It will later
  2410. * allow client side validation if the element
  2411. * provides this feature.
  2412. *
  2413. * @access public
  2414. * @return string javascript needed by the form
  2415. * @todo make this dependent on the format
  2416. * @todo add changeable linebreaks
  2417. * @todo store included javascripts in some kind of static property
  2418. */
  2419. function getScripts()
  2420. {
  2421. $displayedTypes = array();
  2422. $globalJavascript = array();
  2423. $instances = '';
  2424. $cnt = count( $this->elements );
  2425. for ($i = 0; $i < $cnt; ++$i) {
  2426. $instances .= $this->elements[$i]->getInstanceJavascript();
  2427. $type = $this->elements[$i]->getElementName();
  2428. if (in_array($type, $displayedTypes)) {
  2429. continue;
  2430. }
  2431. array_push( $displayedTypes, $type );
  2432. $globalScript = $this->elements[$i]->getGlobalJavascript();
  2433. if (!empty($globalScript)) {
  2434. array_push($globalJavascript, $globalScript);
  2435. }
  2436. }
  2437. $cnt = count( $this->_rules );
  2438. for( $i = 0; $i < $cnt; ++$i )
  2439. {
  2440. $instances .= $this->_rules[$i]['rule']->getInstanceJavascript();
  2441. $type = $this->_rules[$i]['rule']->getRuleName();
  2442. if (in_array($type, $displayedTypes)) {
  2443. continue;
  2444. }
  2445. array_push( $displayedTypes, $type );
  2446. $globalScript = $this->_rules[$i]['rule']->getGlobalJavascript();
  2447. if (!empty($globalScript)) {
  2448. array_push($globalJavascript, $globalScript);
  2449. }
  2450. }
  2451. $optionParams = $this->getOptionParameters('scripts');
  2452. $scriptFolder = $optionParams['folder'];
  2453. if ($optionParams['jsInclude'] === true) {
  2454. $script = "\n";
  2455. foreach ($globalJavascript as $scriptFile) {
  2456. $fullPath = $scriptFolder . '/' . $scriptFile;
  2457. if (!file_exists($fullPath)) {
  2458. patErrorManager::raiseWarning(PATFORMS_WARNING_SCRIPTFILE_NOT_FOUND, 'Could not script file.', $fullPath);
  2459. continue;
  2460. }
  2461. $script .= '<script type="text/javascript" language="Javascript1.3" src="'.$fullPath.'"></script>' . "\n";
  2462. }
  2463. $script .= '<script type="text/javascript" language="Javascript1.3">' . "\n";
  2464. $script .= $instances;
  2465. $script .= '</script>';
  2466. } else {
  2467. $script = "\n".'<script type="text/javascript" language="Javascript1.3">' . "\n";
  2468. foreach ($globalJavascript as $scriptFile) {
  2469. $fullPath = $scriptFolder . '/' . $scriptFile;
  2470. if (!file_exists($fullPath)) {
  2471. patErrorManager::raiseWarning(PATFORMS_WARNING_SCRIPTFILE_NOT_FOUND, 'Could not script file.', $fullPath);
  2472. continue;
  2473. }
  2474. $script .= file_get_contents($fullPath);
  2475. }
  2476. $script .= "\n\n";
  2477. $script .= $instances;
  2478. $script .= "\n";
  2479. $script .= '</script>';
  2480. }
  2481. return $script;
  2482. }
  2483. /**
  2484. * anounce a change in the element to all observers
  2485. *
  2486. * @access private
  2487. * @param string property that changed
  2488. * @param mixed new value of the property
  2489. */
  2490. function _announce( $property, $value )
  2491. {
  2492. $cnt = count( $this->observers );
  2493. for( $i = 0; $i < $cnt; $i++ )
  2494. {
  2495. $this->observers[$i]->notify( $this, $property, $value );
  2496. }
  2497. return true;
  2498. }
  2499. }
  2500. ?>