Dotclear

source: inc/admin/class.dc.form.php @ 1153:1e48950b05af

Revision 1153:1e48950b05af, 14.8 KB checked in by Dsls <dsls@…>, 12 years ago (diff)

Merge

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
16*
17* @uses     Twig_Node
18*
19*/
20class dcFormNode extends Twig_Node
21{
22     public function __construct($name,Twig_NodeInterface $body,$lineno,$tag=null)
23     {
24          parent::__construct(array('body' => $body),array('name' => $name),$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               ->write(");\n");
40          $compiler
41               ->subcompile($this->getNode('body'))
42               ->write("\$context['dc_form']->renderHiddenWidgets();\n")
43               ->write("\$context['dc_form']->endForm();\n")
44          ;
45     }
46}
47
48/**
49 * Template form token parser
50 */
51class dcFormTokenParser extends Twig_TokenParser
52{
53     public function parse(Twig_Token $token)
54     {
55          $lineno = $token->getLine();
56          $stream = $this->parser->getStream();
57          $name = $this->parser->getExpressionParser()->parseExpression();
58          $stream->expect(Twig_Token::BLOCK_END_TYPE);
59          $body = $this->parser->subparse(array($this,'decideBlockEnd'),true);
60          $stream->expect(Twig_Token::BLOCK_END_TYPE);
61         
62          return new dcFormNode($name,$body,$token->getLine(),$this->getTag());
63     }
64     
65     public function decideBlockEnd(Twig_Token $token)
66     {
67          return $token->test('endform');
68     }
69     
70     public function getTag()
71     {
72          return 'form';
73     }
74}
75
76/**
77 * Template form extension
78 */
79class dcFormExtension extends Twig_Extension
80{
81     protected $template;
82     protected $tpl;
83     protected $core;
84     protected $twig;
85     protected $forms;
86     protected $currentForm;
87     protected $blocks;
88     
89     public function __construct($core)
90     {
91          $this->core = $core;
92          $this->tpl = array('@forms/form_layout.html.twig');
93          $this->forms = array();
94          $this->blocks = array();
95          $this->currentForm = null;
96     }
97     
98     public function initRuntime(Twig_Environment $environment)
99     {
100          $this->twig = $environment;
101          $this->twig->getLoader()->addPath(dirname(__FILE__).'/default-templates/forms','forms');
102          foreach ($this->tpl as $tpl) {
103               $this->template = $this->twig->loadTemplate($tpl);
104               $this->blocks = array_merge($this->blocks,$this->template->getBlocks());
105          }
106     }
107     
108     public function addTemplate($tpl) {
109          $this->tpl[]=$tpl;
110          if (isset($this->twig)) {
111               $this->template = $this->twig->loadTemplate($tpl);
112               $this->blocks = array_merge($this->blocks,$this->template->getBlocks());
113          }
114     }
115
116     public function getGlobals()
117     {
118          return array('dc_form' => $this);
119     }
120     
121     public function getFunctions()
122     {
123          return array(
124               new Twig_SimpleFunction(
125                    'widget',
126                    array($this,'renderWidget'),
127                    array('is_safe' => array('html'))
128               ),
129               new Twig_SimpleFunction(
130                    'haswidget',
131                    array($this,'hasWidget'),
132                    array('is_safe' => array('html'))
133               ),
134               new Twig_SimpleFunction(
135                    'form_field',
136                    array($this,'renderField'),
137                    array('is_safe' => array('html'))
138               ),
139               new Twig_SimpleFunction(
140                    '_form_is_choice_group',
141                    array($this,'isChoiceGroup'),
142                    array('is_safe' => array('html'))
143               ),
144               new Twig_SimpleFunction(
145                    '_form_is_choice_selected',
146                    array($this,'isChoiceSelected'),
147                    array('is_safe' => array('html'))
148               )
149          );
150     }
151     
152     public function isChoiceGroup($choice)
153     {
154          return is_array($choice);
155     }
156     
157     public function isChoiceSelected($choice,$value)
158     {
159          return $choice == $value;
160     }
161     
162     public function getTokenParsers()
163     {
164          return array(new dcFormTokenParser());
165     }
166     
167     public function hasWidget($name) {
168          return isset($this->blocks[$name]);
169     }
170     public function renderWidget($name,$attr) {
171          if (!isset($this->blocks[$name]))
172               return '';
173          echo $this->template->renderBlock(
174               $name,
175               $attr,
176               $this->blocks
177          );
178     }
179
180     public function getCurrentForm() {
181          return $this->currentForm;
182     }
183
184     public function renderField($name,$attributes=array(),$extra=array())
185     {
186          $field = $this->currentForm->$name;
187          if ($field) {
188               $attr = $field->getAttributes($attributes);
189               if (isset($attr['attr'])) {
190                    $attr['attr'] = array_merge($attr['attr'],$attributes);
191               } else {
192                    $attr['attr'] = $attributes;
193               }
194               $this->renderWidget(
195                    $field->getWidgetBlock(),
196                    array_merge(
197                         $attr,
198                         $extra
199                    )
200               );
201          }
202     }
203
204     public function renderHiddenWidgets()
205     {
206          foreach ($this->currentForm->getHiddenFields() as $h) {
207               $this->renderField($h->getName());
208          }
209     }
210
211     public function getName()
212     {
213          return 'dc_form';
214     }
215
216     public function addForm(dcForm $form)
217     {
218          $this->forms[$form->getName()] = $form;
219     }
220
221     public function beginForm($name)
222     {
223          if (isset($this->forms[$name])) {
224               $this->currentForm = $this->forms[$name];
225               $this->currentForm->begin();
226          }
227          else {
228               throw new Twig_Error_Runtime(sprintf(
229                    'Form "%s" does not exist',
230                    $name
231               ));
232          }
233     }
234     
235     public function endForm()
236     {
237          $this->currentForm->end();
238          $this->currentForm = null;
239     }
240}
241
242/**
243 * Template form exception
244 */
245class InvalidFieldException extends Exception {
246}
247
248/**
249* dcForm - Template form
250*
251*/
252class dcForm
253{
254     protected $id;
255     protected $name;
256     protected $core;
257     protected $action;
258     protected $fields;
259     protected $method;
260     protected $submitfields;
261     protected $hiddenfields;
262     protected $errors;
263     
264     public function addTemplate($t) {
265          $this->core->tpl->getExtension('dc_form')->addTemplate($t);
266     }
267
268    /**
269     * addNonce -- adds dc nonce to form fields
270     *
271     * @access protected
272     *
273     * @return nothing
274     */
275     protected function addNonce()
276     {
277          $this->addField(
278               new dcFieldHidden(array('xd_check'),
279               $this->core->getNonce())
280          );
281     }
282     
283
284    /**
285     * Defines Name & ID from field
286     *
287     * @param mixed $nid either an array (name, id) or a string (name only, id will be set to null).
288     *
289     * @access protected
290     *
291     * @return nothing.
292     */
293     protected function setNID($nid)
294     {
295          if (is_array($nid)) {
296               $this->name = $nid[0];
297               $this->id = !empty($nid[1]) ? $nid[1] : null;
298          }
299          else {
300               $this->id = null;
301               $this->name = $nid;
302          }
303     }
304     
305     public function getContext() {
306          return array();
307     }
308
309    /**
310     * Class constructor
311     *
312     * @param mixed  $core   dotclear core
313     * @param mixed  $name   form name
314     * @param mixed  $action form action
315     * @param string $method form method ('GET' or 'POST')
316     *
317     * @access public
318     *
319     * @return mixed Value.
320     */
321     public function __construct($core,$name,$action,$method='POST')
322     {
323          $this->core = $core;
324          $this->setNID($name);
325          $this->method = $method;
326          $this->action = $action;
327          $this->fields = array();
328          $this->core->tpl->getExtension('dc_form')->addForm($this);
329          $this->submitfields = array();
330          $this->hiddenfields = array();
331          $this->errors = array();
332          if ($method == 'POST') {
333               $this->addNonce();
334          }
335     }
336     
337
338    /**
339     * Returns form name
340     *
341     * @access public
342     *
343     * @return mixed Value.
344     */
345     public function getName()
346     {
347          return $this->name;
348     }
349     
350     public function getErrors()
351     {
352          return $this->errors;
353     }
354     
355     public function addField(dcField $f)
356     {
357          if ($f instanceof dcFieldAction) {
358               $this->submitfields[$f->getName()] = $f;
359          }
360          if ($f instanceof dcFieldHidden) {
361               $this->hiddenfields[$f->getName()] = $f;
362          }
363          $this->fields[$f->getName()] = $f;
364         
365          return $this;
366     }
367     
368     public function removeField(dcField $f) {
369          $n = $f->getName();
370          if (isset($this->fields[$n])){
371               unset($this->fields[$n]);
372          }
373
374     }
375     public function renameField($field,$newname) {
376          $oldname = $field->getName();
377          if (isset($this->fields[$oldname])) {
378               unset($this->fields[$oldname]);
379               $field->setName($newname);
380               $this->fields[$newname] = $field;
381          }
382     }
383     public function begin()
384     {
385          echo sprintf(
386               '<form%s method="%s" action="%s">',
387               empty($this->id) ? '' : ' id="'.$this->id.'"',
388               $this->method,
389               $this->action
390          );
391     }
392     
393     public function end()
394     {
395          echo '</form>';
396     }
397     
398     public function __isset($name)
399     {
400          return isset($this->fields[$name]);
401     }
402     
403     public function __get($name)
404     {
405          return isset($this->fields[$name]) ?
406               $this->fields[$name] : null;
407     }
408
409     public function __set($name,$value)
410     {
411          if (isset($this->fields[$name])) {
412               $this->fields[$name]->setValue($value);
413          }
414     }
415
416     public function isSubmitted()
417     {
418          $from = $this->method == 'POST' ? $_POST : $_GET;
419     }
420
421     protected function setupFields() {
422          $from = $this->method == 'POST' ? $_POST : $_GET;
423          foreach ($this->fields as $f) {
424               $f->setup($from);
425          }
426     }
427
428     protected function handleActions($submitted) {
429          foreach ($submitted as $f) {
430               $action = $f->getAction();
431               if ($action != NULL) {
432                    $ret = call_user_func($action,$this);
433               }
434          }
435     }
436
437     protected function getSubmittedFields() {
438          $s = array();
439          foreach ($this->submitfields as $f) {
440               if ($f->isDefined()) {
441                    $s[$f->getName()] = $f;
442               }
443          }
444          return $s;
445     }
446
447     public function setup()
448     {
449          $this->setupFields();
450          $submitted = $this->getSubmittedFields();
451          $this->handleActions($submitted);
452     }
453
454     public function check()
455     {
456          foreach ($this->fields as $f) {
457               try {
458                    $f->check();
459               }
460               catch (InvalidFieldException $e) {
461                    $this->errors[] = $e->getMessage();
462               }
463          }
464     }
465     
466     public function getHiddenFields()
467     {
468          return $this->hiddenfields;
469     }
470}
471
472/**
473 * Template form field
474 */
475abstract class dcField implements Countable
476{
477     protected $options;
478     protected $name;
479     protected $values;
480     protected $id;
481     protected $multiple;
482     protected $defined;
483     
484     protected function setNID($nid)
485     {
486          if (is_array($nid)) {
487               $this->name = $nid[0];
488               $this->id = !empty($nid[1]) ? $nid[1] : null;
489          }
490          else {
491               $this->id = $this->name = $nid;
492          }
493     }
494     
495     public function __construct($name,$values,$options=array())
496     {
497          $this->setNID($name);
498          $this->options = new ArrayObject($options);
499          if ($values === NULL){
500               $values = array();
501          }
502          $this->setValues($values);
503          $this->defined = false;
504          $this->multiple = (isset($options['multiple']) && $options['multiple']);
505
506     }
507     
508     public function setValue($value,$offset=0) {
509          $this->values[$offset] = $value;
510     }
511
512     public function setValues($values) {
513          if (is_array($values)) {
514               $this->values = $values;
515          } elseif ($values !== NULL) {
516               $this->values = array($values);
517          }
518
519     }
520
521     public function getValues() {
522          return $this->values;
523     }
524
525     public function getValue($offset=0) {
526          if (isset($this->values[$offset])) {
527               return $this->values[$offset];
528          }
529     }
530
531     public function addValue($value) {
532          $this->values[] = $value;
533     }
534     public function delValue($offset) {
535          if (isset($this->values[$offset])) {
536               array_splice($this->values,$offset,1);
537          }
538     }
539
540     public function count() {
541          return count($this->values);
542     }
543
544     public function __toString()
545     {
546          return join(',',$this->values);
547     }
548     
549     abstract public function getWidgetBlock();
550     
551     public function getAttributes($options)
552     {
553          $offset = isset($options['offset']) ? $options['offset'] : 0;
554
555          $attr = $this->options->getArrayCopy();
556          if (isset($this->values[$offset])) {
557               $attr['value'] = $this->values[$offset];
558          } else {
559               $attr['value'] = $this->getDefaultValue();
560          }
561          if ($offset==0) {
562               $attr['id']=$this->id;
563          }
564          $attr['name'] = $this->name;
565          if ($this->multiple) {
566               $attr['name'] = $attr['name'].'[]';
567          }
568          return $attr;
569     }
570     
571     public function getDefaultValue() {
572          return '';
573     }
574
575     public function getName()
576     {
577          return $this->name;
578     }
579
580     public function setName($name) {
581          $this->setNID($name);
582     }
583
584     public function check()
585     {
586          if (!$this->defined && $this->options['mandatory']) {
587               throw new InvalidFieldException(sprintf(
588                    'Field "%s" is mandatory',
589                    $this->attributes['label'])
590               );
591          }
592     }
593     
594     public function parseValues($from) {
595          if (isset($from[$this->name])) {
596               $n = $from[$this->name];
597               if (!is_array($n)) {
598                    $n = array($n);
599               }
600               return $n;
601          }
602          return array();
603     }
604
605     public function setup($from)
606     {
607          $values = $this->parseValues($from);
608          if (count($values)) {
609               $this->setValues($values);
610               $this->defined = true;
611          }
612     }
613     
614     public function isDefined()
615     {
616          return $this->defined;
617     }
618}
619
620
621/**
622 * Template form field of type "password"
623 */
624class dcFieldPassword extends dcField
625{
626     public function getWidgetBlock()
627     {
628          return "field_password";
629     }
630}
631
632/**
633 * Template form field of type "text"
634 */
635class dcFieldText extends dcField
636{
637     public function getWidgetBlock()
638     {
639     return "field_text";
640     }
641}
642
643/**
644 * Template form field of type "textarea"
645 */
646class dcFieldTextArea extends dcField
647{
648     public function getWidgetBlock()
649     {
650          return "field_textarea";
651     }
652}
653
654/**
655 * Template form field of type "hidden"
656 */
657class dcFieldHidden extends dcField
658{
659     public function getWidgetBlock()
660     {
661          return "field_hidden";
662     }
663}
664
665/**
666 * Template form field of type "checkbox"
667 */
668class dcFieldCheckbox extends dcField
669{
670     public function getWidgetBlock()
671     {
672          return "field_checkbox";
673     }
674
675     public function getDefaultValue() {
676          return 0;
677     }
678}
679
680/**
681 * Template form action
682 */
683abstract class dcFieldAction extends dcField
684{
685     protected $action;
686
687     public function __construct($name,$values,$options=array())
688     {
689          parent::__construct($name,$values,$options);
690
691          if (isset($options['action'])) {
692               $this->action = $options['action'];
693          } else {
694               $this->action = NULL;
695          }
696     }
697
698     public function getAction()
699     {
700          return $this->action;
701     }
702}
703
704/**
705 * Template form field of type "submit"
706 */
707class dcFieldSubmit extends dcFieldAction
708{
709     public function getWidgetBlock()
710     {
711          return "field_submit";
712     }
713}
714
715/**
716 * Template form field of type "combo"
717 */
718class dcFieldCombo extends dcField
719{
720     protected $combo;
721     
722     public function __construct($name,$value,$combo,$options=array())
723     {
724          $this->combo = $combo;
725          parent::__construct($name,$value,$options);
726     }
727     
728     public function getWidgetBlock()
729     {
730          return "field_combo";
731     }
732
733     public function getDefaultValue() {
734          return current($this->combo);
735     }
736
737     public function parseValues($from) {
738          $values = parent::parseValues($from);
739          if (!is_array($values)) {
740               $values = array($values);
741          }
742          foreach ($values as &$v) {
743               if (!isset($this->combo[$v]))
744               $v = $this->getDefaultValue();
745          }
746          return $values;
747     }
748
749     public function getAttributes($options) {
750          $attr = parent::getAttributes($options);
751          $attr['options'] = $this->combo;
752          return $attr;
753     }
754}
755
756?>
Note: See TracBrowser for help on using the repository browser.

Sites map