Passed
Push — master ( 66fe7d...dfeb05 )
by Julito
20:19 queued 13s
created

FormValidator::addStartPanel()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 38
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 1
Metric Value
cc 3
eloc 14
c 1
b 1
f 1
nc 4
nop 3
dl 0
loc 38
rs 9.7998
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
        // Default form class.
43
        if (!isset($attributes['class']) || empty($attributes)) {
44
            $attributes['class'] = 'form-horizontal q-pt-md';
45
        }
46
47
        if (isset($attributes['class']) && false !== strpos($attributes['class'], 'form-search')) {
48
            $layout = 'inline';
49
        }
50
51
        $this->setLayout($layout);
52
53
        // Form template
54
        $formTemplate = $this->getFormTemplate();
55
56
        switch ($layout) {
57
            case self::LAYOUT_HORIZONTAL:
58
                $attributes['class'] = 'ch w-full ';
59
                break;
60
            case self::LAYOUT_BOX_SEARCH:
61
                $attributes['class'] = 'ch w-full flex gap-2';
62
                $formTemplate = $this->getInLineTemplate();
63
                break;
64
            case self::LAYOUT_INLINE:
65
                $attributes['class'] = 'ch flex gap-2 ';
66
                $formTemplate = $this->getInLineTemplate();
67
                break;
68
            case self::LAYOUT_BOX:
69
                $attributes['class'] = 'ch flex gap-1 ';
70
                break;
71
            case self::LAYOUT_GRID:
72
                $attributes['class'] = 'ch form-grid';
73
                $formTemplate = $this->getGridFormTemplate();
74
                break;
75
        }
76
77
        parent::__construct($name, $method, $action, $target, $attributes, $trackSubmit);
78
79
        // Modify the default templates
80
        $renderer = &$this->defaultRenderer();
81
        $renderer->setFormTemplate($formTemplate);
82
83
        // Element template
84
        if ((isset($attributes['class']) && 'form-inline' === $attributes['class']) ||
85
            (self::LAYOUT_INLINE === $layout || self::LAYOUT_BOX_SEARCH === $layout)
86
        ) {
87
            $elementTemplate = ' {label}  {element} ';
88
            $renderer->setElementTemplate($elementTemplate);
89
        } elseif (isset($attributes['class']) && 'form-search' === $attributes['class']) {
90
            $elementTemplate = ' {label}  {element} ';
91
            $renderer->setElementTemplate($elementTemplate);
92
        } else {
93
            $renderer->setElementTemplate($this->getDefaultElementTemplate());
94
95
            // Display a gray div in the buttons
96
            $templateSimple = '<div class="form-actions">{label} {element}</div>';
97
            $renderer->setElementTemplate($templateSimple, 'submit_in_actions');
98
99
            //Display a gray div in the buttons + makes the button available when scrolling
100
            $templateBottom = '<div class="form-actions bottom_actions bg-form">{label} {element}</div>';
101
            $renderer->setElementTemplate($templateBottom, 'submit_fixed_in_bottom');
102
            $renderer->setElementTemplate($templateSimple, 'buttons_in_action');
103
104
            $templateSimpleRight = '<div class="form-actions"> <div class="pull-right">{label} {element}</div></div>';
105
            $renderer->setElementTemplate($templateSimpleRight, 'buttons_in_action_right');
106
        }
107
108
        //Set Header template
109
        $renderer->setHeaderTemplate(' <h1 class="text-2xl font-small text-gray-800 mb-4">{header}<hr /></h1>');
110
111
        $required = '<span class="form_required">*</span> <small>'.get_lang('Required field').'</small>';
112
        if ((self::LAYOUT_INLINE === $layout || self::LAYOUT_BOX_SEARCH === $layout)) {
113
            $required = '';
114
        }
115
        // Set required field template
116
        $this->setRequiredNote($required);
117
118
        $noteTemplate = <<<EOT
119
	<div class="form-group">
120
		<div class="col-sm-offset-2 col-sm-10">{requiredNote}</div>
121
	</div>
122
EOT;
123
        $renderer->setRequiredNoteTemplate($noteTemplate);
124
    }
125
126
    public function getFormTemplate(): string
127
    {
128
        return '
129
                <div class="pt-4">
130
                    <div class="q-card p-4">
131
                        <form{attributes}>
132
                            {content}
133
                            {hidden}
134
                        </form>
135
                    </div>
136
                </div>
137
        ';
138
    }
139
140
    public function getInLineTemplate(): string
141
    {
142
        return '<form{attributes}>
143
            {content}
144
            {hidden}
145
        </form>';
146
    }
147
148
    public function getGridFormTemplate(): string
149
    {
150
        return '
151
        <style>
152
            .form_list {
153
                display: grid;
154
                grid-template-columns:  repeat(auto-fill, minmax(300px, 1fr));;
155
                grid-gap: 10px 30px;
156
                gap: 10px 30px;
157
            }
158
            .form_list .input-group {
159
                display:block;
160
            }
161
        </style>
162
        <form{attributes}>
163
            <div class="form_list">
164
                {content}
165
            </div>
166
        {hidden}
167
        </form>';
168
    }
169
170
    /**
171
     * @todo this function should be added in the element class
172
     */
173
    public function getDefaultElementTemplate(): string
174
    {
175
        return '
176
            <div class="row mb-3 {error_class}">
177
                <label {label-for} class="col-sm-2 col-form-label {extra_label_class}" >
178
                    <!-- BEGIN required --><span class="form_required">*</span><!-- END required -->
179
                    {label}
180
                </label>
181
                <div class="col-sm-8">
182
                    {icon}
183
                    {element}
184
                    <!-- BEGIN label_2 -->
185
                        <p class="help-block">{label_2}</p>
186
                    <!-- END label_2 -->
187
188
                    <!-- BEGIN error -->
189
                        <span class="help-inline help-block">{error}</span>
190
                    <!-- END error -->
191
                </div>
192
                <div class="col-sm-2">
193
                    <!-- BEGIN label_3 -->
194
                        {label_3}
195
                    <!-- END label_3 -->
196
                </div>
197
            </div>';
198
    }
199
200
    /**
201
     * @return string
202
     */
203
    public function getLayout()
204
    {
205
        return $this->layout;
206
    }
207
208
    /**
209
     * @param string $layout
210
     */
211
    public function setLayout($layout)
212
    {
213
        $this->layout = $layout;
214
    }
215
216
    /**
217
     * Adds a text field to the form.
218
     * A trim-filter is attached to the field.
219
     *
220
     * @param string|array $label      The label for the form-element
221
     * @param string       $name       The element name
222
     * @param bool         $required   (optional)    Is the form-element required (default=true)
223
     * @param array        $attributes (optional)    List of attributes for the form-element
224
     *
225
     * @return HTML_QuickForm_text
226
     */
227
    public function addText($name, $label, $required = true, $attributes = [], $createElement = false)
228
    {
229
        if ($createElement) {
230
            $element = $this->createElement('text', $name, $label, $attributes);
231
        } else {
232
            $element = $this->addElement('text', $name, $label, $attributes);
233
        }
234
235
        $this->applyFilter($name, 'trim');
236
        if ($required) {
237
            $this->addRule($name, get_lang('Required field'), 'required');
238
        }
239
240
        return $element;
241
    }
242
243
    /**
244
     * Add hidden course params.
245
     */
246
    public function addCourseHiddenParams()
247
    {
248
        $this->addHidden('cid', api_get_course_id());
249
        $this->addHidden('sid', api_get_session_id());
250
    }
251
252
    /**
253
     * The "date_range_picker" element creates 2 hidden fields
254
     * "elementName" + "_start"  and "elementName" + "_end"
255
     * For example if the name is "range", you will have 2 new fields
256
     * when executing $form->getSubmitValues()
257
     * "range_start" and "range_end".
258
     *
259
     * @param string $name
260
     * @param string $label
261
     * @param bool   $required
262
     * @param array  $attributes
263
     */
264
    public function addDateRangePicker($name, $label, $required = true, $attributes = [])
265
    {
266
        $this->addElement('date_range_picker', $name, $label, $attributes);
267
        $this->addElement('hidden', $name.'_start');
268
        $this->addElement('hidden', $name.'_end');
269
270
        if ($required) {
271
            $this->addRule($name, get_lang('Required field'), 'required');
272
        }
273
    }
274
275
    /**
276
     * @param string $name
277
     * @param string $label
278
     * @param array  $attributes
279
     *
280
     * @return mixed
281
     */
282
    public function addSelectLanguage($name, $label, $options = [], $attributes = [])
283
    {
284
        return $this->addElement('SelectLanguage', $name, $label, $options, $attributes);
285
    }
286
287
    public function addSelectTheme($name, $label, $options = [], $attributes = [])
288
    {
289
        return $this->addElement('SelectTheme', $name, $label, $options, $attributes);
290
    }
291
292
    /**
293
     * @param string       $name
294
     * @param string|array $label
295
     * @param array        $options
296
     * @param array        $attributes
297
     *
298
     * @return SelectAjax
299
     */
300
    public function addSelectAjax($name, $label, $options = [], $attributes = [])
301
    {
302
        if (!isset($attributes['url'])) {
303
            throw new \Exception('select_ajax needs an URL');
304
        }
305
306
        return $this->addElement(
307
            'select_ajax',
308
            $name,
309
            $label,
310
            $options,
311
            $attributes
312
        );
313
    }
314
315
    /**
316
     * @param string $name
317
     * @param string $label
318
     * @param array  $attributes
319
     *
320
     * @return DatePicker
321
     */
322
    public function addDatePicker($name, $label, $attributes = [])
323
    {
324
        return $this->addElement('DatePicker', $name, $label, $attributes);
325
    }
326
327
    /**
328
     * @param string       $name
329
     * @param string|array $label
330
     * @param array        $attributes
331
     *
332
     * @return DateTimePicker
333
     */
334
    public function addDateTimePicker($name, $label, $attributes = [])
335
    {
336
        return $this->addElement('DateTimePicker', $name, $label, $attributes);
337
    }
338
339
    /**
340
     * @param string       $name
341
     * @param string|array $label
342
     * @param array        $attributes
343
     *
344
     * @return DateTimeRangePicker
345
     */
346
    public function addDateTimeRangePicker($name, $label, $attributes = [])
347
    {
348
        return $this->addElement('DateTimeRangePicker', $name, $label, $attributes);
349
    }
350
351
    /**
352
     * @param string $name
353
     * @param string $value
354
     * @param array  $attributes
355
     */
356
    public function addHidden($name, $value, $attributes = [])
357
    {
358
        $this->addElement('hidden', $name, $value, $attributes);
359
    }
360
361
    /**
362
     * @param string       $name
363
     * @param string|array $label
364
     * @param array        $attributes
365
     * @param bool         $required
366
     *
367
     * @return HTML_QuickForm_textarea
368
     */
369
    public function addTextarea($name, $label, $attributes = [], $required = false)
370
    {
371
        $element = $this->addElement('textarea', $name, $label, $attributes);
372
373
        if ($required) {
374
            $this->addRule($name, get_lang('Required field'), 'required');
375
        }
376
377
        return $element;
378
    }
379
380
    /**
381
     * @param string $name
382
     * @param string $label
383
     * @param string $icon          font-awesome
384
     * @param string $style         default|primary|success|info|warning|danger|link
385
     * @param string $size          large|default|small|extra-small
386
     * @param string $class         Example plus is transformed to icon fa fa-plus
387
     * @param array  $attributes
388
     * @param bool   $createElement
389
     *
390
     * @return HTML_QuickForm_button
391
     */
392
    public function addButton(
393
        $name,
394
        $label,
395
        $icon = 'check',
396
        $style = 'default',
397
        $size = 'default',
398
        $class = null,
399
        $attributes = [],
400
        $createElement = false
401
    ) {
402
        if ($createElement) {
403
            return $this->createElement(
404
                'button',
405
                $name,
406
                $label,
407
                $icon,
408
                $style,
409
                $size,
410
                $class,
411
                $attributes
412
            );
413
        }
414
415
        return $this->addElement(
416
            'button',
417
            $name,
418
            $label,
419
            $icon,
420
            $style,
421
            $size,
422
            $class,
423
            $attributes
424
        );
425
    }
426
427
    /**
428
     * Returns a button with the primary color and a check mark.
429
     *
430
     * @param string $label         Text appearing on the button
431
     * @param string $name          Element name (for form treatment purposes)
432
     * @param bool   $createElement Whether to use the create or add method
433
     * @param array  $attributes
434
     *
435
     * @return HTML_QuickForm_button
436
     */
437
    public function addButtonSave($label, $name = 'submit', $createElement = false, $attributes = [])
438
    {
439
        return $this->addButton(
440
            $name,
441
            $label,
442
            'check',
443
            'primary',
444
            null,
445
            null,
446
            $attributes,
447
            $createElement
448
        );
449
    }
450
451
    /**
452
     * Returns a cancel button.
453
     *
454
     * @param string $label         Text appearing on the button
455
     * @param string $name          Element name (for form treatment purposes)
456
     * @param bool   $createElement Whether to use the create or add method
457
     *
458
     * @return HTML_QuickForm_button
459
     */
460
    public function addButtonCancel($label, $name = 'submit', $createElement = false)
461
    {
462
        return $this->addButton(
463
            $name,
464
            $label,
465
            'close',
466
            'danger',
467
            null,
468
            null,
469
            [],
470
            $createElement
471
        );
472
    }
473
474
    /**
475
     * Returns a button with the primary color and a "plus" icon.
476
     *
477
     * @param string $label         Text appearing on the button
478
     * @param string $name          Element name (for form treatment purposes)
479
     * @param bool   $createElement Whether to use the create or add method
480
     * @param array  $attributes    Additional attributes
481
     *
482
     * @return HTML_QuickForm_button
483
     */
484
    public function addButtonCreate($label, $name = 'submit', $createElement = false, $attributes = [])
485
    {
486
        return $this->addButton(
487
            $name,
488
            $label,
489
            'plus',
490
            'primary',
491
            null,
492
            null,
493
            $attributes,
494
            $createElement
495
        );
496
    }
497
498
    /**
499
     * Returns a button with the primary color and a pencil icon.
500
     *
501
     * @param string $label         Text appearing on the button
502
     * @param string $name          Element name (for form treatment purposes)
503
     * @param bool   $createElement Whether to use the create or add method
504
     *
505
     * @return HTML_QuickForm_button
506
     */
507
    public function addButtonUpdate($label, $name = 'submit', $createElement = false)
508
    {
509
        return $this->addButton(
510
            $name,
511
            $label,
512
            'pencil',
513
            'primary',
514
            null,
515
            null,
516
            [],
517
            $createElement
518
        );
519
    }
520
521
    /**
522
     * Returns a button with the danger color and a trash icon.
523
     *
524
     * @param string $label         Text appearing on the button
525
     * @param string $name          Element name (for form treatment purposes)
526
     * @param bool   $createElement Whether to use the create or add method
527
     *
528
     * @return HTML_QuickForm_button
529
     */
530
    public function addButtonDelete($label, $name = 'submit', $createElement = false)
531
    {
532
        return $this->addButton(
533
            $name,
534
            $label,
535
            'delete',
536
            'danger',
537
            null,
538
            null,
539
            [],
540
            $createElement
541
        );
542
    }
543
544
    /**
545
     * Returns a move style button.
546
     *
547
     * @param string $label         Text appearing on the button
548
     * @param string $name          Element name (for form treatment purposes)
549
     * @param bool   $createElement Whether to use the create or add method
550
     *
551
     * @return HTML_QuickForm_button
552
     */
553
    public function addButtonMove($label, $name = 'submit', $createElement = false)
554
    {
555
        return $this->addButton(
556
            $name,
557
            $label,
558
            'arrow-right-bold-circle',
559
            'primary',
560
            null,
561
            null,
562
            [],
563
            $createElement
564
        );
565
    }
566
567
    /**
568
     * Returns a button with the primary color and a paper-plane icon.
569
     *
570
     * @param string $label         Text appearing on the button
571
     * @param string $name          Element name (for form treatment purposes)
572
     * @param bool   $createElement Whether to use the create or add method
573
     * @param array  $attributes
574
     *
575
     * @return HTML_QuickForm_button
576
     */
577
    public function addButtonSend($label, $name = 'submit', $createElement = false, $attributes = [])
578
    {
579
        return $this->addButton(
580
            $name,
581
            $label,
582
            'send',
583
            'primary',
584
            null,
585
            null,
586
            $attributes,
587
            $createElement
588
        );
589
    }
590
591
    /**
592
     * Returns a button with the default (grey?) color and a magnifier icon.
593
     *
594
     * @param string $label Text appearing on the button
595
     * @param string $name  Element name (for form treatment purposes)
596
     *
597
     * @return HTML_QuickForm_button
598
     */
599
    public function addButtonSearch($label = null, $name = 'submit')
600
    {
601
        if (empty($label)) {
602
            $label = get_lang('Search');
603
        }
604
605
        return $this->addButton($name, $label, 'magnify', 'primary');
606
    }
607
608
    /**
609
     * Returns a button with the primary color and a right-pointing arrow icon.
610
     *
611
     * @param string $label      Text appearing on the button
612
     * @param string $name       Element name (for form treatment purposes)
613
     * @param array  $attributes Additional attributes
614
     *
615
     * @return HTML_QuickForm_button
616
     */
617
    public function addButtonNext($label, $name = 'submit', $attributes = [])
618
    {
619
        return $this->addButton(
620
            $name,
621
            $label,
622
            'arrow-right',
623
            'primary',
624
            null,
625
            null,
626
            $attributes
627
        );
628
    }
629
630
    /**
631
     * Returns a button with the primary color and a check mark icon.
632
     *
633
     * @param string $label         Text appearing on the button
634
     * @param string $name          Element name (for form treatment purposes)
635
     * @param bool   $createElement Whether to use the create or add method
636
     *
637
     * @return HTML_QuickForm_button
638
     */
639
    public function addButtonImport($label, $name = 'submit', $createElement = false)
640
    {
641
        return $this->addButton(
642
            $name,
643
            $label,
644
            'check',
645
            'primary',
646
            null,
647
            null,
648
            [],
649
            $createElement
650
        );
651
    }
652
653
    /**
654
     * Returns a button with the primary color and a check-mark icon.
655
     *
656
     * @param string $label         Text appearing on the button
657
     * @param string $name          Element name (for form treatment purposes)
658
     * @param bool   $createElement Whether to use the create or add method
659
     *
660
     * @return HTML_QuickForm_button
661
     */
662
    public function addButtonExport($label, $name = 'submit', $createElement = false)
663
    {
664
        return $this->addButton(
665
            $name,
666
            $label,
667
            'check',
668
            'primary',
669
            null,
670
            null,
671
            [],
672
            $createElement
673
        );
674
    }
675
676
    /**
677
     * Shortcut to filter button.
678
     *
679
     * @param string $label         Text appearing on the button
680
     * @param string $name          Element name (for form treatment purposes)
681
     * @param bool   $createElement Whether to use the create or add method
682
     *
683
     * @return HTML_QuickForm_button
684
     */
685
    public function addButtonFilter($label, $name = 'submit', $createElement = false)
686
    {
687
        return $this->addButton(
688
            $name,
689
            $label,
690
            'filter',
691
            'primary',
692
            null,
693
            null,
694
            [],
695
            $createElement
696
        );
697
    }
698
699
    /**
700
     * Shortcut to reset button.
701
     *
702
     * @param string $label         Text appearing on the button
703
     * @param string $name          Element name (for form treatment purposes)
704
     * @param bool   $createElement Whether to use the create or add method
705
     *
706
     * @return HTML_QuickForm_button
707
     */
708
    public function addButtonReset($label, $name = 'reset', $createElement = false)
709
    {
710
        $icon = 'eraser';
711
        $style = 'default';
712
        $size = 'default';
713
        $class = null;
714
        $attributes = [];
715
716
        if ($createElement) {
717
            return $this->createElement(
718
                'reset',
719
                $name,
720
                $label,
721
                $icon,
722
                $style,
723
                $size,
724
                $class,
725
                $attributes
726
            );
727
        }
728
729
        return $this->addElement(
730
            'reset',
731
            $name,
732
            $label,
733
            $icon,
734
            $style,
735
            $size,
736
            $class,
737
            $attributes
738
        );
739
    }
740
741
    /**
742
     * Returns a button with the primary color and an upload icon.
743
     *
744
     * @param string $label         Text appearing on the button
745
     * @param string $name          Element name (for form treatment purposes)
746
     * @param bool   $createElement Whether to use the create or add method
747
     *
748
     * @return HTML_QuickForm_button
749
     */
750
    public function addButtonUpload($label, $name = 'submit', $createElement = false)
751
    {
752
        return $this->addButton(
753
            $name,
754
            $label,
755
            'upload',
756
            'primary',
757
            null,
758
            null,
759
            [],
760
            $createElement
761
        );
762
    }
763
764
    /**
765
     * Returns a button with the primary color and a download icon.
766
     *
767
     * @param string $label         Text appearing on the button
768
     * @param string $name          Element name (for form treatment purposes)
769
     * @param bool   $createElement Whether to use the create or add method
770
     *
771
     * @return HTML_QuickForm_button
772
     */
773
    public function addButtonDownload($label, $name = 'submit', $createElement = false)
774
    {
775
        return $this->addButton(
776
            $name,
777
            $label,
778
            'download',
779
            'primary',
780
            null,
781
            null,
782
            [],
783
            $createElement
784
        );
785
    }
786
787
    /**
788
     * Returns a button with the primary color and a magnifier icon.
789
     *
790
     * @param string $label         Text appearing on the button
791
     * @param string $name          Element name (for form treatment purposes)
792
     * @param bool   $createElement Whether to use the create or add method
793
     *
794
     * @return HTML_QuickForm_button
795
     */
796
    public function addButtonPreview($label, $name = 'submit', $createElement = false)
797
    {
798
        return $this->addButton(
799
            $name,
800
            $label,
801
            'magnify',
802
            'primary',
803
            null,
804
            null,
805
            [],
806
            $createElement
807
        );
808
    }
809
810
    /**
811
     * Returns a button with the primary color and a copy (double sheet) icon.
812
     *
813
     * @param string $label         Text appearing on the button
814
     * @param string $name          Element name (for form treatment purposes)
815
     * @param bool   $createElement Whether to use the create or add method
816
     *
817
     * @return HTML_QuickForm_button
818
     */
819
    public function addButtonCopy($label, $name = 'submit', $createElement = false)
820
    {
821
        return $this->addButton(
822
            $name,
823
            $label,
824
            'copy',
825
            'primary',
826
            null,
827
            null,
828
            [],
829
            $createElement
830
        );
831
    }
832
833
    /**
834
     * @param string $name
835
     * @param string $label
836
     * @param string $text
837
     * @param array  $attributes
838
     *
839
     * @return HTML_QuickForm_checkbox
840
     */
841
    public function addCheckBox($name, $label, $text = '', $attributes = [])
842
    {
843
        return $this->addElement('checkbox', $name, $label, $text, $attributes);
844
    }
845
846
    /**
847
     * @param string $name
848
     * @param string $label
849
     * @param array  $options
850
     * @param array  $attributes
851
     *
852
     * @return HTML_QuickForm_group
853
     */
854
    public function addCheckBoxGroup($name, $label, $options = [], $attributes = [])
855
    {
856
        $group = [];
857
        foreach ($options as $value => $text) {
858
            $attributes['value'] = $value;
859
            $group[] = $this->createElement(
860
                'checkbox',
861
                $value,
862
                null,
863
                $text,
864
                $attributes
865
            );
866
        }
867
868
        return $this->addGroup($group, $name, $label);
869
    }
870
871
    /**
872
     * @param string $name
873
     * @param string $label
874
     * @param array  $options
875
     * @param array  $attributes
876
     *
877
     * @return HTML_QuickForm_group
878
     */
879
    public function addRadio($name, $label, $options = [], $attributes = [])
880
    {
881
        $group = [];
882
        foreach ($options as $key => $value) {
883
            $group[] = $this->createElement('radio', null, null, $value, $key, $attributes);
884
        }
885
886
        return $this->addGroup($group, $name, $label);
887
    }
888
889
    /**
890
     * @param string|array $label
891
     *
892
     * @return HTML_QuickForm_select
893
     */
894
    public function addSelect(string $name, $label, ?array $options = [], array $attributes = [])
895
    {
896
        return $this->addElement('select', $name, $label, $options, $attributes);
897
    }
898
899
    /**
900
     * @param $name
901
     * @param $label
902
     * @param $collection
903
     * @param array  $attributes
904
     * @param bool   $addNoneOption
905
     * @param string $textCallable  set a function getStringValue() by default __toString()
906
     *
907
     * @return HTML_QuickForm_element
908
     */
909
    public function addSelectFromCollection(
910
        $name,
911
        $label,
912
        $collection,
913
        $attributes = [],
914
        $addNoneOption = false,
915
        $textCallable = ''
916
    ) {
917
        $options = [];
918
919
        if ($addNoneOption) {
920
            $options[0] = get_lang('None');
921
        }
922
923
        if (!empty($collection)) {
924
            foreach ($collection as $item) {
925
                $text = $item;
926
                if (!empty($textCallable)) {
927
                    $text = $item->$textCallable();
928
                }
929
                $options[$item->getId()] = $text;
930
            }
931
        }
932
933
        return $this->addElement('select', $name, $label, $options, $attributes);
934
    }
935
936
    public function addMultiSelect(string $name, $label, array $options, array $attributes = [])
937
    {
938
        $this->addElement('advmultiselect', $name, $label, $options, $attributes);
939
    }
940
941
    /**
942
     * @param string $label
943
     * @param string $text
944
     * @param bool   $createElement
945
     *
946
     * @return HTML_QuickForm_Element
947
     */
948
    public function addLabel($label, $text, $createElement = false)
949
    {
950
        if ($createElement) {
951
            return $this->createElement(
952
                'label',
953
                $label,
954
                $text
955
            );
956
        }
957
958
        return $this->addElement('label', $label, $text);
959
    }
960
961
    /**
962
     * @param string $text
963
     */
964
    public function addHeader($text)
965
    {
966
        if (!empty($text)) {
967
            $this->addElement('header', $text);
968
        }
969
    }
970
971
    /**
972
     * @param string       $name
973
     * @param string|array $label
974
     * @param array        $attributes
975
     *
976
     * @throws Exception if the file doesn't have an id
977
     *
978
     * @return HTML_QuickForm_file
979
     */
980
    public function addFile($name, $label, $attributes = [])
981
    {
982
        try {
983
            $element = $this->addElement('file', $name, $label, $attributes);
984
            if (isset($attributes['crop_image'])) {
985
                $id = $element->getAttribute('id');
986
                if (empty($id)) {
987
                    throw new Exception('If you use the crop functionality the element must have an id');
988
                }
989
                $this->addHtml(
990
                    '
991
                <div class="form-group row" id="'.$id.'-form-group" style="display: none;">
992
                    <div class="offset-md-2 col-sm-8">
993
                        <div class="card-cropper">
994
                            <div id="'.$id.'_crop_image" class="cropCanvas">
995
                                <img id="'.$id.'_preview_image">
996
                            </div>
997
                            <button class="btn btn-primary" type="button" name="cropButton" id="'.$id.'_crop_button">
998
                                <em class="fa fa-crop"></em> '.get_lang('Crop your picture').'
999
                            </button>
1000
                        </div>
1001
                    </div>
1002
                </div>'
1003
                );
1004
                $this->addHidden($id.'_crop_result', '');
1005
                $this->addHidden($id.'_crop_result_for_resource', '');
1006
                $this->addHidden($id.'_crop_image_base_64', '');
1007
            }
1008
        } catch (HTML_Quick | Form_Error $e) {
1009
            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...
1010
        }
1011
1012
        return $element;
1013
    }
1014
1015
    /**
1016
     * @param string $snippet
1017
     */
1018
    public function addHtml($snippet)
1019
    {
1020
        if (empty($snippet)) {
1021
            return false;
1022
        }
1023
        $this->addElement('html', $snippet);
1024
1025
        return true;
1026
    }
1027
1028
    public function addStartPanel(string $id, string $title, bool $open = false)
1029
    {
1030
        $parent = null;
1031
        $html = '
1032
                <script>
1033
                 document.addEventListener("DOMContentLoaded", function() {
1034
                    const button = document.querySelector("#card_'.$id.'");
1035
                        button.addEventListener("click", (e) => {
1036
                        let menu = document.querySelector("#collapse_'.$id.'");
1037
                        if (menu.classList.contains("hidden")) {
1038
                            menu.classList.remove("hidden");
1039
                        } else {
1040
                            menu.classList.add("hidden");
1041
                        }
1042
                    });
1043
                });
1044
                </script>
1045
                <div class="mt-4 rounded-lg">    
1046
                    <div class="px-4 bg-gray-100 border border-gray-50" id="card_'.$id.'">   
1047
                        <h5>
1048
                            <a role="button" 
1049
                                class="'.(($open) ? 'collapse' : ' ').'"  
1050
                                data-toggle="collapse" 
1051
                                data-target="#collapse_'.$id.'" 
1052
                                aria-expanded="true" 
1053
                                aria-controls="collapse_'.$id.'"
1054
                            >
1055
                                '.$title.'
1056
                            </a>
1057
                        </h5>
1058
                    </div>
1059
                    <div 
1060
                        id="collapse_'.$id.'" 
1061
                        class="px-4 border border-gray-50 bg-white hidden collapse '.(($open) ? 'show' : ' ').'" 
1062
                        aria-labelledby="heading_'.$id.'" data-parent="#'.$parent.'">
1063
                    <div id="collapse_contant_'.$id.'"  class="card-body ">';
1064
1065
        $this->addHtml($html);
1066
    }
1067
1068
    public function addEndPanel()
1069
    {
1070
        $this->addHtml('</div></div></div>');
1071
    }
1072
1073
    /**
1074
     * Draws a panel of options see the course_info/infocours.php page.
1075
     *
1076
     * @param string $name      internal name
1077
     * @param string $title     visible title
1078
     * @param array  $groupList list of group or elements
1079
     */
1080
    public function addPanelOption($name, $title, $groupList, $icon, $open, $parent)
1081
    {
1082
        $html = '<div class="card">';
1083
        $html .= '<div class="card-header" id="card_'.$name.'">';
1084
        $html .= '<h5 class="card-title">';
1085
        $html .= '<a role="button" class="'.(($open) ? 'collapse' : ' ').'"  data-toggle="collapse" data-target="#collapse_'.$name.'" aria-expanded="true" aria-controls="collapse_'.$name.'">';
1086
        if ($icon) {
1087
            $html .= Display::return_icon($icon, null, null, ICON_SIZE_SMALL);
1088
        }
1089
        $html .= $title;
1090
        $html .= '</a></h5></div>';
1091
        $html .= '<div id="collapse_'.$name.'" class="collapse '.(($open) ? 'show' : ' ').'" aria-labelledby="heading_'.$name.'" data-parent="#'.$parent.'">';
1092
        $html .= '<div class="card-body">';
1093
1094
        $this->addHtml($html);
1095
1096
        foreach ($groupList as $groupName => $group) {
1097
            // Add group array
1098
            if (!empty($groupName) && is_array($group)) {
1099
                $this->addGroup($group, '', $groupName);
1100
            }
1101
            // Add element
1102
            if ($group instanceof HTML_QuickForm_element) {
1103
                $this->addElement($group);
1104
            }
1105
        }
1106
1107
        $this->addHtml('</div></div></div>');
1108
    }
1109
1110
    /**
1111
     * Adds a HTML-editor to the form.
1112
     *
1113
     * @param string       $name
1114
     * @param string|array $label      The label for the form-element
1115
     * @param bool         $required   (optional) Is the form-element required (default=true)
1116
     * @param bool         $fullPage   (optional) When it is true, the editor loads completed html code for a full page
1117
     * @param array        $config     (optional) Configuration settings for the online editor
1118
     * @param array        $attributes
1119
     *
1120
     * @throws Exception
1121
     * @throws HTML_QuickForm_Error
1122
     */
1123
    public function addHtmlEditor(
1124
        $name,
1125
        $label,
1126
        $required = true,
1127
        $fullPage = false,
1128
        $config = [],
1129
        $attributes = []
1130
    ) {
1131
        $attributes['rows'] = $config['rows'] ?? 15;
1132
        $attributes['cols'] = $config['cols'] ?? 80;
1133
        $attributes['cols-size'] = $config['cols-size'] ?? [];
1134
        $attributes['class'] = $config['class'] ?? [];
1135
        $cleanName = str_replace(['[', ']', '#'], '', $name);
1136
1137
        if (empty($attributes['id'])) {
1138
            $attributes['id'] = $cleanName;
1139
        }
1140
1141
        //$attributes['id'] = $config['id'] ?? 'editor_'.$cleanName;
1142
1143
        $this->addElement('html_editor', $name, $label, $attributes, $config);
1144
        $this->applyFilter($name, 'trim');
1145
        if ($required) {
1146
            $this->addRule($name, get_lang('Required field'), 'required');
1147
        }
1148
1149
        /** @var HtmlEditor $element */
1150
        $element = $this->getElement($name);
1151
        $config['style'] = $config['style'] ?? false;
1152
        if ($fullPage) {
1153
            $config['fullPage'] = true;
1154
            // Adds editor_content.css in ckEditor
1155
            $config['style'] = true;
1156
        }
1157
1158
        if ($element->editor) {
1159
            $element->editor->processConfig($config);
1160
        }
1161
    }
1162
1163
    /**
1164
     * Adds a Google Maps Geolocalization field to the form.
1165
     *
1166
     * @param      $name
1167
     * @param      $label
1168
     * @param bool $hideGeoLocalizationDetails
1169
     */
1170
    public function addGeoLocationMapField($name, $label, $dataValue, $hideGeoLocalizationDetails = false)
1171
    {
1172
        $gMapsPlugin = GoogleMapsPlugin::create();
1173
        $geolocalization = 'true' === $gMapsPlugin->get('enable_api');
1174
1175
        if ($geolocalization && false === $gMapsPlugin->javascriptIncluded) {
1176
            $gmapsApiKey = $gMapsPlugin->get('api_key');
1177
            $url = '//maps.googleapis.com/maps/api/js?key='.$gmapsApiKey;
1178
            $this->addHtml('<script type="text/javascript" src="'.$url.'" ></script>');
1179
            $gMapsPlugin->javascriptIncluded = true;
1180
        }
1181
1182
        $this->addElement(
1183
            'text',
1184
            $name,
1185
            $label,
1186
            ['id' => $name]
1187
        );
1188
1189
        $this->addHidden(
1190
            $name.'_coordinates',
1191
            '',
1192
            ['id' => $name.'_coordinates']
1193
        );
1194
1195
        $this->applyFilter($name, 'stripslashes');
1196
        $this->applyFilter($name, 'trim');
1197
1198
        $this->addHtml(Extrafield::getLocalizationJavascript($name, $dataValue));
1199
1200
        if ($hideGeoLocalizationDetails) {
1201
            $this->addHtml('<div style="display:none">');
1202
        }
1203
1204
        $this->addHtml(
1205
            Extrafield::getLocalizationInput($name, $label)
1206
        );
1207
1208
        if ($hideGeoLocalizationDetails) {
1209
            $this->addHtml('</div>');
1210
        }
1211
    }
1212
1213
    /**
1214
     * @param string       $name
1215
     * @param string|array $label
1216
     *
1217
     * @return mixed
1218
     */
1219
    public function addButtonAdvancedSettings($name, $label = null)
1220
    {
1221
        $label = !empty($label) ? $label : get_lang('Advanced settings');
1222
1223
        return $this->addElement('advanced_settings', $name, $label);
1224
    }
1225
1226
    /**
1227
     * Adds a progress loading image to the form.
1228
     */
1229
    public function addProgress($delay = 2, $label = '')
1230
    {
1231
        if (empty($label)) {
1232
            $label = get_lang('Please stand by...');
1233
        }
1234
        $this->with_progress_bar = true;
1235
        $id = $this->getAttribute('id');
1236
1237
        $this->updateAttributes("onsubmit=\"javascript: addProgress('".$id."')\"");
1238
        $this->addHtml('<script language="javascript" src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/upload.js" type="text/javascript"></script>');
1239
    }
1240
1241
    /**
1242
     * This function has been created for avoiding changes directly within QuickForm class.
1243
     * When we use it, the element is threated as 'required' to be dealt during validation.
1244
     *
1245
     * @param array  $elements The array of elements
1246
     * @param string $message  The message displayed
1247
     */
1248
    public function add_multiple_required_rule($elements, $message)
1249
    {
1250
        $this->_required[] = $elements[0];
1251
        $this->addRule($elements, $message, 'multiple_required');
1252
    }
1253
1254
    /**
1255
     * Displays the form.
1256
     * If an element in the form didn't validate, an error message is showed
1257
     * asking the user to complete the form.
1258
     */
1259
    public function display()
1260
    {
1261
        echo $this->returnForm();
1262
    }
1263
1264
    /**
1265
     * Returns the HTML code of the form.
1266
     *
1267
     * @return string $return_value HTML code of the form
1268
     */
1269
    public function returnForm()
1270
    {
1271
        $returnValue = '';
1272
        /** @var HTML_QuickForm_element $element */
1273
        foreach ($this->_elements as &$element) {
1274
            $element->setLayout($this->getLayout());
1275
            $elementError = parent::getElementError($element->getName());
1276
            if (!is_null($elementError)) {
1277
                $returnValue .= Display::return_message($elementError, 'warning').'<br />';
1278
                break;
1279
            }
1280
        }
1281
1282
        $returnValue .= parent::toHtml();
1283
        // Add div-element which is to hold the progress bar
1284
        $id = $this->getAttribute('id');
1285
        if (isset($this->with_progress_bar) && $this->with_progress_bar) {
1286
            // @todo improve UI
1287
            $returnValue .= '<br />
1288
            <div id="loading_div_'.$id.'" class="loading_div" style="display:none;margin-left:40%; margin-top:10px; height:50px;">
1289
                <div class="wobblebar-loader"></div>
1290
            </div>
1291
            ';
1292
        }
1293
1294
        return $returnValue;
1295
    }
1296
1297
    /**
1298
     * Returns the HTML code of the form.
1299
     * If an element in the form didn't validate, an error message is showed
1300
     * asking the user to complete the form.
1301
     *
1302
     * @return string $return_value HTML code of the form
1303
     *
1304
     * @author Patrick Cool <[email protected]>, Ghent University, august 2006
1305
     * @author Julio Montoya
1306
     *
1307
     * @deprecated use returnForm()
1308
     */
1309
    public function return_form()
1310
    {
1311
        return $this->returnForm();
1312
    }
1313
1314
    /**
1315
     * @return HTML_QuickForm_Renderer_Default
1316
     */
1317
    public static function getDefaultRenderer()
1318
    {
1319
        return
1320
            isset($GLOBALS['_HTML_QuickForm_default_renderer']) ?
1321
                $GLOBALS['_HTML_QuickForm_default_renderer'] : null;
1322
    }
1323
1324
    /**
1325
     * Adds a input of type url to the form.
1326
     *
1327
     * @param string $name       The label for the form-element
1328
     * @param string $label      The element name
1329
     * @param bool   $required   Optional. Is the form-element required (default=true)
1330
     * @param array  $attributes Optional. List of attributes for the form-element
1331
     */
1332
    public function addUrl($name, $label, $required = true, $attributes = [])
1333
    {
1334
        $this->addElement('url', $name, $label, $attributes);
1335
        $this->applyFilter($name, 'trim');
1336
        $this->addRule($name, get_lang('Insert a valid URL'), 'url');
1337
1338
        if ($required) {
1339
            $this->addRule($name, get_lang('Required field'), 'required');
1340
        }
1341
    }
1342
1343
    /**
1344
     * Adds a text field for letters to the form.
1345
     * A trim-filter is attached to the field.
1346
     *
1347
     * @param string $name       The element name
1348
     * @param string $label      The label for the form-element
1349
     * @param bool   $required   Optional. Is the form-element required (default=true)
1350
     * @param array  $attributes Optional. List of attributes for the form-element
1351
     */
1352
    public function addTextLettersOnly(
1353
        $name,
1354
        $label,
1355
        $required = false,
1356
        $attributes = []
1357
    ) {
1358
        $attributes = array_merge(
1359
            $attributes,
1360
            [
1361
                'pattern' => '[a-zA-ZñÑ]+',
1362
                'title' => get_lang('Only letters'),
1363
            ]
1364
        );
1365
1366
        $this->addElement(
1367
            'text',
1368
            $name,
1369
            [
1370
                $label,
1371
                get_lang('Only letters'),
1372
            ],
1373
            $attributes
1374
        );
1375
1376
        $this->applyFilter($name, 'trim');
1377
1378
        if ($required) {
1379
            $this->addRule($name, get_lang('Required field'), 'required');
1380
        }
1381
1382
        $this->addRule(
1383
            $name,
1384
            get_lang('Only letters'),
1385
            'regex',
1386
            '/^[a-zA-ZñÑ]+$/'
1387
        );
1388
    }
1389
1390
    /**
1391
     * @param string $name
1392
     * @param string $label
1393
     * @param array  $attributes
1394
     * @param bool   $required
1395
     *
1396
     * @return HTML_QuickForm_element
1397
     */
1398
    public function addNumeric($name, $label, $attributes = [], $required = false)
1399
    {
1400
        $element = $this->addElement('Number', $name, $label, $attributes);
1401
1402
        if ($required) {
1403
            $this->addRule($name, get_lang('ThisFieldIsRequired'), 'required');
1404
        }
1405
1406
        return $element;
1407
    }
1408
1409
    /**
1410
     * Adds a text field for alphanumeric characters to the form.
1411
     * A trim-filter is attached to the field.
1412
     *
1413
     * @param string $name       The element name
1414
     * @param string $label      The label for the form-element
1415
     * @param bool   $required   Optional. Is the form-element required (default=true)
1416
     * @param array  $attributes Optional. List of attributes for the form-element
1417
     */
1418
    public function addTextAlphanumeric(
1419
        $name,
1420
        $label,
1421
        $required = false,
1422
        $attributes = []
1423
    ) {
1424
        $attributes = array_merge(
1425
            $attributes,
1426
            [
1427
                'pattern' => '[a-zA-Z0-9ñÑ]+',
1428
                'title' => get_lang('Only lettersAndNumbers'),
1429
            ]
1430
        );
1431
1432
        $this->addElement(
1433
            'text',
1434
            $name,
1435
            [
1436
                $label,
1437
                get_lang('Only lettersAndNumbers'),
1438
            ],
1439
            $attributes
1440
        );
1441
1442
        $this->applyFilter($name, 'trim');
1443
1444
        if ($required) {
1445
            $this->addRule($name, get_lang('Required field'), 'required');
1446
        }
1447
1448
        $this->addRule(
1449
            $name,
1450
            get_lang('Only lettersAndNumbers'),
1451
            'regex',
1452
            '/^[a-zA-Z0-9ÑÑ]+$/'
1453
        );
1454
    }
1455
1456
    /**
1457
     * @param string $name
1458
     * @param $label
1459
     * @param bool  $required
1460
     * @param array $attributes
1461
     * @param bool  $allowNegative
1462
     * @param int   $minValue
1463
     * @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...
1464
     */
1465
    public function addFloat(
1466
        $name,
1467
        $label,
1468
        $required = false,
1469
        $attributes = [],
1470
        $allowNegative = false,
1471
        $minValue = null,
1472
        $maxValue = null
1473
    ) {
1474
        $this->addElement(
1475
            'FloatNumber',
1476
            $name,
1477
            $label,
1478
            $attributes
1479
        );
1480
1481
        $this->applyFilter($name, 'trim');
1482
1483
        if ($required) {
1484
            $this->addRule($name, get_lang('Required field'), 'required');
1485
        }
1486
1487
        // Rule allows "," and "."
1488
        /*$this->addRule(
1489
            $name,
1490
            get_lang('Only numbers'),
1491
            'regex',
1492
            '/(^-?\d\d*\.\d*$)|(^-?\d\d*$)|(^-?\.\d\d*$)|(^-?\d\d*\,\d*$)|(^-?\,\d\d*$)/'
1493
        );*/
1494
1495
        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...
1496
            $this->addRule(
1497
                $name,
1498
                get_lang('Negative value'),
1499
                'compare',
1500
                '>=',
1501
                'server',
1502
                false,
1503
                false,
1504
                0
1505
            );
1506
        }
1507
1508
        if (!is_null($minValue)) {
1509
            $this->addRule(
1510
                $name,
1511
                get_lang('Under the minimum.'),
1512
                'compare',
1513
                '>=',
1514
                'server',
1515
                false,
1516
                false,
1517
                $minValue
1518
            );
1519
        }
1520
1521
        if (!is_null($maxValue)) {
1522
            $this->addRule(
1523
                $name,
1524
                get_lang('Value exceeds score.'),
1525
                'compare',
1526
                '<=',
1527
                'server',
1528
                false,
1529
                false,
1530
                $maxValue
1531
            );
1532
        }
1533
    }
1534
1535
    /**
1536
     * Adds a text field for letters and spaces to the form.
1537
     * A trim-filter is attached to the field.
1538
     *
1539
     * @param string $name       The element name
1540
     * @param string $label      The label for the form-element
1541
     * @param bool   $required   Optional. Is the form-element required (default=true)
1542
     * @param array  $attributes Optional. List of attributes for the form-element
1543
     */
1544
    public function addTextLettersAndSpaces(
1545
        $name,
1546
        $label,
1547
        $required = false,
1548
        $attributes = []
1549
    ) {
1550
        $attributes = array_merge(
1551
            $attributes,
1552
            [
1553
                'pattern' => '[a-zA-ZñÑ\s]+',
1554
                'title' => get_lang('Only lettersAndSpaces'),
1555
            ]
1556
        );
1557
1558
        $this->addElement(
1559
            'text',
1560
            $name,
1561
            [
1562
                $label,
1563
                get_lang('Only lettersAndSpaces'),
1564
            ],
1565
            $attributes
1566
        );
1567
1568
        $this->applyFilter($name, 'trim');
1569
1570
        if ($required) {
1571
            $this->addRule($name, get_lang('Required field'), 'required');
1572
        }
1573
1574
        $this->addRule(
1575
            $name,
1576
            get_lang('Only lettersAndSpaces'),
1577
            'regex',
1578
            '/^[a-zA-ZñÑ\s]+$/'
1579
        );
1580
    }
1581
1582
    /**
1583
     * Adds a text field for alphanumeric and spaces characters to the form.
1584
     * A trim-filter is attached to the field.
1585
     *
1586
     * @param string $name       The element name
1587
     * @param string $label      The label for the form-element
1588
     * @param bool   $required   Optional. Is the form-element required (default=true)
1589
     * @param array  $attributes Optional. List of attributes for the form-element
1590
     */
1591
    public function addTextAlphanumericAndSpaces(
1592
        $name,
1593
        $label,
1594
        $required = false,
1595
        $attributes = []
1596
    ) {
1597
        $attributes = array_merge(
1598
            $attributes,
1599
            [
1600
                'pattern' => '[a-zA-Z0-9ñÑ\s]+',
1601
                'title' => get_lang('Only lettersAndNumbersAndSpaces'),
1602
            ]
1603
        );
1604
1605
        $this->addElement(
1606
            'text',
1607
            $name,
1608
            [
1609
                $label,
1610
                get_lang('Only lettersAndNumbersAndSpaces'),
1611
            ],
1612
            $attributes
1613
        );
1614
1615
        $this->applyFilter($name, 'trim');
1616
1617
        if ($required) {
1618
            $this->addRule($name, get_lang('Required field'), 'required');
1619
        }
1620
1621
        $this->addRule(
1622
            $name,
1623
            get_lang('Only lettersAndNumbersAndSpaces'),
1624
            'regex',
1625
            '/^[a-zA-Z0-9ñÑ\s]+$/'
1626
        );
1627
    }
1628
1629
    /**
1630
     * @param string $url
1631
     * @param string $urlToRedirect after upload redirect to this page
1632
     */
1633
    public function addMultipleUpload($url, $urlToRedirect = '')
1634
    {
1635
        $inputName = 'input_file_upload';
1636
        $this->addMultipleUploadJavascript($url, $inputName, $urlToRedirect);
1637
1638
        $this->addHtml('
1639
            <div class="description-upload">
1640
            '.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!').'
1641
            </div>
1642
            <span class="btn btn-success fileinput-button">
1643
                <i class="glyphicon glyphicon-plus"></i>
1644
                <span>'.get_lang('Add files').'</span>
1645
                <!-- The file input field used as target for the file upload widget -->
1646
                <input id="'.$inputName.'" type="file" name="files[]" multiple>
1647
            </span>
1648
            <div id="dropzone">
1649
                <div class="button-load">
1650
                '.get_lang('Click or drag and drop files here to upload them').'
1651
                </div>
1652
            </div>
1653
            <br />
1654
            <!-- The global progress bar -->
1655
            <div id="progress" class="progress">
1656
                <div class="progress-bar progress-bar-success"></div>
1657
            </div>
1658
            <div id="files" class="files"></div>
1659
        ');
1660
    }
1661
1662
    /**
1663
     * @param string $elementName
1664
     * @param string $groupName   if element is inside a group
1665
     *
1666
     * @throws Exception
1667
     */
1668
    public function addPasswordRule($elementName, $groupName = '')
1669
    {
1670
        if ('true' == api_get_setting('security.check_password')) {
1671
            $message = get_lang('this password  is too simple. Use a pass like this').': '.api_generate_password();
1672
1673
            if (!empty($groupName)) {
1674
                $groupObj = $this->getElement($groupName);
1675
1676
                if ($groupObj instanceof HTML_QuickForm_group) {
1677
                    $elementName = $groupObj->getElementName($elementName);
1678
1679
                    if (false === $elementName) {
1680
                        throw new Exception("The $groupName doesn't have the element $elementName");
1681
                    }
1682
1683
                    $this->_rules[$elementName][] = [
1684
                        'type' => 'callback',
1685
                        'format' => 'api_check_password',
1686
                        'message' => $message,
1687
                        'validation' => '',
1688
                        'reset' => false,
1689
                        'group' => $groupName,
1690
                    ];
1691
                }
1692
            } else {
1693
                $this->addRule(
1694
                    $elementName,
1695
                    $message,
1696
                    'callback',
1697
                    'api_check_password'
1698
                );
1699
            }
1700
        }
1701
    }
1702
1703
    /**
1704
     * Add an element with user ID and avatar to the form.
1705
     * It needs a Chamilo\CoreBundle\Entity\User as value. The exported value is the Chamilo\CoreBundle\Entity\User ID.
1706
     *
1707
     * @see \UserAvatar
1708
     *
1709
     * @param string $name
1710
     * @param string $label
1711
     * @param string $imageSize Optional. Small, medium or large image
1712
     * @param string $subtitle  Optional. The subtitle for the field
1713
     *
1714
     * @return \UserAvatar
1715
     */
1716
    public function addUserAvatar($name, $label, $imageSize = 'small', $subtitle = '')
1717
    {
1718
        return $this->addElement('UserAvatar', $name, $label, ['image_size' => $imageSize, 'sub_title' => $subtitle]);
1719
    }
1720
1721
    /**
1722
     * @param array $typeList
1723
     */
1724
    public function addEmailTemplate($typeList)
1725
    {
1726
        $mailManager = new MailTemplateManager();
1727
        foreach ($typeList as $type) {
1728
            $list = $mailManager->get_all(
1729
                ['where' => ['type = ? AND url_id = ?' => [$type, api_get_current_access_url_id()]]]
1730
            );
1731
1732
            $options = [get_lang('Select')];
1733
            $name = $type;
1734
            $defaultId = '';
1735
            foreach ($list as $item) {
1736
                $options[$item['id']] = $item['name'];
1737
                $name = $item['name'];
1738
                if (empty($defaultId)) {
1739
                    $defaultId = 1 == $item['default_template'] ? $item['id'] : '';
1740
                }
1741
            }
1742
1743
            $url = api_get_path(WEB_AJAX_PATH).'mail.ajax.php?a=select_option';
1744
            $typeNoDots = 'email_template_option_'.str_replace('.tpl', '', $type);
1745
            $this->addSelect(
1746
                'email_template_option['.$type.']',
1747
                $name,
1748
                $options,
1749
                ['id' => $typeNoDots]
1750
            );
1751
1752
            $templateNoDots = 'email_template_'.str_replace('.tpl', '', $type);
1753
            $templateNoDotsBlock = 'email_template_block_'.str_replace('.tpl', '', $type);
1754
            $this->addHtml('<div id="'.$templateNoDotsBlock.'" style="display:none">');
1755
            $this->addTextarea(
1756
                $templateNoDots,
1757
                get_lang('Preview'),
1758
                ['disabled' => 'disabled ', 'id' => $templateNoDots, 'rows' => '5']
1759
            );
1760
            $this->addHtml('</div>');
1761
1762
            $this->addHtml("<script>
1763
            $(function() {
1764
                var defaultValue = '$defaultId';
1765
                $('#$typeNoDots').val(defaultValue);
1766
                $('#$typeNoDots').selectpicker('render');
1767
                if (defaultValue != '') {
1768
                    var selected = $('#$typeNoDots option:selected').val();
1769
                    $.ajax({
1770
                        url: '$url' + '&id=' + selected+ '&template_name=$type',
1771
                        success: function (data) {
1772
                            $('#$templateNoDots').html(data);
1773
                            $('#$templateNoDotsBlock').show();
1774
                            return;
1775
                        },
1776
                    });
1777
                }
1778
1779
                $('#$typeNoDots').on('change', function(){
1780
                    var selected = $('#$typeNoDots option:selected').val();
1781
                    $.ajax({
1782
                        url: '$url' + '&id=' + selected,
1783
                        success: function (data) {
1784
                            $('#$templateNoDots').html(data);
1785
                            $('#$templateNoDotsBlock').show();
1786
                            return;
1787
                        },
1788
                    });
1789
                });
1790
            });
1791
            </script>");
1792
        }
1793
    }
1794
1795
    /**
1796
     * Add email rule for an element.
1797
     */
1798
    public function addEmailRule(string $element)
1799
    {
1800
        $this->addRule(
1801
            $element,
1802
            get_lang('The email address is not complete or contains some invalid characters'),
1803
            'email'
1804
        );
1805
    }
1806
1807
    /**
1808
     * @param string $url           page that will handle the upload
1809
     * @param string $inputName
1810
     * @param string $urlToRedirect
1811
     */
1812
    private function addMultipleUploadJavascript($url, $inputName, $urlToRedirect = '')
1813
    {
1814
        $redirectCondition = '';
1815
        if (!empty($urlToRedirect)) {
1816
            $redirectCondition = "window.location.replace('$urlToRedirect'); ";
1817
        }
1818
        $icon = Display::return_icon('file_txt.gif');
1819
        $this->addHtml("
1820
        <script>
1821
        $(function () {
1822
            'use strict';
1823
            $('#".$this->getAttribute('id')."').submit(function() {
1824
                return false;
1825
            });
1826
1827
            $('#dropzone').on('click', function() {
1828
                $('#".$inputName."').click();
1829
            });
1830
1831
            var url = '".$url."';
1832
            var uploadButton = $('<button/>')
1833
                .addClass('btn btn-primary')
1834
                .prop('disabled', true)
1835
                .text('".addslashes(get_lang('Loading'))."')
1836
                .on('click', function () {
1837
                    var \$this = $(this),
1838
                    data = \$this.data();
1839
                    \$this
1840
                        .off('click')
1841
                        .text('".addslashes(get_lang('Cancel'))."')
1842
                        .on('click', function () {
1843
                            \$this.remove();
1844
                            data.abort();
1845
                        });
1846
                    data.submit().always(function () {
1847
                        \$this.remove();
1848
                    });
1849
                });
1850
1851
            $('#".$inputName."').fileupload({
1852
                url: url,
1853
                dataType: 'json',
1854
                // Enable image resizing, except for Android and Opera,
1855
                // which actually support image resizing, but fail to
1856
                // send Blob objects via XHR requests:
1857
                disableImageResize: /Android(?!.*Chrome)|Opera/.test(window.navigator.userAgent),
1858
                previewMaxWidth: 300,
1859
                previewMaxHeight: 169,
1860
                previewCrop: true,
1861
                dropzone: $('#dropzone'),
1862
            }).on('fileuploadadd', function (e, data) {
1863
                data.context = $('<div class=\"row\" />').appendTo('#files');
1864
                $.each(data.files, function (index, file) {
1865
                    var node = $('<div class=\"col-sm-5 file_name\">').text(file.name);
1866
                    node.appendTo(data.context);
1867
                });
1868
            }).on('fileuploadprocessalways', function (e, data) {
1869
                var index = data.index,
1870
                    file = data.files[index],
1871
                    node = $(data.context.children()[index]);
1872
                if (file.preview) {
1873
                    data.context.prepend($('<div class=\"col-sm-4\">').html(file.preview));
1874
                } else {
1875
                    data.context.prepend($('<div class=\"col-sm-4\">').html('".$icon."'));
1876
                }
1877
                if (index + 1 === data.files.length) {
1878
                    data.context.find('button')
1879
                        .text('Upload')
1880
                        .prop('disabled', !!data.files.error);
1881
                }
1882
            }).on('fileuploadprogressall', function (e, data) {
1883
                var progress = parseInt(data.loaded / data.total * 100, 10);
1884
                $('#progress .progress-bar').css(
1885
                    'width',
1886
                    progress + '%'
1887
                );
1888
            }).on('fileuploaddone', function (e, data) {
1889
                $.each(data.result.files, function (index, file) {
1890
                    if (file.error) {
1891
                        var link = $('<div>')
1892
                            .attr({class : 'panel-image'})                            ;
1893
                        $(data.context.children()[index]).parent().wrap(link);
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=\"message-image-danger\"/>').text(file.error)
1898
                        );
1899
                        $(data.context.children()[index]).parent().append(message);
1900
1901
                        return;
1902
                    }
1903
                    if (file.url) {
1904
                        var link = $('<a>')
1905
                            .attr({target: '_blank', class : 'panel-image'})
1906
                            .prop('href', file.url);
1907
                        $(data.context.children()[index]).parent().wrap(link);
1908
                    }
1909
                    // Update file name with new one from Chamilo
1910
                    $(data.context.children()[index]).parent().find('.file_name').html(file.name);
1911
                    var message = $('<div class=\"col-sm-3\">').html(
1912
                        $('<span class=\"message-image-success\"/>').text('".addslashes(get_lang('File upload succeeded!'))."')
1913
                    );
1914
                    $(data.context.children()[index]).parent().append(message);
1915
                });
1916
                $('#dropzone').removeClass('hover');
1917
                ".$redirectCondition."
1918
            }).on('fileuploadfail', function (e, data) {
1919
                $.each(data.files, function (index) {
1920
                    var failedMessage = '".addslashes(get_lang('The file upload has failed.'))."';
1921
                    var error = $('<div class=\"col-sm-3\">').html(
1922
                        $('<span class=\"alert alert-danger\"/>').text(failedMessage)
1923
                    );
1924
                    $(data.context.children()[index]).parent().append(error);
1925
                });
1926
                $('#dropzone').removeClass('hover');
1927
            }).prop('disabled', !$.support.fileInput).parent().addClass($.support.fileInput ? undefined : 'disabled');
1928
1929
            $('#dropzone').on('dragover', function (e) {
1930
                // dragleave callback implementation
1931
                $('#dropzone').addClass('hover');
1932
            });
1933
1934
            $('#dropzone').on('dragleave', function (e) {
1935
                $('#dropzone').removeClass('hover');
1936
            });
1937
            $('.fileinput-button').hide();
1938
        });
1939
        </script>");
1940
    }
1941
}
1942
1943
/**
1944
 * Cleans HTML text filter.
1945
 *
1946
 * @param string $html HTML to clean
1947
 * @param int    $mode (optional)
1948
 *
1949
 * @return string The cleaned HTML
1950
 */
1951
function html_filter($html, $mode = NO_HTML)
1952
{
1953
    $allowed_tags = HTML_QuickForm_Rule_HTML::get_allowed_tags($mode);
1954
    $cleaned_html = kses($html, $allowed_tags);
0 ignored issues
show
Bug introduced by
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

1954
    $cleaned_html = /** @scrutinizer ignore-call */ kses($html, $allowed_tags);
Loading history...
1955
1956
    return $cleaned_html;
1957
}
1958
1959
function html_filter_teacher($html)
1960
{
1961
    return html_filter($html, TEACHER_HTML);
1962
}
1963
1964
function html_filter_student($html)
1965
{
1966
    return html_filter($html, STUDENT_HTML);
1967
}
1968
1969
function html_filter_teacher_fullpage($html)
1970
{
1971
    return html_filter($html, TEACHER_HTML_FULLPAGE);
1972
}
1973
1974
function html_filter_student_fullpage($html)
1975
{
1976
    return html_filter($html, STUDENT_HTML_FULLPAGE);
1977
}
1978
1979
/**
1980
 * Cleans mobile phone number text.
1981
 *
1982
 * @param string $mobilePhoneNumber Mobile phone number to clean
1983
 *
1984
 * @return string The cleaned mobile phone number
1985
 */
1986
function mobile_phone_number_filter($mobilePhoneNumber)
1987
{
1988
    $mobilePhoneNumber = str_replace(['+', '(', ')'], '', $mobilePhoneNumber);
1989
1990
    return ltrim($mobilePhoneNumber, '0');
1991
}
1992