Issues (1796)

main/inc/lib/formvalidator/FormValidator.class.php (4 issues)

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
/**
6
 * Class FormValidator
7
 * create/manipulate/validate user input.
8
 */
9
class FormValidator extends HTML_QuickForm
10
{
11
    public const LAYOUT_HORIZONTAL = 'horizontal';
12
    public const LAYOUT_INLINE = 'inline';
13
    public const LAYOUT_BOX = 'box';
14
    public const LAYOUT_BOX_NO_LABEL = 'box-no-label';
15
    public const LAYOUT_BOX_SEARCH = 'box-search';
16
    public const LAYOUT_GRID = 'grid';
17
18
    public $with_progress_bar = false;
19
    private $layout;
20
21
    /**
22
     * @param string      $name        Name of the form
23
     * @param string      $method      (optional) Method ('post' (default) or 'get')
24
     * @param string      $action      (optional) Action (default is $PHP_SELF)
25
     * @param string|null $target      (optional) Form's target defaults to '_self'
26
     * @param mixed       $attributes  (optional) Extra attributes for <form> tag
27
     * @param bool        $trackSubmit Whether to track if the form was submitted by adding a special hidden field
28
     */
29
    public function __construct(
30
        string $name,
31
        ?string $method = 'post',
32
        ?string $action = '',
33
        ?string $target = '',
34
        ?array $attributes = [],
35
        string $layout = self::LAYOUT_HORIZONTAL,
36
        bool $trackSubmit = true
37
    ) {
38
        if (null === $attributes) {
39
            $attributes = [];
40
        }
41
42
        if (isset($attributes['class']) && str_contains($attributes['class'], 'form-search')) {
43
            $layout = self::LAYOUT_INLINE;
44
        }
45
46
        $this->setLayout($layout);
47
48
        // Form template
49
        $formTemplate = $this->getFormTemplate();
50
51
        switch ($layout) {
52
            case self::LAYOUT_BOX_SEARCH:
53
                $attributes['class'] = 'form--search';
54
                break;
55
            case self::LAYOUT_INLINE:
56
                $attributes['class'] = 'flex flex-row gap-3 ';
57
                break;
58
            case self::LAYOUT_BOX:
59
                $attributes['class'] = 'ch flex gap-1 ';
60
                break;
61
            case self::LAYOUT_GRID:
62
                $attributes['class'] = 'ch form-grid';
63
                $formTemplate = $this->getGridFormTemplate();
64
                break;
65
        }
66
67
        parent::__construct($name, $method, $action, $target, $attributes, $trackSubmit);
68
69
        // Modify the default templates
70
        $renderer = &$this->defaultRenderer();
71
        $renderer->setFormTemplate($formTemplate);
72
73
        // Element template
74
        if ((isset($attributes['class']) && 'form--inline' === $attributes['class']) ||
75
            (self::LAYOUT_INLINE === $layout || self::LAYOUT_BOX_SEARCH === $layout)
76
        ) {
77
            $elementTemplate = ' {label}  {element} ';
78
            $renderer->setElementTemplate($elementTemplate);
79
        } elseif (isset($attributes['class']) && 'form-search' === $attributes['class']) {
80
            $elementTemplate = ' {label}  {element} ';
81
            $renderer->setElementTemplate($elementTemplate);
82
        } else {
83
            $renderer->setElementTemplate($this->getDefaultElementTemplate());
84
85
            // Display a gray div in the buttons
86
            $templateSimple = '<div class="form-actions">{label} {element}</div>';
87
            $renderer->setElementTemplate($templateSimple, 'submit_in_actions');
88
89
            //Display a gray div in the buttons + makes the button available when scrolling
90
            $templateBottom = '<div class="form-actions bottom_actions bg-form">{label} {element}</div>';
91
            $renderer->setElementTemplate($templateBottom, 'submit_fixed_in_bottom');
92
            $renderer->setElementTemplate($templateSimple, 'buttons_in_action');
93
94
            $templateSimpleRight = '<div class="form-actions"> <div class="pull-right">{label} {element}</div></div>';
95
            $renderer->setElementTemplate($templateSimpleRight, 'buttons_in_action_right');
96
        }
97
98
        //Set Header template
99
        $renderer->setHeaderTemplate(' <h1 class="text-h3 font-small text-gray-800 mb-4">{header}<hr /></h1>');
100
101
        $required = '<span class="form_required">*</span> <small>'.get_lang('Required field').'</small>';
102
        if ((self::LAYOUT_INLINE === $layout || self::LAYOUT_BOX_SEARCH === $layout)) {
103
            $required = '';
104
        }
105
        // Set required field template
106
        $this->setRequiredNote($required);
107
108
        if (self::LAYOUT_BOX_SEARCH !== $layout) {
109
            $noteTemplate = <<<EOT
110
	<div class="form-group">
111
		<div class="col-sm-offset-2 col-sm-10">{requiredNote}</div>
112
	</div>
113
EOT;
114
            $renderer->setRequiredNoteTemplate($noteTemplate);
115
        }
116
    }
117
118
    public function getFormTemplate(): string
119
    {
120
        if (self::LAYOUT_BOX_SEARCH == $this->layout) {
121
            return '<form {attributes}>
122
                    <div class="form__group form__group--inline p-inputgroup">
123
                        {content}
124
                        {hidden}
125
                    </div>
126
                </form>';
127
        }
128
129
        return '<form{attributes}>
130
                {content}
131
                {hidden}
132
            </form>';
133
    }
134
135
    public function getGridFormTemplate(): string
136
    {
137
        return '
138
        <style>
139
            .form_list {
140
                display: grid;
141
                grid-template-columns:  repeat(auto-fill, minmax(300px, 1fr));;
142
                grid-gap: 10px 30px;
143
                gap: 10px 30px;
144
            }
145
            .form_list .input-group {
146
                display:block;
147
            }
148
        </style>
149
        <form{attributes}>
150
            <div class="form_list">
151
                {content}
152
            </div>
153
        {hidden}
154
        </form>';
155
    }
156
157
    /**
158
     * @todo this function should be added in the element class
159
     */
160
    public function getDefaultElementTemplate(): string
161
    {
162
        return '
163
            <div class="row mb-3 {error_class}">
164
                <label {label-for} class="col-sm-2 col-form-label {extra_label_class}" >
165
                    <!-- BEGIN required --><span class="form_required">*</span><!-- END required -->
166
                    {label}
167
                </label>
168
                <div class="col-sm-8">
169
                    {icon}
170
                    {element}
171
                    <!-- BEGIN label_2 -->
172
                        <p class="help-block">{label_2}</p>
173
                    <!-- END label_2 -->
174
175
                    <!-- BEGIN error -->
176
                        <span class="help-inline help-block">{error}</span>
177
                    <!-- END error -->
178
                </div>
179
                <div class="col-sm-2">
180
                    <!-- BEGIN label_3 -->
181
                        {label_3}
182
                    <!-- END label_3 -->
183
                </div>
184
            </div>';
185
    }
186
187
    public function getLayout(): string
188
    {
189
        return $this->layout;
190
    }
191
192
    public function setLayout(string $layout)
193
    {
194
        $this->layout = $layout;
195
    }
196
197
    /**
198
     * Adds a text field to the form.
199
     * A trim-filter is attached to the field.
200
     *
201
     * @param string       $name       The element name
202
     * @param string|array $label      The label for the form-element
203
     * @param bool         $required   (optional)    Is the form-element required (default=true)
204
     * @param array        $attributes (optional)    List of attributes for the form-element
205
     * @param bool         $createElement
206
     *
207
     * @throws Exception
208
     *
209
     * @return HTML_QuickForm_text
210
     */
211
    public function addText($name, $label, $required = true, $attributes = [], $createElement = false)
212
    {
213
        if ($createElement) {
214
            $element = $this->createElement('text', $name, $label, $attributes);
215
        } else {
216
            $element = $this->addElement('text', $name, $label, $attributes);
217
        }
218
219
        $this->applyFilter($name, 'trim');
220
        if ($required) {
221
            $this->addRule($name, get_lang('Required field'), 'required');
222
        }
223
224
        return $element;
225
    }
226
227
    /**
228
     * Add hidden course params.
229
     */
230
    public function addCourseHiddenParams()
231
    {
232
        $this->addHidden('cid', api_get_course_int_id());
233
        $this->addHidden('sid', api_get_session_id());
234
    }
235
236
    /**
237
     * The "date_range_picker" element creates 2 hidden fields
238
     * "elementName" + "_start"  and "elementName" + "_end"
239
     * For example if the name is "range", you will have 2 new fields
240
     * when executing $form->getSubmitValues()
241
     * "range_start" and "range_end".
242
     *
243
     * @param string $name
244
     * @param string $label
245
     * @param bool   $required
246
     * @param array  $attributes
247
     */
248
    public function addDateRangePicker($name, $label, $required = true, $attributes = [])
249
    {
250
        $this->addElement('date_range_picker', $name, $label, $attributes);
251
        $this->addElement('hidden', $name.'_start');
252
        $this->addElement('hidden', $name.'_end');
253
254
        if ($required) {
255
            $this->addRule($name, get_lang('Required field'), 'required');
256
        }
257
    }
258
259
    /**
260
     * @param string $name
261
     * @param string $label
262
     * @param array  $attributes
263
     *
264
     * @return mixed
265
     */
266
    public function addSelectLanguage($name, $label, $options = [], $attributes = [])
267
    {
268
        return $this->addElement('SelectLanguage', $name, $label, $options, $attributes);
269
    }
270
271
    public function addSelectTheme($name, $label, $options = [], $attributes = [])
272
    {
273
        return $this->addElement('SelectTheme', $name, $label, $options, $attributes);
274
    }
275
276
    /**
277
     * @param string       $name
278
     * @param string|array $label
279
     * @param array        $options
280
     * @param array        $attributes
281
     *
282
     * @return SelectAjax
283
     */
284
    public function addSelectAjax($name, $label, $options = [], $attributes = [])
285
    {
286
        if (!isset($attributes['url'])) {
287
            throw new \Exception('select_ajax needs an URL');
288
        }
289
290
        return $this->addElement(
291
            'select_ajax',
292
            $name,
293
            $label,
294
            $options,
295
            $attributes
296
        );
297
    }
298
299
    /**
300
     * @param string $name
301
     * @param string $label
302
     * @param array  $attributes
303
     *
304
     * @return DatePicker
305
     */
306
    public function addDatePicker($name, $label, $attributes = [])
307
    {
308
        return $this->addElement('DatePicker', $name, $label, $attributes);
309
    }
310
311
    /**
312
     * @param string       $name
313
     * @param string|array $label
314
     * @param array        $attributes
315
     *
316
     * @return DateTimePicker
317
     */
318
    public function addDateTimePicker($name, $label, $attributes = [])
319
    {
320
        return $this->addElement('DateTimePicker', $name, $label, $attributes);
321
    }
322
323
    /**
324
     * @param string       $name
325
     * @param string|array $label
326
     * @param array        $attributes
327
     *
328
     * @return DateTimeRangePicker
329
     */
330
    public function addDateTimeRangePicker($name, $label, $attributes = [])
331
    {
332
        return $this->addElement('DateTimeRangePicker', $name, $label, $attributes);
333
    }
334
335
    /**
336
     * @param string $name
337
     * @param string|mixed $value
338
     * @param array  $attributes
339
     */
340
    public function addHidden($name, $value, $attributes = [])
341
    {
342
        $this->addElement('hidden', $name, $value, $attributes);
343
    }
344
345
    /**
346
     * @param string       $name
347
     * @param string|array $label
348
     * @param array        $attributes
349
     * @param bool         $required
350
     *
351
     * @return HTML_QuickForm_textarea
352
     */
353
    public function addTextarea($name, $label, $attributes = [], $required = false)
354
    {
355
        $element = $this->addElement('textarea', $name, $label, $attributes);
356
357
        if ($required) {
358
            $this->addRule($name, get_lang('Required field'), 'required');
359
        }
360
361
        return $element;
362
    }
363
364
    /**
365
     * @param string $name
366
     * @param string $label
367
     * @param string $icon          font-awesome
368
     * @param string $style         default|primary|success|info|warning|danger|link
369
     * @param string $size          large|default|small|extra-small
370
     * @param string $class         Example plus is transformed to icon fa fa-plus
371
     * @param array  $attributes
372
     * @param bool   $createElement
373
     *
374
     * @return HTML_QuickForm_button
375
     */
376
    public function addButton(
377
        $name,
378
        $label,
379
        $icon = 'check',
380
        $style = 'default',
381
        $size = 'default',
382
        $class = null,
383
        $attributes = [],
384
        $createElement = false
385
    ) {
386
        if ($createElement) {
387
            return $this->createElement(
388
                'button',
389
                $name,
390
                $label,
391
                $icon,
392
                $style,
393
                $size,
394
                $class,
395
                $attributes
396
            );
397
        }
398
399
        return $this->addElement(
400
            'button',
401
            $name,
402
            $label,
403
            $icon,
404
            $style,
405
            $size,
406
            $class,
407
            $attributes
408
        );
409
    }
410
411
    /**
412
     * Returns a button with the primary color and a check mark.
413
     *
414
     * @param string $label         Text appearing on the button
415
     * @param string $name          Element name (for form treatment purposes)
416
     * @param bool   $createElement Whether to use the create or add method
417
     * @param array  $attributes
418
     *
419
     * @return HTML_QuickForm_button
420
     */
421
    public function addButtonSave($label, $name = 'submit', $createElement = false, $attributes = [])
422
    {
423
        return $this->addButton(
424
            $name,
425
            $label,
426
            'check',
427
            'primary',
428
            null,
429
            null,
430
            $attributes,
431
            $createElement
432
        );
433
    }
434
435
    /**
436
     * Returns a cancel button.
437
     *
438
     * @param string $label         Text appearing on the button
439
     * @param string $name          Element name (for form treatment purposes)
440
     * @param bool   $createElement Whether to use the create or add method
441
     *
442
     * @return HTML_QuickForm_button
443
     */
444
    public function addButtonCancel($label, $name = 'submit', $createElement = false)
445
    {
446
        return $this->addButton(
447
            $name,
448
            $label,
449
            'close',
450
            'danger',
451
            null,
452
            null,
453
            [],
454
            $createElement
455
        );
456
    }
457
458
    /**
459
     * Returns a button with the primary color and a "plus" icon.
460
     *
461
     * @param string $label         Text appearing on the button
462
     * @param string $name          Element name (for form treatment purposes)
463
     * @param bool   $createElement Whether to use the create or add method
464
     * @param array  $attributes    Additional attributes
465
     *
466
     * @return HTML_QuickForm_button
467
     */
468
    public function addButtonCreate($label, $name = 'submit', $createElement = false, $attributes = [])
469
    {
470
        return $this->addButton(
471
            $name,
472
            $label,
473
            'plus',
474
            'primary',
475
            null,
476
            null,
477
            $attributes,
478
            $createElement
479
        );
480
    }
481
482
    /**
483
     * Returns a button with the primary color and a pencil icon.
484
     *
485
     * @param string $label         Text appearing on the button
486
     * @param string $name          Element name (for form treatment purposes)
487
     * @param bool   $createElement Whether to use the create or add method
488
     *
489
     * @return HTML_QuickForm_button
490
     */
491
    public function addButtonUpdate($label, $name = 'submit', $createElement = false)
492
    {
493
        return $this->addButton(
494
            $name,
495
            $label,
496
            'pencil',
497
            'primary',
498
            null,
499
            null,
500
            [],
501
            $createElement
502
        );
503
    }
504
505
    /**
506
     * Returns a button with the danger color and a trash icon.
507
     *
508
     * @param string $label         Text appearing on the button
509
     * @param string $name          Element name (for form treatment purposes)
510
     * @param bool   $createElement Whether to use the create or add method
511
     *
512
     * @return HTML_QuickForm_button
513
     */
514
    public function addButtonDelete($label, $name = 'submit', $createElement = false)
515
    {
516
        return $this->addButton(
517
            $name,
518
            $label,
519
            'delete',
520
            'danger',
521
            null,
522
            null,
523
            [],
524
            $createElement
525
        );
526
    }
527
528
    /**
529
     * Returns a move style button.
530
     *
531
     * @param string $label         Text appearing on the button
532
     * @param string $name          Element name (for form treatment purposes)
533
     * @param bool   $createElement Whether to use the create or add method
534
     *
535
     * @return HTML_QuickForm_button
536
     */
537
    public function addButtonMove($label, $name = 'submit', $createElement = false)
538
    {
539
        return $this->addButton(
540
            $name,
541
            $label,
542
            'arrow-right-bold-circle',
543
            'primary',
544
            null,
545
            null,
546
            [],
547
            $createElement
548
        );
549
    }
550
551
    /**
552
     * Returns a button with the primary color and a paper-plane icon.
553
     *
554
     * @param string $label         Text appearing on the button
555
     * @param string $name          Element name (for form treatment purposes)
556
     * @param bool   $createElement Whether to use the create or add method
557
     * @param array  $attributes
558
     *
559
     * @return HTML_QuickForm_button
560
     */
561
    public function addButtonSend($label, $name = 'submit', $createElement = false, $attributes = [])
562
    {
563
        return $this->addButton(
564
            $name,
565
            $label,
566
            'send',
567
            'primary',
568
            null,
569
            null,
570
            $attributes,
571
            $createElement
572
        );
573
    }
574
575
    /**
576
     * Returns a button with the default (grey?) color and a magnifier icon.
577
     *
578
     * @param string $label Text appearing on the button
579
     * @param string $name  Element name (for form treatment purposes)
580
     *
581
     * @return HTML_QuickForm_button
582
     */
583
    public function addButtonSearch($label = null, $name = 'submit')
584
    {
585
        if (empty($label)) {
586
            $label = get_lang('Search');
587
        }
588
589
        return $this->addButton($name, $label, 'magnify', 'primary');
590
    }
591
592
    /**
593
     * Returns a button with the primary color and a right-pointing arrow icon.
594
     *
595
     * @param string $label      Text appearing on the button
596
     * @param string $name       Element name (for form treatment purposes)
597
     * @param array  $attributes Additional attributes
598
     *
599
     * @return HTML_QuickForm_button
600
     */
601
    public function addButtonNext($label, $name = 'submit', $attributes = [])
602
    {
603
        return $this->addButton(
604
            $name,
605
            $label,
606
            'arrow-right',
607
            'primary',
608
            null,
609
            null,
610
            $attributes
611
        );
612
    }
613
614
    /**
615
     * Returns a button with the primary color and a check mark icon.
616
     *
617
     * @param string $label         Text appearing on the button
618
     * @param string $name          Element name (for form treatment purposes)
619
     * @param bool   $createElement Whether to use the create or add method
620
     *
621
     * @return HTML_QuickForm_button
622
     */
623
    public function addButtonImport($label, $name = 'submit', $createElement = false)
624
    {
625
        return $this->addButton(
626
            $name,
627
            $label,
628
            'check',
629
            'primary',
630
            null,
631
            null,
632
            [],
633
            $createElement
634
        );
635
    }
636
637
    /**
638
     * Returns a button with the primary color and a check-mark icon.
639
     *
640
     * @param string $label         Text appearing on the button
641
     * @param string $name          Element name (for form treatment purposes)
642
     * @param bool   $createElement Whether to use the create or add method
643
     *
644
     * @return HTML_QuickForm_button
645
     */
646
    public function addButtonExport($label, $name = 'submit', $createElement = false)
647
    {
648
        return $this->addButton(
649
            $name,
650
            $label,
651
            'check',
652
            'primary',
653
            null,
654
            null,
655
            [],
656
            $createElement
657
        );
658
    }
659
660
    /**
661
     * Shortcut to filter button.
662
     *
663
     * @param string $label         Text appearing on the button
664
     * @param string $name          Element name (for form treatment purposes)
665
     * @param bool   $createElement Whether to use the create or add method
666
     *
667
     * @return HTML_QuickForm_button
668
     */
669
    public function addButtonFilter($label, $name = 'submit', $createElement = false)
670
    {
671
        return $this->addButton(
672
            $name,
673
            $label,
674
            'filter',
675
            'primary',
676
            null,
677
            null,
678
            [],
679
            $createElement
680
        );
681
    }
682
683
    /**
684
     * Shortcut to reset button.
685
     *
686
     * @param string $label         Text appearing on the button
687
     * @param string $name          Element name (for form treatment purposes)
688
     * @param bool   $createElement Whether to use the create or add method
689
     *
690
     * @return HTML_QuickForm_button
691
     */
692
    public function addButtonReset($label, $name = 'reset', $createElement = false)
693
    {
694
        $icon = 'eraser';
695
        $style = 'default';
696
        $size = 'default';
697
        $class = null;
698
        $attributes = [];
699
700
        if ($createElement) {
701
            return $this->createElement(
702
                'reset',
703
                $name,
704
                $label,
705
                $icon,
706
                $style,
707
                $size,
708
                $class,
709
                $attributes
710
            );
711
        }
712
713
        return $this->addElement(
714
            'reset',
715
            $name,
716
            $label,
717
            $icon,
718
            $style,
719
            $size,
720
            $class,
721
            $attributes
722
        );
723
    }
724
725
    /**
726
     * Returns a button with the primary color and an upload icon.
727
     *
728
     * @param string $label         Text appearing on the button
729
     * @param string $name          Element name (for form treatment purposes)
730
     * @param bool   $createElement Whether to use the create or add method
731
     *
732
     * @return HTML_QuickForm_button
733
     */
734
    public function addButtonUpload($label, $name = 'submit', $createElement = false)
735
    {
736
        return $this->addButton(
737
            $name,
738
            $label,
739
            'upload',
740
            'primary',
741
            null,
742
            null,
743
            [],
744
            $createElement
745
        );
746
    }
747
748
    /**
749
     * Returns a button with the primary color and a download icon.
750
     *
751
     * @param string $label         Text appearing on the button
752
     * @param string $name          Element name (for form treatment purposes)
753
     * @param bool   $createElement Whether to use the create or add method
754
     *
755
     * @return HTML_QuickForm_button
756
     */
757
    public function addButtonDownload($label, $name = 'submit', $createElement = false)
758
    {
759
        return $this->addButton(
760
            $name,
761
            $label,
762
            'download',
763
            'primary',
764
            null,
765
            null,
766
            [],
767
            $createElement
768
        );
769
    }
770
771
    /**
772
     * Returns a button with the primary color and a magnifier icon.
773
     *
774
     * @param string $label         Text appearing on the button
775
     * @param string $name          Element name (for form treatment purposes)
776
     * @param bool   $createElement Whether to use the create or add method
777
     *
778
     * @return HTML_QuickForm_button
779
     */
780
    public function addButtonPreview($label, $name = 'submit', $createElement = false)
781
    {
782
        return $this->addButton(
783
            $name,
784
            $label,
785
            'magnify',
786
            'primary',
787
            null,
788
            null,
789
            [],
790
            $createElement
791
        );
792
    }
793
794
    /**
795
     * Returns a button with the primary color and a copy (double sheet) icon.
796
     *
797
     * @param string $label         Text appearing on the button
798
     * @param string $name          Element name (for form treatment purposes)
799
     * @param bool   $createElement Whether to use the create or add method
800
     *
801
     * @return HTML_QuickForm_button
802
     */
803
    public function addButtonCopy($label, $name = 'submit', $createElement = false)
804
    {
805
        return $this->addButton(
806
            $name,
807
            $label,
808
            'copy',
809
            'primary',
810
            null,
811
            null,
812
            [],
813
            $createElement
814
        );
815
    }
816
817
    /**
818
     * @param string $name
819
     * @param string $label
820
     * @param string $text
821
     * @param array  $attributes
822
     *
823
     * @return HTML_QuickForm_checkbox
824
     */
825
    public function addCheckBox($name, $label, $text = '', $attributes = [])
826
    {
827
        return $this->addElement('checkbox', $name, $label, $text, $attributes);
828
    }
829
830
    /**
831
     * @param string $name
832
     * @param string $label
833
     * @param array  $options
834
     * @param array  $attributes
835
     *
836
     * @return HTML_QuickForm_group
837
     */
838
    public function addCheckBoxGroup($name, $label, $options = [], $attributes = [])
839
    {
840
        $group = [];
841
        foreach ($options as $value => $text) {
842
            $attributes['value'] = $value;
843
            $group[] = $this->createElement(
844
                'checkbox',
845
                $value,
846
                null,
847
                $text,
848
                $attributes
849
            );
850
        }
851
852
        return $this->addGroup($group, $name, $label);
853
    }
854
855
    /**
856
     * @param string $name
857
     * @param string $label
858
     * @param array  $options
859
     * @param array  $attributes
860
     *
861
     * @return HTML_QuickForm_group
862
     */
863
    public function addRadio($name, $label, $options = [], $attributes = [])
864
    {
865
        $group = [];
866
        foreach ($options as $key => $value) {
867
            $group[] = $this->createElement('radio', null, null, $value, $key, $attributes);
868
        }
869
870
        return $this->addGroup($group, $name, $label);
871
    }
872
873
    /**
874
     * @param string|array $label
875
     *
876
     * @return HTML_QuickForm_select
877
     */
878
    public function addSelect(string $name, $label, ?array $options = [], array $attributes = [])
879
    {
880
        return $this->addElement('select', $name, $label, $options, $attributes);
881
    }
882
883
    /**
884
     * @param $name
885
     * @param $label
886
     * @param $collection
887
     * @param array  $attributes
888
     * @param bool   $addNoneOption
889
     * @param string $textCallable  set a function getStringValue() by default __toString()
890
     *
891
     * @return HTML_QuickForm_element
892
     */
893
    public function addSelectFromCollection(
894
        $name,
895
        $label,
896
        $collection,
897
        $attributes = [],
898
        $addNoneOption = false,
899
        $textCallable = ''
900
    ) {
901
        $options = [];
902
903
        if ($addNoneOption) {
904
            $options[0] = get_lang('None');
905
        }
906
907
        if (!empty($collection)) {
908
            foreach ($collection as $item) {
909
                $text = $item;
910
                if (!empty($textCallable)) {
911
                    $text = $item->$textCallable();
912
                }
913
                $options[$item->getId()] = $text;
914
            }
915
        }
916
917
        return $this->addElement('select', $name, $label, $options, $attributes);
918
    }
919
920
    public function addMultiSelect(string $name, $label, array $options, array $attributes = [])
921
    {
922
        $this->addElement('advmultiselect', $name, $label, $options, $attributes);
923
    }
924
925
    /**
926
     * @param string $label
927
     * @param string $text
928
     * @param bool   $createElement
929
     *
930
     * @return HTML_QuickForm_Element
931
     */
932
    public function addLabel($label, $text, $createElement = false)
933
    {
934
        if ($createElement) {
935
            return $this->createElement(
936
                'label',
937
                $label,
938
                $text
939
            );
940
        }
941
942
        return $this->addElement('label', $label, $text);
943
    }
944
945
    /**
946
     * @param string $text
947
     */
948
    public function addHeader($text)
949
    {
950
        if (!empty($text)) {
951
            $this->addElement('header', $text);
952
        }
953
    }
954
955
    /**
956
     * @param string       $name
957
     * @param string|array $label
958
     * @param array        $attributes
959
     *
960
     * @throws Exception if the file doesn't have an id
961
     *
962
     * @return HTML_QuickForm_file
963
     */
964
    public function addFile($name, $label, $attributes = [])
965
    {
966
        try {
967
            $element = $this->addElement('file', $name, $label, $attributes);
968
            if (isset($attributes['crop_image'])) {
969
                $id = $element->getAttribute('id');
970
                if (empty($id)) {
971
                    throw new Exception('If you use the crop functionality the element must have an id');
972
                }
973
                $this->addHtml(
974
                    '
975
                <div class="form-group row" id="'.$id.'-form-group" style="display: none;">
976
                    <div class="offset-md-2 col-sm-8">
977
                        <div class="card-cropper">
978
                            <div id="'.$id.'_crop_image" class="cropCanvas">
979
                                <img id="'.$id.'_preview_image">
980
                            </div>
981
                            <button class="btn btn--primary" type="button" name="cropButton" id="'.$id.'_crop_button">
982
                                <em class="fa fa-crop"></em> '.get_lang('Crop your picture').'
983
                            </button>
984
                        </div>
985
                    </div>
986
                </div>'
987
                );
988
                $this->addHidden($id.'_crop_result', '');
989
                $this->addHidden($id.'_crop_result_for_resource', '');
990
                $this->addHidden($id.'_crop_image_base_64', '');
991
            }
992
        } catch (HTML_Quick | Form_Error $e) {
993
            var_dump($e->getMessage());
0 ignored issues
show
Security Debugging Code introduced by
var_dump($e->getMessage()) looks like debug code. Are you sure you do not want to remove it?
Loading history...
994
        }
995
996
        return $element;
997
    }
998
999
    /**
1000
     * @param string $snippet
1001
     */
1002
    public function addHtml($snippet)
1003
    {
1004
        if (empty($snippet)) {
1005
            return false;
1006
        }
1007
        $this->addElement('html', $snippet);
1008
1009
        return true;
1010
    }
1011
1012
    public function addStartPanel(string $id, string $title, bool $open = false, $icon = null)
1013
    {
1014
        $parent = null;
1015
        $javascript = '
1016
        <script>
1017
            document.addEventListener("DOMContentLoaded", function() {
1018
                const buttons = document.querySelectorAll("#card_'.$id.' a");
1019
                const menus = document.querySelectorAll("#collapse_'.$id.'");
1020
1021
                buttons.forEach((button, index) => {
1022
                    button.addEventListener("click", function() {
1023
                        menus.forEach((menu, menuIndex) => {
1024
                            if (index === menuIndex) {
1025
                                menu.classList.toggle("active");
1026
                            } else {
1027
                                menu.classList.remove("active");
1028
                            }
1029
                        });
1030
                    });
1031
                });
1032
            });
1033
        </script>';
1034
1035
        $this->addHtml($javascript);
1036
1037
        $htmlIcon = '';
1038
        if ($icon) {
1039
            $htmlIcon = Display::getMdiIcon($icon, 'ch-tool-icon', 'float:left;', ICON_SIZE_SMALL);
1040
        }
1041
        $html = '
1042
        <div class="mt-4 rounded-lg">
1043
            <div class="px-4 bg-gray-100 border border-gray-50" id="card_'.$id.'">
1044
                <h5>
1045
                    <a role="button"
1046
                        class="block cursor-pointer"
1047
                        data-toggle="collapse"
1048
                        data-target="#collapse_'.$id.'"
1049
                        aria-expanded="'.(($open) ? 'true' : 'false').'"
1050
                        aria-controls="collapse_'.$id.'"
1051
                    >
1052
                        '.$htmlIcon.'&nbsp;'.$title.'
1053
                    </a>
1054
                </h5>
1055
            </div>
1056
            <div
1057
                id="collapse_'.$id.'"
1058
                class="px-4 border border-gray-50 bg-white collapse custom-collapse '.(($open) ? 'show' : '').'"
1059
            >
1060
                <div id="collapse_contant_'.$id.'"  class="card-body ">';
1061
1062
        $this->addHtml($html);
1063
    }
1064
1065
    public function addEndPanel()
1066
    {
1067
        $this->addHtml('</div></div></div>');
1068
    }
1069
1070
    /**
1071
     * Draws a panel of options see the course_info/infocours.php page.
1072
     *
1073
     * @param string $name      internal name
1074
     * @param string $title     visible title
1075
     * @param array  $groupList list of group or elements
1076
     */
1077
    public function addPanelOption($name, $title, $groupList, $icon, $open)
1078
    {
1079
        $this->addStartPanel($name, $title, $open, $icon);
1080
1081
        foreach ($groupList as $groupName => $group) {
1082
            // Add group array
1083
            if (!empty($groupName) && is_array($group)) {
1084
                $this->addGroup($group, '', $groupName);
1085
            }
1086
            // Add element
1087
            if ($group instanceof HTML_QuickForm_element) {
1088
                $this->addElement($group);
1089
            }
1090
        }
1091
1092
        $this->addEndPanel();
1093
    }
1094
1095
    /**
1096
     * Adds a HTML-editor to the form.
1097
     *
1098
     * @param string       $name
1099
     * @param string|array $label      The label for the form-element
1100
     * @param bool         $required   (optional) Is the form-element required (default=true)
1101
     * @param bool         $fullPage   (optional) When it is true, the editor loads completed html code for a full page
1102
     * @param array        $config     (optional) Configuration settings for the online editor
1103
     * @param array        $attributes
1104
     *
1105
     * @throws Exception
1106
     * @throws HTML_QuickForm_Error
1107
     */
1108
    public function addHtmlEditor(
1109
        $name,
1110
        $label,
1111
        $required = true,
1112
        $fullPage = false,
1113
        $config = [],
1114
        $attributes = []
1115
    ) {
1116
        $attributes['rows'] = $config['rows'] ?? 15;
1117
        $attributes['cols'] = $config['cols'] ?? 80;
1118
        $attributes['cols-size'] = $config['cols-size'] ?? [];
1119
        $attributes['class'] = $config['class'] ?? [];
1120
        $cleanName = str_replace(['[', ']', '#'], '', $name);
1121
1122
        if (empty($attributes['id'])) {
1123
            $attributes['id'] = $cleanName;
1124
        }
1125
1126
        //$attributes['id'] = $config['id'] ?? 'editor_'.$cleanName;
1127
1128
        $this->addElement('html_editor', $name, $label, $attributes, $config);
1129
        $this->applyFilter($name, 'trim');
1130
        if ($required) {
1131
            $this->addRule($name, get_lang('Required field'), 'required');
1132
        }
1133
1134
        /** @var HtmlEditor $element */
1135
        $element = $this->getElement($name);
1136
        $config['style'] = $config['style'] ?? false;
1137
        if ($fullPage) {
1138
            $config['fullPage'] = true;
1139
            // Adds editor_content.css in ckEditor
1140
            $config['style'] = true;
1141
        }
1142
1143
        if ($element->editor) {
1144
            $element->editor->processConfig($config);
1145
        }
1146
    }
1147
1148
    /**
1149
     * Adds a Google Maps Geolocalization field to the form.
1150
     *
1151
     * @param      $name
1152
     * @param      $label
1153
     * @param bool $hideGeoLocalizationDetails
1154
     */
1155
    public function addGeoLocationMapField($name, $label, $dataValue, $hideGeoLocalizationDetails = false)
1156
    {
1157
        $gMapsPlugin = GoogleMapsPlugin::create();
1158
        $geolocalization = 'true' === $gMapsPlugin->get('enable_api');
1159
1160
        if ($geolocalization && false === $gMapsPlugin->javascriptIncluded) {
1161
            $gmapsApiKey = $gMapsPlugin->get('api_key');
1162
            $url = '//maps.googleapis.com/maps/api/js?key='.$gmapsApiKey;
1163
            $this->addHtml('<script type="text/javascript" src="'.$url.'" ></script>');
1164
            $gMapsPlugin->javascriptIncluded = true;
1165
        }
1166
1167
        $this->addElement(
1168
            'text',
1169
            $name,
1170
            $label,
1171
            ['id' => $name]
1172
        );
1173
1174
        $this->addHidden(
1175
            $name.'_coordinates',
1176
            '',
1177
            ['id' => $name.'_coordinates']
1178
        );
1179
1180
        $this->applyFilter($name, 'stripslashes');
1181
        $this->applyFilter($name, 'trim');
1182
1183
        $this->addHtml(Extrafield::getLocalizationJavascript($name, $dataValue));
1184
1185
        if ($hideGeoLocalizationDetails) {
1186
            $this->addHtml('<div style="display:none">');
1187
        }
1188
1189
        $this->addHtml(
1190
            Extrafield::getLocalizationInput($name, $label)
1191
        );
1192
1193
        if ($hideGeoLocalizationDetails) {
1194
            $this->addHtml('</div>');
1195
        }
1196
    }
1197
1198
    /**
1199
     * @param string       $name
1200
     * @param string|array $label
1201
     *
1202
     * @return mixed
1203
     */
1204
    public function addButtonAdvancedSettings($name, $label = null)
1205
    {
1206
        $label = !empty($label) ? $label : get_lang('Advanced settings');
1207
1208
        return $this->addElement('advanced_settings', $name, $label);
1209
    }
1210
1211
    /**
1212
     * Adds a progress loading image to the form.
1213
     */
1214
    public function addProgress($delay = 2, $label = '')
1215
    {
1216
        if (empty($label)) {
1217
            $label = get_lang('Please stand by...');
1218
        }
1219
        $this->with_progress_bar = true;
1220
        $id = $this->getAttribute('id');
1221
1222
        $this->updateAttributes("onsubmit=\"javascript: addProgress('".$id."')\"");
1223
        $this->addHtml('<script language="javascript" src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/upload.js" type="text/javascript"></script>');
1224
    }
1225
1226
    /**
1227
     * This function has been created for avoiding changes directly within QuickForm class.
1228
     * When we use it, the element is threated as 'required' to be dealt during validation.
1229
     *
1230
     * @param array  $elements The array of elements
1231
     * @param string $message  The message displayed
1232
     */
1233
    public function add_multiple_required_rule($elements, $message)
1234
    {
1235
        $this->_required[] = $elements[0];
1236
        $this->addRule($elements, $message, 'multiple_required');
1237
    }
1238
1239
    /**
1240
     * Displays the form.
1241
     * If an element in the form didn't validate, an error message is showed
1242
     * asking the user to complete the form.
1243
     */
1244
    public function display()
1245
    {
1246
        echo $this->returnForm();
1247
    }
1248
1249
    /**
1250
     * Returns the HTML code of the form.
1251
     *
1252
     * @return string $return_value HTML code of the form
1253
     */
1254
    public function returnForm()
1255
    {
1256
        $returnValue = '';
1257
        /** @var HTML_QuickForm_element $element */
1258
        foreach ($this->_elements as &$element) {
1259
            $element->setLayout($this->getLayout());
1260
            $elementError = parent::getElementError($element->getName());
1261
            if (!is_null($elementError)) {
1262
                $returnValue .= Display::return_message($elementError, 'warning').'<br />';
1263
                break;
1264
            }
1265
        }
1266
1267
        $returnValue .= parent::toHtml();
1268
        // Add div-element which is to hold the progress bar
1269
        $id = $this->getAttribute('id');
1270
        if (isset($this->with_progress_bar) && $this->with_progress_bar) {
1271
            // @todo improve UI
1272
            $returnValue .= '<br />
1273
            <div id="loading_div_'.$id.'" class="loading_div" style="display:none;margin-left:40%; margin-top:10px; height:50px;">
1274
                <div class="wobblebar-loader"></div>
1275
            </div>
1276
            ';
1277
        }
1278
1279
        return $returnValue;
1280
    }
1281
1282
    /**
1283
     * Returns the HTML code of the form.
1284
     * If an element in the form didn't validate, an error message is showed
1285
     * asking the user to complete the form.
1286
     *
1287
     * @return string $return_value HTML code of the form
1288
     *
1289
     * @author Patrick Cool <[email protected]>, Ghent University, august 2006
1290
     * @author Julio Montoya
1291
     *
1292
     * @deprecated use returnForm()
1293
     */
1294
    public function return_form()
1295
    {
1296
        return $this->returnForm();
1297
    }
1298
1299
    /**
1300
     * @return HTML_QuickForm_Renderer_Default
1301
     */
1302
    public static function getDefaultRenderer()
1303
    {
1304
        return
1305
            isset($GLOBALS['_HTML_QuickForm_default_renderer']) ?
1306
                $GLOBALS['_HTML_QuickForm_default_renderer'] : null;
1307
    }
1308
1309
    /**
1310
     * Adds a input of type url to the form.
1311
     *
1312
     * @param string $name       The label for the form-element
1313
     * @param string $label      The element name
1314
     * @param bool   $required   Optional. Is the form-element required (default=true)
1315
     * @param array  $attributes Optional. List of attributes for the form-element
1316
     */
1317
    public function addUrl($name, $label, $required = true, $attributes = [])
1318
    {
1319
        $this->addElement('url', $name, $label, $attributes);
1320
        $this->applyFilter($name, 'trim');
1321
        $this->addRule($name, get_lang('Insert a valid URL'), 'url');
1322
1323
        if ($required) {
1324
            $this->addRule($name, get_lang('Required field'), 'required');
1325
        }
1326
    }
1327
1328
    /**
1329
     * Adds a text field for letters to the form.
1330
     * A trim-filter is attached to the field.
1331
     *
1332
     * @param string $name       The element name
1333
     * @param string $label      The label for the form-element
1334
     * @param bool   $required   Optional. Is the form-element required (default=true)
1335
     * @param array  $attributes Optional. List of attributes for the form-element
1336
     */
1337
    public function addTextLettersOnly(
1338
        $name,
1339
        $label,
1340
        $required = false,
1341
        $attributes = []
1342
    ) {
1343
        $attributes = array_merge(
1344
            $attributes,
1345
            [
1346
                'pattern' => '[a-zA-ZñÑ]+',
1347
                'title' => get_lang('Only letters'),
1348
            ]
1349
        );
1350
1351
        $this->addElement(
1352
            'text',
1353
            $name,
1354
            [
1355
                $label,
1356
                get_lang('Only letters'),
1357
            ],
1358
            $attributes
1359
        );
1360
1361
        $this->applyFilter($name, 'trim');
1362
1363
        if ($required) {
1364
            $this->addRule($name, get_lang('Required field'), 'required');
1365
        }
1366
1367
        $this->addRule(
1368
            $name,
1369
            get_lang('Only letters'),
1370
            'regex',
1371
            '/^[a-zA-ZñÑ]+$/'
1372
        );
1373
    }
1374
1375
    /**
1376
     * @param string $name
1377
     * @param string $label
1378
     * @param array  $attributes
1379
     * @param bool   $required
1380
     *
1381
     * @return HTML_QuickForm_element
1382
     */
1383
    public function addNumeric($name, $label, $attributes = [], $required = false)
1384
    {
1385
        $element = $this->addElement('Number', $name, $label, $attributes);
1386
1387
        if ($required) {
1388
            $this->addRule($name, get_lang('ThisFieldIsRequired'), 'required');
1389
        }
1390
1391
        return $element;
1392
    }
1393
1394
    /**
1395
     * Adds a text field for alphanumeric characters to the form.
1396
     * A trim-filter is attached to the field.
1397
     *
1398
     * @param string $name       The element name
1399
     * @param string $label      The label for the form-element
1400
     * @param bool   $required   Optional. Is the form-element required (default=true)
1401
     * @param array  $attributes Optional. List of attributes for the form-element
1402
     */
1403
    public function addTextAlphanumeric(
1404
        $name,
1405
        $label,
1406
        $required = false,
1407
        $attributes = []
1408
    ) {
1409
        $attributes = array_merge(
1410
            $attributes,
1411
            [
1412
                'pattern' => '[a-zA-Z0-9ñÑ]+',
1413
                'title' => get_lang('Only lettersAndNumbers'),
1414
            ]
1415
        );
1416
1417
        $this->addElement(
1418
            'text',
1419
            $name,
1420
            [
1421
                $label,
1422
                get_lang('Only lettersAndNumbers'),
1423
            ],
1424
            $attributes
1425
        );
1426
1427
        $this->applyFilter($name, 'trim');
1428
1429
        if ($required) {
1430
            $this->addRule($name, get_lang('Required field'), 'required');
1431
        }
1432
1433
        $this->addRule(
1434
            $name,
1435
            get_lang('Only lettersAndNumbers'),
1436
            'regex',
1437
            '/^[a-zA-Z0-9ÑÑ]+$/'
1438
        );
1439
    }
1440
1441
    /**
1442
     * @param string $name
1443
     * @param $label
1444
     * @param bool  $required
1445
     * @param array $attributes
1446
     * @param bool  $allowNegative
1447
     * @param int   $minValue
1448
     * @param null  $maxValue
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $maxValue is correct as it would always require null to be passed?
Loading history...
1449
     */
1450
    public function addFloat(
1451
        $name,
1452
        $label,
1453
        $required = false,
1454
        $attributes = [],
1455
        $allowNegative = false,
1456
        $minValue = null,
1457
        $maxValue = null
1458
    ) {
1459
        $this->addElement(
1460
            'FloatNumber',
1461
            $name,
1462
            $label,
1463
            $attributes
1464
        );
1465
1466
        $this->applyFilter($name, 'trim');
1467
1468
        if ($required) {
1469
            $this->addRule($name, get_lang('Required field'), 'required');
1470
        }
1471
1472
        // Rule allows "," and "."
1473
        /*$this->addRule(
1474
            $name,
1475
            get_lang('Only numbers'),
1476
            'regex',
1477
            '/(^-?\d\d*\.\d*$)|(^-?\d\d*$)|(^-?\.\d\d*$)|(^-?\d\d*\,\d*$)|(^-?\,\d\d*$)/'
1478
        );*/
1479
1480
        if (false == $allowNegative) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1481
            $this->addRule(
1482
                $name,
1483
                get_lang('Negative value'),
1484
                'compare',
1485
                '>=',
1486
                'server',
1487
                false,
1488
                false,
1489
                0
1490
            );
1491
        }
1492
1493
        if (!is_null($minValue)) {
1494
            $this->addRule(
1495
                $name,
1496
                get_lang('Under the minimum.'),
1497
                'compare',
1498
                '>=',
1499
                'server',
1500
                false,
1501
                false,
1502
                $minValue
1503
            );
1504
        }
1505
1506
        if (!is_null($maxValue)) {
1507
            $this->addRule(
1508
                $name,
1509
                get_lang('Value exceeds score.'),
1510
                'compare',
1511
                '<=',
1512
                'server',
1513
                false,
1514
                false,
1515
                $maxValue
1516
            );
1517
        }
1518
    }
1519
1520
    /**
1521
     * Adds a text field for letters and spaces to the form.
1522
     * A trim-filter is attached to the field.
1523
     *
1524
     * @param string $name       The element name
1525
     * @param string $label      The label for the form-element
1526
     * @param bool   $required   Optional. Is the form-element required (default=true)
1527
     * @param array  $attributes Optional. List of attributes for the form-element
1528
     */
1529
    public function addTextLettersAndSpaces(
1530
        $name,
1531
        $label,
1532
        $required = false,
1533
        $attributes = []
1534
    ) {
1535
        $attributes = array_merge(
1536
            $attributes,
1537
            [
1538
                'pattern' => '[a-zA-ZñÑ\s]+',
1539
                'title' => get_lang('Only lettersAndSpaces'),
1540
            ]
1541
        );
1542
1543
        $this->addElement(
1544
            'text',
1545
            $name,
1546
            [
1547
                $label,
1548
                get_lang('Only lettersAndSpaces'),
1549
            ],
1550
            $attributes
1551
        );
1552
1553
        $this->applyFilter($name, 'trim');
1554
1555
        if ($required) {
1556
            $this->addRule($name, get_lang('Required field'), 'required');
1557
        }
1558
1559
        $this->addRule(
1560
            $name,
1561
            get_lang('Only lettersAndSpaces'),
1562
            'regex',
1563
            '/^[a-zA-ZñÑ\s]+$/'
1564
        );
1565
    }
1566
1567
    /**
1568
     * Adds a text field for alphanumeric and spaces characters to the form.
1569
     * A trim-filter is attached to the field.
1570
     *
1571
     * @param string $name       The element name
1572
     * @param string $label      The label for the form-element
1573
     * @param bool   $required   Optional. Is the form-element required (default=true)
1574
     * @param array  $attributes Optional. List of attributes for the form-element
1575
     */
1576
    public function addTextAlphanumericAndSpaces(
1577
        $name,
1578
        $label,
1579
        $required = false,
1580
        $attributes = []
1581
    ) {
1582
        $attributes = array_merge(
1583
            $attributes,
1584
            [
1585
                'pattern' => '[a-zA-Z0-9ñÑ\s]+',
1586
                'title' => get_lang('Only lettersAndNumbersAndSpaces'),
1587
            ]
1588
        );
1589
1590
        $this->addElement(
1591
            'text',
1592
            $name,
1593
            [
1594
                $label,
1595
                get_lang('Only lettersAndNumbersAndSpaces'),
1596
            ],
1597
            $attributes
1598
        );
1599
1600
        $this->applyFilter($name, 'trim');
1601
1602
        if ($required) {
1603
            $this->addRule($name, get_lang('Required field'), 'required');
1604
        }
1605
1606
        $this->addRule(
1607
            $name,
1608
            get_lang('Only lettersAndNumbersAndSpaces'),
1609
            'regex',
1610
            '/^[a-zA-Z0-9ñÑ\s]+$/'
1611
        );
1612
    }
1613
1614
    /**
1615
     * @param string $url
1616
     * @param string $urlToRedirect after upload redirect to this page
1617
     */
1618
    public function addMultipleUpload($url, $urlToRedirect = '')
1619
    {
1620
        $inputName = 'input_file_upload';
1621
        $this->addMultipleUploadJavascript($url, $inputName, $urlToRedirect);
1622
1623
        $this->addHtml('
1624
            <div class="description-upload">
1625
            '.get_lang('Click on the box below to select files from your computer (you can use CTRL + clic to select various files at a time), or drag and drop some files from your desktop directly over the box below. The system will handle the rest!').'
1626
            </div>
1627
            <span class="btn btn--success fileinput-button">
1628
                <i class="glyphicon glyphicon-plus"></i>
1629
                <span>'.get_lang('Add files').'</span>
1630
                <!-- The file input field used as target for the file upload widget -->
1631
                <input id="'.$inputName.'" type="file" name="files[]" multiple>
1632
            </span>
1633
            <div id="dropzone">
1634
                <div class="button-load">
1635
                '.get_lang('Click or drag and drop files here to upload them').'
1636
                </div>
1637
            </div>
1638
            <br />
1639
            <!-- The global progress bar -->
1640
            <div id="progress" class="progress">
1641
                <div class="progress-bar progress-bar-success"></div>
1642
            </div>
1643
            <div id="files" class="files"></div>
1644
        ');
1645
    }
1646
1647
    /**
1648
     * @param string $elementName
1649
     * @param string $groupName   if element is inside a group
1650
     *
1651
     * @throws Exception
1652
     */
1653
    public function addPasswordRule($elementName, $groupName = '')
1654
    {
1655
        if ('true' == api_get_setting('security.check_password')) {
1656
            $message = get_lang('this password  is too simple. Use a pass like this').': '.api_generate_password();
1657
1658
            if (!empty($groupName)) {
1659
                $groupObj = $this->getElement($groupName);
1660
1661
                if ($groupObj instanceof HTML_QuickForm_group) {
1662
                    $elementName = $groupObj->getElementName($elementName);
1663
1664
                    if (false === $elementName) {
1665
                        throw new Exception("The $groupName doesn't have the element $elementName");
1666
                    }
1667
1668
                    $this->_rules[$elementName][] = [
1669
                        'type' => 'callback',
1670
                        'format' => 'api_check_password',
1671
                        'message' => $message,
1672
                        'validation' => '',
1673
                        'reset' => false,
1674
                        'group' => $groupName,
1675
                    ];
1676
                }
1677
            } else {
1678
                $this->addRule(
1679
                    $elementName,
1680
                    $message,
1681
                    'callback',
1682
                    'api_check_password'
1683
                );
1684
            }
1685
        }
1686
    }
1687
1688
    /**
1689
     * Add an element with user ID and avatar to the form.
1690
     * It needs a Chamilo\CoreBundle\Entity\User as value. The exported value is the Chamilo\CoreBundle\Entity\User ID.
1691
     *
1692
     * @see \UserAvatar
1693
     *
1694
     * @param string $name
1695
     * @param string $label
1696
     * @param string $imageSize Optional. Small, medium or large image
1697
     * @param string $subtitle  Optional. The subtitle for the field
1698
     *
1699
     * @return \UserAvatar
1700
     */
1701
    public function addUserAvatar($name, $label, $imageSize = 'small', $subtitle = '')
1702
    {
1703
        return $this->addElement('UserAvatar', $name, $label, ['image_size' => $imageSize, 'sub_title' => $subtitle]);
1704
    }
1705
1706
    /**
1707
     * @param array $typeList
1708
     */
1709
    public function addEmailTemplate($typeList)
1710
    {
1711
        $mailManager = new MailTemplateManager();
1712
        foreach ($typeList as $type) {
1713
            $list = $mailManager->get_all(
1714
                ['where' => ['type = ? AND url_id = ?' => [$type, api_get_current_access_url_id()]]]
1715
            );
1716
1717
            $options = [get_lang('Select')];
1718
            $name = $type;
1719
            $defaultId = '';
1720
            foreach ($list as $item) {
1721
                $options[$item['id']] = $item['name'];
1722
                $name = $item['name'];
1723
                if (empty($defaultId)) {
1724
                    $defaultId = 1 == $item['default_template'] ? $item['id'] : '';
1725
                }
1726
            }
1727
1728
            $url = api_get_path(WEB_AJAX_PATH).'mail.ajax.php?a=select_option';
1729
            $typeNoDots = 'email_template_option_'.str_replace('.tpl', '', $type);
1730
            $this->addSelect(
1731
                'email_template_option['.$type.']',
1732
                $name,
1733
                $options,
1734
                ['id' => $typeNoDots]
1735
            );
1736
1737
            $templateNoDots = 'email_template_'.str_replace('.tpl', '', $type);
1738
            $templateNoDotsBlock = 'email_template_block_'.str_replace('.tpl', '', $type);
1739
            $this->addHtml('<div id="'.$templateNoDotsBlock.'" style="display:none">');
1740
            $this->addTextarea(
1741
                $templateNoDots,
1742
                get_lang('Preview'),
1743
                ['disabled' => 'disabled ', 'id' => $templateNoDots, 'rows' => '5']
1744
            );
1745
            $this->addHtml('</div>');
1746
1747
            $this->addHtml("<script>
1748
            $(function() {
1749
                var defaultValue = '$defaultId';
1750
                $('#$typeNoDots').val(defaultValue);
1751
                $('#$typeNoDots').selectpicker('render');
1752
                if (defaultValue != '') {
1753
                    var selected = $('#$typeNoDots option:selected').val();
1754
                    $.ajax({
1755
                        url: '$url' + '&id=' + selected+ '&template_name=$type',
1756
                        success: function (data) {
1757
                            $('#$templateNoDots').html(data);
1758
                            $('#$templateNoDotsBlock').show();
1759
                            return;
1760
                        },
1761
                    });
1762
                }
1763
1764
                $('#$typeNoDots').on('change', function(){
1765
                    var selected = $('#$typeNoDots option:selected').val();
1766
                    $.ajax({
1767
                        url: '$url' + '&id=' + selected,
1768
                        success: function (data) {
1769
                            $('#$templateNoDots').html(data);
1770
                            $('#$templateNoDotsBlock').show();
1771
                            return;
1772
                        },
1773
                    });
1774
                });
1775
            });
1776
            </script>");
1777
        }
1778
    }
1779
1780
    /**
1781
     * Add email rule for an element.
1782
     */
1783
    public function addEmailRule(string $element)
1784
    {
1785
        $this->addRule(
1786
            $element,
1787
            get_lang('The email address is not complete or contains some invalid characters'),
1788
            'email'
1789
        );
1790
    }
1791
1792
    /**
1793
     * @param string $url           page that will handle the upload
1794
     * @param string $inputName
1795
     * @param string $urlToRedirect
1796
     */
1797
    private function addMultipleUploadJavascript($url, $inputName, $urlToRedirect = '')
1798
    {
1799
        $redirectCondition = '';
1800
        if (!empty($urlToRedirect)) {
1801
            $redirectCondition = "window.location.replace('$urlToRedirect'); ";
1802
        }
1803
        $icon = Display::getMdiIcon('text-box-outline', 'ch-tool-icon', null, ICON_SIZE_SMALL);
1804
        $this->addHtml("
1805
        <script>
1806
        $(function () {
1807
            'use strict';
1808
            $('#".$this->getAttribute('id')."').submit(function() {
1809
                return false;
1810
            });
1811
1812
            $('#dropzone').on('click', function() {
1813
                $('#".$inputName."').click();
1814
            });
1815
1816
            var url = '".$url."';
1817
            var uploadButton = $('<button/>')
1818
                .addClass('btn btn--primary')
1819
                .prop('disabled', true)
1820
                .text('".addslashes(get_lang('Loading'))."')
1821
                .on('click', function () {
1822
                    var \$this = $(this),
1823
                    data = \$this.data();
1824
                    \$this
1825
                        .off('click')
1826
                        .text('".addslashes(get_lang('Cancel'))."')
1827
                        .on('click', function () {
1828
                            \$this.remove();
1829
                            data.abort();
1830
                        });
1831
                    data.submit().always(function () {
1832
                        \$this.remove();
1833
                    });
1834
                });
1835
1836
            $('#".$inputName."').fileupload({
1837
                url: url,
1838
                dataType: 'json',
1839
                // Enable image resizing, except for Android and Opera,
1840
                // which actually support image resizing, but fail to
1841
                // send Blob objects via XHR requests:
1842
                disableImageResize: /Android(?!.*Chrome)|Opera/.test(window.navigator.userAgent),
1843
                previewMaxWidth: 300,
1844
                previewMaxHeight: 169,
1845
                previewCrop: true,
1846
                dropzone: $('#dropzone'),
1847
            }).on('fileuploadadd', function (e, data) {
1848
                data.context = $('<div class=\"row\" />').appendTo('#files');
1849
                $.each(data.files, function (index, file) {
1850
                    var node = $('<div class=\"col-sm-5 file_name\">').text(file.name);
1851
                    node.appendTo(data.context);
1852
                });
1853
            }).on('fileuploadprocessalways', function (e, data) {
1854
                var index = data.index,
1855
                    file = data.files[index],
1856
                    node = $(data.context.children()[index]);
1857
                if (file.preview) {
1858
                    data.context.prepend($('<div class=\"col-sm-4\">').html(file.preview));
1859
                } else {
1860
                    data.context.prepend($('<div class=\"col-sm-4\">').html('".$icon."'));
1861
                }
1862
                if (index + 1 === data.files.length) {
1863
                    data.context.find('button')
1864
                        .text('Upload')
1865
                        .prop('disabled', !!data.files.error);
1866
                }
1867
            }).on('fileuploadprogressall', function (e, data) {
1868
                var progress = parseInt(data.loaded / data.total * 100, 10);
1869
                $('#progress .progress-bar').css(
1870
                    'width',
1871
                    progress + '%'
1872
                );
1873
            }).on('fileuploaddone', function (e, data) {
1874
                $.each(data.result.files, function (index, file) {
1875
                    if (file.error) {
1876
                        var link = $('<div>')
1877
                            .attr({class : 'panel-image'})                            ;
1878
                        $(data.context.children()[index]).parent().wrap(link);
1879
                        // Update file name with new one from Chamilo
1880
                        $(data.context.children()[index]).parent().find('.file_name').html(file.name);
1881
                        var message = $('<div class=\"col-sm-3\">').html(
1882
                            $('<span class=\"message-image-danger\"/>').text(file.error)
1883
                        );
1884
                        $(data.context.children()[index]).parent().append(message);
1885
1886
                        return;
1887
                    }
1888
                    if (file.url) {
1889
                        var link = $('<a>')
1890
                            .attr({target: '_blank', class : 'panel-image'})
1891
                            .prop('href', file.url);
1892
                        $(data.context.children()[index]).parent().wrap(link);
1893
                    }
1894
                    // Update file name with new one from Chamilo
1895
                    $(data.context.children()[index]).parent().find('.file_name').html(file.name);
1896
                    var message = $('<div class=\"col-sm-3\">').html(
1897
                        $('<span class=\"alert alert-success\"/>').text('".addslashes(get_lang('File upload succeeded!'))."')
1898
                    );
1899
                    $(data.context.children()[index]).parent().append(message);
1900
                });
1901
                $('#dropzone').removeClass('hover');
1902
                ".$redirectCondition."
1903
            }).on('fileuploadfail', function (e, data) {
1904
                $.each(data.files, function (index) {
1905
                    var failedMessage = '".addslashes(get_lang('The file upload has failed.'))."';
1906
                    var error = $('<div class=\"col-sm-3\">').html(
1907
                        $('<span class=\"alert alert-danger\"/>').text(failedMessage)
1908
                    );
1909
                    $(data.context.children()[index]).parent().append(error);
1910
                });
1911
                $('#dropzone').removeClass('hover');
1912
            }).prop('disabled', !$.support.fileInput).parent().addClass($.support.fileInput ? undefined : 'disabled');
1913
1914
            $('#dropzone').on('dragover', function (e) {
1915
                // dragleave callback implementation
1916
                $('#dropzone').addClass('hover');
1917
            });
1918
1919
            $('#dropzone').on('dragleave', function (e) {
1920
                $('#dropzone').removeClass('hover');
1921
            });
1922
            $('.fileinput-button').hide();
1923
        });
1924
        </script>");
1925
    }
1926
}
1927
1928
/**
1929
 * Cleans HTML text filter.
1930
 *
1931
 * @param string $html HTML to clean
1932
 * @param int    $mode (optional)
1933
 *
1934
 * @return string The cleaned HTML
1935
 */
1936
function html_filter($html, $mode = NO_HTML)
1937
{
1938
    $allowed_tags = HTML_QuickForm_Rule_HTML::get_allowed_tags($mode);
1939
    $cleaned_html = kses($html, $allowed_tags);
0 ignored issues
show
The function kses was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1939
    $cleaned_html = /** @scrutinizer ignore-call */ kses($html, $allowed_tags);
Loading history...
1940
1941
    return $cleaned_html;
1942
}
1943
1944
function html_filter_teacher($html)
1945
{
1946
    return html_filter($html, TEACHER_HTML);
1947
}
1948
1949
function html_filter_student($html)
1950
{
1951
    return html_filter($html, STUDENT_HTML);
1952
}
1953
1954
function html_filter_teacher_fullpage($html)
1955
{
1956
    return html_filter($html, TEACHER_HTML_FULLPAGE);
1957
}
1958
1959
function html_filter_student_fullpage($html)
1960
{
1961
    return html_filter($html, STUDENT_HTML_FULLPAGE);
1962
}
1963
1964
/**
1965
 * Cleans mobile phone number text.
1966
 *
1967
 * @param string $mobilePhoneNumber Mobile phone number to clean
1968
 *
1969
 * @return string The cleaned mobile phone number
1970
 */
1971
function mobile_phone_number_filter($mobilePhoneNumber)
1972
{
1973
    $mobilePhoneNumber = str_replace(['+', '(', ')'], '', $mobilePhoneNumber);
1974
1975
    return ltrim($mobilePhoneNumber, '0');
1976
}
1977