root/trunk/patForms/Element/Combobox.php

Revision 267, 13.8 kB (checked in by schst, 3 years ago)

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

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