ExtraField::addElements()   B
last analyzed

Complexity

Conditions 10
Paths 13

Size

Total Lines 78
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 43
c 0
b 0
f 0
nc 13
nop 17
dl 0
loc 78
rs 7.6666

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 adminPlatform 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('NotFound')."');
497
                            }
498
                        } else {
499
                            alert('Geocode ".get_lang('Error').': '.get_lang('AddressField').' '.get_lang('NotFound')."');
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('SearchGeolocalization').'
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('MyLocation').'
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');
2083
        $types[self::FIELD_TYPE_INTEGER] = get_lang('Integer');
2084
        $types[self::FIELD_TYPE_FILE_IMAGE] = get_lang('Image');
2085
        $types[self::FIELD_TYPE_FLOAT] = get_lang('Float');
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 = Container::getRouter()->generate(
2419
                'legacy_main',
2420
                ['name' => 'extrafield/translate.php', 'extra_field' => $id]
2421
            );
2422
            $translateButton = Display::toolbarButton(
2423
                get_lang('Translate this term'),
2424
                $translateUrl,
2425
                'language',
2426
                'plain'
2427
            );
2428
2429
            $form->addElement('text', 'display_text', [get_lang('Name'), $translateButton]);
2430
        } else {
2431
            $form->addText('display_text', get_lang('Name'));
2432
        }
2433
2434
        $form->addHtmlEditor('description', get_lang('Description'), false);
2435
2436
        // Field type
2437
        $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

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