ExtraField::set_extra_fields_in_form()   F
last analyzed

Complexity

Conditions 146
Paths 3

Size

Total Lines 920
Code Lines 579

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 146
eloc 579
c 0
b 0
f 0
nc 3
nop 16
dl 0
loc 920
rs 3.3333

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\Asset;
6
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
7
use Chamilo\CoreBundle\Entity\ExtraFieldRelTag;
8
use Chamilo\CoreBundle\Enums\ActionIcon;
9
use Chamilo\CoreBundle\Framework\Container;
10
11
class ExtraField extends Model
12
{
13
    public const FIELD_TYPE_TEXT = 1;
14
    public const FIELD_TYPE_TEXTAREA = 2;
15
    public const FIELD_TYPE_RADIO = 3;
16
    public const FIELD_TYPE_SELECT = 4;
17
    public const FIELD_TYPE_SELECT_MULTIPLE = 5;
18
    public const FIELD_TYPE_DATE = 6;
19
    public const FIELD_TYPE_DATETIME = 7;
20
    public const FIELD_TYPE_DOUBLE_SELECT = 8;
21
    public const FIELD_TYPE_DIVIDER = 9;
22
    public const FIELD_TYPE_TAG = 10;
23
    public const FIELD_TYPE_TIMEZONE = 11;
24
    public const FIELD_TYPE_SOCIAL_PROFILE = 12;
25
    public const FIELD_TYPE_CHECKBOX = 13;
26
    public const FIELD_TYPE_MOBILE_PHONE_NUMBER = 14;
27
    public const FIELD_TYPE_INTEGER = 15;
28
    public const FIELD_TYPE_FILE_IMAGE = 16;
29
    public const FIELD_TYPE_FLOAT = 17;
30
    public const FIELD_TYPE_FILE = 18;
31
    public const FIELD_TYPE_VIDEO_URL = 19;
32
    public const FIELD_TYPE_LETTERS_ONLY = 20;
33
    public const FIELD_TYPE_ALPHANUMERIC = 21;
34
    public const FIELD_TYPE_LETTERS_SPACE = 22;
35
    public const FIELD_TYPE_ALPHANUMERIC_SPACE = 23;
36
    public const FIELD_TYPE_GEOLOCALIZATION = 24;
37
    public const FIELD_TYPE_GEOLOCALIZATION_COORDINATES = 25;
38
    public const FIELD_TYPE_SELECT_WITH_TEXT_FIELD = 26;
39
    public const FIELD_TYPE_TRIPLE_SELECT = 27;
40
    public const FIELD_TYPE_DURATION = 28;
41
42
    public $columns = [
43
        'id',
44
        'value_type',
45
        'variable',
46
        'description',
47
        'display_text',
48
        'default_value',
49
        'field_order',
50
        'visible_to_self',
51
        'visible_to_others',
52
        'changeable',
53
        'filter',
54
        'item_type',
55
        //Enable this when field_loggeable is introduced as a table field (2.0)
56
        //'field_loggeable',
57
        'created_at',
58
        'auto_remove',
59
    ];
60
61
    public $ops = [
62
        'eq' => '=', //equal
63
        'ne' => '<>', //not equal
64
        'lt' => '<', //less than
65
        'le' => '<=', //less than or equal
66
        'gt' => '>', //greater than
67
        'ge' => '>=', //greater than or equal
68
        'bw' => 'LIKE', //begins with
69
        'bn' => 'NOT LIKE', //doesn't begin with
70
        'in' => 'LIKE', //is in
71
        'ni' => 'NOT LIKE', //is not in
72
        'ew' => 'LIKE', //ends with
73
        'en' => 'NOT LIKE', //doesn't end with
74
        'cn' => 'LIKE', //contains
75
        'nc' => 'NOT LIKE',  //doesn't contain
76
    ];
77
78
    public $type = 'user';
79
    public $pageName;
80
    public $pageUrl;
81
    public $itemType = 0;
82
    public $table_field_options;
83
    public $table_field_values;
84
    public $table_field_tag;
85
    public $table_field_rel_tag;
86
    public $handler_id;
87
    public $primaryKey;
88
89
    /**
90
     * @param string $type
91
     */
92
    public function __construct($type)
93
    {
94
        parent::__construct();
95
96
        $this->type = $type;
97
        $this->table = Database::get_main_table(TABLE_EXTRA_FIELD);
98
        $this->table_field_options = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
99
        $this->table_field_values = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
100
        $this->table_field_tag = Database::get_main_table(TABLE_MAIN_TAG);
101
        $this->table_field_rel_tag = Database::get_main_table(TABLE_MAIN_EXTRA_FIELD_REL_TAG);
102
        $this->handler_id = 'item_id';
103
        $this->itemType = self::getExtraFieldTypeFromString($this->type);
104
105
        switch ($this->type) {
106
            case 'session':
107
            case 'user':
108
            case 'calendar_event':
109
            case 'course':
110
                $this->primaryKey = 'id';
111
                break;
112
        }
113
114
        $this->pageUrl = 'extra_fields.php?type='.$this->type;
115
        // Example QuestionFields
116
        $this->pageName = ucwords($this->type).'Fields';
117
    }
118
119
    public static function getTypeList(): array
120
    {
121
        return [
122
            'calendar_event' => EntityExtraField::CALENDAR_FIELD_TYPE,
123
            'course' => EntityExtraField::COURSE_FIELD_TYPE,
124
            'user' => EntityExtraField::USER_FIELD_TYPE,
125
            'session' => EntityExtraField::SESSION_FIELD_TYPE,
126
            'exercise' => EntityExtraField::EXERCISE_FIELD_TYPE,
127
            'question' => EntityExtraField::QUESTION_FIELD_TYPE,
128
            'lp' => EntityExtraField::LP_FIELD_TYPE,
129
            'lp_item' => EntityExtraField::LP_ITEM_FIELD_TYPE,
130
            'skill' => EntityExtraField::SKILL_FIELD_TYPE,
131
            'work' => EntityExtraField::WORK_FIELD_TYPE,
132
            'career' => EntityExtraField::CAREER_FIELD_TYPE,
133
            'user_certificate' => EntityExtraField::USER_CERTIFICATE,
134
            'survey' => EntityExtraField::SURVEY_FIELD_TYPE,
135
            'scheduled_announcement' => EntityExtraField::SCHEDULED_ANNOUNCEMENT,
136
            'terms_and_condition' => EntityExtraField::TERMS_AND_CONDITION_TYPE,
137
            'forum_category' => EntityExtraField::FORUM_CATEGORY_TYPE,
138
            'forum_post' => EntityExtraField::FORUM_POST_TYPE,
139
            'track_exercise' => EntityExtraField::TRACK_EXERCISE_FIELD_TYPE,
140
            'portfolio' => EntityExtraField::PORTFOLIO_TYPE,
141
            'lp_view' => EntityExtraField::LP_VIEW_TYPE,
142
            'course_announcement' => EntityExtraField::COURSE_ANNOUNCEMENT,
143
            'message' =>  EntityExtraField::MESSAGE_TYPE,
144
            'document' => EntityExtraField::DOCUMENT_TYPE,
145
            'attendance_calendar' => EntityExtraField::ATTENDANCE_CALENDAR_TYPE,
146
        ];
147
    }
148
149
    public static function getExtraFieldTypeFromString($extraFieldTypeInString): string
150
    {
151
        return self::getTypeList()[$extraFieldTypeInString];
152
    }
153
154
    public static function getExtraFieldTypeFromInt($extraFieldTypeInt)
155
    {
156
        $reversed = array_flip(self::getTypeList());
157
158
        return $reversed[$extraFieldTypeInt];
159
    }
160
161
    /**
162
     * @return array
163
     */
164
    public static function getValidExtraFieldTypes()
165
    {
166
        $result = [
167
            'user',
168
            'course',
169
            'session',
170
            'question',
171
            'lp',
172
            'calendar_event',
173
            'lp_item',
174
            'skill',
175
            'work',
176
            'career',
177
            'user_certificate',
178
            'survey',
179
            'terms_and_condition',
180
            'forum_category',
181
            'forum_post',
182
            'exercise',
183
            'track_exercise',
184
            'lp_view',
185
            'course_announcement',
186
            'message',
187
            'document',
188
            'attendance_calendar',
189
        ];
190
191
        if ('true' === api_get_setting('announcement.allow_scheduled_announcements')) {
192
            $result[] = 'scheduled_announcement';
193
        }
194
        $result[] = 'portfolio';
195
        sort($result);
196
197
        return $result;
198
    }
199
200
    /**
201
     * Converts a string like this:
202
     * France:Paris;Bretagne;Marseille;Lyon|Belgique:Bruxelles;Namur;Liège;Bruges|Peru:Lima;Piura;
203
     * into
204
     * array(
205
     *   'France' =>
206
     *      array('Paris', 'Bretagne', 'Marseille'),
207
     *   'Belgique' =>
208
     *      array('Namur', 'Liège')
209
     * ), etc.
210
     *
211
     * @param string $string
212
     *
213
     * @return array
214
     */
215
    public static function extra_field_double_select_convert_string_to_array($string)
216
    {
217
        $options = explode('|', $string);
218
        $options_parsed = [];
219
        $id = 0;
220
221
        if (!empty($options)) {
222
            foreach ($options as $sub_options) {
223
                $options = explode(':', $sub_options);
224
                $sub_sub_options = isset($options[1]) ? explode(';', $options[1]) : [];
225
                $options_parsed[$id] = [
226
                    'label' => $options[0],
227
                    'options' => $sub_sub_options,
228
                ];
229
                $id++;
230
            }
231
        }
232
233
        return $options_parsed;
234
    }
235
236
    /**
237
     * @param $string
238
     *
239
     * @return array
240
     */
241
    public static function tripleSelectConvertStringToArray($string)
242
    {
243
        $options = [];
244
        foreach (explode('|', $string) as $i => $item0) {
245
            $level1 = explode('\\', $item0);
246
247
            foreach ($level1 as $j => $item1) {
248
                if (0 === $j) {
249
                    $options[] = ['label' => $item1, 'options' => []];
250
251
                    continue;
252
                }
253
254
                foreach (explode(':', $item1) as $k => $item2) {
255
                    if (0 === $k) {
256
                        $options[$i]['options'][] = ['label' => $item2, 'options' => []];
257
258
                        continue;
259
                    }
260
261
                    $options[$i]['options'][$j - 1]['options'][] = explode(';', $item2);
262
                }
263
            }
264
        }
265
266
        array_walk_recursive(
267
            $options,
268
            function (&$item) {
269
                $item = trim($item);
270
            }
271
        );
272
273
        return $options;
274
    }
275
276
    /**
277
     * @param array $options the result of the get_field_options_by_field() array
278
     *
279
     * @return string
280
     */
281
    public static function extra_field_double_select_convert_array_to_string($options)
282
    {
283
        $string = null;
284
        $optionsParsed = self::extra_field_double_select_convert_array_to_ordered_array($options);
285
286
        if (!empty($optionsParsed)) {
287
            foreach ($optionsParsed as $option) {
288
                foreach ($option as $key => $item) {
289
                    $string .= $item['display_text'];
290
                    if (0 == $key) {
291
                        $string .= ':';
292
                    } else {
293
                        if (isset($option[$key + 1])) {
294
                            $string .= ';';
295
                        }
296
                    }
297
                }
298
                $string .= '|';
299
            }
300
        }
301
302
        if (!empty($string)) {
303
            $string = substr($string, 0, strlen($string) - 1);
304
        }
305
306
        return $string;
307
    }
308
309
    /**
310
     * @param array $options The result of the get_field_options_by_field() array
311
     *
312
     * @return string
313
     */
314
    public static function extraFieldSelectWithTextConvertArrayToString(array $options)
315
    {
316
        $parsedOptions = self::extra_field_double_select_convert_array_to_ordered_array($options);
317
318
        if (empty($parsedOptions)) {
319
            return '';
320
        }
321
322
        $string = '';
323
        foreach ($parsedOptions as $options) {
324
            $option = current($options);
325
            $string .= $option['display_text'];
326
            $string .= '|';
327
        }
328
329
        return rtrim($string, '|');
330
    }
331
332
    /**
333
     * @return string
334
     */
335
    public static function tripleSelectConvertArrayToString(array $options)
336
    {
337
        $parsedOptions = self::tripleSelectConvertArrayToOrderedArray($options);
338
        $string = '';
339
        foreach ($parsedOptions['level1'] as $item1) {
340
            $string .= $item1['display_text'];
341
            $level2 = self::getOptionsFromTripleSelect($parsedOptions['level2'], $item1['id']);
342
343
            foreach ($level2 as $item2) {
344
                $string .= '\\'.$item2['display_text'].':';
345
                $level3 = self::getOptionsFromTripleSelect($parsedOptions['level3'], $item2['id']);
346
347
                $string .= implode(';', array_column($level3, 'display_text'));
348
            }
349
350
            $string .= '|';
351
        }
352
353
        return trim($string, '\\|;');
354
    }
355
356
    /**
357
     * @param string $variable
358
     * @param string $dataValue
359
     *
360
     * @return string
361
     */
362
    public static function getLocalizationJavascript($variable, $dataValue)
363
    {
364
        $dataValue = addslashes($dataValue);
365
        $html = "<script>
366
            $(function() {
367
                if (typeof google === 'object') {
368
                    var address = '$dataValue';
369
                    initializeGeo{$variable}(address, false);
370
371
                    $('#geolocalization_extra_{$variable}').on('click', function() {
372
                        var address = $('#{$variable}').val();
373
                        initializeGeo{$variable}(address, false);
374
                        return false;
375
                    });
376
377
                    $('#myLocation_extra_{$variable}').on('click', function() {
378
                        myLocation{$variable}();
379
                        return false;
380
                    });
381
382
                    // When clicking enter
383
                    $('#{$variable}').keypress(function(event) {
384
                        if (event.which == 13) {
385
                            $('#geolocalization_extra_{$variable}').click();
386
                            return false;
387
                        }
388
                    });
389
390
                    // On focus out update city
391
                    $('#{$variable}').focusout(function() {
392
                        $('#geolocalization_extra_{$variable}').click();
393
                        return false;
394
                    });
395
396
                    return;
397
                }
398
399
                $('#map_extra_{$variable}')
400
                    .html('<div class=\"alert alert-info\">"
401
            .addslashes(get_lang('You need to activate the GoogleMaps plugin in administration to see the Map'))
402
            ."</div>');
403
            });
404
405
            function myLocation{$variable}()
406
            {
407
                if (navigator.geolocation) {
408
                    var geoPosition = function(position) {
409
                        var lat = position.coords.latitude;
410
                        var lng = position.coords.longitude;
411
                        var latLng = new google.maps.LatLng(lat, lng);
412
                        initializeGeo{$variable}(false, latLng);
413
                    };
414
415
                    var geoError = function(error) {
416
                        alert('Geocode ".get_lang('Error').": ' + error);
417
                    };
418
419
                    var geoOptions = {
420
                        enableHighAccuracy: true
421
                    };
422
                    navigator.geolocation.getCurrentPosition(geoPosition, geoError, geoOptions);
423
                }
424
            }
425
426
            function initializeGeo{$variable}(address, latLng)
427
            {
428
                var geocoder = new google.maps.Geocoder();
429
                var latlng = new google.maps.LatLng(-34.397, 150.644);
430
                var myOptions = {
431
                    zoom: 15,
432
                    center: latlng,
433
                    mapTypeControl: true,
434
                    mapTypeControlOptions: {
435
                        style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
436
                    },
437
                    navigationControl: true,
438
                    mapTypeId: google.maps.MapTypeId.ROADMAP
439
                };
440
441
                map_{$variable} = new google.maps.Map(
442
                    document.getElementById('map_extra_{$variable}'),
443
                    myOptions
444
                );
445
446
                var parameter = address ? {'address': address} : latLng ? {'latLng': latLng} : false;
447
448
                if (geocoder && parameter) {
449
                    geocoder.geocode(parameter, function(results, status) {
450
                        if (status == google.maps.GeocoderStatus.OK) {
451
                            if (status != google.maps.GeocoderStatus.ZERO_RESULTS) {
452
                                map_{$variable}.setCenter(results[0].geometry.location);
453
454
                                // get city and country
455
                                var defaultAddress = results[0].formatted_address;
456
                                var city = '';
457
                                var country = '';
458
459
                                for (var i=0; i<results[0].address_components.length; i++) {
460
                                    if (results[0].address_components[i].types[0] == \"locality\") {
461
                                        //this is the object you are looking for City
462
                                        city = results[0].address_components[i];
463
                                    }
464
                                    /*if (results[j].address_components[i].types[0] == \"administrative_area_level_1\") {
465
                                        //this is the object you are looking for State
466
                                        region = results[0].address_components[i];
467
                                    }*/
468
                                    if (results[0].address_components[i].types[0] == \"country\") {
469
                                        //this is the object you are looking for
470
                                        country = results[0].address_components[i];
471
                                    }
472
                                }
473
474
                                if (city && city.long_name && country && country.long_name) {
475
                                    defaultAddress = city.long_name + ', ' + country.long_name;
476
                                }
477
                                $('#{$variable}').val(defaultAddress);
478
                                $('#{$variable}_coordinates').val(
479
                                    results[0].geometry.location.lat()+','+results[0].geometry.location.lng()
480
                                );
481
482
                                var infowindow = new google.maps.InfoWindow({
483
                                    content: '<b>' + $('#extra_{$variable}').val() + '</b>',
484
                                    size: new google.maps.Size(150, 50)
485
                                });
486
487
                                var marker = new google.maps.Marker({
488
                                    position: results[0].geometry.location,
489
                                    map: map_{$variable},
490
                                    title: $('#extra_{$variable}').val()
491
                                });
492
                                google.maps.event.addListener(marker, 'click', function() {
493
                                    infowindow.open(map_{$variable}, marker);
494
                                });
495
                            } else {
496
                                alert('".get_lang('Not found')."');
497
                            }
498
                        } else {
499
                            alert('Geocode ".get_lang('Error').': '.get_lang('Address').' '.get_lang('Not found')."');
500
                        }
501
                    });
502
                }
503
            }
504
            </script>";
505
506
        return $html;
507
    }
508
509
    /**
510
     * @param string $variable
511
     * @param string $text
512
     *
513
     * @return string
514
     */
515
    public static function getLocalizationInput($variable, $text)
516
    {
517
        $textHelp = $text;
518
        if (is_array($text)) {
519
            $textHelp = $text[0];
520
        }
521
        return '
522
                <div class="form-group">
523
                    <label for="geolocalization_extra_'.$variable.'"
524
                        class="col-sm-2 control-label"></label>
525
                    <div class="col-sm-8">
526
                        <button class="btn btn--plain"
527
                            id="geolocalization_extra_'.$variable.'"
528
                            name="geolocalization_extra_'.$variable.'"
529
                            type="submit">
530
                            <em class="fa fa-map-marker"></em> '.get_lang('Search for this location').'
531
                        </button>
532
                        <button class="btn btn--plain" id="myLocation_extra_'.$variable.'"
533
                            name="myLocation_extra_'.$variable.'"
534
                            type="submit">
535
                            <em class="fa fa-crosshairs"></em> '.get_lang('My location').'
536
                        </button>
537
                    </div>
538
                </div>
539
                <div class="form-group">
540
                    <label for="map_extra_'.$variable.'" class="col-sm-2 control-label">
541
                        '.$textHelp.' - '.get_lang('Map').'
542
                    </label>
543
                    <div class="col-sm-8">
544
                        <div name="map_extra_'.$variable.'"
545
                            id="map_extra_'.$variable.'" style="width:100%; height:300px;">
546
                        </div>
547
                    </div>
548
                </div>
549
            ';
550
    }
551
552
    /**
553
     * @return int
554
     */
555
    public function get_count()
556
    {
557
        $em = Database::getManager();
558
        $query = $em->getRepository(EntityExtraField::class)->createQueryBuilder('e');
559
        $query->select('count(e.id)');
560
        $query->where('e.itemType = :type');
561
        $query->setParameter('type', $this->getItemType());
562
563
        return $query->getQuery()->getSingleScalarResult();
564
    }
565
566
    /**
567
     * @return int
568
     */
569
    public function getItemType()
570
    {
571
        return (int) $this->itemType;
572
    }
573
574
    /**
575
     * @param string $sidx
576
     * @param string $sord
577
     * @param int    $start
578
     * @param int    $limit
579
     *
580
     * @return array<int, EntityExtraField>
581
     */
582
    public function getAllGrid($sidx, $sord, $start, $limit)
583
    {
584
        switch ($sidx) {
585
            case 'field_order':
586
                $sidx = 'e.fieldOrder';
587
                break;
588
            case 'variable':
589
                $sidx = 'e.variable';
590
                break;
591
            case 'display_text':
592
                $sidx = 'e.displayText';
593
                break;
594
            case 'changeable':
595
                $sidx = 'e.changeable';
596
                break;
597
            case 'visible_to_self':
598
                $sidx = 'e.visibleToSelf';
599
                break;
600
            case 'visible_to_others':
601
                $sidx = 'e.visibleToOthers';
602
                break;
603
            case 'filter':
604
                $sidx = 'e.filter';
605
                break;
606
            case 'auto_remove':
607
                $sidx = 'e.autoRemove';
608
                break;
609
        }
610
        $em = Database::getManager();
611
        $query = $em->getRepository(EntityExtraField::class)->createQueryBuilder('e');
612
        $query
613
            ->where('e.itemType = :type')
614
            ->setParameter('type', $this->getItemType())
615
            ->orderBy($sidx, $sord)
616
            ->setFirstResult($start)
617
            ->setMaxResults($limit);
618
619
        return $query->getQuery()->getResult();
620
    }
621
622
    /**
623
     * Get all the field info for tags.
624
     *
625
     * @param string $variable
626
     *
627
     * @return array|bool
628
     */
629
    public function get_handler_field_info_by_tags($variable)
630
    {
631
        $variable = Database::escape_string($variable);
632
        $sql = "SELECT * FROM {$this->table}
633
                WHERE
634
                    variable = '$variable' AND
635
                    item_type = $this->itemType";
636
        $result = Database::query($sql);
637
        $extraFieldRepo = Container::getExtraFieldRepository();
638
        if (Database::num_rows($result)) {
639
            $row = Database::fetch_assoc($result);
640
            $extraFieldId = $row['id'];
641
            /** @var EntityExtraField $extraField */
642
            $extraField = $extraFieldRepo->find($extraFieldId);
643
            $row['display_text'] = $extraField->getDisplayText();
644
645
            // All the tags of the field
646
            $sql = "SELECT * FROM $this->table_field_tag
647
                    WHERE field_id='".$extraFieldId."'
648
                    ORDER BY id ASC";
649
            $result = Database::query($sql);
650
            while ($option = Database::fetch_assoc($result)) {
651
                $row['options'][$option['id']] = $option;
652
            }
653
654
            return $row;
655
        }
656
657
        return false;
658
    }
659
660
    /**
661
     * @param int $fieldId
662
     *
663
     * @return array|bool
664
     */
665
    public function getFieldInfoByFieldId($fieldId)
666
    {
667
        $fieldId = (int) $fieldId;
668
        $sql = "SELECT * FROM {$this->table}
669
                WHERE
670
                    id = '$fieldId' AND
671
                    item_type = $this->itemType";
672
        $result = Database::query($sql);
673
        if (Database::num_rows($result)) {
674
            $row = Database::fetch_assoc($result);
675
676
            // All the options of the field
677
            $sql = "SELECT * FROM $this->table_field_options
678
                    WHERE field_id='".$fieldId."'
679
                    ORDER BY option_order ASC";
680
            $result = Database::query($sql);
681
            while ($option = Database::fetch_array($result)) {
682
                $row['options'][$option['id']] = $option;
683
            }
684
685
            return $row;
686
        } else {
687
            return false;
688
        }
689
    }
690
691
    /**
692
     * Add elements to a form.
693
     *
694
     * @param FormValidator $form                            The form object to which to attach this element
695
     * @param int           $itemId                          The item (course, user, session, etc) this extra_field is
696
     *                                                       linked to
697
     * @param array         $exclude                         Variables of extra field to exclude
698
     * @param bool          $filter                          Whether to get only the fields with the "filter" flag set
699
     *                                                       to 1 (true) or not (false)
700
     * @param bool          $useTagAsSelect                  Whether to show tag fields as select drop-down or not
701
     * @param array         $showOnlyTheseFields             Limit the extra fields shown to just the list given here
702
     * @param array         $orderFields                     An array containing the names of the fields shown, in the
703
     *                                                       right order
704
     * @param array         $extraData
705
     * @param bool          $orderDependingDefaults
706
     * @param bool          $adminPermissions
707
     * @param array         $separateExtraMultipleSelect
708
     * @param array         $customLabelsExtraMultipleSelect
709
     * @param bool          $addEmptyOptionSelects
710
     * @param array         $introductionTextList
711
     * @param array         $requiredFields
712
     * @param bool          $hideGeoLocalizationDetails
713
     *
714
     * @throws Exception
715
     *
716
     * @return array|bool If relevant, returns a one-element array with JS code to be added to the page HTML headers.
717
     *                    Returns false if the form object was not given
718
     */
719
    public function addElements(
720
        $form,
721
        $itemId = 0,
722
        $exclude = [],
723
        $filter = false,
724
        $useTagAsSelect = false,
725
        $showOnlyTheseFields = [],
726
        $orderFields = [],
727
        $extraData = [],
728
        $orderDependingDefaults = false,
729
        $adminPermissions = false,
730
        $separateExtraMultipleSelect = [],
731
        $customLabelsExtraMultipleSelect = [],
732
        $addEmptyOptionSelects = false,
733
        $introductionTextList = [],
734
        $requiredFields = [],
735
        $hideGeoLocalizationDetails = false,
736
        $help = false
737
    ) {
738
        if (empty($form)) {
739
            return false;
740
        }
741
742
        $itemId = (int) $itemId;
743
        $form->addHidden('item_id', $itemId);
744
        $extraData = false;
745
        if (!empty($itemId)) {
746
            $extraData = $this->get_handler_extra_data($itemId);
747
            if (!empty($showOnlyTheseFields)) {
748
                $setData = [];
749
                foreach ($showOnlyTheseFields as $variable) {
750
                    $extraName = 'extra_'.$variable;
751
                    if (in_array($extraName, array_keys($extraData))) {
752
                        $setData[$extraName] = $extraData[$extraName];
753
                    }
754
                }
755
                $form->setDefaults($setData);
756
            } else {
757
                $form->setDefaults($extraData);
758
            }
759
        }
760
761
        $conditions = [];
762
        if ($filter) {
763
            $conditions = ['filter = ?' => 1];
764
        }
765
766
        $extraFields = $this->get_all($conditions, 'option_order');
767
        $extra = $this->set_extra_fields_in_form(
768
            $form,
769
            $extraData,
770
            $adminPermissions,
771
            $extraFields,
772
            $itemId,
773
            $exclude,
774
            $useTagAsSelect,
775
            $showOnlyTheseFields,
776
            $orderFields,
777
            $orderDependingDefaults,
778
            $separateExtraMultipleSelect,
779
            $customLabelsExtraMultipleSelect,
780
            $addEmptyOptionSelects,
781
            $introductionTextList,
782
            $hideGeoLocalizationDetails,
783
            $help
784
        );
785
786
        if (!empty($requiredFields)) {
787
            /** @var HTML_QuickForm_input $element */
788
            foreach ($form->getElements() as $element) {
789
                $name = str_replace('extra_', '', $element->getName());
790
                if (in_array($name, $requiredFields)) {
791
                    $form->setRequired($element);
792
                }
793
            }
794
        }
795
796
        return $extra;
797
    }
798
799
    /**
800
     * Return an array of all the extra fields available for this item.
801
     *
802
     * @param int $itemId (session_id, question_id, course id)
803
     *
804
     * @return array
805
     */
806
    public function get_handler_extra_data($itemId)
807
    {
808
        if (empty($itemId)) {
809
            return [];
810
        }
811
812
        $extra_data = [];
813
        $fields = $this->get_all();
814
        $field_values = new ExtraFieldValue($this->type);
815
816
        if (!empty($fields)) {
817
            foreach ($fields as $field) {
818
                $field_value = $fieldValueArray = $field_values->get_values_by_handler_and_field_id(
819
                    $itemId,
820
                    $field['id']
821
                );
822
823
                if (self::FIELD_TYPE_TAG == $field['value_type']) {
824
                    $tags = UserManager::get_user_tags_to_string(
825
                        $itemId,
826
                        $field['id'],
827
                        false
828
                    );
829
                    $extra_data['extra_'.$field['variable']] = $tags;
830
831
                    continue;
832
                }
833
834
                if ($field_value) {
835
                    $variable = $field['variable'];
836
                    $field_value = $field_value['field_value'];
837
                    switch ($field['value_type']) {
838
                        case self::FIELD_TYPE_FILE_IMAGE:
839
                        case self::FIELD_TYPE_FILE:
840
                            // Get asset id
841
                            $extra_data['extra_'.$field['variable']] = $fieldValueArray['asset_id'] ?? 0;
842
                            break;
843
                        case self::FIELD_TYPE_TAG:
844
                            $tags = UserManager::get_user_tags_to_string(
845
                                $itemId,
846
                                $field['id'],
847
                                false
848
                            );
849
850
                            $extra_data['extra_'.$field['variable']] = $tags;
851
                            break;
852
                        case self::FIELD_TYPE_DOUBLE_SELECT:
853
                        case self::FIELD_TYPE_SELECT_WITH_TEXT_FIELD:
854
                            $selected_options = explode('::', $field_value);
855
                            $firstOption = isset($selected_options[0]) ? $selected_options[0] : '';
856
                            $secondOption = isset($selected_options[1]) ? $selected_options[1] : '';
857
                            $extra_data['extra_'.$field['variable']]['extra_'.$field['variable']] = $firstOption;
858
                            $extra_data['extra_'.$field['variable']]['extra_'.$field['variable'].'_second'] = $secondOption;
859
860
                            break;
861
                        case self::FIELD_TYPE_SELECT_MULTIPLE:
862
                            $field_value = explode(';', $field_value);
863
                            $extra_data['extra_'.$field['variable']] = $field_value;
864
                            break;
865
                        case self::FIELD_TYPE_RADIO:
866
                            $extra_data['extra_'.$field['variable']]['extra_'.$field['variable']] = $field_value;
867
                            break;
868
                        case self::FIELD_TYPE_TRIPLE_SELECT:
869
                            [$level1, $level2, $level3] = explode(';', $field_value);
870
871
                            $extra_data["extra_$variable"]["extra_$variable"] = $level1;
872
                            $extra_data["extra_$variable"]["extra_{$variable}_second"] = $level2;
873
                            $extra_data["extra_$variable"]["extra_{$variable}_third"] = $level3;
874
                            break;
875
                        case self::FIELD_TYPE_DURATION:
876
                            $extra_data['extra_'.$field['variable']] = self::formatDuration((int) $field_value);
0 ignored issues
show
Bug Best Practice introduced by
The method ExtraField::formatDuration() is not static, but was called statically. ( Ignorable by Annotation )

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

876
                            /** @scrutinizer ignore-call */ 
877
                            $extra_data['extra_'.$field['variable']] = self::formatDuration((int) $field_value);
Loading history...
877
                            break;
878
                        default:
879
                            $extra_data['extra_'.$field['variable']] = $field_value;
880
                            break;
881
                    }
882
                } else {
883
                    // Set default values
884
                    if (isset($field['field_default_value']) &&
885
                        !empty($field['field_default_value'])
886
                    ) {
887
                        $extra_data['extra_'.$field['variable']] = $field['field_default_value'];
888
                    }
889
                }
890
            }
891
        }
892
893
        return $extra_data;
894
    }
895
896
    /**
897
     * Formats a duration in seconds into hh:mm:ss.
898
     */
899
    private function formatDuration(int $seconds): string
900
    {
901
        $hours = floor($seconds / 3600);
902
        $minutes = floor(($seconds % 3600) / 60);
903
        $seconds = $seconds % 60;
904
905
        return sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds);
906
    }
907
908
    /**
909
     * Get an array of all the values from the extra_field and extra_field_options tables
910
     * based on the current object's type.
911
     */
912
    public function get_all(array $options = []): array
913
    {
914
        $order_field_options_by = null;
915
916
        if (func_num_args() > 1) {
917
            $order_field_options_by = func_get_arg(1);
918
        }
919
920
        $options = Database::parse_conditions(['where' => $options]);
921
922
        if (empty($options)) {
923
            $options .= ' WHERE item_type = '.$this->itemType;
924
        } else {
925
            $options .= ' AND item_type = '.$this->itemType;
926
        }
927
928
        $sql = "SELECT * FROM $this->table
929
                $options
930
                ORDER BY field_order ASC
931
        ";
932
933
        $result = Database::query($sql);
934
        $extraFields = Database::store_result($result, 'ASSOC');
935
936
        $extraFieldRepo = Container::getExtraFieldRepository();
937
        $option = new ExtraFieldOption($this->type);
938
        if (!empty($extraFields)) {
939
            foreach ($extraFields as &$extraField) {
940
                $extraFieldId = $extraField['id'];
941
                /** @var EntityExtraField $field */
942
                $field = $extraFieldRepo->find($extraFieldId);
943
                $extraField['display_text'] = $field->getDisplayText();
944
                $extraField['options'] = $option->get_field_options_by_field(
945
                    $extraField['id'],
946
                    false,
947
                    $order_field_options_by
948
                );
949
            }
950
        }
951
952
        return $extraFields;
953
    }
954
955
    /**
956
     * Fetches extra field data with various display and permission checks.
957
     *
958
     * This function retrieves the data for extra fields, applies various filters
959
     * and checks to determine if each field should be displayed based on
960
     * admin permissions, visibility settings, and specific field inclusion or
961
     * exclusion lists. It also handles ordering of fields if an order list is provided.
962
     */
963
    public function getExtraFieldsData(
964
        array $extraData,
965
        bool $adminPermissions = false,
966
        array $extra = [],
967
        array $exclude = [],
968
        array $showOnlyTheseFields = [],
969
        array $orderFields = []
970
    ): array {
971
        $fieldsData = [];
972
973
        if (!empty($extra)) {
974
            $orderedExtraFields = [];
975
            if (!empty($orderFields)) {
976
                foreach ($orderFields as $order) {
977
                    foreach ($extra as $fieldDetails) {
978
                        if ($order == $fieldDetails['variable']) {
979
                            $orderedExtraFields[] = $fieldDetails;
980
                        }
981
                    }
982
                }
983
                $extra = $orderedExtraFields;
984
            }
985
986
            foreach ($extra as $fieldDetails) {
987
                $variable = $fieldDetails['variable'];
988
989
                if (!empty($showOnlyTheseFields) && !in_array($variable, $showOnlyTheseFields)) {
990
                    continue;
991
                }
992
993
                if (!$adminPermissions && 0 == $fieldDetails['visible_to_self']) {
994
                    continue;
995
                }
996
997
                if (in_array($variable, $exclude)) {
998
                    continue;
999
                }
1000
1001
                $fieldData = [
1002
                    'type' => $fieldDetails['value_type'],
1003
                    'variable' => $variable,
1004
                    'title' => get_lang($fieldDetails['display_text']),
1005
                    'defaultValue' => $fieldDetails['field_default_value'] ?? '',
1006
                ];
1007
1008
                if (!empty($fieldDetails['options'])) {
1009
                    $fieldData['options'] = array_map(function ($option) {
1010
                        return [
1011
                            'value' => $option['option_value'],
1012
                            'label' => $option['display_text'],
1013
                        ];
1014
                    }, $fieldDetails['options']);
1015
                }
1016
1017
                if (isset($extraData['extra_' . $variable])) {
1018
                    $fieldData['value'] = $extraData['extra_' . $variable];
1019
                }
1020
1021
                $fieldsData[] = $fieldData;
1022
            }
1023
        }
1024
1025
        return $fieldsData;
1026
    }
1027
1028
    /**
1029
     * Add an element that matches the given extra field to the given $form object.
1030
     *
1031
     * @param FormValidator $form                The form these fields are to be attached to
1032
     * @param array         $extraData
1033
     * @param bool          $adminPermissions    Whether the display is considered without edition limits (true) or not
1034
     *                                           (false)
1035
     * @param array         $extra
1036
     * @param int           $itemId              The item (course, user, session, etc) this extra_field is attached to
1037
     * @param array         $exclude             Extra fields to be skipped, by textual ID
1038
     * @param bool          $useTagAsSelect      Whether to show tag fields as select drop-down or not
1039
     * @param array         $showOnlyTheseFields Limit the extra fields shown to just the list given here
1040
     * @param array         $orderFields         An array containing the names of the fields shown, in the right order
1041
     *
1042
     * @throws Exception
1043
     *
1044
     * @return array If relevant, returns a one-element array with JS code to be added to the page HTML headers
1045
     */
1046
    public function set_extra_fields_in_form(
1047
        $form,
1048
        $extraData,
1049
        $adminPermissions = false,
1050
        $extra = [],
1051
        $itemId = null,
1052
        $exclude = [],
1053
        $useTagAsSelect = false,
1054
        $showOnlyTheseFields = [],
1055
        $orderFields = [],
1056
        $orderDependingDefaults = false,
1057
        $separateExtraMultipleSelect = [],
1058
        $customLabelsExtraMultipleSelect = [],
1059
        $addEmptyOptionSelects = false,
1060
        $introductionTextList = [],
1061
        $hideGeoLocalizationDetails = false,
1062
        $help = false
1063
    ) {
1064
        $jquery_ready_content = null;
1065
1066
        $assetRepo = Container::getAssetRepository();
1067
        $extraFieldRepo = Container::getExtraFieldRepository();
1068
1069
        if (!empty($extra)) {
1070
            $newOrder = [];
1071
            if (!empty($orderFields)) {
1072
                foreach ($orderFields as $order) {
1073
                    foreach ($extra as $field_details) {
1074
                        if ($order == $field_details['variable']) {
1075
                            $newOrder[] = $field_details;
1076
                        }
1077
                    }
1078
                }
1079
                $extra = $newOrder;
1080
            }
1081
1082
            foreach ($extra as $field_details) {
1083
                $variable = $field_details['variable'];
1084
                if (!empty($showOnlyTheseFields)) {
1085
                    if (!in_array($variable, $showOnlyTheseFields)) {
1086
                        continue;
1087
                    }
1088
                }
1089
1090
                // Getting default value id if is set
1091
                $defaultValueId = null;
1092
                if (isset($field_details['options']) && !empty($field_details['options'])) {
1093
                    $valueToFind = null;
1094
                    if (isset($field_details['field_default_value'])) {
1095
                        $valueToFind = $field_details['field_default_value'];
1096
                    }
1097
                    // If a value is found we override the default value
1098
                    if (isset($extraData['extra_'.$variable])) {
1099
                        $valueToFind = $extraData['extra_'.$variable];
1100
                    }
1101
1102
                    foreach ($field_details['options'] as $option) {
1103
                        if ($option['option_value'] == $valueToFind) {
1104
                            $defaultValueId = $option['id'];
1105
                        }
1106
                    }
1107
                }
1108
1109
                if (!$adminPermissions) {
1110
                    if (0 == $field_details['visible_to_self']) {
1111
                        continue;
1112
                    }
1113
1114
                    if (in_array($variable, $exclude)) {
1115
                        continue;
1116
                    }
1117
                }
1118
1119
                if (!empty($introductionTextList) &&
1120
                    in_array($variable, array_keys($introductionTextList))
1121
                ) {
1122
                    $form->addHtml($introductionTextList[$variable]);
1123
                }
1124
1125
                $freezeElement = false;
1126
                if (!$adminPermissions) {
1127
                    $freezeElement = 0 == $field_details['visible_to_self'] || 0 == $field_details['changeable'];
1128
                }
1129
1130
                //$translatedDisplayText = $field_details['display_text'];
1131
                /** @var EntityExtraField $extraField */
1132
                $extraField = $extraFieldRepo->find($field_details['id']);
1133
                $translatedDisplayText = $extraField->getDisplayText();
1134
1135
                $translatedDisplayHelpText = '';
1136
                if ($help) {
1137
                    $translatedDisplayHelpText .= get_lang($field_details['display_text'].'Help');
1138
                }
1139
1140
                if (!empty($translatedDisplayText)) {
1141
                    if (!empty($translatedDisplayHelpText)) {
1142
                        // In this case, exceptionally, display_text is an array
1143
                        // which is then treated by display_form()
1144
                        $field_details['display_text'] = [$translatedDisplayText, $translatedDisplayHelpText];
1145
                    } else {
1146
                        // We have an helper text, use it
1147
                        $field_details['display_text'] = $translatedDisplayText;
1148
                    }
1149
                }
1150
1151
                switch ($field_details['value_type']) {
1152
                    case self::FIELD_TYPE_TEXT:
1153
                        $form->addElement(
1154
                            'text',
1155
                            'extra_'.$variable,
1156
                            $field_details['display_text'],
1157
                            [
1158
                                'id' => 'extra_'.$variable,
1159
                            ]
1160
                        );
1161
                        $form->applyFilter(
1162
                            'extra_'.$variable,
1163
                            'stripslashes'
1164
                        );
1165
                        $form->applyFilter(
1166
                            'extra_'.$variable,
1167
                            'trim'
1168
                        );
1169
                        $form->applyFilter('extra_'.$variable, 'html_filter');
1170
1171
                        if ($freezeElement) {
1172
                            $form->freeze('extra_'.$variable);
1173
                        }
1174
                        break;
1175
                    case self::FIELD_TYPE_TEXTAREA:
1176
                        $form->addHtmlEditor(
1177
                            'extra_'.$variable,
1178
                            $field_details['display_text'],
1179
                            false,
1180
                            false,
1181
                            [
1182
                                'ToolbarSet' => 'Profile',
1183
                                'Width' => '100%',
1184
                                'Height' => '130',
1185
                                'id' => 'extra_'.$variable,
1186
                            ]
1187
                        );
1188
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1189
                        $form->applyFilter('extra_'.$variable, 'trim');
1190
                        $form->applyFilter('extra_'.$variable, 'html_filter');
1191
                        if ($freezeElement) {
1192
                            $form->freeze('extra_'.$variable);
1193
                        }
1194
                        break;
1195
                    case self::FIELD_TYPE_RADIO:
1196
                        $group = [];
1197
                        if (isset($field_details['options']) && !empty($field_details['options'])) {
1198
                            foreach ($field_details['options'] as $option_details) {
1199
                                $options[$option_details['option_value']] = $option_details['display_text'];
1200
                                $group[] = $form->createElement(
1201
                                    'radio',
1202
                                    'extra_'.$variable,
1203
                                    $option_details['option_value'],
1204
                                    get_lang($option_details['display_text']).'<br />',
1205
                                    $option_details['option_value']
1206
                                );
1207
                            }
1208
                        } else {
1209
                            // Fallback: Yes/No
1210
                            $currentValue = $extraData["extra_$variable"] ?? null;
1211
1212
                            $group[] = $form->createElement(
1213
                                'radio',
1214
                                'extra_'.$variable,
1215
                                '1',
1216
                                get_lang('Yes').'<br />',
1217
                                '1'
1218
                            );
1219
                            $group[] = $form->createElement(
1220
                                'radio',
1221
                                'extra_'.$variable,
1222
                                '0',
1223
                                get_lang('No').'<br />',
1224
                                '0'
1225
                            );
1226
1227
                            // Select default if nothing set
1228
                            if (!isset($currentValue)) {
1229
                                if (!empty($field_details['default_value'])) {
1230
                                    $form->setDefaults(['extra_'.$variable => $field_details['default_value']]);
1231
                                } else {
1232
                                    $form->setDefaults(['extra_'.$variable => '0']);
1233
                                }
1234
                            }
1235
                        }
1236
                        $form->addGroup(
1237
                            $group,
1238
                            'extra_'.$variable,
1239
                            $field_details['display_text']
1240
                        );
1241
                        if ($freezeElement) {
1242
                            $form->freeze('extra_'.$variable);
1243
                        }
1244
                        break;
1245
                    case self::FIELD_TYPE_CHECKBOX:
1246
                        $group = [];
1247
                        if (isset($field_details['options']) &&
1248
                            !empty($field_details['options'])
1249
                        ) {
1250
                            foreach ($field_details['options'] as $option_details) {
1251
                                $options[$option_details['option_value']] = $option_details['display_text'];
1252
                                $group[] = $form->createElement(
1253
                                    'checkbox',
1254
                                    'extra_'.$variable,
1255
                                    $option_details['option_value'],
1256
                                    get_lang($option_details['display_text']).'<br />',
1257
                                    $option_details['option_value']
1258
                                );
1259
                            }
1260
                        } else {
1261
                            $fieldVariable = "extra_$variable";
1262
                            $checkboxAttributes = [];
1263
                            if (is_array($extraData) &&
1264
                                array_key_exists($fieldVariable, $extraData)
1265
                            ) {
1266
                                if (!empty($extraData[$fieldVariable])) {
1267
                                    $checkboxAttributes['checked'] = 1;
1268
                                }
1269
                            }
1270
1271
                            if (empty($checkboxAttributes) &&
1272
                                isset($field_details['default_value']) && empty($extraData)) {
1273
                                if (1 == $field_details['default_value']) {
1274
                                    $checkboxAttributes['checked'] = 1;
1275
                                }
1276
                            }
1277
1278
                            // We assume that is a switch on/off with 1 and 0 as values
1279
                            $group[] = $form->createElement(
1280
                                'checkbox',
1281
                                'extra_'.$variable,
1282
                                null,
1283
                                get_lang('Yes'),
1284
                                $checkboxAttributes
1285
                            );
1286
                        }
1287
1288
                        $form->addGroup(
1289
                            $group,
1290
                            'extra_'.$variable,
1291
                            $field_details['display_text']
1292
                        );
1293
                        if ($freezeElement) {
1294
                            $form->freeze('extra_'.$variable);
1295
                        }
1296
                        break;
1297
                    case self::FIELD_TYPE_SELECT:
1298
                        $this->addSelectElement($form, $field_details, $defaultValueId, $freezeElement);
1299
                        break;
1300
                    case self::FIELD_TYPE_SELECT_MULTIPLE:
1301
                        $options = [];
1302
                        if (empty($defaultValueId)) {
1303
                            $options[''] = get_lang('Please select an option');
1304
                        }
1305
                        if (isset($field_details['options']) && !empty($field_details['options'])) {
1306
                            foreach ($field_details['options'] as $optionDetails) {
1307
                                $options[$optionDetails['option_value']] = get_lang($optionDetails['display_text']);
1308
                            }
1309
                        }
1310
1311
                        if ($orderDependingDefaults) {
1312
                            if (isset($extraData['extra_'.$variable])) {
1313
                                $defaultOptions = $extraData['extra_'.$variable];
1314
                                $firstList = [];
1315
                                if ($addEmptyOptionSelects) {
1316
                                    $firstList[] = '';
1317
                                }
1318
                                foreach ($defaultOptions as $key) {
1319
                                    if (isset($options[$key])) {
1320
                                        $firstList[$key] = $options[$key];
1321
                                    }
1322
                                }
1323
                                if (!empty($firstList)) {
1324
                                    $options = array_merge($firstList, $options);
1325
                                }
1326
                            } else {
1327
                                $firstList = [];
1328
                                if ($addEmptyOptionSelects) {
1329
                                    $firstList[] = '&nbsp;';
1330
                                    $options = array_merge($firstList, $options);
1331
                                }
1332
                            }
1333
                        }
1334
                        // OFAJ
1335
                        $separateValue = 0;
1336
                        if (isset($separateExtraMultipleSelect[$variable])) {
1337
                            $separateValue = $separateExtraMultipleSelect[$variable];
1338
                        }
1339
1340
                        if ($separateValue > 0) {
1341
                            for ($i = 0; $i < $separateValue; $i++) {
1342
                                $form->addSelect(
1343
                                    'extra_'.$variable.'['.$i.']',
1344
                                    $customLabelsExtraMultipleSelect[$variable][$i],
1345
                                    $options,
1346
                                    ['id' => 'extra_'.$variable.'_'.$i]
1347
                                );
1348
                            }
1349
                        } else {
1350
                            // Ofaj
1351
                            $attributes = ['multiple' => 'multiple', 'id' => 'extra_'.$variable];
1352
                            $chosenSelect = [
1353
                                'ecouter',
1354
                                'lire',
1355
                                'participer_a_une_conversation',
1356
                                's_exprimer_oralement_en_continu',
1357
                                'ecrire',
1358
                            ];
1359
1360
                            if (in_array($variable, $chosenSelect)) {
1361
                                $attributes['select_chosen'] = true;
1362
                            }
1363
1364
                            // default behaviour
1365
                            $form->addSelect(
1366
                                'extra_'.$variable,
1367
                                $field_details['display_text'],
1368
                                $options,
1369
                                $attributes,
1370
                            );
1371
1372
                        }
1373
1374
                        if ($freezeElement) {
1375
                            $form->freeze('extra_'.$variable);
1376
                        }
1377
                        /*$form->addSelect(
1378
                            'extra_'.$variable,
1379
                            $field_details['display_text'],
1380
                            $options,
1381
                            [
1382
                                'multiple' => 'multiple',
1383
                                'id' => 'extra_'.$variable,
1384
                            ]
1385
                        );
1386
                        if ($freezeElement) {
1387
                            $form->freeze('extra_'.$variable);
1388
                        }*/
1389
                        break;
1390
                    case self::FIELD_TYPE_DATE:
1391
                        $form->addDatePicker('extra_'.$variable, $field_details['display_text']);
1392
                        if ($freezeElement) {
1393
                            $form->freeze('extra_'.$variable);
1394
                        }
1395
                        break;
1396
                    case self::FIELD_TYPE_DATETIME:
1397
                        $form->addDateTimePicker(
1398
                            'extra_'.$variable,
1399
                            $field_details['display_text']
1400
                        );
1401
1402
                        $defaults['extra_'.$variable] = api_get_local_time();
1403
                        if (!isset($form->_defaultValues['extra_'.$variable])) {
1404
                            $form->setDefaults($defaults);
1405
                        }
1406
                        if ($freezeElement) {
1407
                            $form->freeze('extra_'.$variable);
1408
                        }
1409
                        break;
1410
                    case self::FIELD_TYPE_DOUBLE_SELECT:
1411
                        $jquery_ready_content .= self::addDoubleSelectElement(
0 ignored issues
show
Bug Best Practice introduced by
The method ExtraField::addDoubleSelectElement() is not static, but was called statically. ( Ignorable by Annotation )

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

1411
                        $jquery_ready_content .= self::/** @scrutinizer ignore-call */ addDoubleSelectElement(
Loading history...
1412
                            $form,
1413
                            $field_details,
1414
                            $extraData,
1415
                            $freezeElement
1416
                        );
1417
                        break;
1418
                    case self::FIELD_TYPE_DIVIDER:
1419
                        $form->addHtml('
1420
                            <div class="form-group ">
1421
                                <div class="col-sm-12">
1422
                                    <div class="panel-separator">
1423
                                       <h4 id="'.$variable.'" class="form-separator">'
1424
                                            .$field_details['display_text'].'
1425
                                       </h4>
1426
                                    </div>
1427
                                </div>
1428
                            </div>
1429
                        ');
1430
                        break;
1431
                    case self::FIELD_TYPE_TAG:
1432
                        $field_id = $field_details['id'];
1433
                        $separateValue = 0;
1434
                        if (isset($separateExtraMultipleSelect[$variable])) {
1435
                            $separateValue = $separateExtraMultipleSelect[$variable];
1436
                        }
1437
1438
                        $selectedOptions = [];
1439
                        if ($separateValue > 0) {
1440
                            $em = Database::getManager();
1441
                            $fieldTags = $em
1442
                                ->getRepository(ExtraFieldRelTag::class)
1443
                                ->findBy(
1444
                                    [
1445
                                        'field' => $field_id,
1446
                                        'itemId' => $itemId,
1447
                                    ]
1448
                                );
1449
                            // ofaj.
1450
                            for ($i = 0; $i < $separateValue; $i++) {
1451
                                $tagsSelect = $form->addSelect(
1452
                                    'extra_'.$variable.'['.$i.']',
1453
                                    $customLabelsExtraMultipleSelect[$variable][$i], //$field_details['display_text'],
1454
                                    [],
1455
                                    ['id' => 'extra_'.$variable.'_'.$i]
1456
                                );
1457
1458
                                if ($addEmptyOptionSelects) {
1459
                                    $tagsSelect->addOption(
1460
                                        '',
1461
                                        ''
1462
                                    );
1463
                                }
1464
                                /** @var ExtraFieldRelTag $fieldTag */
1465
                                foreach ($fieldTags as $fieldTag) {
1466
                                    $tag = $fieldTag->getTag();
1467
1468
                                    if (empty($tag)) {
1469
                                        continue;
1470
                                    }
1471
1472
                                    $tagsSelect->addOption(
1473
                                        $tag->getTag(),
1474
                                        $tag->getTag()
1475
                                    );
1476
                                }
1477
                            }
1478
                        } else {
1479
                            $tagsSelect = $form->addSelect(
1480
                                "extra_{$variable}",
1481
                                $field_details['display_text'],
1482
                                [],
1483
                                ['style' => 'width: 100%;']
1484
                            );
1485
1486
                            if (false === $useTagAsSelect) {
1487
                                $tagsSelect->setAttribute('class', null);
1488
                            }
1489
1490
                            $tagsSelect->setAttribute(
1491
                                'id',
1492
                                "extra_{$variable}"
1493
                            );
1494
                            $tagsSelect->setMultiple(true);
1495
1496
                            $selectedOptions = [];
1497
                            if ('user' === $this->type) {
1498
                                // The magic should be here
1499
                                $user_tags = UserManager::get_user_tags(
1500
                                    $itemId,
1501
                                    $field_details['id']
1502
                                );
1503
1504
                                if (is_array($user_tags) && count($user_tags) > 0) {
1505
                                    foreach ($user_tags as $tag) {
1506
                                        if (empty($tag['tag'])) {
1507
                                            continue;
1508
                                        }
1509
                                        $tagsSelect->addOption(
1510
                                            $tag['tag'],
1511
                                            $tag['tag'],
1512
                                            [
1513
                                                'selected' => 'selected',
1514
                                                'class' => 'selected',
1515
                                            ]
1516
                                        );
1517
                                        $selectedOptions[] = $tag['tag'];
1518
                                    }
1519
                                }
1520
                                $url = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php';
1521
                            } else {
1522
                                $em = Database::getManager();
1523
                                $fieldTags = $em->getRepository(
1524
                                    ExtraFieldRelTag::class
1525
                                )
1526
                                ->findBy(
1527
                                    [
1528
                                        'field' => $field_id,
1529
                                        'itemId' => $itemId,
1530
                                    ]
1531
                                );
1532
1533
                                /** @var ExtraFieldRelTag $fieldTag */
1534
                                foreach ($fieldTags as $fieldTag) {
1535
                                    $tag = $fieldTag->getTag();
1536
                                    if (empty($tag)) {
1537
                                        continue;
1538
                                    }
1539
                                    $tagsSelect->addOption(
1540
                                        $tag->getTag(),
1541
                                        $tag->getTag()
1542
                                    );
1543
                                    $selectedOptions[] = $tag->getTag();
1544
                                }
1545
1546
                                if (!empty($extraData) && isset($extraData['extra_'.$variable])) {
1547
                                    $data = $extraData['extra_'.$variable];
1548
                                    if (!empty($data)) {
1549
                                        foreach ($data as $option) {
1550
                                            $tagsSelect->addOption(
1551
                                                $option,
1552
                                                $option
1553
                                            );
1554
                                        }
1555
                                    }
1556
                                }
1557
1558
                                if ($useTagAsSelect) {
1559
                                    $fieldTags = $em->getRepository(ExtraFieldRelTag::class)
1560
                                        ->findBy(
1561
                                            [
1562
                                                'field' => $field_id,
1563
                                            ]
1564
                                        );
1565
                                    $tagsAdded = [];
1566
                                    /** @var ExtraFieldRelTag $fieldTag */
1567
                                    foreach ($fieldTags as $fieldTag) {
1568
                                        $tag = $fieldTag->getTag();
1569
1570
                                        if (empty($tag)) {
1571
                                            continue;
1572
                                        }
1573
1574
                                        $tagText = $tag->getTag();
1575
                                        if (in_array($tagText, $tagsAdded)) {
1576
                                            continue;
1577
                                        }
1578
1579
                                        $tagsSelect->addOption(
1580
                                            $tag->getTag(),
1581
                                            $tag->getTag(),
1582
                                            []
1583
                                        );
1584
1585
                                        $tagsAdded[] = $tagText;
1586
                                    }
1587
                                }
1588
                                $url = api_get_path(WEB_AJAX_PATH).'extra_field.ajax.php';
1589
                            }
1590
1591
                            $form->setDefaults(
1592
                                [
1593
                                    'extra_'.$variable => $selectedOptions,
1594
                                ]
1595
                            );
1596
1597
                            if (false == $useTagAsSelect) {
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...
1598
                                $jquery_ready_content .= "
1599
                                $('#extra_$variable').select2({
1600
                                    ajax: {
1601
                                        url: '$url?a=search_tags&field_id=$field_id&type={$this->type}',
1602
                                        processResults: function (data) {
1603
                                            return {
1604
                                                results: data.items
1605
                                            }
1606
                                        }
1607
                                    },
1608
                                    cache: false,
1609
                                    tags: true,
1610
                                    tokenSeparators: [','],
1611
                                    placeholder: '".get_lang('Start to type, then click on this bar to validate tag')."'
1612
                                });
1613
                            ";
1614
                            }
1615
                        }
1616
1617
                        break;
1618
                    case self::FIELD_TYPE_TIMEZONE:
1619
                        $form->addSelect(
1620
                            'extra_'.$variable,
1621
                            $field_details['display_text'],
1622
                            api_get_timezones(),
1623
                        );
1624
                        if ($freezeElement) {
1625
                            $form->freeze('extra_'.$variable);
1626
                        }
1627
                        break;
1628
                    case self::FIELD_TYPE_SOCIAL_PROFILE:
1629
                        // get the social network's favicon
1630
                        $extra_data_variable = isset($extraData['extra_'.$variable]) ? $extraData['extra_'.$variable] : null;
1631
                        $field_default_value = isset($field_details['field_default_value']) ? $field_details['field_default_value'] : null;
1632
                        $icon_path = UserManager::get_favicon_from_url(
1633
                            $extra_data_variable,
1634
                            $field_default_value
1635
                        );
1636
                        // special hack for hi5
1637
                        $leftpad = '1.7';
1638
                        $top = '0.4';
1639
                        $domain = parse_url($icon_path, PHP_URL_HOST);
1640
                        if ('www.hi5.com' === $domain || 'hi5.com' === $domain) {
1641
                            $leftpad = '3';
1642
                            $top = '0';
1643
                        }
1644
                        // print the input field
1645
                        $form->addElement(
1646
                            'text',
1647
                            'extra_'.$variable,
1648
                            $field_details['display_text'],
1649
                            [
1650
                                //'size' => 60,
1651
                                'size' => implode(
1652
                                    '; ',
1653
                                    [
1654
                                        "background-image: url('$icon_path')",
1655
                                        'background-repeat: no-repeat',
1656
                                        "background-position: 0.4em {$top}em",
1657
                                        "padding-left: {$leftpad}em",
1658
                                    ]
1659
                                ),
1660
                            ]
1661
                        );
1662
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1663
                        $form->applyFilter('extra_'.$variable, 'trim');
1664
                        if ($freezeElement) {
1665
                            $form->freeze('extra_'.$variable);
1666
                        }
1667
                        break;
1668
                    case self::FIELD_TYPE_MOBILE_PHONE_NUMBER:
1669
                        $form->addElement(
1670
                            'text',
1671
                            'extra_'.$variable,
1672
                            $field_details['display_text'].' ('.get_lang('Include the country dial code').')',
1673
                            ['size' => 40, 'placeholder' => '(xx)xxxxxxxxx']
1674
                        );
1675
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1676
                        $form->applyFilter('extra_'.$variable, 'trim');
1677
                        $form->applyFilter('extra_'.$variable, 'mobile_phone_number_filter');
1678
                        $form->applyFilter('extra_'.$variable, 'html_filter');
1679
                        $form->addRule(
1680
                            'extra_'.$variable,
1681
                            get_lang('Mobile phone number is incomplete or contains invalid characters'),
1682
                            'mobile_phone_number'
1683
                        );
1684
                        if ($freezeElement) {
1685
                            $form->freeze('extra_'.$variable);
1686
                        }
1687
                        break;
1688
                    case self::FIELD_TYPE_INTEGER:
1689
                        $form->addElement(
1690
                            'number',
1691
                            'extra_'.$variable,
1692
                            $field_details['display_text'],
1693
                            ['class' => 'span1', 'step' => 1]
1694
                        );
1695
1696
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1697
                        $form->applyFilter('extra_'.$variable, 'trim');
1698
                        $form->applyFilter('extra_'.$variable, 'intval');
1699
1700
                        if ($freezeElement) {
1701
                            $form->freeze('extra_'.$variable);
1702
                        }
1703
                        break;
1704
                    case self::FIELD_TYPE_FLOAT:
1705
                        $form->addElement(
1706
                            'number',
1707
                            'extra_'.$variable,
1708
                            $field_details['display_text'],
1709
                            ['class' => 'span1', 'step' => '0.01']
1710
                        );
1711
1712
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1713
                        $form->applyFilter('extra_'.$variable, 'trim');
1714
                        $form->applyFilter('extra_'.$variable, 'floatval');
1715
1716
                        if ($freezeElement) {
1717
                            $form->freeze('extra_'.$variable);
1718
                        }
1719
                        break;
1720
                    case self::FIELD_TYPE_FILE_IMAGE:
1721
                        $fieldVariable = "extra_{$variable}";
1722
                        $fieldTexts = [
1723
                            $field_details['display_text'],
1724
                        ];
1725
1726
                        if (is_array($extraData) && array_key_exists($fieldVariable, $extraData)) {
1727
                            $assetId = $extraData[$fieldVariable];
1728
                            if (!empty($assetId)) {
1729
                                $asset = $assetRepo->find($assetId);
1730
                                if (null !== $asset) {
1731
                                    $fieldTexts[] = Display::img(
1732
                                        $assetRepo->getAssetUrl($asset),
1733
                                        $field_details['display_text'],
1734
                                        ['width' => '300'],
1735
                                        false
1736
                                    );
1737
                                }
1738
                            }
1739
                        }
1740
1741
                        if ('Image' === $fieldTexts[0]) {
1742
                            $fieldTexts[0] = get_lang($fieldTexts[0]);
1743
                        }
1744
1745
                        $form->addFile(
1746
                            $fieldVariable,
1747
                            $fieldTexts,
1748
                            ['accept' => 'image/*', 'id' => 'extra_image', 'crop_image' => 'true']
1749
                        );
1750
1751
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1752
                        $form->applyFilter('extra_'.$variable, 'trim');
1753
1754
                        $allowedPictureTypes = ['jpg', 'jpeg', 'png', 'gif'];
1755
                        $form->addRule(
1756
                            'extra_'.$variable,
1757
                            get_lang('Only PNG, JPG or GIF images allowed').' ('.implode(',', $allowedPictureTypes).')',
1758
                            'filetype',
1759
                            $allowedPictureTypes
1760
                        );
1761
1762
                        if ($freezeElement) {
1763
                            $form->freeze('extra_'.$variable);
1764
                        }
1765
                        break;
1766
                    case self::FIELD_TYPE_FILE:
1767
                        $fieldVariable = "extra_{$variable}";
1768
                        $fieldTexts = [
1769
                            $field_details['display_text'],
1770
                        ];
1771
1772
                        if (is_array($extraData) &&
1773
                            array_key_exists($fieldVariable, $extraData)
1774
                        ) {
1775
                            $assetId = $extraData[$fieldVariable] ?? 0;
1776
                            /** @var Asset $asset */
1777
                            $asset = $assetRepo->find($assetId);
1778
                            if (null !== $asset) {
1779
                                $fileName = $asset->getTitle();
1780
                                $linkUrl = $assetRepo->getAssetUrl($asset);
1781
                                $linkToDelete = '';
1782
                                if (api_is_platform_admin()) {
1783
                                    $url = api_get_path(WEB_AJAX_PATH).'extra_field.ajax.php?type='.$this->type;
1784
                                    $url .= '&a=delete_file&field_id='.$field_details['id'].'&item_id='.$itemId;
1785
                                    $deleteId = $variable.'_delete';
1786
                                    $form->addHtml(
1787
                                        "
1788
                                        <script>
1789
                                            $(function() {
1790
                                                $('#".$deleteId."').on('click', function() {
1791
                                                    $.ajax({
1792
                                                        type: 'GET',
1793
                                                        url: '".$url."',
1794
                                                        success: function(result) {
1795
                                                            if (result == 1) {
1796
                                                                $('#".$variable."').html('".get_lang('Deleted')."');
1797
                                                            }
1798
                                                        }
1799
                                                    });
1800
                                                });
1801
                                            });
1802
                                        </script>
1803
                                    "
1804
                                    );
1805
1806
                                    $linkToDelete = '&nbsp;'.Display::url(
1807
                                        Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Delete')),
1808
                                        'javascript:void(0)',
1809
                                        ['id' => $deleteId]
1810
                                    );
1811
                                }
1812
                                $anchor = Display::url(
1813
                                    $fileName,
1814
                                    $linkUrl,
1815
                                    [
1816
                                        'title' => $field_details['display_text'],
1817
                                        'target' => '_blank',
1818
                                    ]
1819
                                );
1820
                                $fieldTexts[] = '<div id="'.$variable.'">'.$anchor.$linkToDelete.'</div>';
1821
                            }
1822
                        }
1823
1824
                        $form->addElement(
1825
                            'file',
1826
                            $fieldVariable,
1827
                            $fieldTexts,
1828
                            []
1829
                        );
1830
1831
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1832
                        $form->applyFilter('extra_'.$variable, 'trim');
1833
1834
                        if ($freezeElement) {
1835
                            $form->freeze('extra_'.$variable);
1836
                        }
1837
                        break;
1838
                    case self::FIELD_TYPE_VIDEO_URL:
1839
                        $form->addUrl(
1840
                            "extra_{$variable}",
1841
                            $field_details['display_text'],
1842
                            false,
1843
                            ['placeholder' => 'https://']
1844
                        );
1845
                        if ($freezeElement) {
1846
                            $form->freeze('extra_'.$variable);
1847
                        }
1848
                        break;
1849
                    case self::FIELD_TYPE_LETTERS_ONLY:
1850
                        $form->addTextLettersOnly(
1851
                            "extra_{$variable}",
1852
                            $field_details['display_text']
1853
                        );
1854
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1855
1856
                        if ($freezeElement) {
1857
                            $form->freeze('extra_'.$variable);
1858
                        }
1859
                        break;
1860
                    case self::FIELD_TYPE_ALPHANUMERIC:
1861
                        $form->addTextAlphanumeric(
1862
                            "extra_{$variable}",
1863
                            $field_details['display_text']
1864
                        );
1865
                        $form->applyFilter(
1866
                            'extra_'.$variable,
1867
                            'stripslashes'
1868
                        );
1869
                        if ($freezeElement) {
1870
                            $form->freeze('extra_'.$variable);
1871
                        }
1872
                        break;
1873
                    case self::FIELD_TYPE_LETTERS_SPACE:
1874
                        $form->addTextLettersAndSpaces(
1875
                            "extra_{$variable}",
1876
                            $field_details['display_text']
1877
                        );
1878
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1879
1880
                        if ($freezeElement) {
1881
                            $form->freeze('extra_'.$variable);
1882
                        }
1883
                        break;
1884
                    case self::FIELD_TYPE_ALPHANUMERIC_SPACE:
1885
                        $form->addTextAlphanumericAndSpaces(
1886
                            "extra_{$variable}",
1887
                            $field_details['display_text']
1888
                        );
1889
                        $form->applyFilter(
1890
                            'extra_'.$variable,
1891
                            'stripslashes'
1892
                        );
1893
                        if ($freezeElement) {
1894
                            $form->freeze('extra_'.$variable);
1895
                        }
1896
                        break;
1897
                    case self::FIELD_TYPE_GEOLOCALIZATION_COORDINATES:
1898
                    case self::FIELD_TYPE_GEOLOCALIZATION:
1899
                        $dataValue = isset($extraData['extra_'.$variable]) ? $extraData['extra_'.$variable] : '';
1900
                        $form->addGeoLocationMapField(
1901
                            'extra_'.$variable,
1902
                            $field_details['display_text'],
1903
                            $dataValue,
1904
                            $hideGeoLocalizationDetails
1905
                        );
1906
1907
                        if ($freezeElement) {
1908
                            $form->freeze('extra_'.$variable);
1909
                        }
1910
                        break;
1911
                    case self::FIELD_TYPE_SELECT_WITH_TEXT_FIELD:
1912
                        $jquery_ready_content .= $this->addSelectWithTextFieldElement(
1913
                            $form,
1914
                            $field_details,
1915
                            $freezeElement
1916
                        );
1917
                        break;
1918
                    case self::FIELD_TYPE_TRIPLE_SELECT:
1919
                        $jquery_ready_content .= $this->addTripleSelectElement(
1920
                            $form,
1921
                            $field_details,
1922
                            is_array($extraData) ? $extraData : [],
1923
                            $freezeElement
1924
                        );
1925
                        break;
1926
                    case self::FIELD_TYPE_DURATION:
1927
                        $form->addElement(
1928
                            'text',
1929
                            'extra_'.$variable,
1930
                            $field_details['display_text'],
1931
                            ['class' => 'span2', 'placeholder' => 'hh:mm:ss']
1932
                        );
1933
1934
                        $form->addRule(
1935
                            'extra_'.$variable,
1936
                            get_lang('Invalid format'),
1937
                            'callback',
1938
                            'validate_duration_format'
1939
                        );
1940
1941
                        $form->applyFilter('extra_'.$variable, function ($value) {
1942
                            if (preg_match('/^(\d+):([0-5]?\d):([0-5]?\d)$/', $value, $matches)) {
1943
                                return ($matches[1] * 3600) + ($matches[2] * 60) + $matches[3];
1944
                            }
1945
                            return 0;
1946
                        });
1947
1948
                        if (isset($extraData['extra_'.$variable]) && is_numeric($extraData['extra_'.$variable])) {
1949
                            $form->setDefaults([
1950
                                'extra_'.$variable => self::formatDuration((int) $extraData['extra_'.$variable])
0 ignored issues
show
Bug Best Practice introduced by
The method ExtraField::formatDuration() is not static, but was called statically. ( Ignorable by Annotation )

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

1950
                                'extra_'.$variable => self::/** @scrutinizer ignore-call */ formatDuration((int) $extraData['extra_'.$variable])
Loading history...
1951
                            ]);
1952
                        }
1953
1954
                        if ($freezeElement) {
1955
                            $form->freeze('extra_'.$variable);
1956
                        }
1957
                        break;
1958
                }
1959
            }
1960
        }
1961
1962
        $return = [];
1963
        $return['jquery_ready_content'] = $jquery_ready_content;
1964
1965
        return $return;
1966
    }
1967
1968
    /**
1969
     * Validates if a string is in the hh:mm:ss duration format.
1970
     */
1971
    function validate_duration_format($value): bool
1972
    {
1973
        return (bool) preg_match('/^(\d+):([0-5]?\d):([0-5]?\d)$/', $value);
1974
    }
1975
1976
    /**
1977
     * @param array $options
1978
     *
1979
     * @return array
1980
     */
1981
    public static function extra_field_double_select_convert_array_to_ordered_array($options)
1982
    {
1983
        $optionsParsed = [];
1984
        if (!empty($options)) {
1985
            foreach ($options as $option) {
1986
                if (0 == $option['option_value']) {
1987
                    $optionsParsed[$option['id']][] = $option;
1988
                } else {
1989
                    $optionsParsed[$option['option_value']][] = $option;
1990
                }
1991
            }
1992
        }
1993
1994
        return $optionsParsed;
1995
    }
1996
1997
    /**
1998
     * @return array
1999
     */
2000
    public static function tripleSelectConvertArrayToOrderedArray(array $options)
2001
    {
2002
        $level1 = self::getOptionsFromTripleSelect($options, 0);
2003
        $level2 = [];
2004
        $level3 = [];
2005
2006
        foreach ($level1 as $item1) {
2007
            $level2 += self::getOptionsFromTripleSelect($options, $item1['id']);
2008
        }
2009
2010
        foreach ($level2 as $item2) {
2011
            $level3 += self::getOptionsFromTripleSelect($options, $item2['id']);
2012
        }
2013
2014
        return ['level1' => $level1, 'level2' => $level2, 'level3' => $level3];
2015
    }
2016
2017
    /**
2018
     * @param string $type
2019
     *
2020
     * @return array
2021
     */
2022
    public function get_all_extra_field_by_type($type)
2023
    {
2024
        // all the information of the field
2025
        $sql = "SELECT * FROM {$this->table}
2026
                WHERE
2027
                    value_type = '".Database::escape_string($type)."' AND
2028
                    item_type = $this->itemType
2029
                ";
2030
        $result = Database::query($sql);
2031
2032
        $return = [];
2033
        while ($row = Database::fetch_array($result)) {
2034
            $return[] = $row['id'];
2035
        }
2036
2037
        return $return;
2038
    }
2039
2040
    /**
2041
     * @param int $id
2042
     */
2043
    public function get_field_type_by_id($id)
2044
    {
2045
        $types = $this->get_field_types();
2046
        if (isset($types[$id])) {
2047
            return $types[$id];
2048
        }
2049
2050
        return null;
2051
    }
2052
2053
    /**
2054
     * @return array
2055
     */
2056
    public function get_field_types()
2057
    {
2058
        return $this->get_extra_fields_by_handler($this->type);
2059
    }
2060
2061
    /**
2062
     * @param string $handler
2063
     *
2064
     * @return array
2065
     */
2066
    public static function get_extra_fields_by_handler($handler)
2067
    {
2068
        $types = [];
2069
        $types[self::FIELD_TYPE_TEXT] = get_lang('Text');
2070
        $types[self::FIELD_TYPE_TEXTAREA] = get_lang('Text area');
2071
        $types[self::FIELD_TYPE_RADIO] = get_lang('Radio buttons');
2072
        $types[self::FIELD_TYPE_SELECT] = get_lang('Select drop-down');
2073
        $types[self::FIELD_TYPE_SELECT_MULTIPLE] = get_lang('Multiple selection drop-down');
2074
        $types[self::FIELD_TYPE_DATE] = get_lang('Date');
2075
        $types[self::FIELD_TYPE_DATETIME] = get_lang('Date and time');
2076
        $types[self::FIELD_TYPE_DOUBLE_SELECT] = get_lang('Double select');
2077
        $types[self::FIELD_TYPE_DIVIDER] = get_lang('Visual divider');
2078
        $types[self::FIELD_TYPE_TAG] = get_lang('User tag');
2079
        $types[self::FIELD_TYPE_TIMEZONE] = get_lang('Timezone');
2080
        $types[self::FIELD_TYPE_SOCIAL_PROFILE] = get_lang('Social network link');
2081
        $types[self::FIELD_TYPE_MOBILE_PHONE_NUMBER] = get_lang('Mobile phone number');
2082
        $types[self::FIELD_TYPE_CHECKBOX] = get_lang('Checkbox options');
2083
        $types[self::FIELD_TYPE_INTEGER] = get_lang('Integer value');
2084
        $types[self::FIELD_TYPE_FILE_IMAGE] = get_lang('Image');
2085
        $types[self::FIELD_TYPE_FLOAT] = get_lang('Float value');
2086
        $types[self::FIELD_TYPE_FILE] = get_lang('File');
2087
        $types[self::FIELD_TYPE_VIDEO_URL] = get_lang('Video URL');
2088
        $types[self::FIELD_TYPE_LETTERS_ONLY] = get_lang('Text only letters');
2089
        $types[self::FIELD_TYPE_ALPHANUMERIC] = get_lang('Text only alphanumeric characters');
2090
        $types[self::FIELD_TYPE_LETTERS_SPACE] = get_lang('Text letters and spaces');
2091
        $types[self::FIELD_TYPE_ALPHANUMERIC_SPACE] = get_lang('Text alphanumeric characters and spaces');
2092
        $types[self::FIELD_TYPE_GEOLOCALIZATION] = get_lang('Geolocalization');
2093
        $types[self::FIELD_TYPE_GEOLOCALIZATION_COORDINATES] = get_lang('Geolocalization by coordinates');
2094
        $types[self::FIELD_TYPE_SELECT_WITH_TEXT_FIELD] = get_lang('Select with text field');
2095
        $types[self::FIELD_TYPE_TRIPLE_SELECT] = get_lang('Triple select');
2096
        $types[self::FIELD_TYPE_DURATION] = get_lang('Duration (hh:mm:ss)');
2097
2098
        switch ($handler) {
2099
            case 'course':
2100
            case 'session':
2101
            case 'user':
2102
            case 'skill':
2103
                break;
2104
        }
2105
2106
        return $types;
2107
    }
2108
2109
    /**
2110
     * @param array $params
2111
     * @param bool  $showQuery
2112
     *
2113
     * @return int|bool
2114
     */
2115
    public function save($params, $showQuery = false)
2116
    {
2117
        $fieldInfo = self::get_handler_field_info_by_field_variable($params['variable']);
0 ignored issues
show
Bug Best Practice introduced by
The method ExtraField::get_handler_...nfo_by_field_variable() is not static, but was called statically. ( Ignorable by Annotation )

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

2117
        /** @scrutinizer ignore-call */ 
2118
        $fieldInfo = self::get_handler_field_info_by_field_variable($params['variable']);
Loading history...
2118
        $params = $this->clean_parameters($params);
2119
        $params['item_type'] = $this->itemType;
2120
2121
        if ($fieldInfo) {
2122
            return $fieldInfo['id'];
2123
        } else {
2124
            $id = parent::save($params, $showQuery);
2125
            if ($id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
2126
                $fieldOption = new ExtraFieldOption($this->type);
2127
                $params['field_id'] = $id;
2128
                $fieldOption->save($params);
2129
            }
2130
2131
            return $id;
2132
        }
2133
    }
2134
2135
    /**
2136
     * @param string $variable
2137
     *
2138
     * @return array|bool
2139
     */
2140
    public function get_handler_field_info_by_field_variable($variable)
2141
    {
2142
        $variable = Database::escape_string($variable);
2143
        $sql = "SELECT * FROM {$this->table}
2144
                WHERE
2145
                    variable = '$variable' AND
2146
                    item_type = $this->itemType";
2147
        $result = Database::query($sql);
2148
        if (Database::num_rows($result)) {
2149
            $extraFieldRepo = Container::getExtraFieldRepository();
2150
            $row = Database::fetch_assoc($result);
2151
            if ($row) {
2152
                $extraFieldId = $row['id'];
2153
                /** @var EntityExtraField $extraField */
2154
                $field = $extraFieldRepo->find($extraFieldId);
2155
                $row['display_text'] = $field->getDisplayText();
2156
2157
                // All the options of the field
2158
                $sql = "SELECT * FROM $this->table_field_options
2159
                        WHERE field_id='".$extraFieldId."'
2160
                        ORDER BY option_order ASC";
2161
                $result = Database::query($sql);
2162
                while ($option = Database::fetch_array($result)) {
2163
                    $row['options'][$option['id']] = $option;
2164
                }
2165
2166
                return $row;
2167
            }
2168
        }
2169
2170
        return false;
2171
    }
2172
2173
    /**
2174
     * @param array $params
2175
     *
2176
     * @return array
2177
     */
2178
    public function clean_parameters($params)
2179
    {
2180
        if (!isset($params['variable']) || empty($params['variable'])) {
2181
            $params['variable'] = $params['display_text'];
2182
        }
2183
2184
        $params['variable'] = trim(strtolower(str_replace(' ', '_', $params['variable'])));
2185
2186
        if (!isset($params['field_order'])) {
2187
            $max_order = self::get_max_field_order();
0 ignored issues
show
Bug Best Practice introduced by
The method ExtraField::get_max_field_order() is not static, but was called statically. ( Ignorable by Annotation )

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

2187
            /** @scrutinizer ignore-call */ 
2188
            $max_order = self::get_max_field_order();
Loading history...
2188
            $params['field_order'] = $max_order;
2189
        } else {
2190
            $params['field_order'] = (int) $params['field_order'];
2191
        }
2192
2193
        return $params;
2194
    }
2195
2196
    /**
2197
     * @return int
2198
     */
2199
    public function get_max_field_order()
2200
    {
2201
        $sql = "SELECT MAX(field_order)
2202
                FROM {$this->table}
2203
                WHERE
2204
                    item_type = '.$this->itemType.'";
2205
        $res = Database::query($sql);
2206
2207
        $order = 0;
2208
        if (Database::num_rows($res) > 0) {
2209
            $row = Database::fetch_row($res);
2210
            $order = $row[0] + 1;
2211
        }
2212
2213
        return $order;
2214
    }
2215
2216
    /**
2217
     * {@inheritdoc}
2218
     */
2219
    public function update($params, $showQuery = false)
2220
    {
2221
        $params = $this->clean_parameters($params);
2222
        if (isset($params['id'])) {
2223
            $fieldOption = new ExtraFieldOption($this->type);
2224
            $params['field_id'] = $params['id'];
2225
            if (empty($params['value_type'])) {
2226
                $params['value_type'] = $this->type;
2227
            }
2228
            $fieldOption->save($params, $showQuery);
2229
        }
2230
2231
        return parent::update($params, $showQuery);
2232
    }
2233
2234
    /**
2235
     * @param $id
2236
     *
2237
     * @return bool
2238
     */
2239
    public function delete($id)
2240
    {
2241
        $em = Database::getManager();
2242
        $items = $em->getRepository(\Chamilo\CoreBundle\Entity\ExtraFieldSavedSearch::class)->findBy(['field' => $id]);
2243
        if ($items) {
2244
            foreach ($items as $item) {
2245
                $em->remove($item);
2246
            }
2247
            $em->flush();
2248
        }
2249
        $field_option = new ExtraFieldOption($this->type);
2250
        $field_option->delete_all_options_by_field_id($id);
2251
2252
        $session_field_values = new ExtraFieldValue($this->type);
2253
        $session_field_values->delete_all_values_by_field_id($id);
2254
2255
        return parent::delete($id);
2256
    }
2257
2258
    /**
2259
     * @param $breadcrumb
2260
     * @param $action
2261
     */
2262
    public function setupBreadcrumb(&$breadcrumb, $action)
2263
    {
2264
        if ('add' === $action) {
2265
            $breadcrumb[] = ['url' => $this->pageUrl, 'name' => $this->pageName];
2266
            $breadcrumb[] = ['url' => '#', 'name' => get_lang('Add')];
2267
        } elseif ('edit' === $action) {
2268
            $breadcrumb[] = ['url' => $this->pageUrl, 'name' => $this->pageName];
2269
            $breadcrumb[] = ['url' => '#', 'name' => get_lang('Edit')];
2270
        } else {
2271
            $breadcrumb[] = ['url' => '#', 'name' => $this->pageName];
2272
        }
2273
    }
2274
2275
    /**
2276
     * Displays the title + grid.
2277
     */
2278
    public function display()
2279
    {
2280
        $actions = '<a href="../admin/index.php">';
2281
        $actions .= Display::getMdiIcon(ActionIcon::BACK, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Back to').' '.get_lang('Administration'));
2282
        $actions .= '</a>';
2283
        $actions .= '<a href="'.api_get_self().'?action=add&type='.$this->type.'">';
2284
        $actions .= Display::getMdiIcon(ActionIcon::ADD, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Add'));
2285
        $actions .= '</a>';
2286
2287
        echo Display::toolbarAction('toolbar', [$actions]);
2288
        echo Display::grid_html($this->type.'_fields');
2289
    }
2290
2291
    /**
2292
     * @return array
2293
     */
2294
    public function getJqgridColumnNames()
2295
    {
2296
        $columns = [
2297
            get_lang('Name'),
2298
            get_lang('Field label'),
2299
            get_lang('Type'),
2300
            get_lang('Can change'),
2301
            get_lang('Visible to self'),
2302
            get_lang('Visible to others'),
2303
            get_lang('Filter'),
2304
            get_lang('Order'),
2305
            get_lang('Detail'),
2306
        ];
2307
2308
        if ($this->type === 'user') {
2309
            array_splice($columns, -1, 0, get_lang('Auto remove'));
2310
        }
2311
2312
        return $columns;
2313
    }
2314
2315
    /**
2316
     * @return array
2317
     */
2318
    public function getJqgridColumnModel()
2319
    {
2320
        $columnModel = [
2321
            [
2322
                'name' => 'display_text',
2323
                'index' => 'display_text',
2324
                'align' => 'left',
2325
            ],
2326
            [
2327
                'name' => 'variable',
2328
                'index' => 'variable',
2329
                'align' => 'left',
2330
                'sortable' => 'true',
2331
            ],
2332
            [
2333
                'name' => 'value_type',
2334
                'index' => 'value_type',
2335
                'align' => 'left',
2336
                'sortable' => 'true',
2337
            ],
2338
            [
2339
                'name' => 'changeable',
2340
                'index' => 'changeable',
2341
                'align' => 'center',
2342
                'sortable' => 'true',
2343
            ],
2344
            [
2345
                'name' => 'visible_to_self',
2346
                'index' => 'visible_to_self',
2347
                'align' => 'center',
2348
                'sortable' => 'true',
2349
            ],
2350
            [
2351
                'name' => 'visible_to_others',
2352
                'index' => 'visible_to_others',
2353
                'align' => 'center',
2354
                'sortable' => 'true',
2355
            ],
2356
            [
2357
                'name' => 'filter',
2358
                'index' => 'filter',
2359
                'align' => 'center',
2360
                'sortable' => 'true',
2361
            ],
2362
            [
2363
                'name' => 'field_order',
2364
                'index' => 'field_order',
2365
                'align' => 'center',
2366
                'sortable' => 'true',
2367
            ],
2368
            [
2369
                'name' => 'actions',
2370
                'index' => 'actions',
2371
                'align' => 'center',
2372
                'formatter' => 'action_formatter',
2373
                'sortable' => 'false',
2374
            ],
2375
        ];
2376
2377
        if ($this->type === 'user') {
2378
            $autoRemoveColumnModel = [
2379
                'name' => 'auto_remove',
2380
                'index' => 'auto_remove',
2381
                'align' => 'center',
2382
                'sortable' => 'true',
2383
            ];
2384
2385
            array_splice($columnModel, -1, 0, [$autoRemoveColumnModel]);
2386
        }
2387
2388
        return $columnModel;
2389
    }
2390
2391
    /**
2392
     * @param string $url
2393
     * @param string $action
2394
     *
2395
     * @return FormValidator
2396
     */
2397
    public function return_form($url, $action)
2398
    {
2399
        $form = new FormValidator($this->type.'_field', 'post', $url);
2400
2401
        $form->addHidden('type', $this->type);
2402
        $id = isset($_GET['id']) ? (int) $_GET['id'] : null;
2403
        $form->addHidden('id', $id);
2404
2405
        // Setting the form elements
2406
        $header = get_lang('Add');
2407
        $defaults = [];
2408
2409
        if ('edit' === $action) {
2410
            $header = get_lang('Edit');
2411
            // Setting the defaults
2412
            $defaults = $this->get($id);
2413
        }
2414
2415
        $form->addHeader($header);
2416
2417
        if ('edit' === $action) {
2418
            $translateUrl = '/main/extrafield/translate.php?'.http_build_query(['extra_field' => $id]);
2419
            $translateButton = Display::toolbarButton(
2420
                get_lang('Translate this term'),
2421
                $translateUrl,
2422
                'language',
2423
                'plain'
2424
            );
2425
2426
            $form->addElement('text', 'display_text', [get_lang('Name'), $translateButton]);
2427
        } else {
2428
            $form->addText('display_text', get_lang('Name'));
2429
        }
2430
2431
        $form->addHtmlEditor('description', get_lang('Description'), false);
2432
2433
        // Field type
2434
        $types = self::get_field_types();
0 ignored issues
show
Bug Best Practice introduced by
The method ExtraField::get_field_types() is not static, but was called statically. ( Ignorable by Annotation )

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

2434
        /** @scrutinizer ignore-call */ 
2435
        $types = self::get_field_types();
Loading history...
2435
2436
        $form->addSelect(
2437
            'value_type',
2438
            get_lang('Field type'),
2439
            $types,
2440
            ['id' => 'field_type']
2441
        );
2442
        $form->addLabel(get_lang('Example'), '<div id="example">-</div>');
2443
        $form->addText('variable', get_lang('Field label'), false);
2444
        $form->addElement(
2445
            'text',
2446
            'field_options',
2447
            get_lang('Possible values'),
2448
            ['id' => 'field_options', 'class' => 'span6']
2449
        );
2450
2451
        $fieldWithOptions = [
2452
            self::FIELD_TYPE_RADIO,
2453
            self::FIELD_TYPE_SELECT_MULTIPLE,
2454
            self::FIELD_TYPE_SELECT,
2455
            self::FIELD_TYPE_TAG,
2456
            self::FIELD_TYPE_DOUBLE_SELECT,
2457
            self::FIELD_TYPE_SELECT_WITH_TEXT_FIELD,
2458
            self::FIELD_TYPE_TRIPLE_SELECT,
2459
        ];
2460
2461
        if ('edit' === $action) {
2462
            if (in_array($defaults['value_type'], $fieldWithOptions)) {
2463
                $url = Display::url(
2464
                    get_lang('Edit extra field options'),
2465
                    'extra_field_options.php?type='.$this->type.'&field_id='.$id,
2466
                    ['class' => 'btn']
2467
                );
2468
                $form->addLabel(null, $url);
2469
2470
                if (self::FIELD_TYPE_SELECT == $defaults['value_type']) {
2471
                    $urlWorkFlow = Display::url(
2472
                        get_lang('Edit this field\'s workflow'),
2473
                        'extra_field_workflow.php?type='.$this->type.'&field_id='.$id,
2474
                        ['class' => 'btn']
2475
                    );
2476
                    $form->addLabel(null, $urlWorkFlow);
2477
                }
2478
2479
                $form->freeze('field_options');
2480
            }
2481
        }
2482
        $form->addText(
2483
            'default_value',
2484
            get_lang('Default value'),
2485
            false,
2486
            ['id' => 'default_value']
2487
        );
2488
2489
        $group = [];
2490
        $group[] = $form->createElement('radio', 'visible_to_self', null, get_lang('Yes'), 1);
2491
        $group[] = $form->createElement('radio', 'visible_to_self', null, get_lang('No'), 0);
2492
        $form->addGroup($group, '', get_lang('Visible to self'), null, false);
2493
2494
        $group = [];
2495
        $group[] = $form->createElement('radio', 'visible_to_others', null, get_lang('Yes'), 1);
2496
        $group[] = $form->createElement('radio', 'visible_to_others', null, get_lang('No'), 0);
2497
        $form->addGroup($group, '', get_lang('Visible to others'), null, false);
2498
2499
        $group = [];
2500
        $group[] = $form->createElement('radio', 'changeable', null, get_lang('Yes'), 1);
2501
        $group[] = $form->createElement('radio', 'changeable', null, get_lang('No'), 0);
2502
        $form->addGroup($group, '', get_lang('Can change'), null, false);
2503
2504
        $group = [];
2505
        $group[] = $form->createElement('radio', 'filter', null, get_lang('Yes'), 1);
2506
        $group[] = $form->createElement('radio', 'filter', null, get_lang('No'), 0);
2507
        $form->addGroup($group, '', get_lang('Filter'), null, false);
2508
2509
        /* Enable this when field_loggeable is introduced as a table field (2.0)
2510
        $group   = array();
2511
        $group[] = $form->createElement('radio', 'field_loggeable', null, get_lang('Yes'), 1);
2512
        $group[] = $form->createElement('radio', 'field_loggeable', null, get_lang('No'), 0);
2513
        $form->addGroup($group, '', get_lang('Field changes should be logged'), '', false);
2514
        */
2515
2516
        $form->addNumeric('field_order', get_lang('Order'), ['step' => 1, 'min' => 0]);
2517
2518
        if ($this->type == 'user') {
2519
            $form->addElement(
2520
                'checkbox',
2521
                'auto_remove',
2522
                get_lang('Remove on anonymization'),
2523
                get_lang('Remove this value when anonymizing a user, because it could otherwise help identify the user despite the anonymization.')
2524
            );
2525
        }
2526
2527
        if ('edit' == $action) {
2528
            $option = new ExtraFieldOption($this->type);
2529
            $defaults['field_options'] = $option->get_field_options_by_field_to_string($id);
2530
            $form->addButtonUpdate(get_lang('Edit'));
2531
        } else {
2532
            $defaults['visible_to_self'] = 0;
2533
            $defaults['visible_to_others'] = 0;
2534
            $defaults['changeable'] = 0;
2535
            $defaults['filter'] = 0;
2536
            $defaults['auto_remove'] = 0;
2537
            $form->addButtonCreate(get_lang('Add'));
2538
        }
2539
2540
        /*if (!empty($defaults['created_at'])) {
2541
            $defaults['created_at'] = api_convert_and_format_date($defaults['created_at']);
2542
        }
2543
        if (!empty($defaults['updated_at'])) {
2544
            $defaults['updated_at'] = api_convert_and_format_date($defaults['updated_at']);
2545
        }*/
2546
        $form->setDefaults($defaults);
2547
2548
        // Setting the rules
2549
        $form->addRule('display_text', get_lang('Required field'), 'required');
2550
        $form->addRule('value_type', get_lang('Required field'), 'required');
2551
2552
        return $form;
2553
    }
2554
2555
    /**
2556
     * Gets an element.
2557
     *
2558
     * @param int  $id
2559
     *
2560
     * @return array
2561
     */
2562
    public function get($id)
2563
    {
2564
        return parent::get($id);
0 ignored issues
show
Bug Best Practice introduced by
The expression return parent::get($id) also could return the type integer which is incompatible with the documented return type array.
Loading history...
2565
    }
2566
2567
    /**
2568
     * @param $token
2569
     *
2570
     * @return string
2571
     */
2572
    public function getJqgridActionLinks($token)
2573
    {
2574
        //With this function we can add actions to the jgrid (edit, delete, etc)
2575
        $editIcon = Display::getMdiIcon(ActionIcon::EDIT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Edit'));
2576
        $deleteIcon = Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Delete'));
2577
        $confirmMessage = addslashes(
2578
            api_htmlentities(get_lang("Please confirm your choice"), ENT_QUOTES)
2579
        );
2580
2581
        $editButton = <<<JAVASCRIPT
2582
            <a href="?action=edit&type={$this->type}&id=' + options.rowId + '" class="btn btn-link btn-xs">\
2583
                $editIcon\
2584
            </a>
2585
JAVASCRIPT;
2586
        $deleteButton = <<<JAVASCRIPT
2587
            <a \
2588
                    onclick="if (!confirm(\'$confirmMessage\')) {return false;}" \
2589
                href="?sec_token=$token&type={$this->type}&id=' + options.rowId + '&action=delete" \
2590
                class="btn btn-link btn-xs">\
2591
                $deleteIcon\
2592
            </a>
2593
JAVASCRIPT;
2594
2595
        return "function action_formatter(cellvalue, options, rowObject) {
2596
            return '$editButton $deleteButton';
2597
        }";
2598
    }
2599
2600
    /**
2601
     * @param array $columns
2602
     * @param array $column_model
2603
     * @param array $extraFields
2604
     *
2605
     * @return array
2606
     */
2607
    public function getRules(&$columns, &$column_model, $extraFields = [], $checkExtraFieldExistence = false)
2608
    {
2609
        $fields = $this->get_all(
2610
            [
2611
                'visible_to_self = ? AND filter = ?' => [1, 1],
2612
            ],
2613
            'display_text'
2614
        );
2615
        $extraFieldOption = new ExtraFieldOption($this->type);
2616
2617
        $rules = [];
2618
        if (!empty($fields)) {
2619
            foreach ($fields as $field) {
2620
                $search_options = [];
2621
                $type = 'text';
2622
                switch ($field['value_type']) {
2623
                    case self::FIELD_TYPE_SELECT:
2624
                    case self::FIELD_TYPE_DOUBLE_SELECT:
2625
                        $type = 'select';
2626
                        $search_options['sopt'] = ['eq', 'ne'];
2627
                        break;
2628
                    case self::FIELD_TYPE_DURATION:
2629
                        $type = 'text';
2630
                        $search_options['sopt'] = ['cn', 'nc'];
2631
                        break;
2632
                    default:
2633
                        $type = 'text';
2634
                        $search_options['sopt'] = ['cn', 'nc'];
2635
                        break;
2636
                }
2637
2638
                $search_options['searchhidden'] = 'true';
2639
                $search_options['defaultValue'] = isset($search_options['field_default_value'])
2640
                    ? $search_options['field_default_value']
2641
                    : null;
2642
2643
                if (self::FIELD_TYPE_DOUBLE_SELECT == $field['value_type']) {
2644
                    // Add 2 selects
2645
                    $options = $extraFieldOption->get_field_options_by_field($field['id']);
2646
                    $options = self::extra_field_double_select_convert_array_to_ordered_array($options);
2647
2648
                    $first_options = [];
2649
                    if (!empty($options)) {
2650
                        foreach ($options as $option) {
2651
                            foreach ($option as $sub_option) {
2652
                                if (0 == $sub_option['option_value']) {
2653
                                    $first_options[] = $sub_option['field_id'].'#'.$sub_option['id'].':'
2654
                                        .$sub_option['display_text'];
2655
                                }
2656
                            }
2657
                        }
2658
                    }
2659
2660
                    $search_options['value'] = implode(';', $first_options);
2661
                    $search_options['dataInit'] = 'fill_second_select';
2662
2663
                    // First
2664
                    $column_model[] = [
2665
                        'name' => 'extra_'.$field['variable'],
2666
                        'index' => 'extra_'.$field['variable'],
2667
                        'width' => '100',
2668
                        'hidden' => 'true',
2669
                        'search' => 'true',
2670
                        'stype' => 'select',
2671
                        'searchoptions' => $search_options,
2672
                    ];
2673
                    $columns[] = $field['display_text'].' (1)';
2674
                    $rules[] = [
2675
                        'field' => 'extra_'.$field['variable'],
2676
                        'op' => 'cn',
2677
                    ];
2678
2679
                    // Second
2680
                    $search_options['value'] = $field['id'].':';
2681
                    $search_options['dataInit'] = 'register_second_select';
2682
2683
                    $column_model[] = [
2684
                        'name' => 'extra_'.$field['variable'].'_second',
2685
                        'index' => 'extra_'.$field['variable'].'_second',
2686
                        'width' => '100',
2687
                        'hidden' => 'true',
2688
                        'search' => 'true',
2689
                        'stype' => 'select',
2690
                        'searchoptions' => $search_options,
2691
                    ];
2692
                    $columns[] = $field['display_text'].' (2)';
2693
                    $rules[] = ['field' => 'extra_'.$field['variable'].'_second', 'op' => 'cn'];
2694
                    continue;
2695
                } else {
2696
                    $search_options['value'] = $extraFieldOption->getFieldOptionsToString(
2697
                        $field['id'],
2698
                        false,
2699
                        'display_text'
2700
                    );
2701
                }
2702
                $column_model[] = [
2703
                    'name' => 'extra_'.$field['variable'],
2704
                    'index' => 'extra_'.$field['variable'],
2705
                    'width' => '100',
2706
                    'hidden' => 'true',
2707
                    'search' => 'true',
2708
                    'stype' => $type,
2709
                    'searchoptions' => $search_options,
2710
                ];
2711
                $columns[] = $field['display_text'];
2712
                $rules[] = [
2713
                    'field' => 'extra_'.$field['variable'],
2714
                    'op' => 'cn',
2715
                    'data' => '',
2716
                ];
2717
            }
2718
        }
2719
2720
        return $rules;
2721
    }
2722
2723
    public function processExtraFieldSearch($values, $form, $alias, $condition = 'OR')
2724
    {
2725
        // Parse params.
2726
        $fields = [];
2727
        foreach ($values as $key => $value) {
2728
            if ('extra_' !== substr($key, 0, 6) &&
2729
                '_extra_' !== substr($key, 0, 7)
2730
            ) {
2731
                continue;
2732
            }
2733
            if (!empty($value)) {
2734
                $fields[$key] = $value;
2735
            }
2736
        }
2737
2738
        $extraFieldsAll = $this->get_all(['visible_to_self = ? AND filter = ?' => [1, 1]], 'option_order');
2739
        $extraFieldsType = array_column($extraFieldsAll, 'value_type', 'variable');
2740
        $extraFields = array_column($extraFieldsAll, 'variable');
2741
        $filter = new stdClass();
2742
        $defaults = [];
2743
        foreach ($fields as $variable => $col) {
2744
            $variableNoExtra = str_replace('extra_', '', $variable);
2745
            if (isset($values[$variable]) && !empty($values[$variable]) &&
2746
                in_array($variableNoExtra, $extraFields)
2747
            ) {
2748
                $rule = new stdClass();
2749
                $rule->field = $variable;
2750
                $rule->op = 'in';
2751
                $data = $col;
2752
                if (is_array($data) && array_key_exists($variable, $data)) {
2753
                    $data = $col;
2754
                }
2755
                $rule->data = $data;
2756
                $filter->rules[] = $rule;
2757
                $filter->groupOp = 'AND';
2758
2759
                if (ExtraField::FIELD_TYPE_TAG == $extraFieldsType[$variableNoExtra]) {
2760
                    $tagElement = $form->getElement($variable);
2761
                    $tags = [];
2762
                    foreach ($values[$variable] as $tag) {
2763
                        $tag = Security::remove_XSS($tag);
2764
                        $tags[] = $tag;
2765
                        $tagElement->addOption(
2766
                            $tag,
2767
                            $tag
2768
                        );
2769
                    }
2770
                    $defaults[$variable] = $tags;
2771
                } else {
2772
                    if (is_array($data)) {
2773
                        $defaults[$variable] = array_map(['Security', 'remove_XSS'], $data);
2774
                    } else {
2775
                        $defaults[$variable] = Security::remove_XSS($data);
2776
                    }
2777
                }
2778
            }
2779
        }
2780
2781
        $result = $this->getExtraFieldRules($filter, 'extra_', $condition);
2782
        $conditionArray = $result['condition_array'];
2783
2784
        $whereCondition = '';
2785
        $extraCondition = '';
2786
        if (!empty($conditionArray)) {
2787
            $extraCondition = ' ( ';
2788
            $extraCondition .= implode(' AND ', $conditionArray);
2789
            $extraCondition .= ' ) ';
2790
        }
2791
        $whereCondition .= $extraCondition;
2792
        $conditions = $this->parseConditions(
2793
            [
2794
                'where' => $whereCondition,
2795
                'extra' => $result['extra_fields'],
2796
            ],
2797
            $alias
2798
        );
2799
2800
        return ['condition' => $conditions, 'fields' => $fields, 'defaults' => $defaults];
2801
    }
2802
2803
    /**
2804
     * @param $filters
2805
     * @param string $stringToSearch
2806
     *
2807
     * @return array
2808
     */
2809
    public function getExtraFieldRules($filters, $stringToSearch = 'extra_', $condition = 'OR')
2810
    {
2811
        $extraFields = [];
2812
        $conditionArray = [];
2813
2814
        // Getting double select if exists
2815
        $double_select = [];
2816
        if (is_object($filters) &&
2817
            property_exists($filters, 'rules') &&
2818
            is_array($filters->rules) &&
2819
            !empty($filters->rules)
2820
        ) {
2821
            foreach ($filters->rules as $rule) {
2822
                if (empty($rule)) {
2823
                    continue;
2824
                }
2825
                if (false === strpos($rule->field, '_second')) {
2826
                } else {
2827
                    $my_field = str_replace('_second', '', $rule->field);
2828
                    $double_select[$my_field] = $rule->data;
2829
                }
2830
            }
2831
2832
            foreach ($filters->rules as $rule) {
2833
                if (empty($rule)) {
2834
                    continue;
2835
                }
2836
                if (false === strpos($rule->field, $stringToSearch)) {
2837
                    // normal fields
2838
                    $field = $rule->field;
2839
                    if (isset($rule->data) && is_string($rule->data) && -1 != $rule->data) {
2840
                        $conditionArray[] = $this->get_where_clause($field, $rule->op, $rule->data);
2841
                    }
2842
                } else {
2843
                    // Extra fields
2844
                    $ruleField = Database::escapeField($rule->field);
2845
                    if (false === strpos($rule->field, '_second')) {
2846
                        //No _second
2847
                        $original_field = str_replace($stringToSearch, '', $rule->field);
2848
                        $field_option = $this->get_handler_field_info_by_field_variable($original_field);
2849
2850
                        switch ($field_option['value_type']) {
2851
                            case self::FIELD_TYPE_DOUBLE_SELECT:
2852
                            if (isset($double_select[$rule->field])) {
2853
                                $data = explode('#', $rule->data);
2854
                                $rule->data = $data[1].'::'.$double_select[$rule->field];
2855
                            } else {
2856
                                // only was sent 1 select
2857
                                if (is_string($rule->data)) {
2858
                                    $data = explode('#', $rule->data);
2859
                                    $rule->data = $data[1];
2860
                                }
2861
                            }
2862
2863
                            if (!isset($rule->data)) {
2864
                                $conditionArray[] = ' ('
2865
                                .$this->get_where_clause($rule->field, $rule->op, $rule->data)
2866
                                .') ';
2867
                                $extraFields[] = ['field' => $ruleField, 'id' => $field_option['id']];
2868
                            }
2869
                                break;
2870
                            case self::FIELD_TYPE_TAG:
2871
                            if (isset($rule->data)) {
2872
                                if (is_int($rule->data) && -1 == $rule->data) {
2873
                                    break;
2874
                                }
2875
2876
                                    // Where will be injected in the parseConditions()
2877
                                //$where = $this->get_where_clause($rule->field, $rule->op, $rule->data, 'OR');
2878
                                //$conditionArray[] = " ( $where ) ";
2879
                                $extraFields[] = [
2880
                                        'field' => $ruleField,
2881
                                'id' => $field_option['id'],
2882
                                'data' => $rule->data,
2883
                            ];
2884
                            }
2885
                                break;
2886
                            default:
2887
                                if (isset($rule->data)) {
2888
                                    if (is_int($rule->data) && -1 == $rule->data) {
2889
                                        break;
2890
                                    }
2891
                                    $where = $this->get_where_clause($rule->field, $rule->op, $rule->data, 'OR');
2892
                                    $conditionArray[] = " ( $where ) ";
2893
                                    $extraFields[] = [
2894
                                        'field' => $ruleField,
2895
                                        'id' => $field_option['id'],
2896
                                        'data' => $rule->data,
2897
                                    ];
2898
                                }
2899
                                break;
2900
                        }
2901
                    } else {
2902
                        $my_field = str_replace('_second', '', $rule->field);
2903
                        $original_field = str_replace($stringToSearch, '', $my_field);
2904
                        $field_option = $this->get_handler_field_info_by_field_variable($original_field);
2905
                        $extraFields[] = [
2906
                            'field' => $ruleField,
2907
                        'id' => $field_option['id'],
2908
                    ];
2909
                    }
2910
                }
2911
            }
2912
        }
2913
2914
        return ['extra_fields' => $extraFields, 'condition_array' => $conditionArray];
2915
    }
2916
2917
    /**
2918
     * @param $col
2919
     * @param $oper
2920
     * @param $val
2921
     * @param $conditionBetweenOptions
2922
     *
2923
     * @return string
2924
     */
2925
    public function get_where_clause($col, $oper, $val, $conditionBetweenOptions = 'OR')
2926
    {
2927
        $col = Database::escapeField($col);
2928
2929
        if (empty($col)) {
2930
            return '';
2931
        }
2932
        $conditionBetweenOptions = in_array($conditionBetweenOptions, ['OR', 'AND']) ? $conditionBetweenOptions : 'OR';
2933
        if ('bw' === $oper || 'bn' === $oper) {
2934
            $val .= '%';
2935
        }
2936
        if ('ew' === $oper || 'en' === $oper) {
2937
            $val = '%'.$val;
2938
        }
2939
        if ('cn' === $oper || 'nc' === $oper || 'in' === $oper || 'ni' === $oper) {
2940
            if (is_array($val)) {
2941
                $result = '"%'.implode(';', $val).'%"';
2942
                foreach ($val as $item) {
2943
                    $item = trim($item);
2944
                    $result .= ' '.$conditionBetweenOptions.' '.$col.' LIKE "%'.$item.'%"';
2945
                }
2946
                $val = $result;
2947
2948
                return " $col {$this->ops[$oper]} $val ";
2949
            } else {
2950
                if (is_string($val)) {
2951
                    $val = '%'.$val.'%';
2952
                } else {
2953
                    $val = '';
2954
                }
2955
            }
2956
        }
2957
        $val = \Database::escape_string($val);
2958
2959
        return " $col {$this->ops[$oper]} '$val' ";
2960
    }
2961
2962
    /**
2963
     * @param array  $options
2964
     * @param string $alias
2965
     *
2966
     * @return array
2967
     */
2968
    public function parseConditions($options, $alias = 's')
2969
    {
2970
        $inject_extra_fields = null;
2971
        $extraFieldOption = new ExtraFieldOption($this->type);
2972
        $double_fields = [];
2973
2974
        if (isset($options['extra'])) {
2975
            $extra_fields = $options['extra'];
2976
            if (!empty($extra_fields)) {
2977
                $counter = 1;
2978
                $extra_field_obj = new ExtraField($this->type);
2979
                foreach ($extra_fields as &$extra) {
2980
                    if (!isset($extra['id'])) {
2981
                        continue;
2982
                    }
2983
                    $extra_field_info = $extra_field_obj->get($extra['id']);
2984
                    if (empty($extra_field_info)) {
2985
                        continue;
2986
                    }
2987
                    $extra['extra_field_info'] = $extra_field_info;
2988
2989
                    switch ($extra_field_info['value_type']) {
2990
                        case self::FIELD_TYPE_SELECT:
2991
                        case self::FIELD_TYPE_DOUBLE_SELECT:
2992
                            $inject_extra_fields .= " fvo$counter.display_text as {$extra['field']}, ";
2993
                            break;
2994
                        case self::FIELD_TYPE_TAG:
2995
                            // If using OR
2996
                            // If using AND
2997
                            $newCounter = 1;
2998
                            $fields = [];
2999
                            $tagAlias = $extra['field'];
3000
                            foreach ($extra['data'] as $data) {
3001
                                $fields[] = "tag$counter$newCounter.tag";
3002
                                $newCounter++;
3003
                            }
3004
3005
                            if (!empty($fields)) {
3006
                                $tags = implode(' , " ", ', $fields);
3007
                                $inject_extra_fields .= " CONCAT($tags) as $tagAlias, ";
3008
                            }
3009
                            break;
3010
                        default:
3011
                            $inject_extra_fields .= " fv$counter.field_value as {$extra['field']}, ";
3012
                            break;
3013
                    }
3014
3015
                    if (isset($extra_fields_info[$extra['id']])) {
3016
                        $info = $extra_fields_info[$extra['id']];
3017
                    } else {
3018
                        $info = $this->get($extra['id']);
3019
                        $extra_fields_info[$extra['id']] = $info;
3020
                    }
3021
                    if (isset($info['value_type']) && self::FIELD_TYPE_DOUBLE_SELECT == $info['value_type']) {
3022
                        $double_fields[$info['id']] = $info;
3023
                    }
3024
                    $counter++;
3025
                }
3026
            }
3027
        }
3028
3029
        $options_by_double = [];
3030
        foreach ($double_fields as $double) {
3031
            $my_options = $extraFieldOption->get_field_options_by_field($double['id'], true);
3032
            $options_by_double['extra_'.$double['variable']] = $my_options;
3033
        }
3034
3035
        $field_value_to_join = [];
3036
        //filter can be all/any = and/or
3037
        $inject_joins = null;
3038
        $inject_where = null;
3039
        $where = null;
3040
3041
        // Removing double 1=1
3042
        if (!empty($options['extra']) && !empty($extra_fields)) {
3043
            // Removing double 1=1
3044
            if (empty($options['where'])) {
3045
                $options['where'] = ' 1 = 1 ';
3046
            }
3047
            $options['where'] = str_replace(' 1 = 1  AND', '', $options['where']);
3048
            // Always OR
3049
            $counter = 1;
3050
            foreach ($extra_fields as $extra_info) {
3051
                $extra_field_info = $extra_info['extra_field_info'];
3052
                $inject_joins .= " INNER JOIN $this->table_field_values fv$counter
3053
                                       ON ($alias.".$this->primaryKey." = fv$counter.".$this->handler_id.') ';
3054
                // Add options
3055
                switch ($extra_field_info['value_type']) {
3056
                        case self::FIELD_TYPE_SELECT:
3057
                        case self::FIELD_TYPE_DOUBLE_SELECT:
3058
                            $options['where'] = str_replace(
3059
                                $extra_info['field'],
3060
                                'fv'.$counter.'.field_id = '.$extra_info['id'].' AND fvo'.$counter.'.option_value',
3061
                                $options['where']
3062
                            );
3063
                            $inject_joins .= "
3064
                                 INNER JOIN $this->table_field_options fvo$counter
3065
                                 ON (
3066
                                    fv$counter.field_id = fvo$counter.field_id AND
3067
                                    fv$counter.field_value = fvo$counter.option_value
3068
                                 )
3069
                                ";
3070
                            break;
3071
                        case self::FIELD_TYPE_TAG:
3072
                            $newCounter = 1;
3073
                            if (isset($extra_info['data']) && !empty($extra_info['data'])) {
3074
                                $whereTag = [];
3075
                                foreach ($extra_info['data'] as $data) {
3076
                                    $data = Database::escape_string($data);
3077
                                    $key = $counter.$newCounter;
3078
                                    $whereTag[] = ' tag'.$key.'.tag LIKE "%'.$data.'%" ';
3079
                                    $inject_joins .= "
3080
                                        INNER JOIN $this->table_field_rel_tag tag_rel$key
3081
                                        ON (
3082
                                            tag_rel$key.field_id = ".$extra_info['id']." AND
3083
                                            tag_rel$key.item_id = $alias.".$this->primaryKey."
3084
                                        )
3085
                                        INNER JOIN $this->table_field_tag tag$key
3086
                                        ON (tag$key.id = tag_rel$key.tag_id)
3087
                                    ";
3088
                                    $newCounter++;
3089
                                }
3090
                                if (!empty($whereTag)) {
3091
                                    $options['where'] .= ' AND  ('.implode(' OR ', $whereTag).') ';
3092
                                }
3093
                            }
3094
                            break;
3095
                        default:
3096
                            // text, textarea, etc
3097
                            $options['where'] = str_replace(
3098
                                $extra_info['field'],
3099
                                'fv'.$counter.'.field_id = '.$extra_info['id'].' AND fv'.$counter.'.field_value',
3100
                                $options['where']
3101
                            );
3102
                            break;
3103
                    }
3104
                $field_value_to_join[] = " fv$counter.$this->handler_id ";
3105
                $counter++;
3106
            }
3107
        }
3108
3109
        if (!empty($options['where'])) {
3110
            $where .= ' AND '.$options['where'];
3111
        }
3112
3113
        $order = '';
3114
        if (!empty($options['order'])) {
3115
            $order = ' ORDER BY '.$options['order'];
3116
        }
3117
        $limit = '';
3118
        if (!empty($options['limit'])) {
3119
            $limit = ' LIMIT '.$options['limit'];
3120
        }
3121
3122
        return [
3123
            'order' => $order,
3124
            'limit' => $limit,
3125
            'where' => $where,
3126
            'inject_where' => $inject_where,
3127
            'inject_joins' => $inject_joins,
3128
            'field_value_to_join' => $field_value_to_join,
3129
            'inject_extra_fields' => $inject_extra_fields,
3130
        ];
3131
    }
3132
3133
    /**
3134
     * Get the extra fields and their formatted values.
3135
     *
3136
     * @param int|string $itemId   The item ID (It could be a session_id, course_id or user_id)
3137
     * @param bool       $filter
3138
     * @param array      $onlyShow (list of extra fields variables to show)
3139
     *
3140
     * @return array The extra fields data
3141
     */
3142
    public function getDataAndFormattedValues($itemId, $filter = false, $onlyShow = [])
3143
    {
3144
        $valuesData = [];
3145
        $fields = $this->get_all();
3146
        $em = Database::getManager();
3147
        $repoTag = $em->getRepository(ExtraFieldRelTag::class);
3148
3149
        foreach ($fields as $field) {
3150
            if ('1' != $field['visible_to_self']) {
3151
                continue;
3152
            }
3153
3154
            if ($filter && 1 != $field['filter']) {
3155
                continue;
3156
            }
3157
3158
            if (!empty($onlyShow) && !in_array($field['variable'], $onlyShow)) {
3159
                continue;
3160
            }
3161
3162
            $valueAsArray = [];
3163
            $fieldValue = new ExtraFieldValue($this->type);
3164
            $valueData = $fieldValue->get_values_by_handler_and_field_id($itemId, $field['id'], true);
3165
3166
            $fieldType = (int) $field['value_type'];
3167
            if (self::FIELD_TYPE_TAG === $fieldType) {
3168
                $tags = $repoTag->findBy(['field' => $field['id'], 'itemId' => $itemId]);
3169
                if ($tags) {
3170
                    $data = [];
3171
                    /** @var ExtraFieldRelTag $tag */
3172
                    foreach ($tags as $extraFieldTag) {
3173
                        $tag = $extraFieldTag->getTag();
3174
                        $data[] = $tag->getTag();
3175
                    }
3176
                    $valueData = implode(',', $data);
3177
                    $valueAsArray = $data;
3178
                }
3179
            }
3180
3181
            if (!$valueData) {
3182
                continue;
3183
            }
3184
            $displayedValue = get_lang('None');
3185
            switch ($fieldType) {
3186
                case self::FIELD_TYPE_CHECKBOX:
3187
                    $displayedValue = get_lang('No');
3188
                    if (false !== $valueData && '1' == $valueData['field_value']) {
3189
                        $displayedValue = get_lang('Yes');
3190
                    }
3191
                    break;
3192
                case self::FIELD_TYPE_DATE:
3193
                    if (false !== $valueData && !empty($valueData['field_value'])) {
3194
                        $displayedValue = api_format_date($valueData['field_value'], DATE_FORMAT_LONG_NO_DAY);
3195
                    }
3196
                    break;
3197
                case self::FIELD_TYPE_TAG:
3198
                    if (!empty($valueData)) {
3199
                        $displayedValue = $valueData;
3200
                    }
3201
                    break;
3202
                case self::FIELD_TYPE_FILE:
3203
                case self::FIELD_TYPE_FILE_IMAGE:
3204
                    if (false === $valueData || empty($valueData['asset_id'])) {
3205
                        break;
3206
                    }
3207
                    $assetId = $valueData['asset_id'];
3208
                    $assetRepo = Container::getAssetRepository();
3209
                    /** @var Asset|null $asset */
3210
                    $asset = $assetRepo->find($assetId);
3211
3212
                    if (null === $asset) {
3213
                        break;
3214
                    }
3215
3216
                    $url = $assetRepo->getAssetUrl($asset);
3217
3218
                    if (self::FIELD_TYPE_FILE_IMAGE === $fieldType) {
3219
                        $image = Display::img(
3220
                            $url,
3221
                            $field['display_text'],
3222
                            ['width' => '300'],
3223
                            false
3224
                        );
3225
                        $displayedValue = Display::url(
3226
                            $image,
3227
                            $url,
3228
                            ['target' => '_blank']
3229
                        );
3230
                    } else {
3231
                        $displayedValue = Display::url(
3232
                            get_lang('Download'),
3233
                            $url,
3234
                            [
3235
                                'title' => $field['display_text'],
3236
                                'target' => '_blank',
3237
                                'class' => 'download_extra_field',
3238
                            ]
3239
                        );
3240
                    }
3241
                    break;
3242
                case self::FIELD_TYPE_SELECT:
3243
                case self::FIELD_TYPE_SELECT_MULTIPLE:
3244
                    $displayedValue = $valueData['value'] ?? $valueData['field_value'];
3245
                    break;
3246
                default:
3247
                    $displayedValue = $valueData['field_value'];
3248
                    break;
3249
            }
3250
3251
            $valuesData[] = [
3252
                'variable' => $field['variable'],
3253
                'text' => $field['display_text'],
3254
                'value' => $displayedValue,
3255
                'value_as_array' => $valueAsArray,
3256
            ];
3257
        }
3258
3259
        return $valuesData;
3260
    }
3261
3262
    /**
3263
     * @param int    $fieldId
3264
     * @param string $tag
3265
     *
3266
     * @return array
3267
     */
3268
    public function getAllUserPerTag($fieldId, $tag)
3269
    {
3270
        $tagRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3271
        $tag = Database::escape_string($tag);
3272
        $fieldId = (int) $fieldId;
3273
3274
        $sql = "SELECT user_id
3275
                FROM {$this->table_field_tag} f INNER JOIN $tagRelUserTable ft
3276
                ON tag_id = f.id
3277
                WHERE tag = '$tag' AND f.field_id = $fieldId;
3278
        ";
3279
3280
        $result = Database::query($sql);
3281
3282
        return Database::store_result($result, 'ASSOC');
3283
    }
3284
3285
    /**
3286
     * @param int $fieldId
3287
     * @param int $tagId
3288
     *
3289
     * @return array
3290
     */
3291
    public function getAllSkillPerTag($fieldId, $tagId)
3292
    {
3293
        $skillTable = Database::get_main_table(TABLE_MAIN_SKILL);
3294
        $tagRelExtraTable = Database::get_main_table(TABLE_MAIN_EXTRA_FIELD_REL_TAG);
3295
        $fieldId = (int) $fieldId;
3296
        $tagId = (int) $tagId;
3297
3298
        $sql = "SELECT s.id
3299
                FROM $skillTable s INNER JOIN $tagRelExtraTable t
3300
                ON t.item_id = s.id
3301
                WHERE tag_id = $tagId AND t.field_id = $fieldId;
3302
        ";
3303
3304
        $result = Database::query($sql);
3305
        $result = Database::store_result($result, 'ASSOC');
3306
3307
        $skillList = [];
3308
        foreach ($result as $index => $value) {
3309
            $skillList[$value['id']] = $value['id'];
3310
        }
3311
3312
        return $skillList;
3313
    }
3314
3315
    /**
3316
     * @param string $from
3317
     * @param string $search
3318
     * @param array  $options
3319
     *
3320
     * @return array
3321
     */
3322
    public function searchOptionsFromTags($from, $search, $options)
3323
    {
3324
        $extraFieldInfo = $this->get_handler_field_info_by_field_variable(str_replace('extra_', '', $from));
3325
        $extraFieldInfoTag = $this->get_handler_field_info_by_field_variable(str_replace('extra_', '', $search));
3326
        if (empty($extraFieldInfo) || empty($extraFieldInfoTag)) {
3327
            return [];
3328
        }
3329
3330
        $id = $extraFieldInfo['id'];
3331
        $tagId = $extraFieldInfoTag['id'];
3332
3333
        $table = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
3334
        $tagRelExtraTable = Database::get_main_table(TABLE_MAIN_EXTRA_FIELD_REL_TAG);
3335
        $tagTable = Database::get_main_table(TABLE_MAIN_TAG);
3336
        $optionsTable = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
3337
        $cleanOptions = [];
3338
        foreach ($options as $option) {
3339
            $cleanOptions[] = Database::escape_string($option);
3340
        }
3341
        $cleanOptions = array_filter($cleanOptions);
3342
3343
        if (empty($cleanOptions)) {
3344
            return [];
3345
        }
3346
3347
        $value = implode("','", $cleanOptions);
3348
3349
        $sql = "SELECT DISTINCT t.*, v.field_value as value, o.display_text
3350
                FROM $tagRelExtraTable te
3351
                INNER JOIN $tagTable t
3352
                ON (t.id = te.tag_id AND te.field_id = t.field_id AND te.field_id = $tagId)
3353
                INNER JOIN $table v
3354
                ON (te.item_id = v.item_id AND v.field_id = $id)
3355
                INNER JOIN $optionsTable o
3356
                ON (o.option_value = v.field_value)
3357
                WHERE v.field_value IN ('".$value."')
3358
                ORDER BY o.option_order, t.tag
3359
               ";
3360
        $result = Database::query($sql);
3361
3362
        return Database::store_result($result);
3363
    }
3364
3365
    public static function getExtraFieldTypesWithFiles(): array
3366
    {
3367
        return [self::FIELD_TYPE_FILE_IMAGE, self::FIELD_TYPE_FILE];
3368
    }
3369
3370
    /**
3371
     * @param \FormValidator $form
3372
     * @param int            $defaultValueId
3373
     * @param bool           $freezeElement
3374
     */
3375
    private function addSelectElement(FormValidator $form, array $fieldDetails, $defaultValueId, $freezeElement = false)
3376
    {
3377
        $get_lang_variables = false;
3378
        if (in_array(
3379
            $fieldDetails['variable'],
3380
            ['mail_notify_message', 'mail_notify_invitation', 'mail_notify_group_message']
3381
        )) {
3382
            $get_lang_variables = true;
3383
        }
3384
3385
        // Get extra field workflow
3386
        $addOptions = [];
3387
        $optionsExists = false;
3388
        $options = [];
3389
3390
        $optionList = [];
3391
        if (!empty($fieldDetails['options'])) {
3392
            foreach ($fieldDetails['options'] as $option_details) {
3393
                $optionList[$option_details['id']] = $option_details;
3394
                if ($get_lang_variables) {
3395
                    $options[$option_details['option_value']] = $option_details['display_text'];
3396
                } else {
3397
                    if ($optionsExists) {
3398
                        // Adding always the default value
3399
                        /*if ($option_details['id'] == $defaultValueId) {
3400
                            $options[$option_details['option_value']] = $option_details['display_text'];
3401
                        } else {
3402
                            if (isset($addOptions) && !empty($addOptions)) {
3403
                                // Parsing filters
3404
                                if (in_array($option_details['id'], $addOptions)) {
3405
                                    $options[$option_details['option_value']] = $option_details['display_text'];
3406
                                }
3407
                            }
3408
                        }*/
3409
                    } else {
3410
                        // Normal behaviour
3411
                        $options[$option_details['option_value']] = $option_details['display_text'];
3412
                    }
3413
                }
3414
            }
3415
3416
            // Setting priority message
3417
            if (isset($optionList[$defaultValueId])
3418
                && isset($optionList[$defaultValueId]['priority'])
3419
            ) {
3420
                if (!empty($optionList[$defaultValueId]['priority'])) {
3421
                    $priorityId = $optionList[$defaultValueId]['priority'];
3422
                    $option = new ExtraFieldOption($this->type);
3423
                    $messageType = $option->getPriorityMessageType($priorityId);
3424
                    $form->addElement(
3425
                        'label',
3426
                        null,
3427
                        Display::return_message(
3428
                            $optionList[$defaultValueId]['priority_message'],
3429
                            $messageType
3430
                        )
3431
                    );
3432
                }
3433
            }
3434
        }
3435
3436
        $select = $form->addSelect(
3437
            'extra_'.$fieldDetails['variable'],
3438
            $fieldDetails['display_text'],
3439
            [],
3440
            ['id' => 'extra_'.$fieldDetails['variable']]
3441
        );
3442
3443
        if (empty($defaultValueId)) {
3444
            $select->addOption(get_lang('Please select an option'), '');
3445
        }
3446
3447
        foreach ($options as $value => $text) {
3448
            if (empty($value)) {
3449
                $select->addOption($text, $value);
3450
                continue;
3451
            }
3452
3453
            $valueParts = explode('#', $text);
3454
            $dataValue = count($valueParts) > 1 ? array_shift($valueParts) : '';
3455
3456
            $select->addOption(get_lang(implode('', $valueParts)), $value, ['data-value' => $dataValue]);
3457
        }
3458
3459
        if ($freezeElement) {
3460
            $form->freeze('extra_'.$fieldDetails['variable']);
3461
        }
3462
    }
3463
3464
    /**
3465
     * @param \FormValidator $form
3466
     * @param array          $fieldDetails
3467
     * @param array          $extraData
3468
     * @param bool           $freezeElement
3469
     *
3470
     * @return string JavaScript code
3471
     */
3472
    private function addDoubleSelectElement(FormValidator $form, $fieldDetails, $extraData, $freezeElement = false)
3473
    {
3474
        $firstSelectId = 'first_extra_'.$fieldDetails['variable'];
3475
        $secondSelectId = 'second_extra_'.$fieldDetails['variable'];
3476
3477
        $jqueryReadyContent = "
3478
            $('#$firstSelectId').on('change', function() {
3479
                var id = $(this).val();
3480
3481
                if (!id) {
3482
                    $('#$secondSelectId').empty().selectpicker('refresh');
3483
3484
                    return;
3485
                }
3486
3487
                $.getJSON(_p.web_ajax + 'extra_field.ajax.php?1=1&a=get_second_select_options', {
3488
                    'type': '{$this->type}',
3489
                    'field_id': {$fieldDetails['id']},
3490
                    'option_value_id': id
3491
                })
3492
                    .done(function(data) {
3493
                        $('#$secondSelectId').empty();
3494
                        $.each(data, function(index, value) {
3495
                            $('#second_extra_{$fieldDetails['variable']}').append(
3496
                                $('<option>', {value: index, text: value})
3497
                            );
3498
                        });
3499
                        $('#$secondSelectId').selectpicker('refresh');
3500
                    });
3501
            });
3502
        ";
3503
3504
        $firstId = null;
3505
        if (!empty($extraData)) {
3506
            if (isset($extraData['extra_'.$fieldDetails['variable']])) {
3507
                $firstId = $extraData['extra_'.$fieldDetails['variable']]['extra_'.$fieldDetails['variable']];
3508
            }
3509
        }
3510
3511
        $options = $this->extra_field_double_select_convert_array_to_ordered_array($fieldDetails['options']);
3512
        $values = ['' => get_lang('Select')];
3513
3514
        $second_values = [];
3515
        if (!empty($options)) {
3516
            foreach ($options as $option) {
3517
                foreach ($option as $sub_option) {
3518
                    if ('0' == $sub_option['option_value']) {
3519
                        $values[$sub_option['id']] = get_lang($sub_option['display_text']);
3520
3521
                        continue;
3522
                    }
3523
3524
                    if ($firstId === $sub_option['option_value']) {
3525
                        $second_values[$sub_option['id']] = get_lang($sub_option['display_text']);
3526
                    }
3527
                }
3528
            }
3529
        }
3530
3531
        $form
3532
            ->defaultRenderer()
3533
            ->setGroupElementTemplate('<p>{element}</p>', 'extra_'.$fieldDetails['variable']);
3534
        $group = [];
3535
        $group[] = $form->createElement(
3536
            'select',
3537
            'extra_'.$fieldDetails['variable'],
3538
            null,
3539
            $values,
3540
            ['id' => $firstSelectId]
3541
        );
3542
        $group[] = $form->createElement(
3543
            'select',
3544
            'extra_'.$fieldDetails['variable'].'_second',
3545
            null,
3546
            $second_values,
3547
            ['id' => $secondSelectId]
3548
        );
3549
        $form->addGroup(
3550
            $group,
3551
            'extra_'.$fieldDetails['variable'],
3552
            $fieldDetails['display_text']
3553
        );
3554
3555
        if ($freezeElement) {
3556
            $form->freeze('extra_'.$fieldDetails['variable']);
3557
        }
3558
3559
        return $jqueryReadyContent;
3560
    }
3561
3562
    /**
3563
     * @param \FormValidator $form
3564
     * @param bool           $freezeElement Optional
3565
     *
3566
     * @return string JavaScript code
3567
     */
3568
    private function addSelectWithTextFieldElement(
3569
        FormValidator $form,
3570
        array $fieldDetails,
3571
        $freezeElement = false
3572
    ) {
3573
        $firstSelectId = 'slct_extra_'.$fieldDetails['variable'];
3574
        $txtSelectId = 'txt_extra_'.$fieldDetails['variable'];
3575
3576
        $jqueryReadyContent = "
3577
            $('#$firstSelectId').on('change', function() {
3578
                var id = $(this).val();
3579
3580
                if (!id) {
3581
                    $('#$txtSelectId').val('');
3582
                }
3583
            });
3584
        ";
3585
3586
        $options = $this->extra_field_double_select_convert_array_to_ordered_array($fieldDetails['options']);
3587
        $values = ['' => get_lang('Select')];
3588
3589
        if (!empty($options)) {
3590
            foreach ($options as $option) {
3591
                foreach ($option as $sub_option) {
3592
                    if ('0' != $sub_option['option_value']) {
3593
                        continue;
3594
                    }
3595
3596
                    $values[$sub_option['id']] = get_lang($sub_option['display_text']);
3597
                }
3598
            }
3599
        }
3600
3601
        $form
3602
            ->defaultRenderer()
3603
            ->setGroupElementTemplate('<p>{element}</p>', 'extra_'.$fieldDetails['variable']);
3604
        $group = [];
3605
        $group[] = $form->createElement(
3606
            'select',
3607
            'extra_'.$fieldDetails['variable'],
3608
            null,
3609
            $values,
3610
            ['id' => $firstSelectId]
3611
        );
3612
        $group[] = $form->createElement(
3613
            'text',
3614
            'extra_'.$fieldDetails['variable'].'_second',
3615
            null,
3616
            ['id' => $txtSelectId]
3617
        );
3618
        $form->addGroup(
3619
            $group,
3620
            'extra_'.$fieldDetails['variable'],
3621
            $fieldDetails['display_text']
3622
        );
3623
3624
        if ($freezeElement) {
3625
            $form->freeze('extra_'.$fieldDetails['variable']);
3626
        }
3627
3628
        return $jqueryReadyContent;
3629
    }
3630
3631
    /**
3632
     * @param \FormValidator $form
3633
     * @param bool           $freezeElement
3634
     *
3635
     * @return string
3636
     */
3637
    private function addTripleSelectElement(
3638
        FormValidator $form,
3639
        array $fieldDetails,
3640
        array $extraData,
3641
        $freezeElement
3642
    ) {
3643
        $variable = $fieldDetails['variable'];
3644
        $id = $fieldDetails['id'];
3645
        $slctFirstId = "first_extra$variable";
3646
        $slctSecondId = "second_extra$variable";
3647
        $slctThirdId = "third_extra$variable";
3648
        $langSelect = get_lang('Select');
3649
3650
        $js = "
3651
            (function () {
3652
                var slctFirst = $('#$slctFirstId'),
3653
                    slctSecond = $('#$slctSecondId'),
3654
                    slctThird = $('#$slctThirdId');
3655
3656
                slctFirst.on('change', function () {
3657
                    slctSecond.empty().selectpicker('refresh');
3658
                    slctThird.empty().selectpicker('refresh');
3659
3660
                    var level = $(this).val();
3661
3662
                    if (!level) {
3663
                        return;
3664
                    }
3665
3666
                    $.getJSON(_p.web_ajax + 'extra_field.ajax.php', {
3667
                        'a': 'get_second_select_options',
3668
                        'type': '$this->type',
3669
                        'field_id': $id,
3670
                        'option_value_id': level
3671
                    })
3672
                        .done(function (data) {
3673
                            slctSecond.append(
3674
                                $('<option>', {value: '', text: '$langSelect'})
3675
                            );
3676
3677
                            $.each(data, function (index, value) {
3678
                                var valueParts = value.split('#'),
3679
                                    dataValue = valueParts.length > 1 ? valueParts.shift() : '';
3680
3681
                                slctSecond.append(
3682
                                    $('<option>', {value: index, text: valueParts.join(''), 'data-value': dataValue})
3683
                                );
3684
                            });
3685
3686
                            slctSecond.selectpicker('refresh');
3687
                        });
3688
                });
3689
                slctSecond.on('change', function () {
3690
                    slctThird.empty().selectpicker('refresh');
3691
3692
                    var level = $(this).val();
3693
3694
                    if (!level) {
3695
                        return;
3696
                    }
3697
3698
                    $.getJSON(_p.web_ajax + 'extra_field.ajax.php', {
3699
                        'a': 'get_second_select_options',
3700
                        'type': '$this->type',
3701
                        'field_id': $id,
3702
                        'option_value_id': level
3703
                    })
3704
                        .done(function (data) {
3705
                            slctThird.append(
3706
                                $('<option>', {value: '', text: '$langSelect'})
3707
                            );
3708
3709
                            $.each(data, function (index, value) {
3710
                                var valueParts = value.split('#'),
3711
                                    dataValue = valueParts.length > 1 ? valueParts.shift() : '';
3712
3713
                                slctThird.append(
3714
                                    $('<option>', {value: index, text: valueParts.join(''), 'data-value': dataValue})
3715
                                );
3716
                            });
3717
3718
                            slctThird.selectpicker('refresh');
3719
                        });
3720
                });
3721
            })();
3722
        ";
3723
3724
        $firstId = isset($extraData["extra_$variable"]["extra_$variable"])
3725
            ? $extraData["extra_$variable"]["extra_$variable"]
3726
            : '';
3727
        $secondId = isset($extraData["extra_$variable"]["extra_{$variable}_second"])
3728
            ? $extraData["extra_$variable"]["extra_{$variable}_second"]
3729
            : '';
3730
3731
        $options = $this->tripleSelectConvertArrayToOrderedArray($fieldDetails['options']);
3732
        $values1 = ['' => $langSelect];
3733
        $values2 = ['' => $langSelect];
3734
        $values3 = ['' => $langSelect];
3735
        $level1 = $this->getOptionsFromTripleSelect($options['level1'], 0);
3736
        $level2 = $this->getOptionsFromTripleSelect($options['level2'], $firstId);
3737
        $level3 = $this->getOptionsFromTripleSelect($options['level3'], $secondId);
3738
        /** @var \HTML_QuickForm_select $slctFirst */
3739
        $slctFirst = $form->createElement('select', "extra_$variable", null, $values1, ['id' => $slctFirstId]);
3740
        /** @var \HTML_QuickForm_select $slctSecond */
3741
        $slctSecond = $form->createElement(
3742
            'select',
3743
            "extra_{$variable}_second",
3744
            null,
3745
            $values2,
3746
            ['id' => $slctSecondId]
3747
        );
3748
        /** @var \HTML_QuickForm_select $slctThird */
3749
        $slctThird = $form->createElement('select', "extra_{$variable}_third", null, $values3, ['id' => $slctThirdId]);
3750
3751
        foreach ($level1 as $item1) {
3752
            $valueParts = explode('#', $item1['display_text']);
3753
            $dataValue = count($valueParts) > 1 ? array_shift($valueParts) : '';
3754
            $slctFirst->addOption(get_lang(implode('', $valueParts)), $item1['id'], ['data-value' => $dataValue]);
3755
        }
3756
3757
        foreach ($level2 as $item2) {
3758
            $valueParts = explode('#', $item2['display_text']);
3759
            $dataValue = count($valueParts) > 1 ? array_shift($valueParts) : '';
3760
            $slctSecond->addOption(get_lang(implode('', $valueParts)), $item2['id'], ['data-value' => $dataValue]);
3761
        }
3762
3763
        foreach ($level3 as $item3) {
3764
            $valueParts = explode('#', $item3['display_text']);
3765
            $dataValue = count($valueParts) > 1 ? array_shift($valueParts) : '';
3766
            $slctThird->addOption(get_lang(implode('', $valueParts)), $item3['id'], ['data-value' => $dataValue]);
3767
        }
3768
3769
        $form
3770
            ->defaultRenderer()
3771
            ->setGroupElementTemplate('<p>{element}</p>', "extra_$variable");
3772
        $form->addGroup([$slctFirst, $slctSecond, $slctThird], "extra_$variable", $fieldDetails['display_text']);
3773
3774
        if ($freezeElement) {
3775
            $form->freeze('extra_'.$fieldDetails['variable']);
3776
        }
3777
3778
        return $js;
3779
    }
3780
3781
    /**
3782
     * @param int $parentId
3783
     *
3784
     * @return array
3785
     */
3786
    private static function getOptionsFromTripleSelect(array $options, $parentId)
3787
    {
3788
        return array_filter(
3789
            $options,
3790
            function ($option) use ($parentId) {
3791
                return $option['option_value'] == $parentId;
3792
            }
3793
        );
3794
    }
3795
}
3796