source: trunk/patForms.php @ 265

Revision 265, 70.0 KB checked in by sfuchs, 8 years ago (diff)

added patForms::convertElement() and patForms_Element::convertElement() according to #153

  • 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 filter and returns it.
1229    *
1230    * You may pass an array as second parameter that contains
1231    * parameters for the filter. patForms will check for setter methods
1232    * for all keys and set the corresponding values.
1233    *
1234    * This eases the creating of simple filter objects.
1235    *
1236    * @access   public
1237    * @param    string                      The name of the filter to create - have a look at the Filter/ subfolder for a list of available filters.
1238    * @param    array                       Optional parameters for the filter, if you provide a parameter, make sure the filter implements a set[Paramname]() method.
1239    *                                       This will be automated with interceptors in the PHP5 version of patForms
1240    * @return   object patForms_Filter      The filter, or error object
1241    */
1242    function &createFilter( $name, $params = null )
1243    {
1244        $filter =   &patForms::_createModule( 'Filter', $name );
1245
1246        if( !is_array( $params ) )
1247        {
1248            return  $filter;
1249        }
1250
1251        foreach( $params as $param => $value )
1252        {
1253            $setter     =   'set' . ucfirst( $param );
1254            if( method_exists( $filter, $setter ) )
1255            {
1256                $filter->$setter( $value );
1257            }
1258        }
1259        return  $filter;
1260    }
1261
1262   /**
1263    * creates a new rule from the patForms rule collection and returns it.
1264    *
1265    * If your rules are not located in patForms/Rule you have to load and
1266    * instantiate them on your own.
1267    *
1268    * @access   public
1269    * @param    string                  The name of the rule to create - have a look at the Rule/ subfolder for a list of available rules.
1270    * @param    string                  The id of the rule, needed if the rule uses client side actions.
1271    * @return   object patForms_Rule    The rule object, or error object
1272    */
1273    function &createRule( $name, $id = null )
1274    {
1275        $rule   =   &patForms::_createModule( 'Rule', $name );
1276        if( $id != null )
1277        {
1278            $rule->setId( $id );
1279        }
1280        return $rule;
1281    }
1282
1283   /**
1284    * creates a new observer from the patForms observer collection and returns it.
1285    *
1286    * If your observers are not located in patForms/Observer you have to load and
1287    * instantiate them on your own.
1288    *
1289    * @access   public
1290    * @param    string                      The name of the observer to create - have a look at the Observer/ subfolder for a list of available observers.
1291    * @return   object patForms_Observer    The observer object, or error object
1292    */
1293    function &createObserver( $name )
1294    {
1295        $observer = &patForms::_createModule( 'Observer', $name );
1296
1297        return $observer;
1298    }
1299
1300   /**
1301    * creates a new module for patForms
1302    *
1303    * @access   private
1304    * @param    string  $type       type of the module. Possible values are 'Renderer', 'Rule'
1305    * @param    string  $name       The name of the renderer to create - have a look at the renderers/ subfolder for a list of available renderers.
1306    * @return   object  $module     The module object, or an error object
1307    */
1308    function &_createModule( $type, $name )
1309    {
1310        $baseFile       =   PATFORMS_INCLUDE_PATH . '/'.$type.'.php';
1311        $baseClass  =   'patForms_'.$type;
1312
1313        // if there is an underscore in the module name, we want
1314        // to load the module from a subfolder, so we transform
1315        // all underscores to slashes.
1316        $pathName   =   $name;
1317        if( strstr( $pathName, '_' ) )
1318        {
1319            $pathName   =   str_replace( '_', '/', $name );
1320        }
1321
1322        $moduleFile     =   PATFORMS_INCLUDE_PATH . '/'.$type.'/'.$pathName.'.php';
1323        $moduleClass    =   'patForms_'.$type.'_'.$name;
1324
1325        if( !class_exists( $baseClass ) )
1326        {
1327            if( !file_exists( $baseFile ) )
1328            {
1329                return patErrorManager::raiseError(
1330                    PATFORMS_ERROR_NO_MODULE_BASE_FILE,
1331                    $type .' base file could not be found',
1332                    'Tried to load base file in path "'.$baseFile.'"'
1333                );
1334            }
1335
1336            include_once $baseFile;
1337        }
1338
1339        if( !class_exists( $moduleClass ) )
1340        {
1341            if( !file_exists( $moduleFile ) )
1342            {
1343                return patErrorManager::raiseError(
1344                    PATFORMS_ERROR_MODULE_NOT_FOUND,
1345                    $type.' "'.$name.'" file "'.$moduleFile. '" could not be found.'
1346                );
1347            }
1348
1349            include_once $moduleFile;
1350        }
1351
1352        $module =   &new $moduleClass();
1353
1354        return $module;
1355    }
1356
1357   /**
1358    * adds an element to the form - has to be a patForms_Element object. Use the {@link createElement()}
1359    * method to create a new element object. Also takes care of passing on the form's configuration
1360    * including the mode, format and submitted flags to the element.
1361    *
1362    * @access   public
1363    * @param    object  &$element   The patForms_Element object to add to this form.
1364    * @return   bool    $success    True if everything went well, false otherwise.
1365    * @see      patForms_Element
1366    * @see      createElement()
1367    */
1368    function addElement( &$element )
1369    {
1370        if( !is_object( $element ) )
1371        {
1372            return patErrorManager::raiseError(
1373                PATFORMS_ERROR_ELEMENT_IS_NO_OBJECT,
1374                'The addElement() method expects an element object, "'.gettype( $element ).'" given.'
1375            );
1376        }
1377
1378        if( patErrorManager::isError( $element ) )
1379        {
1380            return patErrorManager::raiseError(
1381                PATFORMS_ERROR_UNEXPECTED_ERROR,
1382                'The element you are trying to add is a patError object, and not a patForms element object.'
1383            );
1384        }
1385
1386        if( !$element->getId() ) {
1387            $element->setId( $this->getElementId() );
1388        }
1389        $element->setMode( $this->getMode() );
1390        $element->setFormat( $this->getFormat() );
1391        $element->setSubmitted( $this->isSubmitted() );
1392        $element->setLocale( $this->getLocale() );
1393        $element->setNamespace( $this->getNamespace() );
1394
1395        $this->elements[]   =&  $element;
1396
1397        return true;
1398    }
1399
1400   /**
1401    * replaces an element in the form
1402    *
1403    * @access   public
1404    * @param    object  $element    The patForms_Element object to be replaced
1405    * @param    object  &$replace   The element that will replace the old element
1406    * @return   bool    $success    True if everything went well, false otherwise.
1407    * @see      patForms_Element
1408    * @see      addElement()
1409    */
1410    function replaceElement( $element, &$replace )
1411    {
1412        if( !is_object( $replace ) ) {
1413            return patErrorManager::raiseError(
1414                PATFORMS_ERROR_ELEMENT_IS_NO_OBJECT,
1415                'The addElement() method expects an element object, "'.gettype( $replace ).'" given.'
1416            );
1417        }
1418
1419        if( patErrorManager::isError( $replace ) ) {
1420            return patErrorManager::raiseError(
1421                PATFORMS_ERROR_UNEXPECTED_ERROR,
1422                'The element you are trying to add is a patError object, and not a patForms element object.'
1423            );
1424        }
1425
1426        if (is_object($element)) {
1427            $element = $element->getId();
1428        }
1429
1430        $cnt = count($this->elements);
1431        for ($i = 0; $i < $cnt; $i++) {
1432            if ($this->elements[$i]->getId() === $element) {
1433
1434                if( !$replace->getId() ) {
1435                    $replace->setId( $this->getElementId() );
1436                }
1437                $replace->setMode( $this->getMode() );
1438                $replace->setFormat( $this->getFormat() );
1439                $replace->setSubmitted( $this->isSubmitted() );
1440                $replace->setLocale( $this->getLocale() );
1441
1442                $this->elements[$i] = &$replace;
1443                return true;
1444            }
1445
1446            // the current element is a container
1447            if (method_exists($this->elements[$i], 'replaceElement')) {
1448                $result = $this->elements[$i]->replaceElement($element, $replace);
1449                if ($result === true) {
1450                    return $result;
1451                }
1452            }
1453        }
1454
1455        return false;
1456    }
1457
1458   /**
1459    * Get an element by its name.
1460    *
1461    * @access   public
1462    * @param    string  $name   name of the element
1463    * @return   object          patForms element
1464    * @deprecated   please use patForms::getElementByName() instead
1465    */
1466    function &getElement( $name )
1467    {
1468        return $this->getElementByName( $name );
1469    }
1470
1471   /**
1472    * Get an element by its name.
1473    *
1474    * @access   public
1475    * @param    string  $name   name of the element
1476    * @return   mixed           either a patForms element or an array containing patForms elements
1477    * @see      getElementById()
1478    */
1479    function &getElementByName( $name )
1480    {
1481        if( $name == '__form' ) {
1482            return $this;
1483        }
1484
1485        $elements = array();
1486        $cnt      = count( $this->elements );
1487        for ($i = 0; $i < $cnt; $i++) {
1488            if ($this->elements[$i]->getName() == $name) {
1489                $elements[] = &$this->elements[$i];
1490                continue;
1491            }
1492            if (method_exists($this->elements[$i], 'getElementById')) {
1493                patErrorManager::pushExpect(PATFORMS_ERROR_ELEMENT_NOT_FOUND);
1494                $result = &$this->elements[$i]->getElementByName($name);
1495                patErrorManager::popExpect();
1496                if (!patErrorManager::isError($result)) {
1497                    if (is_array($result)) {
1498                        $cnt2 = count( $result );
1499                        for ($j = 0; $j < $cnt2; $j++) {
1500                            $elements[] = &$result[$j];
1501                        }
1502                    } else {
1503                        $elements[] = &$result;
1504                    }
1505                }
1506            }
1507        }
1508
1509        switch( count( $elements ) )
1510        {
1511            case    0:
1512                return patErrorManager::raiseError(
1513                    PATFORMS_ERROR_ELEMENT_NOT_FOUND,
1514                    'Element '.$name.' could not be found.'
1515                );
1516                break;
1517            case    1:
1518                return  $elements[0];
1519                break;
1520            default:
1521                return  $elements;
1522                break;
1523        }
1524    }
1525
1526   /**
1527    * Get an element by its id.
1528    *
1529    * @access   public
1530    * @param    string  $id     id of the element
1531    * @return   object          patForms element
1532    */
1533    function &getElementById( $id )
1534    {
1535        $cnt    =   count( $this->elements );
1536        for( $i = 0; $i < $cnt; $i++ )
1537        {
1538            if( $this->elements[$i]->getId() == $id ) {
1539                return $this->elements[$i];
1540            }
1541            if (method_exists($this->elements[$i], 'getElementById')) {
1542                patErrorManager::pushExpect(PATFORMS_ERROR_ELEMENT_NOT_FOUND);
1543                $result = &$this->elements[$i]->getElementById($id);
1544                patErrorManager::popExpect();
1545                if (!patErrorManager::isError($result)) {
1546                    return $result;
1547                }
1548            }
1549        }
1550        return patErrorManager::raiseError(
1551            PATFORMS_ERROR_ELEMENT_NOT_FOUND,
1552            'Element '.$name.' could not be found.'
1553        );
1554    }
1555
1556   /**
1557    * Get all elements of the form
1558    *
1559    * @access   public
1560    * @return   array   all elements of the form
1561    */
1562    function &getElements()
1563    {
1564        return  $this->elements;
1565    }
1566
1567   /**
1568    * Creates a new form element and returns a reference to it.
1569    *
1570    * The optional $filters array has to be in the following format:
1571    *
1572    * <pre>
1573    * array(
1574    *       array(
1575    *              'filter' => 'Multiplier',
1576    *              'params' => array( 'multiplier' => 6 )
1577    *            )
1578    *      )
1579    * </pre>
1580    *
1581    * @access   public
1582    * @param    string  $name       The name of the element
1583    * @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.
1584    * @param    array   $attributes Attributes for the element
1585    * @param    array   $filters    Optional filters that will be applied
1586    * @return   object patForms_Element $element    The element object, or patError if failed.
1587    */
1588    function &createElement( $name, $type, $attributes, $filters = null, $children = null )
1589    {
1590        $element =& patForms::_createModule( 'Element', $type );
1591        if( patErrorManager::isError( $element ) )
1592        {
1593            return  $element;
1594        }
1595
1596        $attributes['name'] =   $name;
1597        if( !isset( $attributes['id'] ) ) {
1598            $attributes['id'] = patForms::getElementId();
1599        }
1600
1601        // add default attributes - do this the 'silent' way be checking whether
1602        // the element supports the given attribute, as the element throws a notice
1603        // if it does not support it - this is not expected from default attributes.
1604        foreach( patForms::getStaticProperty( 'defaultAttributes' ) as $attributeName => $attributeValue )
1605        {
1606            if( !$element->hasAttribute( $attributeName ) )
1607            {
1608                continue;
1609            }
1610
1611            $element->setAttribute( $attributeName, $attributeValue );
1612        }
1613
1614        // set the given attributes normally
1615        $success = $element->setAttributes( $attributes );
1616        if( patErrorManager::isError( $success ) )
1617        {
1618            return $success;
1619        }
1620
1621        if (is_array($children)) {
1622            foreach ($children as $child) {
1623                $childName = $child['attributes']['name'];
1624
1625                $childEl = &patForms::createElement($childName, $child['type'], $child['attributes']);
1626                if( isset( $child["renderer"] ) ) {
1627                    $childEl->setRenderer( $child["renderer"] );
1628                }
1629
1630                $element->addElement($childEl);
1631            }
1632        }
1633
1634        $success = $element->_init();
1635        if( patErrorManager::isError( $success ) ) {
1636            return $success;
1637        }
1638
1639        // if we don't have any filters to add, we're done
1640        if( !is_array( $filters ) )
1641        {
1642            return $element;
1643        }
1644
1645        $cnt    =   count( $filters );
1646        for( $i = 0; $i < $cnt; $i++ )
1647        {
1648            $params =   isset( $filters[$i]['params'] ) ? $filters[$i]['params'] : null;
1649            $filter =   &patForms::createFilter( $filters[$i]['filter'], $params );
1650            if( patErrorManager::isError( $filter ) )
1651            {
1652                continue;
1653            }
1654            $element->applyFilter( $filter );
1655        }
1656
1657        return $element;
1658    }
1659
1660   /**
1661    * Convert an element to another element type
1662    *
1663    * @access   public
1664    * @param    string  $name   name of the element
1665    * @param    string  $type   desired element type
1666    * @return   object  the new element
1667    */
1668    function convertElement($name, $type)
1669    {
1670        $element = &$this->getElement($name);
1671        $element = patForms_Element::convertElement($element, $type);
1672    }
1673
1674   /**
1675    * retrieves the validation errors from all elements in the form. Use this if the validateForm()
1676    * method returned false.
1677    *
1678    * @access   public
1679    * q
1680    * @return   array   $errors Array containing an array with validation errors for each element in the form.
1681    * @todo     replace __form with the name of the form, once attributes are implemented
1682    */
1683    function getValidationErrors($withElements = true)
1684    {
1685        $found  =   false;
1686        $errors =   array();
1687
1688        if( !empty( $this->validationErrors ) )
1689        {
1690            $errors['__form']   =   $this->validationErrors;
1691            $found = true;
1692        }
1693
1694        if ($withElements === false) {
1695            return $errors;
1696        }
1697
1698        $cnt = count( $this->elements );
1699        for( $i = 0; $i < $cnt; ++$i )
1700        {
1701            $name   =   $this->elements[$i]->getAttribute( 'name' );
1702            if( $name === false )
1703            {
1704                continue;
1705            }
1706
1707            $elementErrors = $this->elements[$i]->getValidationErrors();
1708
1709            if( empty( $elementErrors ) )
1710                continue;
1711
1712            $errors[$name]  =   $elementErrors;
1713            $found = true;
1714        }
1715
1716        if( $found )
1717            return $errors;
1718
1719        return false;
1720    }
1721
1722   /**
1723    * retrieves the values for all elements in the form.
1724    *
1725    * @access   public
1726    * @param    array       desired fields
1727    * @param    integer     Mode that should be used to return values in groups
1728    * @return   array       The values for all elements, as elementname => elementvalue.
1729    *
1730    * @todo     remove the ugly Group check and replace with something better
1731    * @todo     implement something similar for getValidation errors
1732    */
1733    function getValues( $fields = null, $type = PATFORMS_VALUES_NESTED )
1734    {
1735        $values =   array();
1736
1737        $cnt = count( $this->elements );
1738        for( $i = 0; $i < $cnt; ++$i )
1739        {
1740            $name   =   $this->elements[$i]->getAttribute( 'name' );
1741            if( $name === false ) {
1742                continue;
1743            }
1744
1745            if( is_array( $fields ) && !in_array( $name, $fields ) ) {
1746                continue;
1747            }
1748
1749            $tmpVal = $this->elements[$i]->getValue();
1750            if (!is_array($tmpVal) || $this->elements[$i]->elementName != 'Group') {
1751                $values[$name] = $tmpVal;
1752                continue;
1753            }
1754
1755            switch ($type) {
1756                case PATFORMS_VALUES_FLATTENED:
1757                    $values = array_merge($values, $tmpVal);
1758                    break;
1759                case PATFORMS_VALUES_PREFIXED:
1760                    foreach ($tmpVal as $key => $val) {
1761                        $values[$name.'_'.$key] = $val;
1762                    }
1763                    break;
1764                case PATFORMS_VALUES_NESTED:
1765                default:
1766                    $values[$name] = $tmpVal;
1767                    break;
1768
1769            }
1770        }
1771        return $values;
1772    }
1773
1774   /**
1775    * sets the values for all elements in the form. Use this to fill your form with external
1776    * data, like a db query. Caution: if you do this and set the form to submitted, the values
1777    * will be overwritten by any values present in the $_GET or $_POST variables.
1778    *
1779    * @access   public
1780    * @param    array   $values The values for all elements, as elementname => elementvalue.
1781    */
1782    function setValues( $values, $overrideUserInput = false )
1783    {
1784        patErrorManager::pushExpect(PATFORMS_ERROR_ELEMENT_NOT_FOUND);
1785        foreach ($values as $elName => $value) {
1786            $el = &$this->getElementByName($elName);
1787            if (patErrorManager::isError($el)) {
1788                continue;
1789            }
1790
1791            if (is_array($el)) {
1792                for ($i = 0; $i < count($el); $i++) {
1793                    if ($overrideUserInput === true) {
1794                        $el[$i]->setValue($value);
1795                    } else {
1796                        $el[$i]->setDefaultValue($value);
1797                    }
1798                }
1799            } else {
1800                if ($overrideUserInput === true) {
1801                    $el->setValue($value);
1802                } else {
1803                    $el->setDefaultValue($value);
1804                }
1805            }
1806        }
1807        patErrorManager::popExpect();
1808        return true;
1809    }
1810
1811   /**
1812    * retrieves the current mode of the form
1813    *
1814    * @access   public
1815    * @return   string  $mode   The current form mode
1816    * @see      setMode()
1817    * @see      $mode
1818    */
1819    function getMode()
1820    {
1821        return $this->mode;
1822    }
1823
1824   /**
1825    * returns the locale that is currently set for the form.
1826    *
1827    * @access   public
1828    * @return   string  $locale The locale.
1829    * @see      setLocale()
1830    * @see      $locale
1831    */
1832    function getLocale()
1833    {
1834        return $this->locale;
1835    }
1836
1837   /**
1838    * retrieves the current format of the form
1839    *
1840    * @access   public
1841    * @return   string  $format The current form format
1842    * @see      setFormat()
1843    * @see      format
1844    */
1845    function getFormat()
1846    {
1847        return $this->format;
1848    }
1849
1850   /**
1851    * retrieves the current method of the form
1852    *
1853    * @access   public
1854    * @return   string  $method The request method
1855    * @see      setMethod()
1856    */
1857    function getMethod()
1858    {
1859        return $this->getAttribute( 'method' );
1860    }
1861
1862   /**
1863    * retrieves the current action of the form
1864    *
1865    * @access   public
1866    * @return   string  $action     Action of the form
1867    * @see      setAction()
1868    */
1869    function getAction()
1870    {
1871        $action = $this->getAttribute( 'action' );
1872        if( !empty( $action ) )
1873            return $action;
1874        return  $_SERVER['PHP_SELF'];
1875    }
1876
1877   /**
1878    * adds an atribute to the form's attribute collection. If the attribute
1879    * already exists, it is overwritten.
1880    *
1881    * @access   public
1882    * @param    string  $attributeName  The name of the attribute to add
1883    * @param    string  $atributeValue  The value of the attribute
1884    */
1885    function setAttribute( $attributeName, $attributeValue )
1886    {
1887        if( !isset( $this->attributeDefinition[$attributeName] ) )
1888        {
1889            patErrorManager::raiseNotice(
1890                PATFORMS_NOTICE_ATTRIBUTE_NOT_SUPPORTED,
1891                "The attribute '".$attributeName."' is not supported by the form, skipped it. [".get_class( $this )."]"
1892            );
1893            return true;
1894        }
1895
1896        $this->attributes[$attributeName]   =   $attributeValue;
1897
1898        return true;
1899    }
1900
1901   /**
1902    * adds several attributes at once to the form's attribute collection.
1903    * Any existing attributes will be overwritten.
1904    *
1905    * @access   public
1906    * @param    array   $attributes The attributes to add
1907    * @see      setAttribute()
1908    */
1909    function setAttributes( $attributes )
1910    {
1911        if( !is_array( $attributes ) )
1912        {
1913            return patErrorManager::raiseError(
1914                PATFORMS_NOTICE_ARRAY_EXPECTED,
1915                "setAttributes: array expected"
1916            );
1917        }
1918
1919        foreach( $attributes as $attributeName => $attributeValue )
1920        {
1921            $this->setAttribute( $attributeName, $attributeValue );
1922        }
1923
1924        return true;
1925    }
1926
1927   /**
1928    * retrieves the value of a form attribute.
1929    *
1930    * @access   public
1931    * @param    string  $attribute  The name of the attribute to retrieve
1932    * @return   mixed   $attributeValue The value of the attribute, or false if it does not exist in the attributes collection.
1933    * @see      setAttribute()
1934    */
1935    function getAttribute( $attribute )
1936    {
1937        if( !isset( $this->attributes[$attribute] ) )
1938        {
1939            return false;
1940        }
1941
1942        return $this->attributes[$attribute];
1943    }
1944
1945   /**
1946    * retrieves all attributes of the form, or only the specified attributes.
1947    *
1948    * @access   public
1949    * @param    array   $attributes Optional: The names of the attributes to retrieve. Only the attributes that exist will be returned.
1950    * @return   array   $result     The attributes
1951    * @see      getAttribute()
1952    */
1953    function getAttributes( $attributes = array() )
1954    {
1955        if( empty( $attributes ) )
1956        {
1957            return $this->attributes;
1958        }
1959
1960        $result =   array();
1961        foreach( $attributes as $attribute )
1962        {
1963            if( $attributeValue = $this->getAttribute( $attribute ) )
1964            {
1965                $result[$attribute] =   $attributeValue;
1966            }
1967        }
1968
1969        return $result;
1970    }
1971
1972   /**
1973    * Loads the default attribute values into the attributes collection. Done directly
1974    * on startup (in the consructor).
1975    *
1976    * The action defaults to the path of the current script, with session
1977    * ID appended automatically, if SID has been defined.
1978    *
1979    * @access   public
1980    * @return   bool    $success    Always returns true.
1981    * @see      $attributeDefaults
1982    */
1983    function loadAttributeDefaults()
1984    {
1985        foreach( $this->attributeDefinition as $attributeName => $attributeDef )
1986        {
1987            if( isset( $attributeDef['default'] ) )
1988            {
1989                $this->attributes[$attributeName]   =   $attributeDef['default'];
1990            }
1991
1992            if( $attributeName == 'action' )
1993            {
1994                $this->attributes[$attributeName]   =   $_SERVER['PHP_SELF'];
1995                /**
1996                 * session has been started, append session ID
1997                 */
1998                if( defined( 'SID' ) )
1999                    $this->attributes[$attributeName] .= '?' . SID;
2000            }
2001        }
2002
2003        return true;
2004    }
2005
2006   /**
2007    * retrieves the form's current submitted state.
2008    *
2009    * If autoValidate is used, it will check for the submitVar and
2010    * set the submitted flag accordingly
2011    *
2012    * @access   public
2013    * @return   bool    $state  True if it has been submitted, false otherwise.
2014    * @see      setSubmitted(), setAutoValidate()
2015    * @see      submitted
2016    */
2017    function isSubmitted()
2018    {
2019        if( $this->submitted === true )
2020        {
2021            return true;
2022        }
2023
2024        if( !isset( $this->submitVar ) )
2025        {
2026            return  false;
2027        }
2028
2029        if( !$this->autoValidate )
2030        {
2031            return  false;
2032        }
2033
2034        if( isset( $_GET[$this->submitVar] ) || isset( $_POST[$this->submitVar] ) )
2035        {
2036            $this->setSubmitted( true );
2037        }
2038
2039        return $this->submitted;
2040    }
2041
2042   /**
2043    * Creates a new patForms_Creator object
2044    *
2045    * @static
2046    * @access   public
2047    * @return   object  $creator    The creator object, or a patError object on failure
2048    */
2049    function createCreator( $type )
2050    {
2051        return patForms::_createModule( 'Creator', $type );
2052    }
2053
2054   /**
2055    * get the element name of the form
2056    *
2057    * @access   public
2058    * @return   string  name of the form
2059    */
2060    function getElementName()
2061    {
2062        return $this->elementName;
2063    }
2064
2065   /**
2066    * get next error offset
2067    *
2068    * @access   public
2069    * @return   integer
2070    */
2071    function getErrorOffset( $requiredCodes = 100 )
2072    {
2073        $offset                 =   $this->nextErrorOffset;
2074        $this->nextErrorOffset  =   $this->nextErrorOffset + $requiredCodes;
2075        return   $offset;
2076    }
2077
2078   /**
2079    * add error codes and messages for validator method
2080    *
2081    * @access   public
2082    * @param    array   defintions
2083    * @param    integer offset for the error codes
2084    */
2085    function addValidatorErrorCodes( $defs, $offset = 1000 )
2086    {
2087        foreach( $defs as $lang => $codes )
2088        {
2089            if( !isset( $this->validatorErrorCodes[$lang] ) )
2090            {
2091                $this->validatorErrorCodes[$lang]   =   array();
2092            }
2093
2094            foreach( $codes as $code => $message )
2095            {
2096                $this->validatorErrorCodes[$lang][($offset+$code)]  =   $message;
2097            }
2098        }
2099    }
2100
2101   /**
2102    * add a validation error to the whole form
2103    *
2104    * This can be achieved by adding a validation rule to the form.
2105    *
2106    * @access   public
2107    * @param    integer $code
2108    * @param    array   $vars   fill named placeholder with values
2109    * @return   boolean $result true on success
2110    * @see      addRule()
2111    */
2112    function addValidationError( $code, $vars = array() )
2113    {
2114        $error      =   false;
2115        $lang       =   $this->locale;
2116        $element    =   $this->getElementName();
2117
2118        // find error message for selected language
2119        while( true )
2120        {
2121            // error message matches language code
2122            if( isset( $this->validatorErrorCodes[$lang][$code] ) )
2123            {
2124                $error  =   array( "element" => $element, "code" => $code, "message" => $this->validatorErrorCodes[$lang][$code] );
2125                break;
2126            }
2127            // no message found and no fallback-langauage available
2128            else if ( $lang == "C" )
2129            {
2130                break;
2131            }
2132
2133            $lang_old   =   $lang;
2134
2135            // look for other languages
2136            if( strlen( $lang ) > 5 )
2137            {
2138                list( $lang, $trash ) = explode( ".", $lang );
2139            }
2140            else if( strlen( $lang ) > 2 )
2141            {
2142                list( $lang, $trash ) = explode( "_", $lang );
2143            }
2144            else
2145            {
2146                $lang   =   "C";
2147            }
2148
2149            // inform developer about missing language
2150            patErrorManager::raiseNotice(
2151                PATFORMS_NOTICE_VALIDATOR_ERROR_LOCALE_UNDEFINED,
2152                "Required Validation Error-Code for language: $lang_old not available. Now trying language: $lang",
2153                "Add language definition in used element or choose other language"
2154            );
2155
2156        }
2157
2158        // get default Error!
2159        if( !$error )
2160        {
2161            patErrorManager::raiseWarning(
2162                PATFORMS_WARNING_VALIDATOR_ERROR_UNDEFINED,
2163                "No Error Message for this validation Error was defined",
2164                "Review the error-definition for validation-errors in your element '$element'."
2165            );
2166            $error  =   array( "element" => $element, "code" => 0, "message" => "Unknown validation Error" );
2167        }
2168
2169        // insert values to placeholders
2170        if( !empty( $vars ) )
2171        {
2172            foreach( $vars as $key => $value )
2173            {
2174                $error["message"]   =   str_replace( "[". strtoupper( $key ) ."]", $value, $error["message"] );
2175            }
2176        }
2177
2178        array_push( $this->validationErrors, $error );
2179        $this->valid    =   false;
2180        return  true;
2181    }
2182
2183   /**
2184    * Retrieves a new element id, used to give each added element a unique id for this
2185    * form (id can be overwritten by setting the id attribute specifically).
2186    *
2187    * @static
2188    * @access   public
2189    * @return   int $elementId  The new element id.
2190    */
2191    function getElementId()
2192    {
2193        $GLOBALS['_patForms']['elementCounter']++;
2194        return 'pfo'.$GLOBALS['_patForms']['elementCounter'];
2195    }
2196
2197   /**
2198    * attach an observer
2199    *
2200    * @access   public
2201    * @param    object  patForms_Observer
2202    * @see      createObserver()
2203    * @uses     patForms_Element::createObserver()
2204    */
2205    function attachObserver( &$observer, $where = PATFORMS_OBSERVER_ATTACH_TO_ELEMENTS )
2206    {
2207        /**
2208         * attach the observer to all elements
2209         */
2210        if( ( $where & PATFORMS_OBSERVER_ATTACH_TO_ELEMENTS ) == PATFORMS_OBSERVER_ATTACH_TO_ELEMENTS )
2211        {
2212            $cnt    =   count( $this->elements );
2213            for( $i = 0; $i < $cnt; ++$i )
2214            {
2215                $this->elements[$i]->attachObserver( $observer );
2216            }
2217        }
2218
2219        /**
2220         * attach the observer to the form
2221         */
2222        if( ( $where & PATFORMS_OBSERVER_ATTACH_TO_FORM ) == PATFORMS_OBSERVER_ATTACH_TO_FORM )
2223        {
2224            $this->observers[] = &$observer;
2225        }
2226        return true;
2227    }
2228
2229   /**
2230    * Retrieve the content for the start of the form, including any
2231    * additional content, e.g. global scripts if the scripts option
2232    * is enabled.
2233    *
2234    * @access   public
2235    * @return   string  $formStart  The form start content
2236    * @todo     use format to build a dynamic method
2237    */
2238    function serializeStart()
2239    {
2240        $methodName =   "serializeStart".ucfirst( $this->getFormat() ).ucfirst( $this->getMode() );
2241
2242        if( !method_exists( $this, $methodName ) )
2243        {
2244            return patErrorManager::raiseError(
2245                PATFORMS_ERROR_METHOD_FOR_MODE_NOT_AVAILABLE,
2246                "Method for patForms mode '".$this->getMode()."' (".$methodName.") is not available."
2247            );
2248        }
2249
2250        return  $this->$methodName();
2251    }
2252
2253   /**
2254    * Serializes the form's start element for html format, in default mode.
2255    *
2256    * @access   private
2257    * @return   mixed   $formStart  The serialized start content, or a patError object.
2258    */
2259    function serializeStartHtmlDefault()
2260    {
2261        $attributes = $this->getAttributesFor( $this->format );
2262        if( patErrorManager::isError( $attributes ) )
2263        {
2264            return $attributes;
2265        }
2266
2267        $content = patForms::createTag( 'form', 'opening', $attributes );
2268
2269        if ($this->optionEnabled( 'scripts' )) {
2270            $content .= $this->getScripts();
2271        }
2272
2273        return $content;
2274    }
2275
2276   /**
2277    * Serializes the form's start element for html format, in readonly mode.
2278    *
2279    * @access   private
2280    * @return   mixed   $formStart  The serialized start content, or a patError object.
2281    */
2282    function serializeStartHtmlReadonly()
2283    {
2284        $attributes = $this->getAttributesFor( $this->format );
2285        if( patErrorManager::isError( $attributes ) )
2286        {
2287            return $attributes;
2288        }
2289
2290        return patForms::createTag( 'form', 'opening', $attributes );
2291    }
2292
2293   /**
2294    * Retrieve the content for the end of the form.
2295    *
2296    * @access   public
2297    * @return   string  $formEnd    The form end content
2298    */
2299    function serializeEnd()
2300    {
2301        $methodName =   "serializeEnd".ucfirst( $this->getFormat() ).ucfirst( $this->getMode() );
2302
2303        if( !method_exists( $this, $methodName ) )
2304        {
2305            return patErrorManager::raiseError(
2306                PATFORMS_ERROR_METHOD_FOR_MODE_NOT_AVAILABLE,
2307                "Method for patForms mode '".$this->getMode()."' (".$methodName.") is not available."
2308            );
2309        }
2310
2311        return  $this->$methodName();
2312    }
2313
2314   /**
2315    * Retrieves the content for the end of the form for html format,
2316    * in default mode.
2317    *
2318    * @access   private
2319    * @return   string  $formEnd    The form end content
2320    */
2321    function serializeEndHtmlDefault()
2322    {
2323        return  patForms::createTag( 'form', 'closing' );
2324    }
2325
2326   /**
2327    * Retrieves the content for the end of the form for html format,
2328    * in readonly mode.
2329    *
2330    * @access   private
2331    * @return   string  $formEnd    The form end content
2332    */
2333    function serializeEndHtmlReadonly()
2334    {
2335        return  $this->serializeEndHtmlDefault();
2336    }
2337
2338   /**
2339    * [helper method] create an element HTML source from its attribute collection and
2340    * returns it.
2341    *
2342    * @static
2343    * @access   protected
2344    * @param    string  $tagname        The name of the element / tag
2345    * @param    string  $type           Optional: the type of element to generate. Valid parameters are full|opening|closing|empty. Defaults to "full".
2346    * @param    mixed   $value          The value of the element
2347    * @return   string  $element        The HTML source of the element
2348    */
2349    function createTag( $tagname, $type = "full", $attributes = array(), $value = false )
2350    {
2351        switch( $type )
2352        {
2353            case "closing":
2354                return  "</$tagname>";
2355                break;
2356
2357            case "empty":
2358            case "opening":
2359                $tag    =   "<".$tagname;
2360
2361                // create attribute collection
2362                foreach( $attributes as $attributeName => $attributeValue )
2363                {
2364                    $tag    =   $tag . " ".$attributeName."=\"".htmlentities( (string)$attributeValue )."\"";
2365                }
2366
2367                // empty tag?
2368                if( $type == "empty" )
2369                {
2370                    $tag    =   $tag . " />";
2371                    return  $tag;
2372                }
2373
2374                $tag    =   $tag . ">";
2375                return  $tag;
2376
2377                break;
2378
2379            case "full":
2380                if( $value === false )
2381                {
2382                    return patForms::createTag( $tagname, "empty", $attributes );
2383                }
2384
2385                return patForms::createTag( $tagname, "opening", $attributes ).htmlentities( $value ).patForms::createTag( $tagname, "closing" );
2386                break;
2387        }
2388    }
2389
2390   /**
2391    * validates the current attribute collection according to the attributes definition
2392    * and the given output format, and returns the list of valid attributes.
2393    *
2394    * @access   private
2395    * @param    string  $format     The output format to retrieve the attributes for.
2396    * @return   mixed   $attributes The list of attributes, or false if failed.
2397    */
2398    function getAttributesFor( $format )
2399    {
2400        $attributes =   array();
2401
2402        foreach( $this->attributeDefinition as $attributeName => $attributeDef )
2403        {
2404            if( !isset( $this->attributes[$attributeName] ) )
2405            {
2406                if( $attributeDef["required"] )
2407                {
2408                    return patErrorManager::raiseError(
2409                        PATFORMS_ERROR_ATTRIBUTE_REQUIRED,
2410                        'patForms needs the attribute "'.$attributeName.'" to be set.',
2411                        'See the patForms attribute definition of patForms for a complete attribute reference.'
2412                    );
2413                }
2414
2415                continue;
2416            }
2417
2418            $attributeValue =   $this->attributes[$attributeName];
2419
2420            if( !in_array( $format, $attributeDef["outputFormats"] ) )
2421            {
2422                continue;
2423            }
2424
2425            if( isset( $attributeDef["format"] ) )
2426            {
2427                if( !$this->_checkAttributeFormat( $attributeValue, $attributeDef["format"] ) )
2428                {
2429                    return patErrorManager::raiseError(
2430                        PATFORMS_ERROR_CAN_NOT_VERIFY_FORMAT,
2431                        "Format '".$attributeDef["format"]."' could not be verified for patForms attribute '".$attributeName."' => '".$attributeValue."'"
2432                    );
2433                }
2434            }
2435
2436            $attributes[$attributeName] =   $attributeValue;
2437        }
2438
2439        return $attributes;
2440    }
2441
2442   /**
2443    * checks the format of an attribute value according to the given format.
2444    *
2445    * @access   private
2446    * @param    mixed   $attributeValue The attribute value to check
2447    * @param    string  $format         The format to check the attribute value against
2448    * @return   bool    $result         True if format check succeeded, false otherwise.
2449    * @see      createAttributes()
2450    * @todo     Implement this method sometime
2451    */
2452    function _checkAttributeFormat( $attributeValue, $format )
2453    {
2454        return true;
2455    }
2456
2457   /**
2458    * Enables a patForms option.
2459    *
2460    * See the {@link $options} property for an exhaustive list of available options.
2461    *
2462    * @access   public
2463    * @param    string  $option     The option to enable
2464    * @param    array   $params     Optional parameters for the option
2465    * @return   mixed   $result     True on success, patError object otherwise.
2466    * @see      disableOption()
2467    * @see      optionEnabled()
2468    * @see      $options
2469    */
2470    function enableOption( $option, $params = array() )
2471    {
2472        if( !in_array( $option, array_keys( $this->options ) ) )
2473        {
2474            return patErrorManager::raiseNotice(
2475                PATFORMS_NOTICE_INVALID_OPTION,
2476                'The option "'.$option.'" is not a valid patForms option.'
2477            );
2478        }
2479
2480        $this->options[$option]['enabled'] = true;
2481        $this->options[$option]['params']  = array_merge($this->options[$option]['params'], $params);
2482
2483        // now update all available elements too
2484        $cnt = count( $this->elements );
2485        for( $i=0; $i < $cnt; $i++ )
2486        {
2487            $this->elements[$i]->enableOption( $option, $params );
2488        }
2489
2490        return true;
2491    }
2492
2493   /**
2494    * Disables a patForms option
2495    *
2496    * See the {@link $options} property for an exhaustive list of available options.
2497    *
2498    * @access   public
2499    * @param    string  $option The option to disable
2500    * @return   mixed   $result True on success, patError object otherwise.
2501    * @see      enableOption()
2502    * @see      optionEnabled()
2503    * @see      $options
2504    */
2505    function disableOption( $option )
2506    {
2507        if( !in_array( $option, array_keys( $this->options ) ) )
2508        {
2509            return patErrorManager::raiseNotice(
2510                PATFORMS_NOTICE_INVALID_OPTION,
2511                'The option "'.$option.'" is not a valid patForms option.'
2512            );
2513        }
2514
2515        $this->options[$option]['enabled']  =   false;
2516
2517        // now update all available elements too
2518        $cnt = count( $this->elements );
2519        for( $i=0; $i < $cnt; $i++ )
2520        {
2521            $this->elements[$i]->disableOption( $option );
2522        }
2523
2524        return true;
2525    }
2526
2527   /**
2528    * Checks whether the given option is enabled.
2529    *
2530    * @access   public
2531    * @param    string  $option     The option to check
2532    * @return   bool    $enabled    True if enabled, false otherwise.
2533    * @see      enableOption()
2534    * @see      disableOption()
2535    * @see      getOptionParameters()
2536    * @see      $options
2537    */
2538    function optionEnabled( $option )
2539    {
2540        if( !isset( $this->options[$option] ) )
2541            return false;
2542
2543        return $this->options[$option]['enabled'];
2544    }
2545
2546   /**
2547    * returns the parameters that have been supplied for an option
2548    *
2549    * @access   public
2550    * @param    string  $option     The option to check
2551    * @return   array
2552    * @see      enableOption()
2553    * @see      disableOption()
2554    * @see      optionEnabled()
2555    * @see      $options
2556    */
2557    function getOptionParameters( $option )
2558    {
2559        if( !isset( $this->options[$option] ) )
2560            return false;
2561
2562        return $this->options[$option]['params'];
2563    }
2564
2565   /**
2566    * returns single parameter that has been supplied for an option
2567    *
2568    * @access   public
2569    * @param    string  $option     The option to check
2570    * @return   array
2571    * @see      enableOption()
2572    * @see      disableOption()
2573    * @see      optionEnabled()
2574    * @see      $options
2575    */
2576    function getOptionParameter($option, $parameter)
2577    {
2578        if(!isset($this->options[$option][$parameter])) {
2579            return false;
2580        }
2581
2582        return $this->options[$option]['params'][$parameter];
2583    }
2584
2585   /**
2586    * Set the form to auto validate
2587    *
2588    * If you use this method, patForms will check the _GET and _POST variables
2589    * for the variable you specified. If it is set, patForms assumes, that
2590    * the form has been submitted.
2591    *
2592    * When creating a start tag for the form, the value will be inserted automatically.
2593    *
2594    * @access   public
2595    * @param    string  $submitVar
2596    */
2597    function setAutoValidate( $submitVar )
2598    {
2599        $this->autoValidate =   true;
2600        $this->submitVar    =   $submitVar;
2601    }
2602
2603   /**
2604    * register a new event
2605    *
2606    * After registering an event, you may register one or more
2607    * event handlers for this event an then trigger the event.
2608    *
2609    * This lets you extend the functionality of patForms.
2610    *
2611    * @access   public
2612    * @param    string  event name
2613    * @return   boolean true, if event could be registered
2614    * @see      registerEventHandler()
2615    * @see      triggerEvent()
2616    */
2617    function registerEvent( $name )
2618    {
2619        $event  =   'on' . $name;
2620        if( in_array( $event, $this->_validEvents ) )
2621        {
2622            return patErrorManager::raiseNotice(
2623                                                PATFORMS_NOTICE_EVENT_ALREADY_REGISTERED,
2624                                                'Event "'.$event.'" already has been registered or is built-in event'
2625                                                );
2626        }
2627        array_push( $this->_validEvents, $event );
2628        return true;
2629    }
2630
2631   /**
2632    * Register an event handler
2633    *
2634    * An event handler can be any valid PHP callback. You may pass
2635    * one of the following values:
2636    * - string functionname to call a globally declared function
2637    * - array( string classname, string methodname) to call a static method
2638    * - array( object obj, string methodname) to call a method of an object
2639    *
2640    * When the handler is called, two parameters will be passed:
2641    * - object form  : a patForms object
2642    * - string event : the name of the event has should be handled.
2643    *
2644    * An event handler should always return true. If false is returned,
2645    * the event will be cancelled.
2646    *
2647    * Currently handlers for the following events can be registered:
2648    * - onSubmit
2649    * - onSuccess
2650    * - onError
2651    *
2652    * @access   public
2653    * @param    string  event name
2654    * @param    mixed   event handler
2655    * @return   boolean true, if the handler could be registered
2656    * @see      triggerEvent()
2657    * @see      $_validEvents
2658    */
2659    function registerEventHandler( $event, $handler )
2660    {
2661        if( !in_array( $event, $this->_validEvents ) )
2662        {
2663            return patErrorManager::raiseError(
2664                                                PATFORMS_ERROR_UNKNOWN_EVENT,
2665                                                'Cannot register event handler for unknown event "' . $event .'".'
2666                                                );
2667        }
2668
2669        if( !is_callable( $handler ) )
2670        {
2671            return patErrorManager::raiseError(
2672                                                PATFORMS_ERROR_INVALID_HANDLER,
2673                                                'Event handler is not callable.'
2674                                                );
2675        }
2676
2677        if( !isset( $this->_eventHandler[$event] ) )
2678        {
2679            $this->_eventHandler[$event]    =   array();
2680        }
2681
2682        $this->_eventHandler[$event][]  =   &$handler;
2683        return true;
2684    }
2685
2686   /**
2687    * set event handler object.
2688    *
2689    * An event handler object is used to handle all
2690    * registered events. The object has to provide methods
2691    * for all events it should handle, the names of the methods
2692    * have to be the same as the names of the events.
2693    *
2694    * @access   public
2695    * @param    object  event handler object
2696    * @param    array   method names, used to change the names of the methods
2697    * @return   boolean
2698    */
2699    function registerEventHandlerObject( &$obj, $methods = array() )
2700    {
2701        if( empty( $methods ) )
2702        {
2703            foreach( $this->_validEvents as $event )
2704            {
2705                if( !method_exists( $obj, $event ) )
2706                    continue;
2707
2708                $methods[$event]    =   $event;
2709            }
2710        }
2711
2712        foreach( $methods as $event => $method )
2713        {
2714            if( !isset( $this->_eventHandler[$event] ) )
2715            {
2716                $this->_eventHandler[$event]    =   array();
2717            }
2718
2719            $this->_eventHandler[$event][]  =   array( &$obj, $method );
2720        }
2721
2722        return  true;
2723    }
2724
2725   /**
2726    * Trigger an event
2727    *
2728    * In most cases there's no need to call this event
2729    * from outside the class. The method is declared public
2730    * to allow you to trigger custom events.
2731    *
2732    * @access   public
2733    * @param    string  Event name. The event name must not contain 'on', as this will be
2734    *                   prefixed automatically.
2735    */
2736    function triggerEvent( $event )
2737    {
2738        $handlerName    =   'on' . $event;
2739
2740        if( !isset( $this->_eventHandler[$handlerName] ) || empty( $this->_eventHandler[$handlerName] ) )
2741        {
2742            return true;
2743        }
2744
2745        $cnt    =   count( $this->_eventHandler[$handlerName] );
2746        for( $i = 0; $i < $cnt; $i++ )
2747        {
2748            $result =   call_user_func( $this->_eventHandler[$handlerName][$i], $this, $event );
2749            if( $result == false )
2750            {
2751                return $result;
2752            }
2753        }
2754        return true;
2755    }
2756
2757   /**
2758    * Serializes the entire form to XML, all elements included
2759    *
2760    * @access   public
2761    * @param    string  $namespace  Optional namespace to use for the tags
2762    * @return   string  $xml        The XML representation of the form
2763    * @see      patForms_Element::toXML()
2764    * @todo     needs patForms_Element, maybe switch to PEAR::XML_Util
2765    */
2766    function toXML( $namespace = null )
2767    {
2768        $tagName = 'Form';
2769
2770        // prepend Namespace
2771        if( $namespace != null )
2772        {
2773            $tagName    =   $namespace.':'.$tagName;
2774        }
2775
2776        // get all attributes
2777        $attributes =   $this->getAttributes();
2778
2779        // create valid XML attributes
2780        foreach( $attributes as $key => $value )
2781        {
2782            $attributes[$key]   =   strtr( $value, $this->xmlEntities );
2783        }
2784
2785        $elements = '';
2786        for( $i = 0; $i < $this->elementCounter; $i++ )
2787        {
2788            $elements .= $this->elements[$i]->toXML( $namespace );
2789        }
2790
2791        return  patForms::createTag( $tagName, "full", $attributes, $elements );
2792    }
2793
2794   /**
2795    * Set a static property.
2796    *
2797    * Static properties are stored in an array in a global variable,
2798    * until PHP5 is ready to use.
2799    *
2800    * @static
2801    * @param    string  property name
2802    * @param    mixed   property value
2803    * @see      getStaticProperty()
2804    */
2805    function setStaticProperty( $property, &$value )
2806    {
2807        $GLOBALS["_patForms"][$property]    =   &$value;
2808    }
2809
2810   /**
2811    * Get a static property.
2812    *
2813    * Static properties are stored in an array in a global variable,
2814    * until PHP5 is ready to use.
2815    *
2816    * @static
2817    * @param    string  property name
2818    * @return   mixed   property value
2819    * @see      setStaticProperty()
2820    */
2821    function &getStaticProperty( $property )
2822    {
2823        if( isset( $GLOBALS["_patForms"][$property] ) )
2824        {
2825            return  $GLOBALS["_patForms"][$property];
2826        }
2827        return  patErrorManager::raiseWarning(
2828            PATFORMS_ERROR_NO_STATIC_PROPERTY,
2829            'Static property "'.$property.'" could not be retreived, it does not exist.'
2830        );
2831    }
2832
2833   /**
2834    * Retrieves the form's name
2835    *
2836    * If no name is set, it will use 'patForms' as name.
2837    *
2838    * @access   public
2839    * @return   string  $name   The name of the form.
2840    */
2841    function getName()
2842    {
2843        if( isset( $this->attributes['name'] ) )
2844            return $this->attributes['name'];
2845        return 'patForms';
2846    }
2847
2848   /**
2849    * get the javascript for the form
2850    *
2851    * This is still in alpha state. It will later
2852    * allow client side validation if the element
2853    * provides this feature.
2854    *
2855    * @access   public
2856    * @return   string  javascript needed by the form
2857    * @todo     make this dependent on the format
2858    * @todo     add changeable linebreaks
2859    * @todo     store included javascripts in some kind of static property
2860    */
2861    function getScripts()
2862    {
2863        // let elements and rules recursively register their scripts to the form
2864        foreach ($this->elements as $element) {
2865            $element->registerJavascripts($this);
2866        }
2867
2868        $optionParams = $this->getOptionParameters('scripts');
2869        $scriptFolder = $optionParams['folder'];
2870
2871        if ($optionParams['jsInclude'] === true) {
2872            $script = "\n";
2873            foreach ($this->globalJavascript as $scriptFile) {
2874                $fullPath = $scriptFolder . '/' . $scriptFile;
2875                if (!file_exists($fullPath)) {
2876                    patErrorManager::raiseWarning(PATFORMS_WARNING_SCRIPTFILE_NOT_FOUND, 'Could not script file.', $fullPath);
2877                    continue;
2878                }
2879                $script .= '<script type="text/javascript" language="Javascript1.3" src="'.$fullPath.'"></script>' . "\n";
2880            }
2881            $script .= '<script type="text/javascript" language="Javascript1.3">' . "\n";
2882            $script .= implode ('', $this->instanceJavascript);
2883            $script .= '</script>';
2884        } else {
2885            $script = "\n".'<script type="text/javascript" language="Javascript1.3">' . "\n";
2886            foreach ($this->globalJavascript as $scriptFile) {
2887                $fullPath = $scriptFolder . '/' . $scriptFile;
2888                if (!file_exists($fullPath)) {
2889                    patErrorManager::raiseWarning(PATFORMS_WARNING_SCRIPTFILE_NOT_FOUND, 'Could not script file.', $fullPath);
2890                    continue;
2891                }
2892                $script .= file_get_contents($fullPath);
2893            }
2894            $script .= "\n\n";
2895            $script .= implode ('', $this->instanceJavascript);
2896            $script .= "\n";
2897            $script .= '</script>';
2898        }
2899
2900        return $script;
2901    }
2902
2903   /**
2904    * Registers a global script to the form
2905    *
2906    * This method gets called from elements and rules.
2907    *
2908    * @access   public
2909    * @param    string $type   type of the element or rule
2910    * @param    string $script path to a scriptfile
2911    * @see      patForms::getScripts()
2912    * @see      patForms_Element::registerJavascripts()
2913    * @see      patForms_Rule::registerJavascripts()
2914    */
2915
2916    function registerGlobalJavascript($type, $script)
2917    {
2918        $this->globalJavascript[$type] = $script;
2919    }
2920
2921   /**
2922    * Registers an instance script to the form
2923    *
2924    * This method gets called from elements and rules.
2925    *
2926    * @access   public
2927    * @param    string $script a script (source, not a filename)
2928    * @see      patForms::getScripts()
2929    * @see      patForms_Element::registerJavascripts()
2930    * @see      patForms_Rule::registerJavascripts()
2931    */
2932
2933    function registerInstanceJavascript($script)
2934    {
2935        $this->instanceJavascript[] = $script;
2936    }
2937
2938   /**
2939    * anounce a change in the element to all observers
2940    *
2941    * @access   private
2942    * @param    string      property that changed
2943    * @param    mixed       new value of the property
2944    */
2945    function _announce( $property, $value )
2946    {
2947        $cnt = count( $this->observers );
2948        for( $i = 0; $i < $cnt; $i++ )
2949        {
2950            $this->observers[$i]->notify( $this, $property, $value );
2951        }
2952        return true;
2953    }
2954}
2955?>
Note: See TracBrowser for help on using the repository browser.