source: trunk/patForms/Element/Set.php @ 267

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

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

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