source: trunk/patForms/Element/Enum.php @ 248

Revision 248, 14.4 KB checked in by argh, 10 years ago (diff)

Fixed bugs #147 (support for disabled attribute in values) and #148 (support for merging datasource values with already defined values)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1<?php
2/**
3 * simple dropdown patForms element that builds and validates enumarated fields.
4 *
5 * $Id$
6 *
7 * @access      protected
8 * @package     patForms
9 * @subpackage  Element
10 */
11
12/**
13 * Error: no default value has been set
14 */
15define( 'PATFORMS_ELEMENT_ENUM_NOTICE_NO_DEFAULT_VALUE_AVAILABLE', 'patForms:Element:Enum:01');
16
17/**
18 * Warning: no values to fill the list with
19 */
20define( 'PATFORMS_ELEMENT_ENUM_WARNING_NO_VALUES', 'patForms:Element:Enum:02' );
21 
22/**
23 * simple dropdown patForms element that builds and validates enumarated fields.
24 *
25 * $Id$
26 *
27 * @access      protected
28 * @package     patForms
29 * @subpackage  Element
30 * @author      Sebastian Mordziol <argh@php-tools.net>
31 * @author      Stephan Schmidt <schst@php-tools.net>
32 * @license     LGPL, see license.txt for details
33 */
34class patForms_Element_Enum extends patForms_Element
35{
36   /**
37    * Stores the name of the element - this is used mainly by the patForms
38    * error management and should be set in every element class.
39    * @access   public
40    */
41    var $elementName    =   'Enum';
42
43   /**
44    * javascript that will be displayed only once
45    *
46    * @access   private
47    * @var      array
48    */
49    var $globalJavascript   =   array(
50                                        'html' => 'Html/Element/Enum.js'
51                                    );
52
53   /**
54    * javascript that will be displayed once per instance
55    *
56    * @access   private
57    * @var      array
58    */
59    var $instanceJavascript =   array(
60                                        'html' => "var pfe_[ELEMENT::NAME] = new pFEC_Enum( '[ELEMENT::ID]' );\n"
61                                    );
62
63   /**
64    * the type of the element - set this to the type of element you are creating
65    * if you want to use the {@link patForms_Element::element2html()} method to
66    * create the final HTML tag for your element.
67    *
68    * @access   public
69    * @see      patForms_Element::element2html()
70    */
71    var $elementType    =   array(  "html"  =>  "select",
72                                );
73   
74   /**
75    * set here which attributes you want to include in the element if you want to use
76    * the {@link patForms_Element::convertDefinition2Attributes()} method to automatically
77    * convert the values from your element definition into element attributes.
78    *
79    * @access   protected
80    * @see      patForms_Element::convertDefinition2Attribute()
81    */
82    var $attributeDefinition    =   array( 
83           
84            'id'            =>  array(  'required'      =>  false,
85                                        'format'        =>  'string',
86                                        'outputFormats' =>  array( 'html' ),
87                                    ),
88            'name'          =>  array(  'required'      =>  true,
89                                        'format'        =>  'string',
90                                        'outputFormats' =>  array( 'html' ),
91                                    ),
92            'title'         =>  array(  'required'      =>  false,
93                                        'format'        =>  'string',
94                                        'outputFormats' =>  array( 'html' ),
95                                        'modifiers'     =>  array( 'insertSpecials' => array() ),
96                                    ),
97            'description'   =>  array(  'required'      =>  false,
98                                        'format'        =>  'string',
99                                        'outputFormats' =>  array(),
100                                        'modifiers'     =>  array( 'insertSpecials' => array() ),
101                                    ),
102            'default'       =>  array(  'required'      =>  false,
103                                        'format'        =>  'string',
104                                        'outputFormats' =>  array(),
105                                    ),
106            'label'         =>  array(  'required'      =>  false,
107                                        'format'        =>  'string',
108                                        'outputFormats' =>  array(),
109                                    ),
110            'display'       =>  array(  'required'      =>  false,
111                                        'format'        =>  'string',
112                                        'default'       =>  'yes',
113                                        'outputFormats' =>  array(),
114                                    ),
115            'edit'          =>  array(  'required'      =>  false,
116                                        'format'        =>  'string',
117                                        'default'       =>  'yes',
118                                        'outputFormats' =>  array(),
119                                    ),
120            'required'      =>  array(  'required'      =>  false,
121                                        'format'        =>  'string',
122                                        'default'       =>  'yes',
123                                        'outputFormats' =>  array(),
124                                    ),
125            'value'         =>  array(  'required'      =>  false,
126                                        'format'        =>  'string',
127                                        'outputFormats' =>  array(),
128                                    ),
129            'style'         =>  array(  'required'      =>  false,
130                                        'outputFormats' =>  array( 'html' ),
131                                        'format'        =>  'string',
132                                    ),
133            'class'         =>  array(  'required'      =>  false,
134                                        'outputFormats' =>  array( 'html' ),
135                                        'format'        =>  'string',
136                                    ),
137            'onchange'      =>  array(  'required'      =>  false,
138                                        'format'        =>  'string',
139                                        'outputFormats' =>  array( 'html' ),
140                                        'modifiers'     =>  array( 'insertSpecials' => array() ),
141                                    ),
142            'onclick'       =>  array(  'required'      =>  false,
143                                        'format'        =>  'string',
144                                        'outputFormats' =>  array( 'html' ),
145                                        'modifiers'     =>  array( 'insertSpecials' => array() ),
146                                    ),
147            'onfocus'       =>  array(  'required'      =>  false,
148                                        'format'        =>  'string',
149                                        'outputFormats' =>  array( 'html' ),
150                                        'modifiers'     =>  array( 'insertSpecials' => array() ),
151                                    ),
152            'onmouseover'   =>  array(  'required'      =>  false,
153                                        'format'        =>  'string',
154                                        'outputFormats' =>  array( 'html' ),
155                                        'modifiers'     =>  array( 'insertSpecials' => array() ),
156                                    ),
157            'onmouseout'    =>  array(  'required'      =>  false,
158                                        'format'        =>  'string',
159                                        'outputFormats' =>  array( 'html' ),
160                                        'modifiers'     =>  array( 'insertSpecials' => array() ),
161                                    ),
162            'onblur'        =>  array(  'required'      =>  false,
163                                        'format'        =>  'string',
164                                        'outputFormats' =>  array( 'html' ),
165                                        'modifiers'     =>  array( 'insertSpecials' => array() ),
166                                    ),
167            'accesskey'     =>  array(  'required'      =>  false,
168                                        'format'        =>  'string',
169                                        'outputFormats' =>  array( 'html' ),
170                                    ),
171            'position'      =>  array(  'required'      =>  false,
172                                        'format'        =>  'int',
173                                        'outputFormats' =>  array(),
174                                    ),
175            'tabindex'      =>  array(  'required'      =>  false,
176                                        'format'        =>  'int',
177                                        'outputFormats' =>  array( 'html' ),
178                                    ),
179            'values'        =>  array(  'required'      =>  false,
180                                        'format'        =>  'values',
181                                        'outputFormats' =>  array(),
182                                    ),
183            'disabled'      =>  array(  'required'      =>  false,
184                                        'format'        =>  'string',
185                                        'default'       =>  'no',
186                                        'outputFormats' =>  array( 'html' ),
187                                    ),
188            'size'      =>  array(      'required'      =>  false,
189                                        'format'        =>  'int',
190                                        'default'       =>  '1',
191                                        'outputFormats' =>  array( 'html' ),
192                                    ),
193            'datasource'    =>  array(  'required'      =>  false,
194                                        'format'        =>  'datasource',
195                                        'outputFormats' =>  array(),
196                                    ),
197            'maxsize'       =>  array(  'required'      =>  false,
198                                        'format'        =>  'string',
199                                        'default'       =>  '5',
200                                        'outputFormats' =>  array(),
201                                    ),
202        );
203
204    /**
205     *  define error codes an messages for each form element
206     *
207     *  @access private
208     *  @var    array   $validatorErrorCodes
209     */
210    var $validatorErrorCodes  =   array(
211        "C" =>  array(
212            1   =>  "This field is required, please complete it.",
213            2   =>  "The value given for the element does not match any of the possible values.",
214        ),
215        "de" => array(
216            1   =>  "Pflichtfeld. Bitte vervollständigen Sie Ihre Angabe.",
217            2   =>  "Der angegebene Wert stimmt mit keinem der möglichen Werte überein.",
218        ),
219        "fr" => array(
220            1   =>  "Ce champ est obligatoire.",
221            2   =>  "La valeur de ce champ ne correspond à aucune des valeurs admises.",
222        )
223    );
224   
225   /**
226    * Stores the value that will be displayed in readonly mode
227    * when no entry has been selected, in the available locales.
228    *
229    * @access   private
230    * @var      array
231    */
232    var $defaultReadonlyValue  =   array(
233        "C" =>  "No selection",
234        "de" => "Keine Angabe",
235        "fr" => "Pas de sélection.",
236    );
237
238   /**
239    * stores the data source object for the element - use the {@link setDataSource()} method to
240    * define a data source for this element.
241    * @access   protected
242    * @see      setDataSource()
243    */
244    var $dataSource =   false;
245   
246   /**
247    * sets the data source for this element. If you set a data source object, the element will
248    * ignore the 'values' attribute and request the values from the data source object. The
249    * data source object only needs to implement the getValues() method.
250    *
251    * @access   public
252    * @param    object  &$dataSource    The data source to use.
253    * @see      dataSource
254    */
255    function setDataSource( &$dataSource )
256    {
257        $this->attributes["datasource"] =&  $dataSource;
258    }
259   
260   /**
261    * retrieves the values to fill the list with. If a data source object has been set,
262    * tries to retrieve them from there, otherwise takes them from the 'values' attribute.
263    *
264    * @access   public
265    * @return   mixed   $values Array with values, or false if failed.
266    * @see      setDataSource()
267    */
268    function getValues()
269    {
270        $values = array();
271       
272        if( isset( $this->attributes["datasource"] ) )
273        {
274            if( is_object( $this->attributes["datasource"] ) )
275            {
276                $values =   $this->attributes["datasource"]->getValues();
277            }
278            else
279            {
280                /**
281                 * if the datasource is no object, it could
282                 * be a callback
283                 *
284                 * The name of the element will be passed to the callback
285                 */
286                if( is_callable( $this->attributes["datasource"], false ) )
287                {
288                    $values = call_user_func( $this->attributes["datasource"], $this->getName() );
289                }
290            }
291        }
292         
293        $values = array_merge( $this->attributes["values"], $values );
294        if( empty( $values ) ) 
295        {
296            return patErrorManager::raiseWarning(
297                PATFORMS_ELEMENT_ENUM_WARNING_NO_VALUES,
298                'No values set to create an Enum field',
299                'The Enum element ['.$this->attributes['name'].'] has no values to create a list from'
300            );
301        }
302
303        return $values;
304    }
305   
306   /**
307    * element creation method for the 'HTML' format in the 'default' form mode.
308    *
309    * @access   public
310    * @param    mixed   value of the element
311    * @return   mixed   $element    The element, or false if failed.
312    */
313    function serializeHtmlDefault( $value )
314    {
315        // handle display attribute
316        if( $this->attributes['display'] == 'no' )
317        {
318            return $this->createDisplaylessTag( $value );
319        }
320
321        if( $this->attributes['edit'] == 'no' )
322        {
323            return $this->serializeHtmlReadonly( $value );
324        }
325       
326        $values = $this->getValues();
327        if( patErrorManager::isError( $values ) )
328        {
329            return $values;
330        }
331       
332        // automatic size adjustment depending on element value list
333        if( $this->attributes['size'] == 'auto' )
334        {
335            $maxsize    =   count( $values );
336            if( $this->attributes['maxsize'] != 'none' && $maxsize > $this->attributes['maxsize'] )
337            {
338                $maxsize    =   $this->attributes['maxsize'];
339            }
340           
341            $this->attributes['size']   =   $maxsize;
342        }
343
344        $element    =   $this->createTag( "select", "opening", $this->getAttributesFor( $this->getFormat() ) );
345       
346        foreach( $values as $line => $optionDef )
347        {
348            $attribs    =   array(  "value" =>  $optionDef["value"] );
349           
350            if ( isset( $optionDef['disabled'] ) && $optionDef['disabled'] == 'yes' ) 
351            {
352                $attribs['disabled'] = 'disabled';
353            }
354           
355            if( !empty( $optionDef["value"] ) && $optionDef["value"] == $value )
356            {
357                $attribs["selected"]    =   "selected";
358            }
359           
360            $element    .=  $this->createTag( "option", "full", $attribs, $optionDef["label"] );
361        }
362       
363        $element    .=  $this->createTag( "select", "closing" );
364       
365        // and return to sender...
366        return $element;
367    }
368   
369   /**
370    * element creation method for the 'HTML' format in the 'readonly' form mode.
371    *
372    * @access   public
373    * @param    mixed   value of the element
374    * @return   string  $value  The element's value
375    */
376    function serializeHtmlReadonly( $value )
377    {
378        $element    =   null;
379        $values     =   $this->getValues();
380       
381        if( patErrorManager::isError( $values ) )
382        {
383            return $values;
384        }
385       
386        $tag = $this->createDisplaylessTag( $value );
387       
388        if( $this->attributes['display'] == 'no' )
389        {
390            return $tag;
391        }
392       
393        // empty value -> no entry selected - display the readonly
394        // default value instead.
395        if( $value === '' )
396        {
397            return $this->getReadonlyDefaultValue().$tag;
398        }
399       
400        foreach( $values as $line => $optionDef )
401        {
402            if( $optionDef["value"] == $value )
403            {
404                $element    =   $optionDef["label"];
405                break;
406            }
407        }
408       
409        if( empty( $element ) )
410        {
411            $element = $this->getReadonlyDefaultValue();
412        }
413       
414        return $element.$tag;
415    }
416   
417   /**
418    * Retrieves the default value to display in the element's readonly mode if the
419    * user has not selected any entry, according to the selected locale
420    *
421    * @access   public
422    * @return   string  $defaultValue   The default readonly value in the needed locale
423    */
424    function getReadonlyDefaultValue()
425    {   
426        $lang   =   $this->locale;
427   
428        if( !isset( $this->defaultReadonlyValue[$lang] ) )
429        {
430            patErrorManager::raiseNotice(
431                PATFORMS_ELEMENT_ENUM_NOTICE_NO_DEFAULT_VALUE_AVAILABLE,
432                'There is no default readonly value available for the locale "'.$lang.'", using default locale "C" instead.'
433            );
434           
435            return $this->defaultReadonlyValue['C'];
436        }
437       
438        return $this->defaultReadonlyValue[$lang];
439    }
440
441   /**
442    * validates the element.
443    *
444    * @access   public
445    * @param    mixed   value of the element
446    * @return   bool    $isValid    True if element could be validated, false otherwise.
447    */
448    function validateElement( $value )
449    {
450        $values =   $this->getValues();
451       
452        if( $values === false )
453        {
454            $this->valid    =   false;
455            return false;
456        }
457       
458        // required & empty
459        if( isset( $this->attributes["required"] ) && $this->attributes["required"] == "yes" && strlen( $value ) == 0 )
460        {
461            $this->addValidationError( 1 );
462            return false;
463        }
464
465        // is value in values list?
466        $found = false;
467        foreach( $values as $line => $optionDef )
468        {
469            if( $optionDef["value"] == $value )
470            {
471                $found  =   true;
472            }
473        }
474       
475        if( !$found )
476        {
477            $this->addValidationError( 2 );
478            return false;
479        }
480       
481        return true;
482    }
483
484   /**
485    * create XML representation of the element
486    *
487    * This can be used when you need to store the structure
488    * of your form in flat files or create form templates that can
489    * be read by patForms_Parser at a later point.
490    *
491    * @access   public
492    * @param    string      namespace
493    * @uses     getElementName()
494    * @see      patForms_Parser
495    */
496    function toXML( $namespace = null )
497    {
498        $tagName    =   $this->getElementName();
499
500        // prepend Namespace
501        if( $namespace != null )
502        {
503            $tagName    =   "$namespace:$tagName";
504            $optName    =   "$namespace:Option";
505        }
506        else
507            $optName    =   "Option";
508       
509        // get all attributes
510        $attributes =   $this->getAttributes();
511        $options = $attributes['values'];
512        unset( $attributes['values'] );
513       
514        // create valid XML attributes
515        foreach( $attributes as $key => $value )
516        {
517            $attributes[$key]   =   strtr( $value, $this->xmlEntities );
518        }
519
520        $tag = $this->createTag( $tagName, "opening", $attributes );
521        foreach( $options as $opt)
522        {
523            $tag .= $this->createTag( $optName, "empty", $opt );
524        }
525        $tag .= $this->createTag( $tagName, "closing" );
526       
527        return $tag;
528    }
529
530   /**
531    * convert a value to a human readable version
532    *
533    * This method should be redefined in the actual element classes.
534    *
535    * @access   public
536    * @param    mixed       value to convert
537    * @return   string      converted value
538    */
539    function convertValueToHumanReadable($value)
540    {
541        $values = $this->getValues();
542        foreach ($values as $optionDef) {
543            if ($optionDef['value'] == $value) {
544                return $optionDef['label'];
545            }
546        }
547        return $value;
548    }
549}
550?>
Note: See TracBrowser for help on using the repository browser.