source: trunk/patForms.php @ 267

Revision 267, 70.4 KB checked in by schst, 8 years ago (diff)

Pass the element to the datasource, to allow context-sensitive datasources (as requested #151)

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