Dotclear

source: inc/admin/class.dc.form.php @ 1154:e85c1417b8a4

Revision 1154:e85c1417b8a4, 15.3 KB checked in by Dsls <dsls@…>, 12 years ago (diff)

Intermediate commit, needs to be better documented

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

Sites map