Dotclear

source: inc/admin/class.dc.form.php @ 1491:2b9253624dbe

Revision 1491:2b9253624dbe, 31.1 KB checked in by Dsls, 11 years ago (diff)

Implemented last merges from default, post.php is now more flexible.
added twig function : form_field_attr

Line 
1<?php
2# -- BEGIN LICENSE BLOCK ---------------------------------------
3#
4# This file is part of Dotclear 2.
5#
6# Copyright (c) 2003-2011 Olivier Meunier & Association Dotclear
7# Licensed under the GPL version 2.0 license.
8# See LICENSE file or
9# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10#
11# -- END LICENSE BLOCK -----------------------------------------
12if (!defined('DC_RC_PATH')) { return; }
13
14/**
15* dcFormNode Twig Node for Form handling
16*
17* @uses     Twig_Node
18*
19*/
20class dcFormNode extends Twig_Node
21{
22     public function __construct($name,Twig_NodeInterface $body,$attr,$lineno,$tag=null)
23     {
24          parent::__construct(array('body' => $body),array('name' => $name, 'attr' => $attr),$lineno,$tag);
25     }
26     
27     /**
28     * Compiles the node to PHP.
29     *
30     * @param Twig_Compiler A Twig_Compiler instance
31     */
32     public function compile(Twig_Compiler $compiler)
33     {
34          $compiler
35               ->addDebugInfo($this);
36          $compiler
37               ->write("\$context['dc_form']->beginForm(")
38               ->subcompile($this->getAttribute('name'));
39          if ($this->getAttribute('attr') !== null) {
40               $compiler
41                    ->write(',')
42                    ->subcompile($this->getAttribute('attr'));
43          }
44          $compiler
45               ->write(");\n");
46          $compiler
47               ->subcompile($this->getNode('body'))
48               ->write("\$context['dc_form']->renderHiddenWidgets();\n")
49               ->write("\$context['dc_form']->endForm();\n")
50          ;
51     }
52}
53
54/**
55 * Template form token parser
56 */
57class dcFormTokenParser extends Twig_TokenParser
58{
59    /**
60     * parse - parses form tag
61     * General syntax is :
62     *  {% form 'formname' %}
63     *  ... {{ form_field (...)}}
64     *  {% endform %}
65     * Specific attributes can be passed to the form, enabling to set
66     * attributes to the form template :
67     * {% form 'formname' with {'id':'myform'} %}
68     * @param mixed \Twig_Token Description.
69     *
70     * @access public
71     *
72     * @return mixed Value.
73     */
74     public function parse(Twig_Token $token)
75     {
76          $lineno = $token->getLine();
77          $stream = $this->parser->getStream();
78          $name = $this->parser->getExpressionParser()->parseExpression();
79          $attr = null;
80          /* parse optional context */
81          if ($stream->test(Twig_Token::NAME_TYPE, 'with')) {
82               $stream->next();
83               $attr = $this->parser->getExpressionParser()->parseExpression();
84          }
85          $stream->expect(Twig_Token::BLOCK_END_TYPE);
86          $body = $this->parser->subparse(array($this,'decideBlockEnd'),true);
87          $stream->expect(Twig_Token::BLOCK_END_TYPE);
88
89          return new dcFormNode($name,$body,$attr,$token->getLine(),$this->getTag());
90     }
91
92     public function decideBlockEnd(Twig_Token $token)
93     {
94          return $token->test('endform');
95     }
96
97     public function getTag()
98     {
99          return 'form';
100     }
101}
102
103/**
104 * Template form extension
105 */
106class dcFormExtension extends Twig_Extension
107{
108     protected $template;
109     protected $tpl;
110     protected $core;
111     protected $twig;
112     protected $forms;
113     protected $currentForm;
114     protected $blocks;
115
116     public function __construct($core)
117     {
118          $this->core = $core;
119          $this->tpl = array('@forms/form_layout.html.twig');
120          $this->forms = array();
121          $this->blocks = array();
122          $this->currentForm = null;
123     }
124
125     public function initRuntime(Twig_Environment $environment)
126     {
127          $this->twig = $environment;
128          $this->twig->getLoader()->addPath(dirname(__FILE__).'/default-templates/forms','forms');
129          foreach ($this->tpl as $tpl) {
130               $this->template = $this->twig->loadTemplate($tpl);
131               $this->blocks = array_merge($this->blocks,$this->template->getBlocks());
132          }
133     }
134
135     public function addTemplate($tpl) {
136          $this->tpl[]=$tpl;
137          if (isset($this->twig)) {
138               $this->template = $this->twig->loadTemplate($tpl);
139               $this->blocks = array_merge($this->blocks,$this->template->getBlocks());
140          }
141     }
142
143     public function getGlobals()
144     {
145          return array('dc_form' => $this);
146     }
147
148     public function getFunctions()
149     {
150          return array(
151               new Twig_SimpleFunction(
152                    'widget',
153                    array($this,'renderWidget'),
154                    array('is_safe' => array('html'))
155               ),
156               new Twig_SimpleFunction(
157                    'haswidget',
158                    array($this,'hasWidget'),
159                    array('is_safe' => array('html'))
160               ),
161               new Twig_SimpleFunction(
162                    'form_field',
163                    array($this,'renderField'),
164                    array('is_safe' => array('html'))
165               ),
166               new Twig_SimpleFunction(
167                    'form_field_attr',
168                    array($this,'getFieldAttributes'),
169                    array('is_safe' => array('html'))
170               ),
171               new Twig_SimpleFunction(
172                    '_form_is_choice_group',
173                    array($this,'isChoiceGroup'),
174                    array('is_safe' => array('html'))
175               ),
176               new Twig_SimpleFunction(
177                    '_form_is_choice_selected',
178                    array($this,'isChoiceSelected'),
179                    array('is_safe' => array('html'))
180               )
181          );
182     }
183
184    /**
185     * isChoiceGroup - binding for twig function "_form_is_choice_group"
186     *                        returns whether a choice is a group or not
187     * @param mixed $choice the choice.
188     *
189     * @access public
190     *
191     * @return boolean true is choice is a group (optgroup).
192     */
193     public function isChoiceGroup($choice)
194     {
195          return is_array($choice);
196     }
197
198    /**
199     * isChoiceSelected - binding for twig function "_form_is_choice_selected"
200     *                        returns whether current choice matches a value or not
201     * @param mixed $choice the choixe.
202     * @param mixed $value  the value to check matching.
203     *
204     * @access public
205     *
206     * @return boolean if choice is matching the value.
207     */
208     public function isChoiceSelected($choice,$value)
209     {
210          return $choice == $value;
211     }
212
213    /**
214     * getTokenParsers returns token parsers
215     *
216     * @access public
217     *
218     * @return mixed Value.
219     */
220     public function getTokenParsers()
221     {
222          return array(new dcFormTokenParser());
223     }
224
225    /**
226     * hasWidget - binding for twig "haswidget" function
227     *    returns whether a widget is defined or not
228     *
229     * @param mixed $name the widget name.
230     *
231     * @access public
232     *
233     * @return boolean true if the widget exists.
234     */
235     public function hasWidget($name) {
236          return isset($this->blocks[$name]);
237     }
238
239    /**
240     * renderWidget - binding for 'widget' twig function
241     * behaves exactly like "block" function, except that a context
242     * can be passed to the function
243     *
244     * @param mixed $name the widget (block) name to render.
245     * @param mixed $attr Description the context for this block.
246     *
247     *
248     * @return mixed Value.
249     */
250     public function renderWidget($name,$attr) {
251          if (!isset($this->blocks[$name]))
252               return '';
253          echo $this->template->renderBlock(
254               $name,
255               $attr,
256               $this->blocks
257          );
258     }
259
260    /**
261     * getCurrentForm - returns current form if called within a {% form %} tag
262     *
263     * @access public
264     *
265     * @return string the current form.
266     */
267     public function getCurrentForm() {
268          return $this->currentForm;
269     }
270
271    /**
272     * renderField - binding for 'form_field' twig function; renders a field
273     *
274     * @param mixed $name       field name as defined on php side.
275     * @param array $attributes html attributes for field (ex : class, ...).
276     * @param array $extra      extra attributes that may be template specific.
277     *
278     * @access public
279     *
280     * @return mixed Value.
281     */
282     public function renderField($name,$attributes=array(),$extra=array())
283     {
284          $field = $this->currentForm->getField($name);
285          if ($field) {
286               $attr = $field->getAttributes($attributes);
287               if (isset($attr['attr'])) {
288                    $attr['attr'] = array_merge($attr['attr'],$attributes);
289               } else {
290                    $attr['attr'] = $attributes;
291               }
292               $this->renderWidget(
293               $field->getWidgetBlock(),
294                    array_merge(
295                         $attr,
296                         $extra
297                    )
298               );
299          }
300     }
301
302    /**
303     * getFieldAttributes - binding for 'form_field_attr' twig function; returns all field attributes
304     *
305     * @param mixed $name       field name as defined on php side.
306     * @param mixed $name       the attribute name, null to grab all attributes as an array
307     *
308     * @access public
309     *
310     * @return array the field attributes
311     */   
312     public function getFieldAttributes($name,$attr=null)
313     {
314          $field = $this->currentForm->getField($name);
315          if ($field) {
316               $a = $field->getAttributes();
317               if ($attr !== null) {
318                    if (isset($a[$attr])) {
319                         return $a[$attr];
320                    } else {
321                         return null;
322                    }
323               } else {
324                    return $field->getAttributes();
325               }
326          } else {
327               return array();
328          }
329     }
330
331    /**
332     * renderHiddenWidgets -- renders all form hidden fields
333     *
334     * @access public
335     *
336     * @return mixed Value.
337     */
338     public function renderHiddenWidgets()
339     {
340          foreach ($this->currentForm->getHiddenFields() as $h) {
341               $this->renderField($h->getName());
342          }
343     }
344
345     public function getName()
346     {
347          return 'dc_form';
348     }
349
350    /**
351     * addForm -- registers a new form
352     *
353     * @param mixed \dcForm Description.
354     *
355     * @access public
356     *
357     * @return mixed Value.
358     */
359     public function addForm(dcForm $form)
360     {
361          $this->forms[$form->getName()] = $form;
362     }
363
364    /**
365     * beginForm -- displays form beginning
366     *
367     * @param mixed $name form name.
368     * @param array $attr extra attributes.
369     *
370     * @access public
371     *
372     * @return mixed Value.
373     */
374     public function beginForm($name,$attr=array())
375     {
376          if (isset($this->forms[$name])) {
377               $this->currentForm = $this->forms[$name];
378               $this->currentForm->begin($attr);
379          }
380          else {
381               throw new Twig_Error_Runtime(sprintf(
382                    'Form "%s" does not exist',
383                    $name
384               ));
385          }
386     }
387
388    /**
389     * endForm -- displays form ending
390     *
391     * @access public
392     *
393     * @return mixed Value.
394     */
395     public function endForm()
396     {
397          $this->currentForm->end();
398          $this->currentForm = null;
399     }
400}
401
402/**
403 * Template form exception
404 */
405class InvalidFieldException extends Exception {
406}
407
408/**
409* dcForm - Template form
410*
411*/
412class dcForm
413{
414     /** @var string form id */
415     protected $id;
416     /** @var string form name */
417     protected $name;
418     /** @var dcCore dcCore instance */
419     protected $core;
420     /** @var string form action */
421     protected $action;
422     /** @var array(dcField) list of form fields */
423     protected $fields;
424     /** @var string form method (GET/POST) */
425     protected $method;
426     /** @var array(dcField) list of submit fields */
427     protected $submitfields;
428     /** @var array(dcField) list of hidden fields */
429     protected $hiddenfields;
430     /** @var array(dcField) list of form errors */
431     protected $errors;
432     /** @var array() list of form properties */
433     protected $properties;
434     
435     
436    /**
437     * Class constructor
438     *
439     * @param mixed  $core   dotclear core
440     * @param mixed  $name   form name - can be an array (name,id)
441     * @param mixed  $action form action
442     * @param string $method form method ('GET' or 'POST')
443     *
444     * @access public
445     *
446     * @return mixed Value.
447     */
448     public function __construct($core,$name,$action,$method='POST')
449     {
450          $this->core = $core;
451          $this->setNID($name);
452          $this->method = $method;
453          $this->action = $action;
454          $this->fields = array();
455          $this->core->tpl->getExtension('dc_form')->addForm($this);
456          $this->submitfields = array();
457          $this->hiddenfields = array();
458          $this->errors = array();
459          $this->properties = array();
460          if ($method == 'POST') {
461               $this->addNonce();
462          }
463     }
464
465     
466    /**
467     * setProperty - sets form property
468     *
469     * @param string $name the property name.
470     * @param mixed $value the property value.
471     *
472     * @access public
473     */
474     public function setProperty($prop,$value) {
475          $this->properties[$prop]=$value;
476     }
477     
478    /**
479     * getProperty - gets form property
480     *
481     * @param string $name the property name.
482      *
483     * @return mixed the property value, null if no property found.
484     * @access public
485     */   
486     public function getProperty($prop) {
487          if (isset($this->properties[$prop])) {
488               return $this->properties[$prop];
489          } else {
490               return null;
491          }
492     }
493    /**
494     * addTemplate - Adds a template file to enrich form fields
495     *
496     * @param string $t the template file.
497     *
498     * @access public
499     */
500     public function addTemplate($t) {
501          $this->core->tpl->getExtension('dc_form')->addTemplate($t);
502     }
503
504    /**
505     * addNonce -- adds dc nonce to form fields
506     *
507     * @access protected
508     *
509     * @return nothing
510     */
511     protected function addNonce()
512     {
513          $this->addField(
514               new dcFieldHidden(array('xd_check'),
515               $this->core->getNonce())
516          );
517     }
518
519    /**
520     * Defines Name & ID from field
521     *
522     * @param mixed $nid either an array (name, id) or a string
523     *                   (name only, id will be set to null).
524     *
525     * @access protected
526     *
527     * @return nothing.
528     */
529     protected function setNID($nid)
530     {
531          if (is_array($nid)) {
532               $this->name = $nid[0];
533               $this->id = !empty($nid[1]) ? $nid[1] : null;
534          }
535          else {
536               $this->id = null;
537               $this->name = $nid;
538          }
539     }
540
541    /**
542     * getContext - returns form context (to fill-in twig context for instance),
543     *                   if any
544     *
545     * @access public
546     *
547     * @return array the form context.
548     */
549     public function getContext() {
550          return array();
551     }
552
553
554    /**
555     * Returns form name
556     *
557     * @access public
558     *
559     * @return mixed Value.
560     */
561     public function getName()
562     {
563          return $this->name;
564     }
565
566    /**
567     * getErrors - returns form errors
568     *
569     * @access public
570     *
571     * @return array the list of errors.
572     */
573     public function getErrors()
574     {
575          return $this->errors;
576     }
577
578    /**
579     * addField - adds a new field to form
580     *
581     * @param mixed \dcField the field to add.
582     *
583     * @access public
584     *
585     * @return dcForm the form instance (therefore addField can be chained)
586     */
587     public function addField(dcField $f)
588     {
589          if ($f instanceof dcFieldAction) {
590               $this->submitfields[$f->getName()] = $f;
591          }
592          if ($f instanceof dcFieldHidden) {
593               $this->hiddenfields[$f->getName()] = $f;
594          }
595          $this->fields[$f->getName()] = $f;
596
597          return $this;
598     }
599
600    /**
601     * getField - retrieves a field form form
602     *
603     * @param string the field name
604     *
605     * @access public
606     *
607     * @return dcForm the requested field
608     */   
609      public function getField($name) {
610          if (isset($this->fields[$name])) {
611               return $this->fields[$name];
612          } else {
613               return null;
614          }
615     }
616     
617    /**
618     * removeField - removes a field
619     *
620     * @param mixed \dcField the field to remove.
621     *
622     * @access public
623     *
624     * @return dcForm the form instance (therefore addField can be chained)
625     */
626     public function removeField(dcField $f) {
627          $n = $f->getName();
628          if (isset($this->fields[$n])){
629               unset($this->fields[$n]);
630          }
631          return $this;
632     }
633
634
635    /**
636     * renameField - renames a field
637     *
638     * @param mixed $field   the field to rename.
639     * @param mixed $newname new field name
640     *
641     * @access public
642     *
643     *
644     * @return dcForm the form instance (therefore addField can be chained)
645     */
646     public function renameField($field,$newname) {
647          $oldname = $field->getName();
648          if (isset($this->fields[$oldname])) {
649               unset($this->fields[$oldname]);
650               $field->setName($newname);
651               $this->fields[$newname] = $field;
652          }
653          return $this;
654     }
655
656    /**
657     * begin - begins a form. Should be not be called directly, it is handled
658     *              by the Twig Form extension.
659     *
660     * @param array $attr form extra attributes, if any.
661     *
662     * @access public
663     *
664     * @return mixed Value.
665     */
666     public function begin($attr=array())
667     {
668          $attr['method'] = $this->method;
669          $attr['action'] = $this->action;
670          if (!empty($this->id)) {
671               $attr['id'] = $this->id;
672          }
673          $this->core->tpl->getExtension('dc_form')->renderWidget(
674               'beginform',
675               $attr);
676     }
677
678    /**
679     * end - ends a form. Should be not be called directly, it is handled
680     *              by the Twig Form extension.
681     *
682     * @access public
683     *
684     * @return mixed Value.
685     */
686     public function end($attr=array())
687     {
688          $this->core->tpl->getExtension('dc_form')->renderWidget(
689               'endform',$attr);
690     }
691
692    /**
693     * __isset - magic method isset, checks whether a field exists
694     *                   example : if (isset($form->field1))
695     *
696     * @param mixed $name field name to check.
697     *
698     * @access public
699     *
700     * @return boolean true if the field exists.
701     */
702     public function __isset($name)
703     {
704          return isset($this->fields[$name]);
705     }
706
707    /**
708     * __get -- magic method, retrieves a field from a form
709     *              example : $f = $form->field1
710     *
711     * @param mixed $name Description.
712     *
713     * @access public
714     *
715     * @return mixed Value.
716     */
717     public function __get($name)
718     {
719          if (isset($this->fields[$name])) {
720               $f = $this->fields[$name];
721               if ($f->isMultiple()) {
722                    return $f->getValues();
723               } else {
724                    return $f->getValue();
725               }
726          } else {
727               return $this->getProperty($name);
728          }
729     }
730
731    /**
732     * __set -- magic method, sets a value for a given form field
733     *              example : $form->field1 = 'my value';
734     *
735     * @param mixed $name  the field name.
736     * @param mixed $value the field value.
737     *
738     * @access public
739     */
740     public function __set($name,$value)
741     {
742          if (isset($this->fields[$name])) {
743               $f = $this->fields[$name];
744               if ($f->isMultiple()) {
745                    $this->fields[$name]->setValues($value);
746               } else {
747                    $this->fields[$name]->setValue($value);
748               }
749          } else {
750               $this->setProperty($name,$value);
751          }
752     }
753
754    /**
755     * setupFields - initializes form & fields from $_GET or $_POST
756     *
757     * @access protected
758     */
759     protected function setupFields() {
760          $from = $this->method == 'POST' ? $_POST : $_GET;
761          if (!empty($from)) {
762               foreach ($this->fields as $f) {
763                    $f->setup($from);
764               }
765          }
766     }
767
768    /**
769     * handleActions - handle appropriate actions, according to submitted fields
770     *
771     * @param mixed $submitted the fields that have been submitted.
772     *
773     * @access protected
774     */
775     protected function handleActions($submitted) {
776          $hasActions = false;
777          foreach ($submitted as $f) {
778               $action = $f->getAction();
779               if ($action != NULL) {
780                    $hasActions = true;
781                    $ret = call_user_func($action,$this);
782               }
783          }
784     }
785
786    /**
787     * getSubmittedFields - retrieves fields that have been submitted, if any
788     *
789     * @access protected
790     *
791     * @return array the list of submitted fields.
792     */
793     protected function getSubmittedFields() {
794          $s = array();
795          foreach ($this->submitfields as $f) {
796               if ($f->isDefined()) {
797                    $s[$f->getName()] = $f;
798               }
799          }
800          return $s;
801     }
802
803    /**
804     * isSubmitted - returns whether form has been submitted or not
805     *
806     * @access public
807     *
808     * @return boolean true if the form has been submitted.
809     */   
810     public function isSubmitted() {
811          foreach ($this->submitfields as $f) {
812               if ($f->isDefined()) {
813                    return true;
814               }
815          }
816          return false;       
817     }
818     
819    /**
820     * setup - sets up the form, given the parameters given to the page
821     *              should be called after fields have been defined.
822     *
823     * @access public
824     *
825     * @return mixed Value.
826     */
827     public function setup()
828     {
829          $this->setupFields();
830          $submitted = $this->getSubmittedFields();
831          $this->handleActions($submitted);
832     }
833
834    /**
835     * check - checks if the form is valid, errors are filled in, in case of
836     *              incorrect fields
837     *
838     * @access public
839     */
840     public function check(dcAdminContext $ctx)
841     {
842          $valid = true;
843          foreach ($this->fields as $f) {
844               try {
845                    $f->check();
846               }
847               catch (InvalidFieldException $e) {
848                    $valid = false;
849                    $ctx->addError($e->getMessage());
850               }
851          }
852          if (!$valid) {
853               throw new InvalidFieldException ("Some fields are missing");
854          }
855     }
856
857     public function getFieldIDs() {
858          return array_keys($this->fields);
859     }
860     
861    /**
862     * getHiddenFields - returns the list of hidden fields
863     *
864     * @access public
865     *
866     * @return array the list of hidden fields.
867     */
868     public function getHiddenFields()
869     {
870          return $this->hiddenfields;
871     }
872}
873
874/**
875 * Template form field
876 */
877abstract class dcField implements Countable
878{
879     /** @var string field options */
880     protected $options;
881     /** @var string field name */
882     protected $name;
883     /** @var string field values */
884     protected $values;
885     /** @var string field id */
886     protected $id;
887     /** @var boolean true if field can contain multiple values */
888     protected $multiple;
889     /** @var boolean true if the field has been defined */
890     protected $defined;
891
892    /**
893     * __construct - constructor
894     *
895     * @param string $name   Name or array(name,id) for field.
896     * @param array $values  field values.
897     * @param array $options options
898     *
899     * Currently globally available options are :
900     *  * multiple : true/false. Enable multiple values for field
901     * @access public
902     *
903     * @return mixed Value.
904     */
905     public function __construct($name,$values,$options=array())
906     {
907          $this->setNID($name);
908          $this->options = new ArrayObject($options);
909          if ($values === NULL){
910               $values = array();
911          }
912          $this->setValues($values);
913          $this->defined = false;
914          $this->multiple = (isset($options['multiple']) && $options['multiple']);
915
916     }
917
918    /**
919     * defines whether a field is multiple or not
920     *
921     * @param boolean true if the field is multiple
922      *
923     * @access public
924     */
925     public function setMultiple($m=true) {
926          $this->multiple = $m;
927     }
928     
929    /**
930     * Returns whether can have multiple values or not
931     *
932     * @return boolean true if the field has multiple values
933      *
934     * @access public
935     */
936     public function isMultiple($m=true) {
937          return $this->multiple;
938     }
939
940    /**
941     * setNID - defines fiels name & id
942     *
943     * @param mixed $nid field name (string) or an array containing  name (1st)
944     *                   and id (2nd field).
945     *
946     * @access protected
947     */
948     protected function setNID($nid)
949     {
950          if (is_array($nid)) {
951               $this->name = $nid[0];
952               $this->id = !empty($nid[1]) ? $nid[1] : null;
953          }
954          else {
955               $this->id = $this->name = $nid;
956          }
957     }
958
959    /**
960     * setValue - sets field value
961     *
962     * @param mixed $value  field value.
963     * @param int   $offset value offset to define (default 0).
964     *
965     * @access public
966     */
967     public function setValue($value,$offset=0) {
968          $this->values[$offset] = $value;
969     }
970
971    /**
972     * setValues - set field values
973     *
974     * @param mixed $values the array of values. If not an array, the parameter
975     *                      will be converted to an array containing the value
976     *
977     * @access public
978     */
979     public function setValues($values) {
980          if (is_array($values)) {
981               $this->values = $values;
982          } elseif ($values !== NULL) {
983               $this->values = array($values);
984          }
985
986     }
987
988    /**
989     * getValues - return field values
990     *
991     * @access public
992     *
993     * @return mixed the array of values.
994     */
995     public function getValues() {
996          return $this->values;
997     }
998
999    /**
1000     * getValue - retrieves a field value
1001     *
1002     * @param int $offset value offset (by default 1st value is returned).
1003     *
1004     * @access public
1005     *
1006     * @return mixed the field value.
1007     */
1008     public function getValue($offset=0) {
1009          if (isset($this->values[$offset])) {
1010               return $this->values[$offset];
1011          }
1012     }
1013
1014    /**
1015     * addValue -- Adds a value to the field values.
1016     *
1017     * @param mixed $value Description.
1018     *
1019     * @access public
1020     *
1021     * @return mixed Value.
1022     */
1023     public function addValue($value) {
1024          $this->values[] = $value;
1025     }
1026     public function delValue($offset) {
1027          if (isset($this->values[$offset])) {
1028               array_splice($this->values,$offset,1);
1029          }
1030     }
1031
1032    /**
1033     * count -- returns the number of field values
1034     *
1035     * @access public
1036     *
1037     * @return integer the number of values.
1038     */
1039     public function count() {
1040          return count($this->values);
1041     }
1042
1043     public function __toString()
1044     {
1045          return join(',',$this->values);
1046     }
1047     
1048     abstract public function getWidgetBlock();
1049     
1050     public function isEmpty() {
1051          return (count($this->values) == 0) || empty($this->values[0]);
1052     }
1053
1054    /**
1055     * getAttributes - retrieve field attributes that will be given to the
1056     *                   twig widget
1057     *
1058     * @param array $options extra options given to the widget
1059     *
1060     * @access public
1061     *
1062     * @return array the attributes.
1063     */
1064     public function getAttributes($options=array())
1065     {
1066          $offset = isset($options['offset']) ? $options['offset'] : 0;
1067
1068          $attr = $this->options->getArrayCopy();
1069          if (isset($this->values[$offset])) {
1070               $attr['value'] = $this->values[$offset];
1071          } else {
1072               $attr['value'] = $this->getDefaultValue();
1073          }
1074          if ($offset==0 && !empty($this->id)) {
1075               $attr['id']=$this->id;
1076          }
1077          $attr['name'] = $this->name;
1078          if ($this->multiple) {
1079               $attr['name'] = $attr['name'].'[]';
1080          }
1081          return $attr;
1082     }
1083
1084    /**
1085     * getDefaultValue - returns field default value
1086     *
1087     * @access public
1088     *
1089     * @return mixed the field default value.
1090     */
1091     public function getDefaultValue() {
1092          return '';
1093     }
1094
1095    /**
1096     * getName - returns field name
1097     *
1098     * @access public
1099     *
1100     * @return string the field name.
1101     */
1102     public function getName()
1103     {
1104          return $this->name;
1105     }
1106
1107    /**
1108     * setName - defines field name and/or field id
1109     *
1110     * @param mixed $name the field name or an array containing name and id.
1111     *
1112     * @access public
1113     */
1114     public function setName($name) {
1115          $this->setNID($name);
1116     }
1117
1118    /**
1119     * check - checks whether the field is valid or not - raises an exception
1120     *              if not valid
1121     * @access public
1122     */
1123     public function check()
1124     {
1125          if (isset($this->options ['required']) && $this->options['required']) {
1126               if (!$this->defined || $this->isEmpty()) {
1127                    throw new InvalidFieldException(sprintf(
1128                         'Field "%s" is mandatory',
1129                         $this->options['label'])
1130                    );
1131               }
1132          }
1133     }
1134
1135    /**
1136     * parseValues - parses field value from context (GET or POST)
1137     *                   and returns parsed value(s)
1138     *                   NOTE : the field is not updated with this method
1139     *                   use setup() to also update the field.
1140     * @param mixed $from the context (usually $_GET or $_POST).
1141     *
1142     * @access public
1143     *
1144     * @return array the list of values (empty array if no value).
1145     */
1146     public function parseValues($from) {
1147          if (isset($from[$this->name])) {
1148               $n = $from[$this->name];
1149               if (!is_array($n)) {
1150                    $n = array($n);
1151               }
1152               return $n;
1153          }
1154          return array();
1155     }
1156
1157    /**
1158     * setup - sets up the field from conetxt (GET or $POST)
1159     *
1160     * @param mixed $from the context (usually $_GET or $_POST).
1161     *
1162     * @access public
1163     *
1164     * @return mixed Value.
1165     */
1166     public function setup($from)
1167     {
1168          $values = $this->parseValues($from);
1169          if (count($values)) {
1170               $this->setValues($values);
1171               $this->defined = true;
1172          }
1173     }
1174
1175    /**
1176     * isDefined - returns whether the field is defined or not
1177     *                   (a field is defined if some values are set after setup())
1178     * @access public
1179     *
1180     * @return mixed Value.
1181     */
1182     public function isDefined()
1183     {
1184          return $this->defined;
1185     }
1186}
1187
1188
1189/**
1190 * Template form field of type "password"
1191 */
1192class dcFieldPassword extends dcField
1193{
1194     public function getWidgetBlock()
1195     {
1196          return "field_password";
1197     }
1198}
1199
1200/**
1201 * Template form field of type "text"
1202 */
1203class dcFieldText extends dcField
1204{
1205     public function getWidgetBlock()
1206     {
1207     return "field_text";
1208     }
1209}
1210
1211/**
1212 * Template form field of type "textarea"
1213 */
1214class dcFieldTextArea extends dcField
1215{
1216     public function getWidgetBlock()
1217     {
1218          return "field_textarea";
1219     }
1220}
1221
1222/**
1223 * Template form field of type "hidden"
1224 */
1225class dcFieldHidden extends dcField
1226{
1227     public function getWidgetBlock()
1228     {
1229          return "field_hidden";
1230     }
1231}
1232
1233/**
1234 * Template form field of type "checkbox"
1235 */
1236class dcFieldCheckbox extends dcField
1237{
1238     protected $checked;
1239     
1240     public function __construct($name,$values,$options=array())
1241     {
1242          $val = array();
1243          if (!is_array($values)) {
1244               $values = array("1" => !empty($values));
1245          }
1246          $this->checked = $values;
1247          parent::__construct($name,array_keys($values),$options);
1248     }
1249
1250    /**
1251     * setValue - sets field value
1252     *
1253     * @param mixed $value  field value.
1254     * @param int   $offset value offset to define (default 0).
1255     *
1256     * @access public
1257     */
1258     public function setValue($value,$offset=0) {
1259          $this->checked[$this->values[0]] = $value;
1260     }
1261     
1262     public function getValue($offset=0) {
1263          $val = parent::getValue($offset);
1264          if (isset($this->checked[$val])) {
1265               return $this->checked[$val]?$val:false;
1266          } else {
1267               return false;
1268          }
1269     }
1270     
1271     public function getAttributes($options=array())
1272     {
1273          $a = parent::getAttributes($options);
1274         
1275          $val = $a['value'];
1276          if (isset($this->checked[$val]) && $this->checked[$val]) {
1277               $a['checked']='checked';
1278          }
1279          return $a;
1280     }
1281
1282     public function setup($from)
1283     {
1284          $this->defined = true;
1285          $values = $this->parseValues($from);
1286          foreach ($this->checked as $k=>&$v) {
1287               $v=false;
1288          }
1289          foreach ($values as $v) {
1290               $this->checked[$v] = true;
1291          }
1292          $this->setValues(array_keys($this->checked));
1293     }
1294
1295     public function getWidgetBlock()
1296     {
1297          return "field_checkbox";
1298     }
1299
1300     public function getDefaultValue() {
1301          return false;
1302     }
1303}
1304
1305/**
1306 * Template form action
1307 */
1308abstract class dcFieldAction extends dcField
1309{
1310     protected $action;
1311
1312     public function __construct($name,$values,$options=array())
1313     {
1314          parent::__construct($name,$values,$options);
1315
1316          if (isset($options['action'])) {
1317               $this->action = $options['action'];
1318          } else {
1319               $this->action = NULL;
1320          }
1321     }
1322
1323     public function getAction()
1324     {
1325          return $this->action;
1326     }
1327}
1328
1329/**
1330 * Template form field of type "submit"
1331 */
1332class dcFieldSubmit extends dcFieldAction
1333{
1334     public function getWidgetBlock()
1335     {
1336          return "field_submit";
1337     }
1338}
1339
1340/**
1341 * Template form field of type "combo"
1342 */
1343class dcFieldCombo extends dcField
1344{
1345     protected $combo;
1346     protected $combo_values;
1347
1348     public function __construct($name,$value,$combo,$options=array())
1349     {
1350          $this->combo = $combo;
1351          $this->combo_values = $combo;
1352          foreach ($combo as $k=>$v) {
1353               if (is_array($v)) {
1354                    unset($this->combo_values[$k]);
1355                    $this->combo_values = array_merge($v,$this->combo_values);
1356               }
1357          }
1358          parent::__construct($name,$value,$options);
1359     }
1360
1361     public function getWidgetBlock()
1362     {
1363          return "field_combo";
1364     }
1365
1366     public function getDefaultValue() {
1367          return current(array_keys($this->combo_values));
1368     }
1369
1370     public function parseValues($from) {
1371          $values = parent::parseValues($from);
1372          if (!is_array($values)) {
1373               $values = array($values);
1374          }
1375          foreach ($values as &$v) {
1376               if (!isset($this->combo_values[$v])) {
1377                    $v = $this->getDefaultValue();
1378               }
1379          }
1380          return $values;
1381     }
1382
1383     public function getAttributes($options=array()) {
1384          $attr = parent::getAttributes($options);
1385          $attr['options'] = $this->combo;
1386          return $attr;
1387     }
1388}
1389
1390?>
Note: See TracBrowser for help on using the repository browser.

Sites map