source: trunk/patForms.php @ 281

Revision 281, 71.5 KB checked in by schst, 8 years ago (diff)

Pass event-handler errors to the user, check, whether DB connection could be established and queries were successful (fixes bug #140), adjust CSV-Storage coding standards

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