Issues (2037)

main/inc/lib/formvalidator/FormValidator.class.php (1 issue)

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

2044
    $cleaned_html = /** @scrutinizer ignore-call */ kses($html, $allowed_tags);
Loading history...
2045
2046
    return $cleaned_html;
2047
}
2048
2049
function html_filter_teacher($html)
2050
{
2051
    return html_filter($html, TEACHER_HTML);
2052
}
2053
2054
function html_filter_student($html)
2055
{
2056
    return html_filter($html, STUDENT_HTML);
2057
}
2058
2059
function html_filter_teacher_fullpage($html)
2060
{
2061
    return html_filter($html, TEACHER_HTML_FULLPAGE);
2062
}
2063
2064
function html_filter_student_fullpage($html)
2065
{
2066
    return html_filter($html, STUDENT_HTML_FULLPAGE);
2067
}
2068
2069
/**
2070
 * Cleans mobile phone number text.
2071
 *
2072
 * @param string $mobilePhoneNumber Mobile phone number to clean
2073
 *
2074
 * @return string The cleaned mobile phone number
2075
 */
2076
function mobile_phone_number_filter($mobilePhoneNumber)
2077
{
2078
    $mobilePhoneNumber = str_replace(['+', '(', ')'], '', $mobilePhoneNumber);
2079
2080
    return ltrim($mobilePhoneNumber, '0');
2081
}
2082
2083
/**
2084
 * Cleans JS from a URL.
2085
 *
2086
 * @param string $html URL to clean
2087
 * @param int    $mode (optional)
2088
 *
2089
 * @return string The cleaned URL
2090
 */
2091
function plain_url_filter($html, $mode = NO_HTML)
2092
{
2093
    $allowed_tags = HTML_QuickForm_Rule_HTML::get_allowed_tags($mode);
2094
    $html = kses_no_null($html);
2095
    $html = kses_js_entities($html);
2096
    $allowed_html_fixed = kses_array_lc($allowed_tags);
2097
2098
    return kses_split($html, $allowed_html_fixed, ['http', 'https']);
2099
}
2100