Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
29.41% covered (danger)
29.41%
25 / 85
29.27% covered (danger)
29.27%
12 / 41
CRAP
0.00% covered (danger)
0.00%
0 / 1
tao_helpers_form_FormElement
29.41% covered (danger)
29.41%
25 / 85
29.27% covered (danger)
29.27%
12 / 41
1458.98
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRawValue
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setValue
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addClass
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
2.03
 removeClass
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 addAttribute
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setAttribute
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setAttributes
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 disable
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isDisabled
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 renderAttributes
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getWidget
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 getDescription
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 setDescription
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setUnit
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLevel
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setLevel
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addValidator
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 addValidators
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 getValidators
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setForcedValid
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 validate
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
30
 preValidate
16.67% covered (danger)
16.67%
1 / 6
0.00% covered (danger)
0.00%
0 / 1
19.47
 isValid
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getError
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getInvalidValues
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setInvalidValues
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setHelp
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getHelp
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 removeValidator
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 feed
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getEvaluatedValue
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getValue
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 isBreakOnFirstError
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setBreakOnFirstError
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAttributes
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getInputValue
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 feedInputValue
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 render
n/a
0 / 0
n/a
0 / 0
0
 getDecodedValue
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
4.59
1<?php
2
3/*
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; under version 2
7 * of the License (non-upgradable).
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 * Copyright (c) 2008-2010 (original work) Deutsche Institut für Internationale Pädagogische Forschung
19 *                         (under the project TAO-TRANSFER);
20 *               2009-2012 (update and modification) Public Research Centre Henri Tudor
21 *                         (under the project TAO-SUSTAIN & TAO-DEV);
22 *               2021 (original work) Open Assessment Technologies SA
23 */
24
25declare(strict_types=1);
26
27use oat\oatbox\validator\ValidatorInterface;
28use oat\tao\helpers\form\elements\xhtml\SearchTextBox;
29use oat\tao\helpers\form\elements\xhtml\SearchDropdown;
30use oat\tao\helpers\form\validators\PreliminaryValidationInterface;
31
32// Defining aliases for old style class names for backward compatibility
33// phpcs:disable PSR1.Files.SideEffects
34class_alias(SearchTextBox::class, \tao_helpers_form_elements_xhtml_Searchtextbox::class);
35class_alias(SearchDropdown::class, \tao_helpers_form_elements_xhtml_Searchdropdown::class);
36// phpcs:enable PSR1.Files.SideEffects
37
38/**
39 * Represents a form. It provides the default behavior for form management and
40 * be overridden for any rendering mode.
41 * A form is composed by a set of FormElements.
42 *
43 * The form data flow is:
44 * 1. add the elements to the form instance
45 * 2. run evaluate (initElements, update states (submited, valid, etc), update)
46 * 3. render form
47 *
48 * @author Joel Bout, <joel@taotesting.com>
49 * @package tao
50 */
51abstract class tao_helpers_form_FormElement
52{
53    public const WIDGET_ID = '';
54
55    /**
56     * the name of the element
57     *
58     * @access protected
59     * @var string
60     */
61    protected $name = '';
62
63    /**
64     * the value of the element
65     *
66     * @access protected
67     * @var mixed
68     */
69    protected $value;
70
71    /**
72     * the list of element attributes (key/value pairs)
73     *
74     * @access protected
75     * @var array
76     */
77    protected $attributes = [];
78
79    /**
80     * the widget links to the element
81     *
82     * @access protected
83     * @deprecated
84     * @see tao_helpers_form_FormElement::WIDGET_ID
85     */
86    protected $widget = '';
87
88    /**
89     * the element description
90     *
91     * @access protected
92     * @var string
93     */
94    protected $description = '';
95
96    /**
97     * used to display an element regarding the others
98     *
99     * @access protected
100     * @var int
101     */
102    protected $level = 1;
103
104    /**
105     * The list of validators links to the elements
106     *
107     * @access protected
108     * @var array
109     */
110    protected $validators = [];
111
112    /**
113     * the error message to display when the element validation has failed
114     *
115     * @access protected
116     * @var array
117     */
118    protected $error = [];
119
120    /**
121     * to force the validation of the element
122     *
123     * @access protected
124     * @var bool
125     */
126    protected $forcedValid = false;
127
128    /**
129     * Stop or not after first validation error occurred
130     * @var bool
131     */
132    protected $breakOnFirstError = true;
133
134    /**
135     * add a unit to the element (only for rendering purposes)
136     *
137     * @access protected
138     * @var string
139     */
140    protected $unit = '';
141
142    /**
143     * Short description of attribute help
144     *
145     * @access protected
146     * @var string
147     */
148    protected $help = '';
149
150    /** @var mixed */
151    private $inputValue;
152
153    /** @var array */
154    private $invalidValues = [];
155
156    /** @var bool */
157    private $isValid = true;
158
159    /**
160     * Short description of method __construct
161     *
162     * @author Joel Bout, <joel@taotesting.com>
163     * @param  string $name
164     * @return void
165     */
166    public function __construct($name = '')
167    {
168        $this->name = $name;
169    }
170
171    /**
172     * Short description of method getName
173     *
174     * @author Joel Bout, <joel@taotesting.com>
175     * @return string
176     */
177    public function getName()
178    {
179        return (string)$this->name;
180    }
181
182    /**
183     * Short description of method setName
184     *
185     * @author Joel Bout, <joel@taotesting.com>
186     * @param  string $name
187     * @return void
188     */
189    public function setName($name)
190    {
191        $this->name = $name;
192    }
193
194    /**
195     * Returns the raw data of the request, that was stored by the feed function
196     * the element. Mainly used by the Validators
197     *
198     * @author joel bout, joel@taotesting.com
199     * @return mixed
200     */
201    public function getRawValue()
202    {
203        return $this->value;
204    }
205
206    /**
207     * Short description of method setValue
208     *
209     * @author Joel Bout, <joel@taotesting.com>
210     * @param  string $value
211     * @return void
212     */
213    public function setValue($value)
214    {
215        $this->value = $value;
216    }
217
218    /**
219     * Add a CSS class jQuery style
220     *
221     * @author Dieter Raber, <dieter@taotesting.com>
222     * @param string $className
223     */
224    public function addClass($className)
225    {
226        $existingClasses = !empty($this->attributes['class'])
227            ? explode(' ', $this->attributes['class'])
228            : [];
229        $existingClasses[] = $className;
230        $this->attributes['class'] = implode(' ', array_unique($existingClasses));
231    }
232
233
234    /**
235     * Remove a CSS class jQuery style
236     *
237     * @author Dieter Raber, <dieter@taotesting.com>
238     * @param string $className
239     */
240    public function removeClass($className)
241    {
242        $existingClasses = !empty($this->attributes['class'])
243            ? explode(' ', $this->attributes['class'])
244            : [];
245        unset($existingClasses[array_search($className, $existingClasses, true)]);
246        $this->attributes['class'] = implode(' ', $existingClasses);
247    }
248
249
250    /**
251     * Short description of method addAttribute
252     *
253     * @author Joel Bout, <joel@taotesting.com>
254     * @param  string $key
255     * @param  string $value
256     * @return void
257     */
258    public function addAttribute($key, $value)
259    {
260        $this->attributes[$key] = $value;
261    }
262
263
264    /**
265     * Short description of method setAttribute
266     *
267     * @author Joel Bout, <joel@taotesting.com>
268     * @param  string $key
269     * @param  string $value
270     * @return void
271     */
272    public function setAttribute($key, $value)
273    {
274        $this->attributes[$key] = $value;
275    }
276
277    /**
278     * Short description of method setAttributes
279     *
280     * @author Joel Bout, <joel@taotesting.com>
281     * @param  array $attributes
282     * @return void
283     */
284    public function setAttributes($attributes)
285    {
286        $this->attributes = $attributes;
287    }
288
289    /**
290     * Disables a UI input
291     */
292    public function disable()
293    {
294        $this->addAttribute('disabled', 'disabled');
295    }
296
297    public function isDisabled(): bool
298    {
299        return isset($this->attributes['disabled']) && $this->attributes['disabled'] === 'disabled';
300    }
301
302    /**
303     * Short description of method renderAttributes
304     *
305     * @author Joel Bout, <joel@taotesting.com>
306     * @return string
307     */
308    protected function renderAttributes()
309    {
310        $returnValue = '';
311
312        foreach ($this->attributes as $key => $value) {
313            $returnValue .= " {$key}='{$value}";
314        }
315
316        return $returnValue;
317    }
318
319    /**
320     * Short description of method getWidget
321     *
322     * @author Joel Bout, <joel@taotesting.com>
323     * @return string
324     */
325    public function getWidget(): string
326    {
327        return $this->widget ?: static::WIDGET_ID;
328    }
329
330    /**
331     * Short description of method getDescription
332     *
333     * @author Joel Bout, <joel@taotesting.com>
334     * @return string
335     */
336    public function getDescription()
337    {
338
339        if (empty($this->description)) {
340            $returnValue = ucfirst(strtolower($this->name));
341        } else {
342            $returnValue = __($this->description);
343        }
344
345        return (string) $returnValue;
346    }
347
348    /**
349     * Short description of method setDescription
350     *
351     * @author Joel Bout, <joel@taotesting.com>
352     * @param  string $description
353     * @return void
354     */
355    public function setDescription($description)
356    {
357        $this->description = $description;
358    }
359
360    /**
361     * Short description of method setUnit
362     *
363     * @author Joel Bout, <joel@taotesting.com>
364     * @param  string $unit
365     * @return void
366     */
367    public function setUnit($unit)
368    {
369        $this->unit = $unit;
370    }
371
372    /**
373     * Short description of method getLevel
374     *
375     * @author Joel Bout, <joel@taotesting.com>
376     * @return int
377     */
378    public function getLevel()
379    {
380        return (int) $this->level;
381    }
382
383    /**
384     * Short description of method setLevel
385     *
386     * @author Joel Bout, <joel@taotesting.com>
387     * @param  int $level
388     * @return void
389     */
390    public function setLevel($level)
391    {
392        $this->level = $level;
393    }
394
395    /**
396     * Short description of method addValidator
397     *
398     * @author Joel Bout, <joel@taotesting.com>
399     * @param ValidatorInterface $validator
400     */
401    public function addValidator(ValidatorInterface $validator)
402    {
403        if ($validator instanceof tao_helpers_form_validators_NotEmpty) {
404            $this->addAttribute('required', true);
405        }
406        $this->validators[] = $validator;
407    }
408
409    /**
410     * Short description of method addValidators
411     *
412     * @author Joel Bout, <joel@taotesting.com>
413     * @param  array $validators
414     * @return void
415     */
416    public function addValidators($validators)
417    {
418        foreach ($validators as $validator) {
419            $this->addValidator($validator);
420        }
421    }
422
423    /**
424     * @return ValidatorInterface[]
425     */
426    public function getValidators(): array
427    {
428        return $this->validators;
429    }
430
431    /**
432     * Short description of method setForcedValid
433     *
434     * @access public
435     * @author Joel Bout, <joel@taotesting.com>
436     */
437    public function setForcedValid()
438    {
439        $this->forcedValid = true;
440    }
441
442    /**
443     * Short description of method validate
444     *
445     * @author Joel Bout, <joel@taotesting.com>
446     * @return bool
447     */
448    public function validate()
449    {
450        if ($this->forcedValid) {
451            return true;
452        }
453
454        $returnValue = true;
455
456        /** @var ValidatorInterface $validator */
457        foreach ($this->validators as $validator) {
458            if ($validator->evaluate($this->getRawValue())) {
459                continue;
460            }
461
462            $returnValue = false;
463            $this->error[] = $validator->getMessage();
464
465            if ($this->isBreakOnFirstError()) {
466                break;
467            }
468        }
469
470        return $returnValue;
471    }
472
473    public function preValidate(): void
474    {
475        /** @var ValidatorInterface $validator */
476        foreach ($this->validators as $validator) {
477            if (
478                $validator instanceof PreliminaryValidationInterface
479                && $validator->isPreValidationRequired()
480                && !$validator->evaluate($this->getRawValue())
481            ) {
482                $this->isValid = false;
483                $this->error[] = $validator->getMessage();
484            }
485        }
486    }
487
488    public function isValid(): bool
489    {
490        return $this->isValid;
491    }
492
493    /**
494     * Short description of method getError
495     *
496     * @author Joel Bout, <joel@taotesting.com>
497     * @return string
498     */
499    public function getError()
500    {
501        return implode("\n", $this->error);
502    }
503
504    public function getInvalidValues(): array
505    {
506        return $this->invalidValues;
507    }
508
509    public function setInvalidValues(array $invalidValues): void
510    {
511        $this->invalidValues = $invalidValues;
512    }
513
514    /**
515     * Short description of method setHelp
516     *
517     * @author Joel Bout, <joel@taotesting.com>
518     * @param  string $help
519     * @return void
520     */
521    public function setHelp($help)
522    {
523        $this->help = $help;
524    }
525
526    /**
527     * Short description of method getHelp
528     *
529     * @author Joel Bout, <joel@taotesting.com>
530     * @return string
531     */
532    public function getHelp()
533    {
534        return (string) $this->help;
535    }
536
537    /**
538     * Short description of method removeValidator
539     *
540     * @author Joel Bout, <joel@taotesting.com>
541     * @param  string $name
542     * @return bool
543     */
544    public function removeValidator($name)
545    {
546        $returnValue = false;
547
548        $name = (string) $name;
549        if (strpos($name, 'tao_helpers_form_validators_') === 0) {
550            $name = str_replace('tao_helpers_form_validators_', '', $name);
551        }
552        if (isset($this->validators[$name])) {
553            unset($this->validators[$name]);
554            $returnValue = true;
555        }
556
557        return $returnValue;
558    }
559
560    /**
561     * Reads the submitted data into the form element
562     *
563     * @author Joel Bout, <joel@taotesting.com>
564     */
565    public function feed()
566    {
567        $value = $this->getDecodedValue($this->name);
568
569        if ($value !== null) {
570            $this->setValue(tao_helpers_Uri::decode($_POST[$this->name]));
571        }
572    }
573
574    /**
575     * Returns the evaluated data that is calculated from the raw data and might
576     * unchanged for simple form elements. Used for storage of the data.
577     *
578     * @author joel bout, joel@taotesting.com
579     * @return string
580     */
581    public function getEvaluatedValue()
582    {
583        return tao_helpers_Uri::decode($this->getRawValue());
584    }
585
586    /**
587     * Legacy code compliance method. When the getRawValue and the
588     * methods were added, the fact that the getValue method was still invoked
589     * TAO was not taken into account. To solve the problem, the getValue method
590     * added to this class. Its implementation will call the getRawValue method
591     * it has the same behaviour as the old getValue.
592     *
593     * @author Jérôme Bogaerts
594     * @deprecated
595     * @return mixed
596     */
597    public function getValue()
598    {
599        common_Logger::d('deprecated function getValue() called', [ 'TAO', 'DEPRECATED' ]);
600
601        return $this->getRawValue();
602    }
603
604    /**
605     * @return bool
606     */
607    public function isBreakOnFirstError()
608    {
609        return $this->breakOnFirstError;
610    }
611
612    /**
613     * @param bool $breakOnFirstError
614     */
615    public function setBreakOnFirstError($breakOnFirstError)
616    {
617        $this->breakOnFirstError = $breakOnFirstError;
618    }
619
620    public function getAttributes(): array
621    {
622        return $this->attributes;
623    }
624
625    /**
626     * @return mixed
627     */
628    public function getInputValue()
629    {
630        return $this->inputValue;
631    }
632
633    public function feedInputValue(): void
634    {
635        $this->inputValue = $this->getDecodedValue($this->name);
636    }
637
638    /**
639     * Will render the Form Element.
640     *
641     */
642    abstract public function render();
643
644    private function getDecodedValue(string $name): ?string
645    {
646        if (isset($_POST[$name]) && $name !== 'uri' && $name !== 'classUri') {
647            return tao_helpers_Uri::decode($_POST[$this->name]);
648        }
649
650        return null;
651    }
652}