source: trunk/patForms.php @ 374

Revision 374, 76.0 KB checked in by schst, 7 years ago (diff)

Removed decorateElement() (should not have been committed)

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