ExtraField::set_extra_fields_in_form()   F
last analyzed

Complexity

Conditions 139
Paths 3

Size

Total Lines 863
Code Lines 540

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 139
eloc 540
nc 3
nop 16
dl 0
loc 863
rs 3.3333
c 0
b 0
f 0

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\Entity\Tag;
9
use Chamilo\CoreBundle\Framework\Container;
10
use Chamilo\CoreBundle\Component\Utils\ActionIcon;
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
41
    public $columns = [
42
        'id',
43
        'value_type',
44
        'variable',
45
        'description',
46
        'display_text',
47
        'default_value',
48
        'field_order',
49
        'visible_to_self',
50
        'visible_to_others',
51
        'changeable',
52
        'filter',
53
        'item_type',
54
        //Enable this when field_loggeable is introduced as a table field (2.0)
55
        //'field_loggeable',
56
        'created_at',
57
        'auto_remove',
58
    ];
59
60
    public $ops = [
61
        'eq' => '=', //equal
62
        'ne' => '<>', //not equal
63
        'lt' => '<', //less than
64
        'le' => '<=', //less than or equal
65
        'gt' => '>', //greater than
66
        'ge' => '>=', //greater than or equal
67
        'bw' => 'LIKE', //begins with
68
        'bn' => 'NOT LIKE', //doesn't begin with
69
        'in' => 'LIKE', //is in
70
        'ni' => 'NOT LIKE', //is not in
71
        'ew' => 'LIKE', //ends with
72
        'en' => 'NOT LIKE', //doesn't end with
73
        'cn' => 'LIKE', //contains
74
        'nc' => 'NOT LIKE',  //doesn't contain
75
    ];
76
77
    public $type = 'user';
78
    public $pageName;
79
    public $pageUrl;
80
    public $itemType = 0;
81
    public $table_field_options;
82
    public $table_field_values;
83
    public $table_field_tag;
84
    public $table_field_rel_tag;
85
    public $handler_id;
86
    public $primaryKey;
87
88
    /**
89
     * @param string $type
90
     */
91
    public function __construct($type)
92
    {
93
        parent::__construct();
94
95
        $this->type = $type;
96
        $this->table = Database::get_main_table(TABLE_EXTRA_FIELD);
97
        $this->table_field_options = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
98
        $this->table_field_values = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
99
        $this->table_field_tag = Database::get_main_table(TABLE_MAIN_TAG);
100
        $this->table_field_rel_tag = Database::get_main_table(TABLE_MAIN_EXTRA_FIELD_REL_TAG);
101
        $this->handler_id = 'item_id';
102
        $this->itemType = self::getExtraFieldTypeFromString($this->type);
103
104
        switch ($this->type) {
105
            case 'session':
106
            case 'user':
107
            case 'calendar_event':
108
            case 'course':
109
                $this->primaryKey = 'id';
110
                break;
111
        }
112
113
        $this->pageUrl = 'extra_fields.php?type='.$this->type;
114
        // Example QuestionFields
115
        $this->pageName = ucwords($this->type).'Fields';
116
    }
117
118
    public static function getTypeList(): array
119
    {
120
        return [
121
            'calendar_event' => EntityExtraField::CALENDAR_FIELD_TYPE,
122
            'course' => EntityExtraField::COURSE_FIELD_TYPE,
123
            'user' => EntityExtraField::USER_FIELD_TYPE,
124
            'session' => EntityExtraField::SESSION_FIELD_TYPE,
125
            'exercise' => EntityExtraField::EXERCISE_FIELD_TYPE,
126
            'question' => EntityExtraField::QUESTION_FIELD_TYPE,
127
            'lp' => EntityExtraField::LP_FIELD_TYPE,
128
            'lp_item' => EntityExtraField::LP_ITEM_FIELD_TYPE,
129
            'skill' => EntityExtraField::SKILL_FIELD_TYPE,
130
            'work' => EntityExtraField::WORK_FIELD_TYPE,
131
            'career' => EntityExtraField::CAREER_FIELD_TYPE,
132
            'user_certificate' => EntityExtraField::USER_CERTIFICATE,
133
            'survey' => EntityExtraField::SURVEY_FIELD_TYPE,
134
            'scheduled_announcement' => EntityExtraField::SCHEDULED_ANNOUNCEMENT,
135
            'terms_and_condition' => EntityExtraField::TERMS_AND_CONDITION_TYPE,
136
            'forum_category' => EntityExtraField::FORUM_CATEGORY_TYPE,
137
            'forum_post' => EntityExtraField::FORUM_POST_TYPE,
138
            'track_exercise' => EntityExtraField::TRACK_EXERCISE_FIELD_TYPE,
139
            'portfolio' => EntityExtraField::PORTFOLIO_TYPE,
140
            'lp_view' => EntityExtraField::LP_VIEW_TYPE,
141
            'course_announcement' => EntityExtraField::COURSE_ANNOUNCEMENT,
142
            'message' =>  EntityExtraField::MESSAGE_TYPE,
143
            'document' => EntityExtraField::DOCUMENT_TYPE,
144
            'attendance_calendar' => EntityExtraField::ATTENDANCE_CALENDAR_TYPE,
145
        ];
146
    }
147
148
    public static function getExtraFieldTypeFromString($extraFieldTypeInString): string
149
    {
150
        return self::getTypeList()[$extraFieldTypeInString];
151
    }
152
153
    public static function getExtraFieldTypeFromInt($extraFieldTypeInt)
154
    {
155
        $reversed = array_flip(self::getTypeList());
156
157
        return $reversed[$extraFieldTypeInt];
158
    }
159
160
    /**
161
     * @return array
162
     */
163
    public static function getValidExtraFieldTypes()
164
    {
165
        $result = [
166
            'user',
167
            'course',
168
            'session',
169
            'question',
170
            'lp',
171
            'calendar_event',
172
            'lp_item',
173
            'skill',
174
            'work',
175
            'career',
176
            'user_certificate',
177
            'survey',
178
            'terms_and_condition',
179
            'forum_category',
180
            'forum_post',
181
            'exercise',
182
            'track_exercise',
183
            'lp_view',
184
            'course_announcement',
185
            'message',
186
            'document',
187
            'attendance_calendar',
188
        ];
189
190
        if ('true' === api_get_setting('announcement.allow_scheduled_announcements')) {
191
            $result[] = 'scheduled_announcement';
192
        }
193
        $result[] = 'portfolio';
194
        sort($result);
195
196
        return $result;
197
    }
198
199
    /**
200
     * Converts a string like this:
201
     * France:Paris;Bretagne;Marseille;Lyon|Belgique:Bruxelles;Namur;Liège;Bruges|Peru:Lima;Piura;
202
     * into
203
     * array(
204
     *   'France' =>
205
     *      array('Paris', 'Bretagne', 'Marseille'),
206
     *   'Belgique' =>
207
     *      array('Namur', 'Liège')
208
     * ), etc.
209
     *
210
     * @param string $string
211
     *
212
     * @return array
213
     */
214
    public static function extra_field_double_select_convert_string_to_array($string)
215
    {
216
        $options = explode('|', $string);
217
        $options_parsed = [];
218
        $id = 0;
219
220
        if (!empty($options)) {
221
            foreach ($options as $sub_options) {
222
                $options = explode(':', $sub_options);
223
                $sub_sub_options = isset($options[1]) ? explode(';', $options[1]) : [];
224
                $options_parsed[$id] = [
225
                    'label' => $options[0],
226
                    'options' => $sub_sub_options,
227
                ];
228
                $id++;
229
            }
230
        }
231
232
        return $options_parsed;
233
    }
234
235
    /**
236
     * @param $string
237
     *
238
     * @return array
239
     */
240
    public static function tripleSelectConvertStringToArray($string)
241
    {
242
        $options = [];
243
        foreach (explode('|', $string) as $i => $item0) {
244
            $level1 = explode('\\', $item0);
245
246
            foreach ($level1 as $j => $item1) {
247
                if (0 === $j) {
248
                    $options[] = ['label' => $item1, 'options' => []];
249
250
                    continue;
251
                }
252
253
                foreach (explode(':', $item1) as $k => $item2) {
254
                    if (0 === $k) {
255
                        $options[$i]['options'][] = ['label' => $item2, 'options' => []];
256
257
                        continue;
258
                    }
259
260
                    $options[$i]['options'][$j - 1]['options'][] = explode(';', $item2);
261
                }
262
            }
263
        }
264
265
        array_walk_recursive(
266
            $options,
267
            function (&$item) {
268
                $item = trim($item);
269
            }
270
        );
271
272
        return $options;
273
    }
274
275
    /**
276
     * @param array $options the result of the get_field_options_by_field() array
277
     *
278
     * @return string
279
     */
280
    public static function extra_field_double_select_convert_array_to_string($options)
281
    {
282
        $string = null;
283
        $optionsParsed = self::extra_field_double_select_convert_array_to_ordered_array($options);
284
285
        if (!empty($optionsParsed)) {
286
            foreach ($optionsParsed as $option) {
287
                foreach ($option as $key => $item) {
288
                    $string .= $item['display_text'];
289
                    if (0 == $key) {
290
                        $string .= ':';
291
                    } else {
292
                        if (isset($option[$key + 1])) {
293
                            $string .= ';';
294
                        }
295
                    }
296
                }
297
                $string .= '|';
298
            }
299
        }
300
301
        if (!empty($string)) {
302
            $string = substr($string, 0, strlen($string) - 1);
303
        }
304
305
        return $string;
306
    }
307
308
    /**
309
     * @param array $options The result of the get_field_options_by_field() array
310
     *
311
     * @return string
312
     */
313
    public static function extraFieldSelectWithTextConvertArrayToString(array $options)
314
    {
315
        $parsedOptions = self::extra_field_double_select_convert_array_to_ordered_array($options);
316
317
        if (empty($parsedOptions)) {
318
            return '';
319
        }
320
321
        $string = '';
322
        foreach ($parsedOptions as $options) {
323
            $option = current($options);
324
            $string .= $option['display_text'];
325
            $string .= '|';
326
        }
327
328
        return rtrim($string, '|');
329
    }
330
331
    /**
332
     * @return string
333
     */
334
    public static function tripleSelectConvertArrayToString(array $options)
335
    {
336
        $parsedOptions = self::tripleSelectConvertArrayToOrderedArray($options);
337
        $string = '';
338
        foreach ($parsedOptions['level1'] as $item1) {
339
            $string .= $item1['display_text'];
340
            $level2 = self::getOptionsFromTripleSelect($parsedOptions['level2'], $item1['id']);
341
342
            foreach ($level2 as $item2) {
343
                $string .= '\\'.$item2['display_text'].':';
344
                $level3 = self::getOptionsFromTripleSelect($parsedOptions['level3'], $item2['id']);
345
346
                $string .= implode(';', array_column($level3, 'display_text'));
347
            }
348
349
            $string .= '|';
350
        }
351
352
        return trim($string, '\\|;');
353
    }
354
355
    /**
356
     * @param string $variable
357
     * @param string $dataValue
358
     *
359
     * @return string
360
     */
361
    public static function getLocalizationJavascript($variable, $dataValue)
362
    {
363
        $dataValue = addslashes($dataValue);
364
        $html = "<script>
365
            $(function() {
366
                if (typeof google === 'object') {
367
                    var address = '$dataValue';
368
                    initializeGeo{$variable}(address, false);
369
370
                    $('#geolocalization_extra_{$variable}').on('click', function() {
371
                        var address = $('#{$variable}').val();
372
                        initializeGeo{$variable}(address, false);
373
                        return false;
374
                    });
375
376
                    $('#myLocation_extra_{$variable}').on('click', function() {
377
                        myLocation{$variable}();
378
                        return false;
379
                    });
380
381
                    // When clicking enter
382
                    $('#{$variable}').keypress(function(event) {
383
                        if (event.which == 13) {
384
                            $('#geolocalization_extra_{$variable}').click();
385
                            return false;
386
                        }
387
                    });
388
389
                    // On focus out update city
390
                    $('#{$variable}').focusout(function() {
391
                        $('#geolocalization_extra_{$variable}').click();
392
                        return false;
393
                    });
394
395
                    return;
396
                }
397
398
                $('#map_extra_{$variable}')
399
                    .html('<div class=\"alert alert-info\">"
400
            .addslashes(get_lang('You need to activate the GoogleMaps plugin in adminPlatform to see the Map'))
401
            ."</div>');
402
            });
403
404
            function myLocation{$variable}()
405
            {
406
                if (navigator.geolocation) {
407
                    var geoPosition = function(position) {
408
                        var lat = position.coords.latitude;
409
                        var lng = position.coords.longitude;
410
                        var latLng = new google.maps.LatLng(lat, lng);
411
                        initializeGeo{$variable}(false, latLng);
412
                    };
413
414
                    var geoError = function(error) {
415
                        alert('Geocode ".get_lang('Error').": ' + error);
416
                    };
417
418
                    var geoOptions = {
419
                        enableHighAccuracy: true
420
                    };
421
                    navigator.geolocation.getCurrentPosition(geoPosition, geoError, geoOptions);
422
                }
423
            }
424
425
            function initializeGeo{$variable}(address, latLng)
426
            {
427
                var geocoder = new google.maps.Geocoder();
428
                var latlng = new google.maps.LatLng(-34.397, 150.644);
429
                var myOptions = {
430
                    zoom: 15,
431
                    center: latlng,
432
                    mapTypeControl: true,
433
                    mapTypeControlOptions: {
434
                        style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
435
                    },
436
                    navigationControl: true,
437
                    mapTypeId: google.maps.MapTypeId.ROADMAP
438
                };
439
440
                map_{$variable} = new google.maps.Map(
441
                    document.getElementById('map_extra_{$variable}'),
442
                    myOptions
443
                );
444
445
                var parameter = address ? {'address': address} : latLng ? {'latLng': latLng} : false;
446
447
                if (geocoder && parameter) {
448
                    geocoder.geocode(parameter, function(results, status) {
449
                        if (status == google.maps.GeocoderStatus.OK) {
450
                            if (status != google.maps.GeocoderStatus.ZERO_RESULTS) {
451
                                map_{$variable}.setCenter(results[0].geometry.location);
452
453
                                // get city and country
454
                                var defaultAddress = results[0].formatted_address;
455
                                var city = '';
456
                                var country = '';
457
458
                                for (var i=0; i<results[0].address_components.length; i++) {
459
                                    if (results[0].address_components[i].types[0] == \"locality\") {
460
                                        //this is the object you are looking for City
461
                                        city = results[0].address_components[i];
462
                                    }
463
                                    /*if (results[j].address_components[i].types[0] == \"administrative_area_level_1\") {
464
                                        //this is the object you are looking for State
465
                                        region = results[0].address_components[i];
466
                                    }*/
467
                                    if (results[0].address_components[i].types[0] == \"country\") {
468
                                        //this is the object you are looking for
469
                                        country = results[0].address_components[i];
470
                                    }
471
                                }
472
473
                                if (city && city.long_name && country && country.long_name) {
474
                                    defaultAddress = city.long_name + ', ' + country.long_name;
475
                                }
476
                                $('#{$variable}').val(defaultAddress);
477
                                $('#{$variable}_coordinates').val(
478
                                    results[0].geometry.location.lat()+','+results[0].geometry.location.lng()
479
                                );
480
481
                                var infowindow = new google.maps.InfoWindow({
482
                                    content: '<b>' + $('#extra_{$variable}').val() + '</b>',
483
                                    size: new google.maps.Size(150, 50)
484
                                });
485
486
                                var marker = new google.maps.Marker({
487
                                    position: results[0].geometry.location,
488
                                    map: map_{$variable},
489
                                    title: $('#extra_{$variable}').val()
490
                                });
491
                                google.maps.event.addListener(marker, 'click', function() {
492
                                    infowindow.open(map_{$variable}, marker);
493
                                });
494
                            } else {
495
                                alert('".get_lang('NotFound')."');
496
                            }
497
                        } else {
498
                            alert('Geocode ".get_lang('Error').': '.get_lang('AddressField').' '.get_lang('NotFound')."');
499
                        }
500
                    });
501
                }
502
            }
503
            </script>";
504
505
        return $html;
506
    }
507
508
    /**
509
     * @param string $variable
510
     * @param string $text
511
     *
512
     * @return string
513
     */
514
    public static function getLocalizationInput($variable, $text)
515
    {
516
        $textHelp = $text;
517
        if (is_array($text)) {
518
            $textHelp = $text[0];
519
        }
520
        return '
521
                <div class="form-group">
522
                    <label for="geolocalization_extra_'.$variable.'"
523
                        class="col-sm-2 control-label"></label>
524
                    <div class="col-sm-8">
525
                        <button class="btn btn--plain"
526
                            id="geolocalization_extra_'.$variable.'"
527
                            name="geolocalization_extra_'.$variable.'"
528
                            type="submit">
529
                            <em class="fa fa-map-marker"></em> '.get_lang('SearchGeolocalization').'
530
                        </button>
531
                        <button class="btn btn--plain" id="myLocation_extra_'.$variable.'"
532
                            name="myLocation_extra_'.$variable.'"
533
                            type="submit">
534
                            <em class="fa fa-crosshairs"></em> '.get_lang('MyLocation').'
535
                        </button>
536
                    </div>
537
                </div>
538
                <div class="form-group">
539
                    <label for="map_extra_'.$variable.'" class="col-sm-2 control-label">
540
                        '.$textHelp.' - '.get_lang('Map').'
541
                    </label>
542
                    <div class="col-sm-8">
543
                        <div name="map_extra_'.$variable.'"
544
                            id="map_extra_'.$variable.'" style="width:100%; height:300px;">
545
                        </div>
546
                    </div>
547
                </div>
548
            ';
549
    }
550
551
    /**
552
     * @return int
553
     */
554
    public function get_count()
555
    {
556
        $em = Database::getManager();
557
        $query = $em->getRepository(EntityExtraField::class)->createQueryBuilder('e');
558
        $query->select('count(e.id)');
559
        $query->where('e.itemType = :type');
560
        $query->setParameter('type', $this->getItemType());
561
562
        return $query->getQuery()->getSingleScalarResult();
563
    }
564
565
    /**
566
     * @return int
567
     */
568
    public function getItemType()
569
    {
570
        return (int) $this->itemType;
571
    }
572
573
    /**
574
     * @param string $sidx
575
     * @param string $sord
576
     * @param int    $start
577
     * @param int    $limit
578
     *
579
     * @return array<int, EntityExtraField>
580
     */
581
    public function getAllGrid($sidx, $sord, $start, $limit)
582
    {
583
        switch ($sidx) {
584
            case 'field_order':
585
                $sidx = 'e.fieldOrder';
586
                break;
587
            case 'variable':
588
                $sidx = 'e.variable';
589
                break;
590
            case 'display_text':
591
                $sidx = 'e.displayText';
592
                break;
593
            case 'changeable':
594
                $sidx = 'e.changeable';
595
                break;
596
            case 'visible_to_self':
597
                $sidx = 'e.visibleToSelf';
598
                break;
599
            case 'visible_to_others':
600
                $sidx = 'e.visibleToOthers';
601
                break;
602
            case 'filter':
603
                $sidx = 'e.filter';
604
                break;
605
            case 'auto_remove':
606
                $sidx = 'e.autoRemove';
607
                break;
608
        }
609
        $em = Database::getManager();
610
        $query = $em->getRepository(EntityExtraField::class)->createQueryBuilder('e');
611
        $query
612
            ->where('e.itemType = :type')
613
            ->setParameter('type', $this->getItemType())
614
            ->orderBy($sidx, $sord)
615
            ->setFirstResult($start)
616
            ->setMaxResults($limit);
617
618
        return $query->getQuery()->getResult();
619
    }
620
621
    /**
622
     * Get all the field info for tags.
623
     *
624
     * @param string $variable
625
     *
626
     * @return array|bool
627
     */
628
    public function get_handler_field_info_by_tags($variable)
629
    {
630
        $variable = Database::escape_string($variable);
631
        $sql = "SELECT * FROM {$this->table}
632
                WHERE
633
                    variable = '$variable' AND
634
                    item_type = $this->itemType";
635
        $result = Database::query($sql);
636
        $extraFieldRepo = Container::getExtraFieldRepository();
637
        if (Database::num_rows($result)) {
638
            $row = Database::fetch_assoc($result);
639
            $extraFieldId = $row['id'];
640
            /** @var EntityExtraField $extraField */
641
            $extraField = $extraFieldRepo->find($extraFieldId);
642
            $row['display_text'] = $extraField->getDisplayText();
643
644
            // All the tags of the field
645
            $sql = "SELECT * FROM $this->table_field_tag
646
                    WHERE field_id='".$extraFieldId."'
647
                    ORDER BY id ASC";
648
            $result = Database::query($sql);
649
            while ($option = Database::fetch_assoc($result)) {
650
                $row['options'][$option['id']] = $option;
651
            }
652
653
            return $row;
654
        }
655
656
        return false;
657
    }
658
659
    /**
660
     * @param int $fieldId
661
     *
662
     * @return array|bool
663
     */
664
    public function getFieldInfoByFieldId($fieldId)
665
    {
666
        $fieldId = (int) $fieldId;
667
        $sql = "SELECT * FROM {$this->table}
668
                WHERE
669
                    id = '$fieldId' AND
670
                    item_type = $this->itemType";
671
        $result = Database::query($sql);
672
        if (Database::num_rows($result)) {
673
            $row = Database::fetch_assoc($result);
674
675
            // All the options of the field
676
            $sql = "SELECT * FROM $this->table_field_options
677
                    WHERE field_id='".$fieldId."'
678
                    ORDER BY option_order ASC";
679
            $result = Database::query($sql);
680
            while ($option = Database::fetch_array($result)) {
681
                $row['options'][$option['id']] = $option;
682
            }
683
684
            return $row;
685
        } else {
686
            return false;
687
        }
688
    }
689
690
    /**
691
     * Add elements to a form.
692
     *
693
     * @param FormValidator $form                            The form object to which to attach this element
694
     * @param int           $itemId                          The item (course, user, session, etc) this extra_field is
695
     *                                                       linked to
696
     * @param array         $exclude                         Variables of extra field to exclude
697
     * @param bool          $filter                          Whether to get only the fields with the "filter" flag set
698
     *                                                       to 1 (true) or not (false)
699
     * @param bool          $useTagAsSelect                  Whether to show tag fields as select drop-down or not
700
     * @param array         $showOnlyTheseFields             Limit the extra fields shown to just the list given here
701
     * @param array         $orderFields                     An array containing the names of the fields shown, in the
702
     *                                                       right order
703
     * @param array         $extraData
704
     * @param bool          $orderDependingDefaults
705
     * @param bool          $adminPermissions
706
     * @param array         $separateExtraMultipleSelect
707
     * @param array         $customLabelsExtraMultipleSelect
708
     * @param bool          $addEmptyOptionSelects
709
     * @param array         $introductionTextList
710
     * @param array         $requiredFields
711
     * @param bool          $hideGeoLocalizationDetails
712
     *
713
     * @throws Exception
714
     *
715
     * @return array|bool If relevant, returns a one-element array with JS code to be added to the page HTML headers.
716
     *                    Returns false if the form object was not given
717
     */
718
    public function addElements(
719
        $form,
720
        $itemId = 0,
721
        $exclude = [],
722
        $filter = false,
723
        $useTagAsSelect = false,
724
        $showOnlyTheseFields = [],
725
        $orderFields = [],
726
        $extraData = [],
727
        $orderDependingDefaults = false,
728
        $adminPermissions = false,
729
        $separateExtraMultipleSelect = [],
730
        $customLabelsExtraMultipleSelect = [],
731
        $addEmptyOptionSelects = false,
732
        $introductionTextList = [],
733
        $requiredFields = [],
734
        $hideGeoLocalizationDetails = false,
735
        $help = false
736
    ) {
737
        if (empty($form)) {
738
            return false;
739
        }
740
741
        $itemId = (int) $itemId;
742
        $form->addHidden('item_id', $itemId);
743
        $extraData = false;
744
        if (!empty($itemId)) {
745
            $extraData = $this->get_handler_extra_data($itemId);
746
            if (!empty($showOnlyTheseFields)) {
747
                $setData = [];
748
                foreach ($showOnlyTheseFields as $variable) {
749
                    $extraName = 'extra_'.$variable;
750
                    if (in_array($extraName, array_keys($extraData))) {
751
                        $setData[$extraName] = $extraData[$extraName];
752
                    }
753
                }
754
                $form->setDefaults($setData);
755
            } else {
756
                $form->setDefaults($extraData);
757
            }
758
        }
759
760
        $conditions = [];
761
        if ($filter) {
762
            $conditions = ['filter = ?' => 1];
763
        }
764
765
        $extraFields = $this->get_all($conditions, 'option_order');
766
        $extra = $this->set_extra_fields_in_form(
767
            $form,
768
            $extraData,
769
            $adminPermissions,
770
            $extraFields,
771
            $itemId,
772
            $exclude,
773
            $useTagAsSelect,
774
            $showOnlyTheseFields,
775
            $orderFields,
776
            $orderDependingDefaults,
777
            $separateExtraMultipleSelect,
778
            $customLabelsExtraMultipleSelect,
779
            $addEmptyOptionSelects,
780
            $introductionTextList,
781
            $hideGeoLocalizationDetails,
782
            $help
783
        );
784
785
        if (!empty($requiredFields)) {
786
            /** @var HTML_QuickForm_input $element */
787
            foreach ($form->getElements() as $element) {
788
                $name = str_replace('extra_', '', $element->getName());
789
                if (in_array($name, $requiredFields)) {
790
                    $form->setRequired($element);
791
                }
792
            }
793
        }
794
795
        return $extra;
796
    }
797
798
    /**
799
     * Return an array of all the extra fields available for this item.
800
     *
801
     * @param int $itemId (session_id, question_id, course id)
802
     *
803
     * @return array
804
     */
805
    public function get_handler_extra_data($itemId)
806
    {
807
        if (empty($itemId)) {
808
            return [];
809
        }
810
811
        $extra_data = [];
812
        $fields = $this->get_all();
813
        $field_values = new ExtraFieldValue($this->type);
814
815
        if (!empty($fields)) {
816
            foreach ($fields as $field) {
817
                $field_value = $fieldValueArray = $field_values->get_values_by_handler_and_field_id(
818
                    $itemId,
819
                    $field['id']
820
                );
821
822
                if (self::FIELD_TYPE_TAG == $field['value_type']) {
823
                    $tags = UserManager::get_user_tags_to_string(
824
                        $itemId,
825
                        $field['id'],
826
                        false
827
                    );
828
                    $extra_data['extra_'.$field['variable']] = $tags;
829
830
                    continue;
831
                }
832
833
                if ($field_value) {
834
                    $variable = $field['variable'];
835
                    $field_value = $field_value['field_value'];
836
                    switch ($field['value_type']) {
837
                        case self::FIELD_TYPE_FILE_IMAGE:
838
                        case self::FIELD_TYPE_FILE:
839
                            // Get asset id
840
                            $extra_data['extra_'.$field['variable']] = $fieldValueArray['asset_id'] ?? 0;
841
                            break;
842
                        case self::FIELD_TYPE_TAG:
843
                            $tags = UserManager::get_user_tags_to_string(
844
                                $itemId,
845
                                $field['id'],
846
                                false
847
                            );
848
849
                            $extra_data['extra_'.$field['variable']] = $tags;
850
                            break;
851
                        case self::FIELD_TYPE_DOUBLE_SELECT:
852
                        case self::FIELD_TYPE_SELECT_WITH_TEXT_FIELD:
853
                            $selected_options = explode('::', $field_value);
854
                            $firstOption = isset($selected_options[0]) ? $selected_options[0] : '';
855
                            $secondOption = isset($selected_options[1]) ? $selected_options[1] : '';
856
                            $extra_data['extra_'.$field['variable']]['extra_'.$field['variable']] = $firstOption;
857
                            $extra_data['extra_'.$field['variable']]['extra_'.$field['variable'].'_second'] = $secondOption;
858
859
                            break;
860
                        case self::FIELD_TYPE_SELECT_MULTIPLE:
861
                            $field_value = explode(';', $field_value);
862
                            $extra_data['extra_'.$field['variable']] = $field_value;
863
                            break;
864
                        case self::FIELD_TYPE_RADIO:
865
                            $extra_data['extra_'.$field['variable']]['extra_'.$field['variable']] = $field_value;
866
                            break;
867
                        case self::FIELD_TYPE_TRIPLE_SELECT:
868
                            [$level1, $level2, $level3] = explode(';', $field_value);
869
870
                            $extra_data["extra_$variable"]["extra_$variable"] = $level1;
871
                            $extra_data["extra_$variable"]["extra_{$variable}_second"] = $level2;
872
                            $extra_data["extra_$variable"]["extra_{$variable}_third"] = $level3;
873
                            break;
874
                        default:
875
                            $extra_data['extra_'.$field['variable']] = $field_value;
876
                            break;
877
                    }
878
                } else {
879
                    // Set default values
880
                    if (isset($field['field_default_value']) &&
881
                        !empty($field['field_default_value'])
882
                    ) {
883
                        $extra_data['extra_'.$field['variable']] = $field['field_default_value'];
884
                    }
885
                }
886
            }
887
        }
888
889
        return $extra_data;
890
    }
891
892
    /**
893
     * Get an array of all the values from the extra_field and extra_field_options tables
894
     * based on the current object's type.
895
     */
896
    public function get_all(array $options = []): array
897
    {
898
        $order_field_options_by = null;
899
900
        if (func_num_args() > 1) {
901
            $order_field_options_by = func_get_arg(1);
902
        }
903
904
        $options = Database::parse_conditions(['where' => $options]);
905
906
        if (empty($options)) {
907
            $options .= ' WHERE item_type = '.$this->itemType;
908
        } else {
909
            $options .= ' AND item_type = '.$this->itemType;
910
        }
911
912
        $sql = "SELECT * FROM $this->table
913
                $options
914
                ORDER BY field_order ASC
915
        ";
916
917
        $result = Database::query($sql);
918
        $extraFields = Database::store_result($result, 'ASSOC');
919
920
        $extraFieldRepo = Container::getExtraFieldRepository();
921
        $option = new ExtraFieldOption($this->type);
922
        if (!empty($extraFields)) {
923
            foreach ($extraFields as &$extraField) {
924
                $extraFieldId = $extraField['id'];
925
                /** @var EntityExtraField $field */
926
                $field = $extraFieldRepo->find($extraFieldId);
927
                $extraField['display_text'] = $field->getDisplayText();
928
                $extraField['options'] = $option->get_field_options_by_field(
929
                    $extraField['id'],
930
                    false,
931
                    $order_field_options_by
932
                );
933
            }
934
        }
935
936
        return $extraFields;
937
    }
938
939
    /**
940
     * Fetches extra field data with various display and permission checks.
941
     *
942
     * This function retrieves the data for extra fields, applies various filters
943
     * and checks to determine if each field should be displayed based on
944
     * admin permissions, visibility settings, and specific field inclusion or
945
     * exclusion lists. It also handles ordering of fields if an order list is provided.
946
     */
947
    public function getExtraFieldsData(
948
        array $extraData,
949
        bool $adminPermissions = false,
950
        array $extra = [],
951
        array $exclude = [],
952
        array $showOnlyTheseFields = [],
953
        array $orderFields = []
954
    ): array {
955
        $fieldsData = [];
956
957
        if (!empty($extra)) {
958
            $orderedExtraFields = [];
959
            if (!empty($orderFields)) {
960
                foreach ($orderFields as $order) {
961
                    foreach ($extra as $fieldDetails) {
962
                        if ($order == $fieldDetails['variable']) {
963
                            $orderedExtraFields[] = $fieldDetails;
964
                        }
965
                    }
966
                }
967
                $extra = $orderedExtraFields;
968
            }
969
970
            foreach ($extra as $fieldDetails) {
971
                $variable = $fieldDetails['variable'];
972
973
                if (!empty($showOnlyTheseFields) && !in_array($variable, $showOnlyTheseFields)) {
974
                    continue;
975
                }
976
977
                if (!$adminPermissions && 0 == $fieldDetails['visible_to_self']) {
978
                    continue;
979
                }
980
981
                if (in_array($variable, $exclude)) {
982
                    continue;
983
                }
984
985
                $fieldData = [
986
                    'type' => $fieldDetails['value_type'],
987
                    'variable' => $variable,
988
                    'title' => get_lang($fieldDetails['display_text']),
989
                    'defaultValue' => $fieldDetails['field_default_value'] ?? '',
990
                ];
991
992
                if (!empty($fieldDetails['options'])) {
993
                    $fieldData['options'] = array_map(function ($option) {
994
                        return [
995
                            'value' => $option['option_value'],
996
                            'label' => $option['display_text'],
997
                        ];
998
                    }, $fieldDetails['options']);
999
                }
1000
1001
                if (isset($extraData['extra_' . $variable])) {
1002
                    $fieldData['value'] = $extraData['extra_' . $variable];
1003
                }
1004
1005
                $fieldsData[] = $fieldData;
1006
            }
1007
        }
1008
1009
        return $fieldsData;
1010
    }
1011
1012
    /**
1013
     * Add an element that matches the given extra field to the given $form object.
1014
     *
1015
     * @param FormValidator $form                The form these fields are to be attached to
1016
     * @param array         $extraData
1017
     * @param bool          $adminPermissions    Whether the display is considered without edition limits (true) or not
1018
     *                                           (false)
1019
     * @param array         $extra
1020
     * @param int           $itemId              The item (course, user, session, etc) this extra_field is attached to
1021
     * @param array         $exclude             Extra fields to be skipped, by textual ID
1022
     * @param bool          $useTagAsSelect      Whether to show tag fields as select drop-down or not
1023
     * @param array         $showOnlyTheseFields Limit the extra fields shown to just the list given here
1024
     * @param array         $orderFields         An array containing the names of the fields shown, in the right order
1025
     *
1026
     * @throws Exception
1027
     *
1028
     * @return array If relevant, returns a one-element array with JS code to be added to the page HTML headers
1029
     */
1030
    public function set_extra_fields_in_form(
1031
        $form,
1032
        $extraData,
1033
        $adminPermissions = false,
1034
        $extra = [],
1035
        $itemId = null,
1036
        $exclude = [],
1037
        $useTagAsSelect = false,
1038
        $showOnlyTheseFields = [],
1039
        $orderFields = [],
1040
        $orderDependingDefaults = false,
1041
        $separateExtraMultipleSelect = [],
1042
        $customLabelsExtraMultipleSelect = [],
1043
        $addEmptyOptionSelects = false,
1044
        $introductionTextList = [],
1045
        $hideGeoLocalizationDetails = false,
1046
        $help = false
1047
    ) {
1048
        $jquery_ready_content = null;
1049
1050
        $assetRepo = Container::getAssetRepository();
1051
        $extraFieldRepo = Container::getExtraFieldRepository();
1052
1053
        if (!empty($extra)) {
1054
            $newOrder = [];
1055
            if (!empty($orderFields)) {
1056
                foreach ($orderFields as $order) {
1057
                    foreach ($extra as $field_details) {
1058
                        if ($order == $field_details['variable']) {
1059
                            $newOrder[] = $field_details;
1060
                        }
1061
                    }
1062
                }
1063
                $extra = $newOrder;
1064
            }
1065
1066
            foreach ($extra as $field_details) {
1067
                $variable = $field_details['variable'];
1068
                if (!empty($showOnlyTheseFields)) {
1069
                    if (!in_array($variable, $showOnlyTheseFields)) {
1070
                        continue;
1071
                    }
1072
                }
1073
1074
                // Getting default value id if is set
1075
                $defaultValueId = null;
1076
                if (isset($field_details['options']) && !empty($field_details['options'])) {
1077
                    $valueToFind = null;
1078
                    if (isset($field_details['field_default_value'])) {
1079
                        $valueToFind = $field_details['field_default_value'];
1080
                    }
1081
                    // If a value is found we override the default value
1082
                    if (isset($extraData['extra_'.$variable])) {
1083
                        $valueToFind = $extraData['extra_'.$variable];
1084
                    }
1085
1086
                    foreach ($field_details['options'] as $option) {
1087
                        if ($option['option_value'] == $valueToFind) {
1088
                            $defaultValueId = $option['id'];
1089
                        }
1090
                    }
1091
                }
1092
1093
                if (!$adminPermissions) {
1094
                    if (0 == $field_details['visible_to_self']) {
1095
                        continue;
1096
                    }
1097
1098
                    if (in_array($variable, $exclude)) {
1099
                        continue;
1100
                    }
1101
                }
1102
1103
                if (!empty($introductionTextList) &&
1104
                    in_array($variable, array_keys($introductionTextList))
1105
                ) {
1106
                    $form->addHtml($introductionTextList[$variable]);
1107
                }
1108
1109
                $freezeElement = false;
1110
                if (!$adminPermissions) {
1111
                    $freezeElement = 0 == $field_details['visible_to_self'] || 0 == $field_details['changeable'];
1112
                }
1113
1114
                //$translatedDisplayText = $field_details['display_text'];
1115
                /** @var EntityExtraField $extraField */
1116
                $extraField = $extraFieldRepo->find($field_details['id']);
1117
                $translatedDisplayText = $extraField->getDisplayText();
1118
1119
                $translatedDisplayHelpText = '';
1120
                if ($help) {
1121
                    $translatedDisplayHelpText .= get_lang($field_details['display_text'].'Help');
1122
                }
1123
1124
                if (!empty($translatedDisplayText)) {
1125
                    if (!empty($translatedDisplayHelpText)) {
1126
                        // In this case, exceptionally, display_text is an array
1127
                        // which is then treated by display_form()
1128
                        $field_details['display_text'] = [$translatedDisplayText, $translatedDisplayHelpText];
1129
                    } else {
1130
                        // We have an helper text, use it
1131
                        $field_details['display_text'] = $translatedDisplayText;
1132
                    }
1133
                }
1134
1135
                switch ($field_details['value_type']) {
1136
                    case self::FIELD_TYPE_TEXT:
1137
                        $form->addElement(
1138
                            'text',
1139
                            'extra_'.$variable,
1140
                            $field_details['display_text'],
1141
                            [
1142
                                'id' => 'extra_'.$variable,
1143
                            ]
1144
                        );
1145
                        $form->applyFilter(
1146
                            'extra_'.$variable,
1147
                            'stripslashes'
1148
                        );
1149
                        $form->applyFilter(
1150
                            'extra_'.$variable,
1151
                            'trim'
1152
                        );
1153
                        $form->applyFilter('extra_'.$variable, 'html_filter');
1154
1155
                        if ($freezeElement) {
1156
                            $form->freeze('extra_'.$variable);
1157
                        }
1158
                        break;
1159
                    case self::FIELD_TYPE_TEXTAREA:
1160
                        $form->addHtmlEditor(
1161
                            'extra_'.$variable,
1162
                            $field_details['display_text'],
1163
                            false,
1164
                            false,
1165
                            [
1166
                                'ToolbarSet' => 'Profile',
1167
                                'Width' => '100%',
1168
                                'Height' => '130',
1169
                                'id' => 'extra_'.$variable,
1170
                            ]
1171
                        );
1172
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1173
                        $form->applyFilter('extra_'.$variable, 'trim');
1174
                        $form->applyFilter('extra_'.$variable, 'html_filter');
1175
                        if ($freezeElement) {
1176
                            $form->freeze('extra_'.$variable);
1177
                        }
1178
                        break;
1179
                    case self::FIELD_TYPE_RADIO:
1180
                        $group = [];
1181
                        if (isset($field_details['options']) &&
1182
                            !empty($field_details['options'])
1183
                        ) {
1184
                            foreach ($field_details['options'] as $option_details) {
1185
                                $options[$option_details['option_value']] = $option_details['display_text'];
1186
                                $group[] = $form->createElement(
1187
                                    'radio',
1188
                                    'extra_'.$variable,
1189
                                    $option_details['option_value'],
1190
                                    get_lang($option_details['display_text']).'<br />',
1191
                                    $option_details['option_value']
1192
                                );
1193
                            }
1194
                        }
1195
                        $form->addGroup(
1196
                            $group,
1197
                            'extra_'.$variable,
1198
                            $field_details['display_text']
1199
                        );
1200
                        if ($freezeElement) {
1201
                            $form->freeze('extra_'.$variable);
1202
                        }
1203
                        break;
1204
                    case self::FIELD_TYPE_CHECKBOX:
1205
                        $group = [];
1206
                        if (isset($field_details['options']) &&
1207
                            !empty($field_details['options'])
1208
                        ) {
1209
                            foreach ($field_details['options'] as $option_details) {
1210
                                $options[$option_details['option_value']] = $option_details['display_text'];
1211
                                $group[] = $form->createElement(
1212
                                    'checkbox',
1213
                                    'extra_'.$variable,
1214
                                    $option_details['option_value'],
1215
                                    get_lang($option_details['display_text']).'<br />',
1216
                                    $option_details['option_value']
1217
                                );
1218
                            }
1219
                        } else {
1220
                            $fieldVariable = "extra_$variable";
1221
                            $checkboxAttributes = [];
1222
                            if (is_array($extraData) &&
1223
                                array_key_exists($fieldVariable, $extraData)
1224
                            ) {
1225
                                if (!empty($extraData[$fieldVariable])) {
1226
                                    $checkboxAttributes['checked'] = 1;
1227
                                }
1228
                            }
1229
1230
                            if (empty($checkboxAttributes) &&
1231
                                isset($field_details['default_value']) && empty($extraData)) {
1232
                                if (1 == $field_details['default_value']) {
1233
                                    $checkboxAttributes['checked'] = 1;
1234
                                }
1235
                            }
1236
1237
                            // We assume that is a switch on/off with 1 and 0 as values
1238
                            $group[] = $form->createElement(
1239
                                'checkbox',
1240
                                'extra_'.$variable,
1241
                                null,
1242
                                get_lang('Yes'),
1243
                                $checkboxAttributes
1244
                            );
1245
                        }
1246
1247
                        $form->addGroup(
1248
                            $group,
1249
                            'extra_'.$variable,
1250
                            $field_details['display_text']
1251
                        );
1252
                        if ($freezeElement) {
1253
                            $form->freeze('extra_'.$variable);
1254
                        }
1255
                        break;
1256
                    case self::FIELD_TYPE_SELECT:
1257
                        $this->addSelectElement($form, $field_details, $defaultValueId, $freezeElement);
1258
                        break;
1259
                    case self::FIELD_TYPE_SELECT_MULTIPLE:
1260
                        $options = [];
1261
                        if (empty($defaultValueId)) {
1262
                            $options[''] = get_lang('Please select an option');
1263
                        }
1264
                        if (isset($field_details['options']) && !empty($field_details['options'])) {
1265
                            foreach ($field_details['options'] as $optionDetails) {
1266
                                $options[$optionDetails['option_value']] = get_lang($optionDetails['display_text']);
1267
                            }
1268
                        }
1269
1270
                        if ($orderDependingDefaults) {
1271
                            if (isset($extraData['extra_'.$variable])) {
1272
                                $defaultOptions = $extraData['extra_'.$variable];
1273
                                $firstList = [];
1274
                                if ($addEmptyOptionSelects) {
1275
                                    $firstList[] = '';
1276
                                }
1277
                                foreach ($defaultOptions as $key) {
1278
                                    if (isset($options[$key])) {
1279
                                        $firstList[$key] = $options[$key];
1280
                                    }
1281
                                }
1282
                                if (!empty($firstList)) {
1283
                                    $options = array_merge($firstList, $options);
1284
                                }
1285
                            } else {
1286
                                $firstList = [];
1287
                                if ($addEmptyOptionSelects) {
1288
                                    $firstList[] = '&nbsp;';
1289
                                    $options = array_merge($firstList, $options);
1290
                                }
1291
                            }
1292
                        }
1293
                        // OFAJ
1294
                        $separateValue = 0;
1295
                        if (isset($separateExtraMultipleSelect[$variable])) {
1296
                            $separateValue = $separateExtraMultipleSelect[$variable];
1297
                        }
1298
1299
                        if ($separateValue > 0) {
1300
                            for ($i = 0; $i < $separateValue; $i++) {
1301
                                $form->addSelect(
1302
                                    'extra_'.$variable.'['.$i.']',
1303
                                    $customLabelsExtraMultipleSelect[$variable][$i],
1304
                                    $options,
1305
                                    ['id' => 'extra_'.$variable.'_'.$i]
1306
                                );
1307
                            }
1308
                        } else {
1309
                            // Ofaj
1310
                            $attributes = ['multiple' => 'multiple', 'id' => 'extra_'.$variable];
1311
                            $chosenSelect = [
1312
                                'ecouter',
1313
                                'lire',
1314
                                'participer_a_une_conversation',
1315
                                's_exprimer_oralement_en_continu',
1316
                                'ecrire',
1317
                            ];
1318
1319
                            if (in_array($variable, $chosenSelect)) {
1320
                                $attributes['select_chosen'] = true;
1321
                            }
1322
1323
                            // default behaviour
1324
                            $form->addSelect(
1325
                                'extra_'.$variable,
1326
                                $field_details['display_text'],
1327
                                $options,
1328
                                $attributes,
1329
                            );
1330
1331
                        }
1332
1333
                        if ($freezeElement) {
1334
                            $form->freeze('extra_'.$variable);
1335
                        }
1336
                        /*$form->addSelect(
1337
                            'extra_'.$variable,
1338
                            $field_details['display_text'],
1339
                            $options,
1340
                            [
1341
                                'multiple' => 'multiple',
1342
                                'id' => 'extra_'.$variable,
1343
                            ]
1344
                        );
1345
                        if ($freezeElement) {
1346
                            $form->freeze('extra_'.$variable);
1347
                        }*/
1348
                        break;
1349
                    case self::FIELD_TYPE_DATE:
1350
                        $form->addDatePicker('extra_'.$variable, $field_details['display_text']);
1351
                        if ($freezeElement) {
1352
                            $form->freeze('extra_'.$variable);
1353
                        }
1354
                        break;
1355
                    case self::FIELD_TYPE_DATETIME:
1356
                        $form->addDateTimePicker(
1357
                            'extra_'.$variable,
1358
                            $field_details['display_text']
1359
                        );
1360
1361
                        $defaults['extra_'.$variable] = api_get_local_time();
1362
                        if (!isset($form->_defaultValues['extra_'.$variable])) {
1363
                            $form->setDefaults($defaults);
1364
                        }
1365
                        if ($freezeElement) {
1366
                            $form->freeze('extra_'.$variable);
1367
                        }
1368
                        break;
1369
                    case self::FIELD_TYPE_DOUBLE_SELECT:
1370
                        $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

1370
                        $jquery_ready_content .= self::/** @scrutinizer ignore-call */ addDoubleSelectElement(
Loading history...
1371
                            $form,
1372
                            $field_details,
1373
                            $extraData,
1374
                            $freezeElement
1375
                        );
1376
                        break;
1377
                    case self::FIELD_TYPE_DIVIDER:
1378
                        $form->addHtml('
1379
                            <div class="form-group ">
1380
                                <div class="col-sm-12">
1381
                                    <div class="panel-separator">
1382
                                       <h4 id="'.$variable.'" class="form-separator">'
1383
                                            .$field_details['display_text'].'
1384
                                       </h4>
1385
                                    </div>
1386
                                </div>
1387
                            </div>
1388
                        ');
1389
                        break;
1390
                    case self::FIELD_TYPE_TAG:
1391
                        $field_id = $field_details['id'];
1392
                        $separateValue = 0;
1393
                        if (isset($separateExtraMultipleSelect[$variable])) {
1394
                            $separateValue = $separateExtraMultipleSelect[$variable];
1395
                        }
1396
1397
                        $selectedOptions = [];
1398
                        if ($separateValue > 0) {
1399
                            $em = Database::getManager();
1400
                            $fieldTags = $em
1401
                                ->getRepository(ExtraFieldRelTag::class)
1402
                                ->findBy(
1403
                                    [
1404
                                        'field' => $field_id,
1405
                                        'itemId' => $itemId,
1406
                                    ]
1407
                                );
1408
                            // ofaj.
1409
                            for ($i = 0; $i < $separateValue; $i++) {
1410
                                $tagsSelect = $form->addSelect(
1411
                                    'extra_'.$variable.'['.$i.']',
1412
                                    $customLabelsExtraMultipleSelect[$variable][$i], //$field_details['display_text'],
1413
                                    [],
1414
                                    ['id' => 'extra_'.$variable.'_'.$i]
1415
                                );
1416
1417
                                if ($addEmptyOptionSelects) {
1418
                                    $tagsSelect->addOption(
1419
                                        '',
1420
                                        ''
1421
                                    );
1422
                                }
1423
                                /** @var ExtraFieldRelTag $fieldTag */
1424
                                foreach ($fieldTags as $fieldTag) {
1425
                                    $tag = $fieldTag->getTag();
1426
1427
                                    if (empty($tag)) {
1428
                                        continue;
1429
                                    }
1430
1431
                                    $tagsSelect->addOption(
1432
                                        $tag->getTag(),
1433
                                        $tag->getTag()
1434
                                    );
1435
                                }
1436
                            }
1437
                        } else {
1438
                            $tagsSelect = $form->addSelect(
1439
                                "extra_{$variable}",
1440
                                $field_details['display_text'],
1441
                                [],
1442
                                ['style' => 'width: 100%;']
1443
                            );
1444
1445
                            if (false === $useTagAsSelect) {
1446
                                $tagsSelect->setAttribute('class', null);
1447
                            }
1448
1449
                            $tagsSelect->setAttribute(
1450
                                'id',
1451
                                "extra_{$variable}"
1452
                            );
1453
                            $tagsSelect->setMultiple(true);
1454
1455
                            $selectedOptions = [];
1456
                            if ('user' === $this->type) {
1457
                                // The magic should be here
1458
                                $user_tags = UserManager::get_user_tags(
1459
                                    $itemId,
1460
                                    $field_details['id']
1461
                                );
1462
1463
                                if (is_array($user_tags) && count($user_tags) > 0) {
1464
                                    foreach ($user_tags as $tag) {
1465
                                        if (empty($tag['tag'])) {
1466
                                            continue;
1467
                                        }
1468
                                        $tagsSelect->addOption(
1469
                                            $tag['tag'],
1470
                                            $tag['tag'],
1471
                                            [
1472
                                                'selected' => 'selected',
1473
                                                'class' => 'selected',
1474
                                            ]
1475
                                        );
1476
                                        $selectedOptions[] = $tag['tag'];
1477
                                    }
1478
                                }
1479
                                $url = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php';
1480
                            } else {
1481
                                $em = Database::getManager();
1482
                                $fieldTags = $em->getRepository(
1483
                                    ExtraFieldRelTag::class
1484
                                )
1485
                                ->findBy(
1486
                                    [
1487
                                        'field' => $field_id,
1488
                                        'itemId' => $itemId,
1489
                                    ]
1490
                                );
1491
1492
                                /** @var ExtraFieldRelTag $fieldTag */
1493
                                foreach ($fieldTags as $fieldTag) {
1494
                                    $tag = $fieldTag->getTag();
1495
                                    if (empty($tag)) {
1496
                                        continue;
1497
                                    }
1498
                                    $tagsSelect->addOption(
1499
                                        $tag->getTag(),
1500
                                        $tag->getTag()
1501
                                    );
1502
                                    $selectedOptions[] = $tag->getTag();
1503
                                }
1504
1505
                                if (!empty($extraData) && isset($extraData['extra_'.$variable])) {
1506
                                    $data = $extraData['extra_'.$variable];
1507
                                    if (!empty($data)) {
1508
                                        foreach ($data as $option) {
1509
                                            $tagsSelect->addOption(
1510
                                                $option,
1511
                                                $option
1512
                                            );
1513
                                        }
1514
                                    }
1515
                                }
1516
1517
                                if ($useTagAsSelect) {
1518
                                    $fieldTags = $em->getRepository(ExtraFieldRelTag::class)
1519
                                        ->findBy(
1520
                                            [
1521
                                                'field' => $field_id,
1522
                                            ]
1523
                                        );
1524
                                    $tagsAdded = [];
1525
                                    /** @var ExtraFieldRelTag $fieldTag */
1526
                                    foreach ($fieldTags as $fieldTag) {
1527
                                        $tag = $fieldTag->getTag();
1528
1529
                                        if (empty($tag)) {
1530
                                            continue;
1531
                                        }
1532
1533
                                        $tagText = $tag->getTag();
1534
                                        if (in_array($tagText, $tagsAdded)) {
1535
                                            continue;
1536
                                        }
1537
1538
                                        $tagsSelect->addOption(
1539
                                            $tag->getTag(),
1540
                                            $tag->getTag(),
1541
                                            []
1542
                                        );
1543
1544
                                        $tagsAdded[] = $tagText;
1545
                                    }
1546
                                }
1547
                                $url = api_get_path(WEB_AJAX_PATH).'extra_field.ajax.php';
1548
                            }
1549
1550
                            $form->setDefaults(
1551
                                [
1552
                                    'extra_'.$variable => $selectedOptions,
1553
                                ]
1554
                            );
1555
1556
                            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...
1557
                                $jquery_ready_content .= "
1558
                                $('#extra_$variable').select2({
1559
                                    ajax: {
1560
                                        url: '$url?a=search_tags&field_id=$field_id&type={$this->type}',
1561
                                        processResults: function (data) {
1562
                                            return {
1563
                                                results: data.items
1564
                                            }
1565
                                        }
1566
                                    },
1567
                                    cache: false,
1568
                                    tags: true,
1569
                                    tokenSeparators: [','],
1570
                                    placeholder: '".get_lang('Start to type, then click on this bar to validate tag')."'
1571
                                });
1572
                            ";
1573
                            }
1574
                        }
1575
1576
                        break;
1577
                    case self::FIELD_TYPE_TIMEZONE:
1578
                        $form->addSelect(
1579
                            'extra_'.$variable,
1580
                            $field_details['display_text'],
1581
                            api_get_timezones(),
1582
                        );
1583
                        if ($freezeElement) {
1584
                            $form->freeze('extra_'.$variable);
1585
                        }
1586
                        break;
1587
                    case self::FIELD_TYPE_SOCIAL_PROFILE:
1588
                        // get the social network's favicon
1589
                        $extra_data_variable = isset($extraData['extra_'.$variable]) ? $extraData['extra_'.$variable] : null;
1590
                        $field_default_value = isset($field_details['field_default_value']) ? $field_details['field_default_value'] : null;
1591
                        $icon_path = UserManager::get_favicon_from_url(
1592
                            $extra_data_variable,
1593
                            $field_default_value
1594
                        );
1595
                        // special hack for hi5
1596
                        $leftpad = '1.7';
1597
                        $top = '0.4';
1598
                        $domain = parse_url($icon_path, PHP_URL_HOST);
1599
                        if ('www.hi5.com' === $domain || 'hi5.com' === $domain) {
1600
                            $leftpad = '3';
1601
                            $top = '0';
1602
                        }
1603
                        // print the input field
1604
                        $form->addElement(
1605
                            'text',
1606
                            'extra_'.$variable,
1607
                            $field_details['display_text'],
1608
                            [
1609
                                //'size' => 60,
1610
                                'size' => implode(
1611
                                    '; ',
1612
                                    [
1613
                                        "background-image: url('$icon_path')",
1614
                                        'background-repeat: no-repeat',
1615
                                        "background-position: 0.4em {$top}em",
1616
                                        "padding-left: {$leftpad}em",
1617
                                    ]
1618
                                ),
1619
                            ]
1620
                        );
1621
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1622
                        $form->applyFilter('extra_'.$variable, 'trim');
1623
                        if ($freezeElement) {
1624
                            $form->freeze('extra_'.$variable);
1625
                        }
1626
                        break;
1627
                    case self::FIELD_TYPE_MOBILE_PHONE_NUMBER:
1628
                        $form->addElement(
1629
                            'text',
1630
                            'extra_'.$variable,
1631
                            $field_details['display_text'].' ('.get_lang('Include the country dial code').')',
1632
                            ['size' => 40, 'placeholder' => '(xx)xxxxxxxxx']
1633
                        );
1634
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1635
                        $form->applyFilter('extra_'.$variable, 'trim');
1636
                        $form->applyFilter('extra_'.$variable, 'mobile_phone_number_filter');
1637
                        $form->applyFilter('extra_'.$variable, 'html_filter');
1638
                        $form->addRule(
1639
                            'extra_'.$variable,
1640
                            get_lang('Mobile phone number is incomplete or contains invalid characters'),
1641
                            'mobile_phone_number'
1642
                        );
1643
                        if ($freezeElement) {
1644
                            $form->freeze('extra_'.$variable);
1645
                        }
1646
                        break;
1647
                    case self::FIELD_TYPE_INTEGER:
1648
                        $form->addElement(
1649
                            'number',
1650
                            'extra_'.$variable,
1651
                            $field_details['display_text'],
1652
                            ['class' => 'span1', 'step' => 1]
1653
                        );
1654
1655
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1656
                        $form->applyFilter('extra_'.$variable, 'trim');
1657
                        $form->applyFilter('extra_'.$variable, 'intval');
1658
1659
                        if ($freezeElement) {
1660
                            $form->freeze('extra_'.$variable);
1661
                        }
1662
                        break;
1663
                    case self::FIELD_TYPE_FLOAT:
1664
                        $form->addElement(
1665
                            'number',
1666
                            'extra_'.$variable,
1667
                            $field_details['display_text'],
1668
                            ['class' => 'span1', 'step' => '0.01']
1669
                        );
1670
1671
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1672
                        $form->applyFilter('extra_'.$variable, 'trim');
1673
                        $form->applyFilter('extra_'.$variable, 'floatval');
1674
1675
                        if ($freezeElement) {
1676
                            $form->freeze('extra_'.$variable);
1677
                        }
1678
                        break;
1679
                    case self::FIELD_TYPE_FILE_IMAGE:
1680
                        $fieldVariable = "extra_{$variable}";
1681
                        $fieldTexts = [
1682
                            $field_details['display_text'],
1683
                        ];
1684
1685
                        if (is_array($extraData) && array_key_exists($fieldVariable, $extraData)) {
1686
                            $assetId = $extraData[$fieldVariable];
1687
                            if (!empty($assetId)) {
1688
                                $asset = $assetRepo->find($assetId);
1689
                                if (null !== $asset) {
1690
                                    $fieldTexts[] = Display::img(
1691
                                        $assetRepo->getAssetUrl($asset),
1692
                                        $field_details['display_text'],
1693
                                        ['width' => '300'],
1694
                                        false
1695
                                    );
1696
                                }
1697
                            }
1698
                        }
1699
1700
                        if ('Image' === $fieldTexts[0]) {
1701
                            $fieldTexts[0] = get_lang($fieldTexts[0]);
1702
                        }
1703
1704
                        $form->addFile(
1705
                            $fieldVariable,
1706
                            $fieldTexts,
1707
                            ['accept' => 'image/*', 'id' => 'extra_image', 'crop_image' => 'true']
1708
                        );
1709
1710
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1711
                        $form->applyFilter('extra_'.$variable, 'trim');
1712
1713
                        $allowedPictureTypes = ['jpg', 'jpeg', 'png', 'gif'];
1714
                        $form->addRule(
1715
                            'extra_'.$variable,
1716
                            get_lang('Only PNG, JPG or GIF images allowed').' ('.implode(',', $allowedPictureTypes).')',
1717
                            'filetype',
1718
                            $allowedPictureTypes
1719
                        );
1720
1721
                        if ($freezeElement) {
1722
                            $form->freeze('extra_'.$variable);
1723
                        }
1724
                        break;
1725
                    case self::FIELD_TYPE_FILE:
1726
                        $fieldVariable = "extra_{$variable}";
1727
                        $fieldTexts = [
1728
                            $field_details['display_text'],
1729
                        ];
1730
1731
                        if (is_array($extraData) &&
1732
                            array_key_exists($fieldVariable, $extraData)
1733
                        ) {
1734
                            $assetId = $extraData[$fieldVariable] ?? 0;
1735
                            /** @var Asset $asset */
1736
                            $asset = $assetRepo->find($assetId);
1737
                            if (null !== $asset) {
1738
                                $fileName = $asset->getTitle();
1739
                                $linkUrl = $assetRepo->getAssetUrl($asset);
1740
                                $linkToDelete = '';
1741
                                if (api_is_platform_admin()) {
1742
                                    $url = api_get_path(WEB_AJAX_PATH).'extra_field.ajax.php?type='.$this->type;
1743
                                    $url .= '&a=delete_file&field_id='.$field_details['id'].'&item_id='.$itemId;
1744
                                    $deleteId = $variable.'_delete';
1745
                                    $form->addHtml(
1746
                                        "
1747
                                        <script>
1748
                                            $(function() {
1749
                                                $('#".$deleteId."').on('click', function() {
1750
                                                    $.ajax({
1751
                                                        type: 'GET',
1752
                                                        url: '".$url."',
1753
                                                        success: function(result) {
1754
                                                            if (result == 1) {
1755
                                                                $('#".$variable."').html('".get_lang('Deleted')."');
1756
                                                            }
1757
                                                        }
1758
                                                    });
1759
                                                });
1760
                                            });
1761
                                        </script>
1762
                                    "
1763
                                    );
1764
1765
                                    $linkToDelete = '&nbsp;'.Display::url(
1766
                                        Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Delete')),
1767
                                        'javascript:void(0)',
1768
                                        ['id' => $deleteId]
1769
                                    );
1770
                                }
1771
                                $anchor = Display::url(
1772
                                    $fileName,
1773
                                    $linkUrl,
1774
                                    [
1775
                                        'title' => $field_details['display_text'],
1776
                                        'target' => '_blank',
1777
                                    ]
1778
                                );
1779
                                $fieldTexts[] = '<div id="'.$variable.'">'.$anchor.$linkToDelete.'</div>';
1780
                            }
1781
                        }
1782
1783
                        $form->addElement(
1784
                            'file',
1785
                            $fieldVariable,
1786
                            $fieldTexts,
1787
                            []
1788
                        );
1789
1790
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1791
                        $form->applyFilter('extra_'.$variable, 'trim');
1792
1793
                        if ($freezeElement) {
1794
                            $form->freeze('extra_'.$variable);
1795
                        }
1796
                        break;
1797
                    case self::FIELD_TYPE_VIDEO_URL:
1798
                        $form->addUrl(
1799
                            "extra_{$variable}",
1800
                            $field_details['display_text'],
1801
                            false,
1802
                            ['placeholder' => 'https://']
1803
                        );
1804
                        if ($freezeElement) {
1805
                            $form->freeze('extra_'.$variable);
1806
                        }
1807
                        break;
1808
                    case self::FIELD_TYPE_LETTERS_ONLY:
1809
                        $form->addTextLettersOnly(
1810
                            "extra_{$variable}",
1811
                            $field_details['display_text']
1812
                        );
1813
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1814
1815
                        if ($freezeElement) {
1816
                            $form->freeze('extra_'.$variable);
1817
                        }
1818
                        break;
1819
                    case self::FIELD_TYPE_ALPHANUMERIC:
1820
                        $form->addTextAlphanumeric(
1821
                            "extra_{$variable}",
1822
                            $field_details['display_text']
1823
                        );
1824
                        $form->applyFilter(
1825
                            'extra_'.$variable,
1826
                            'stripslashes'
1827
                        );
1828
                        if ($freezeElement) {
1829
                            $form->freeze('extra_'.$variable);
1830
                        }
1831
                        break;
1832
                    case self::FIELD_TYPE_LETTERS_SPACE:
1833
                        $form->addTextLettersAndSpaces(
1834
                            "extra_{$variable}",
1835
                            $field_details['display_text']
1836
                        );
1837
                        $form->applyFilter('extra_'.$variable, 'stripslashes');
1838
1839
                        if ($freezeElement) {
1840
                            $form->freeze('extra_'.$variable);
1841
                        }
1842
                        break;
1843
                    case self::FIELD_TYPE_ALPHANUMERIC_SPACE:
1844
                        $form->addTextAlphanumericAndSpaces(
1845
                            "extra_{$variable}",
1846
                            $field_details['display_text']
1847
                        );
1848
                        $form->applyFilter(
1849
                            'extra_'.$variable,
1850
                            'stripslashes'
1851
                        );
1852
                        if ($freezeElement) {
1853
                            $form->freeze('extra_'.$variable);
1854
                        }
1855
                        break;
1856
                    case self::FIELD_TYPE_GEOLOCALIZATION_COORDINATES:
1857
                    case self::FIELD_TYPE_GEOLOCALIZATION:
1858
                        $dataValue = isset($extraData['extra_'.$variable]) ? $extraData['extra_'.$variable] : '';
1859
                        $form->addGeoLocationMapField(
1860
                            'extra_'.$variable,
1861
                            $field_details['display_text'],
1862
                            $dataValue,
1863
                            $hideGeoLocalizationDetails
1864
                        );
1865
1866
                        if ($freezeElement) {
1867
                            $form->freeze('extra_'.$variable);
1868
                        }
1869
                        break;
1870
                    case self::FIELD_TYPE_SELECT_WITH_TEXT_FIELD:
1871
                        $jquery_ready_content .= $this->addSelectWithTextFieldElement(
1872
                            $form,
1873
                            $field_details,
1874
                            $freezeElement
1875
                        );
1876
                        break;
1877
                    case self::FIELD_TYPE_TRIPLE_SELECT:
1878
                        $jquery_ready_content .= $this->addTripleSelectElement(
1879
                            $form,
1880
                            $field_details,
1881
                            is_array($extraData) ? $extraData : [],
1882
                            $freezeElement
1883
                        );
1884
                        break;
1885
                }
1886
            }
1887
        }
1888
1889
        $return = [];
1890
        $return['jquery_ready_content'] = $jquery_ready_content;
1891
1892
        return $return;
1893
    }
1894
1895
    /**
1896
     * @param array $options
1897
     *
1898
     * @return array
1899
     */
1900
    public static function extra_field_double_select_convert_array_to_ordered_array($options)
1901
    {
1902
        $optionsParsed = [];
1903
        if (!empty($options)) {
1904
            foreach ($options as $option) {
1905
                if (0 == $option['option_value']) {
1906
                    $optionsParsed[$option['id']][] = $option;
1907
                } else {
1908
                    $optionsParsed[$option['option_value']][] = $option;
1909
                }
1910
            }
1911
        }
1912
1913
        return $optionsParsed;
1914
    }
1915
1916
    /**
1917
     * @return array
1918
     */
1919
    public static function tripleSelectConvertArrayToOrderedArray(array $options)
1920
    {
1921
        $level1 = self::getOptionsFromTripleSelect($options, 0);
1922
        $level2 = [];
1923
        $level3 = [];
1924
1925
        foreach ($level1 as $item1) {
1926
            $level2 += self::getOptionsFromTripleSelect($options, $item1['id']);
1927
        }
1928
1929
        foreach ($level2 as $item2) {
1930
            $level3 += self::getOptionsFromTripleSelect($options, $item2['id']);
1931
        }
1932
1933
        return ['level1' => $level1, 'level2' => $level2, 'level3' => $level3];
1934
    }
1935
1936
    /**
1937
     * @param string $type
1938
     *
1939
     * @return array
1940
     */
1941
    public function get_all_extra_field_by_type($type)
1942
    {
1943
        // all the information of the field
1944
        $sql = "SELECT * FROM {$this->table}
1945
                WHERE
1946
                    value_type = '".Database::escape_string($type)."' AND
1947
                    item_type = $this->itemType
1948
                ";
1949
        $result = Database::query($sql);
1950
1951
        $return = [];
1952
        while ($row = Database::fetch_array($result)) {
1953
            $return[] = $row['id'];
1954
        }
1955
1956
        return $return;
1957
    }
1958
1959
    /**
1960
     * @param int $id
1961
     */
1962
    public function get_field_type_by_id($id)
1963
    {
1964
        $types = $this->get_field_types();
1965
        if (isset($types[$id])) {
1966
            return $types[$id];
1967
        }
1968
1969
        return null;
1970
    }
1971
1972
    /**
1973
     * @return array
1974
     */
1975
    public function get_field_types()
1976
    {
1977
        return $this->get_extra_fields_by_handler($this->type);
1978
    }
1979
1980
    /**
1981
     * @param string $handler
1982
     *
1983
     * @return array
1984
     */
1985
    public static function get_extra_fields_by_handler($handler)
1986
    {
1987
        $types = [];
1988
        $types[self::FIELD_TYPE_TEXT] = get_lang('Text');
1989
        $types[self::FIELD_TYPE_TEXTAREA] = get_lang('Text area');
1990
        $types[self::FIELD_TYPE_RADIO] = get_lang('Radio buttons');
1991
        $types[self::FIELD_TYPE_SELECT] = get_lang('Select drop-down');
1992
        $types[self::FIELD_TYPE_SELECT_MULTIPLE] = get_lang('Multiple selection drop-down');
1993
        $types[self::FIELD_TYPE_DATE] = get_lang('Date');
1994
        $types[self::FIELD_TYPE_DATETIME] = get_lang('Date and time');
1995
        $types[self::FIELD_TYPE_DOUBLE_SELECT] = get_lang('Double select');
1996
        $types[self::FIELD_TYPE_DIVIDER] = get_lang('Visual divider');
1997
        $types[self::FIELD_TYPE_TAG] = get_lang('User tag');
1998
        $types[self::FIELD_TYPE_TIMEZONE] = get_lang('Timezone');
1999
        $types[self::FIELD_TYPE_SOCIAL_PROFILE] = get_lang('Social network link');
2000
        $types[self::FIELD_TYPE_MOBILE_PHONE_NUMBER] = get_lang('Mobile phone number');
2001
        $types[self::FIELD_TYPE_CHECKBOX] = get_lang('Checkbox');
2002
        $types[self::FIELD_TYPE_INTEGER] = get_lang('Integer');
2003
        $types[self::FIELD_TYPE_FILE_IMAGE] = get_lang('Image');
2004
        $types[self::FIELD_TYPE_FLOAT] = get_lang('Float');
2005
        $types[self::FIELD_TYPE_FILE] = get_lang('File');
2006
        $types[self::FIELD_TYPE_VIDEO_URL] = get_lang('Video URL');
2007
        $types[self::FIELD_TYPE_LETTERS_ONLY] = get_lang('Text only letters');
2008
        $types[self::FIELD_TYPE_ALPHANUMERIC] = get_lang('Text only alphanumeric characters');
2009
        $types[self::FIELD_TYPE_LETTERS_SPACE] = get_lang('Text letters and spaces');
2010
        $types[self::FIELD_TYPE_ALPHANUMERIC_SPACE] = get_lang('Text alphanumeric characters and spaces');
2011
        $types[self::FIELD_TYPE_GEOLOCALIZATION] = get_lang('Geolocalization');
2012
        $types[self::FIELD_TYPE_GEOLOCALIZATION_COORDINATES] = get_lang('Geolocalization by coordinates');
2013
        $types[self::FIELD_TYPE_SELECT_WITH_TEXT_FIELD] = get_lang('Select with text field');
2014
        $types[self::FIELD_TYPE_TRIPLE_SELECT] = get_lang('Triple select');
2015
2016
        switch ($handler) {
2017
            case 'course':
2018
            case 'session':
2019
            case 'user':
2020
            case 'skill':
2021
                break;
2022
        }
2023
2024
        return $types;
2025
    }
2026
2027
    /**
2028
     * @param array $params
2029
     * @param bool  $showQuery
2030
     *
2031
     * @return int|bool
2032
     */
2033
    public function save($params, $showQuery = false)
2034
    {
2035
        $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

2035
        /** @scrutinizer ignore-call */ 
2036
        $fieldInfo = self::get_handler_field_info_by_field_variable($params['variable']);
Loading history...
2036
        $params = $this->clean_parameters($params);
2037
        $params['item_type'] = $this->itemType;
2038
2039
        if ($fieldInfo) {
2040
            return $fieldInfo['id'];
2041
        } else {
2042
            $id = parent::save($params, $showQuery);
2043
            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...
2044
                $fieldOption = new ExtraFieldOption($this->type);
2045
                $params['field_id'] = $id;
2046
                $fieldOption->save($params);
2047
            }
2048
2049
            return $id;
2050
        }
2051
    }
2052
2053
    /**
2054
     * @param string $variable
2055
     *
2056
     * @return array|bool
2057
     */
2058
    public function get_handler_field_info_by_field_variable($variable)
2059
    {
2060
        $variable = Database::escape_string($variable);
2061
        $sql = "SELECT * FROM {$this->table}
2062
                WHERE
2063
                    variable = '$variable' AND
2064
                    item_type = $this->itemType";
2065
        $result = Database::query($sql);
2066
        if (Database::num_rows($result)) {
2067
            $extraFieldRepo = Container::getExtraFieldRepository();
2068
            $row = Database::fetch_assoc($result);
2069
            if ($row) {
2070
                $extraFieldId = $row['id'];
2071
                /** @var EntityExtraField $extraField */
2072
                $field = $extraFieldRepo->find($extraFieldId);
2073
                $row['display_text'] = $field->getDisplayText();
2074
2075
                // All the options of the field
2076
                $sql = "SELECT * FROM $this->table_field_options
2077
                        WHERE field_id='".$extraFieldId."'
2078
                        ORDER BY option_order ASC";
2079
                $result = Database::query($sql);
2080
                while ($option = Database::fetch_array($result)) {
2081
                    $row['options'][$option['id']] = $option;
2082
                }
2083
2084
                return $row;
2085
            }
2086
        }
2087
2088
        return false;
2089
    }
2090
2091
    /**
2092
     * @param array $params
2093
     *
2094
     * @return array
2095
     */
2096
    public function clean_parameters($params)
2097
    {
2098
        if (!isset($params['variable']) || empty($params['variable'])) {
2099
            $params['variable'] = $params['display_text'];
2100
        }
2101
2102
        $params['variable'] = trim(strtolower(str_replace(' ', '_', $params['variable'])));
2103
2104
        if (!isset($params['field_order'])) {
2105
            $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

2105
            /** @scrutinizer ignore-call */ 
2106
            $max_order = self::get_max_field_order();
Loading history...
2106
            $params['field_order'] = $max_order;
2107
        } else {
2108
            $params['field_order'] = (int) $params['field_order'];
2109
        }
2110
2111
        return $params;
2112
    }
2113
2114
    /**
2115
     * @return int
2116
     */
2117
    public function get_max_field_order()
2118
    {
2119
        $sql = "SELECT MAX(field_order)
2120
                FROM {$this->table}
2121
                WHERE
2122
                    item_type = '.$this->itemType.'";
2123
        $res = Database::query($sql);
2124
2125
        $order = 0;
2126
        if (Database::num_rows($res) > 0) {
2127
            $row = Database::fetch_row($res);
2128
            $order = $row[0] + 1;
2129
        }
2130
2131
        return $order;
2132
    }
2133
2134
    /**
2135
     * {@inheritdoc}
2136
     */
2137
    public function update($params, $showQuery = false)
2138
    {
2139
        $params = $this->clean_parameters($params);
2140
        if (isset($params['id'])) {
2141
            $fieldOption = new ExtraFieldOption($this->type);
2142
            $params['field_id'] = $params['id'];
2143
            if (empty($params['value_type'])) {
2144
                $params['value_type'] = $this->type;
2145
            }
2146
            $fieldOption->save($params, $showQuery);
2147
        }
2148
2149
        return parent::update($params, $showQuery);
2150
    }
2151
2152
    /**
2153
     * @param $id
2154
     *
2155
     * @return bool
2156
     */
2157
    public function delete($id)
2158
    {
2159
        $em = Database::getManager();
2160
        $items = $em->getRepository(\Chamilo\CoreBundle\Entity\ExtraFieldSavedSearch::class)->findBy(['field' => $id]);
2161
        if ($items) {
2162
            foreach ($items as $item) {
2163
                $em->remove($item);
2164
            }
2165
            $em->flush();
2166
        }
2167
        $field_option = new ExtraFieldOption($this->type);
2168
        $field_option->delete_all_options_by_field_id($id);
2169
2170
        $session_field_values = new ExtraFieldValue($this->type);
2171
        $session_field_values->delete_all_values_by_field_id($id);
2172
2173
        return parent::delete($id);
2174
    }
2175
2176
    /**
2177
     * @param $breadcrumb
2178
     * @param $action
2179
     */
2180
    public function setupBreadcrumb(&$breadcrumb, $action)
2181
    {
2182
        if ('add' === $action) {
2183
            $breadcrumb[] = ['url' => $this->pageUrl, 'name' => $this->pageName];
2184
            $breadcrumb[] = ['url' => '#', 'name' => get_lang('Add')];
2185
        } elseif ('edit' === $action) {
2186
            $breadcrumb[] = ['url' => $this->pageUrl, 'name' => $this->pageName];
2187
            $breadcrumb[] = ['url' => '#', 'name' => get_lang('Edit')];
2188
        } else {
2189
            $breadcrumb[] = ['url' => '#', 'name' => $this->pageName];
2190
        }
2191
    }
2192
2193
    /**
2194
     * Displays the title + grid.
2195
     */
2196
    public function display()
2197
    {
2198
        $actions = '<a href="../admin/index.php">';
2199
        $actions .= Display::getMdiIcon(ActionIcon::BACK, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Back to').' '.get_lang('Administration'));
2200
        $actions .= '</a>';
2201
        $actions .= '<a href="'.api_get_self().'?action=add&type='.$this->type.'">';
2202
        $actions .= Display::getMdiIcon(ActionIcon::ADD, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Add'));
2203
        $actions .= '</a>';
2204
2205
        echo Display::toolbarAction('toolbar', [$actions]);
2206
        echo Display::grid_html($this->type.'_fields');
2207
    }
2208
2209
    /**
2210
     * @return array
2211
     */
2212
    public function getJqgridColumnNames()
2213
    {
2214
        $columns = [
2215
            get_lang('Name'),
2216
            get_lang('Field label'),
2217
            get_lang('Type'),
2218
            get_lang('Can change'),
2219
            get_lang('Visible to self'),
2220
            get_lang('Visible to others'),
2221
            get_lang('Filter'),
2222
            get_lang('Order'),
2223
            get_lang('Detail'),
2224
        ];
2225
2226
        if ($this->type === 'user') {
2227
            array_splice($columns, -1, 0, get_lang('Auto remove'));
2228
        }
2229
2230
        return $columns;
2231
    }
2232
2233
    /**
2234
     * @return array
2235
     */
2236
    public function getJqgridColumnModel()
2237
    {
2238
        $columnModel = [
2239
            [
2240
                'name' => 'display_text',
2241
                'index' => 'display_text',
2242
                'align' => 'left',
2243
            ],
2244
            [
2245
                'name' => 'variable',
2246
                'index' => 'variable',
2247
                'align' => 'left',
2248
                'sortable' => 'true',
2249
            ],
2250
            [
2251
                'name' => 'value_type',
2252
                'index' => 'value_type',
2253
                'align' => 'left',
2254
                'sortable' => 'true',
2255
            ],
2256
            [
2257
                'name' => 'changeable',
2258
                'index' => 'changeable',
2259
                'align' => 'center',
2260
                'sortable' => 'true',
2261
            ],
2262
            [
2263
                'name' => 'visible_to_self',
2264
                'index' => 'visible_to_self',
2265
                'align' => 'center',
2266
                'sortable' => 'true',
2267
            ],
2268
            [
2269
                'name' => 'visible_to_others',
2270
                'index' => 'visible_to_others',
2271
                'align' => 'center',
2272
                'sortable' => 'true',
2273
            ],
2274
            [
2275
                'name' => 'filter',
2276
                'index' => 'filter',
2277
                'align' => 'center',
2278
                'sortable' => 'true',
2279
            ],
2280
            [
2281
                'name' => 'field_order',
2282
                'index' => 'field_order',
2283
                'align' => 'center',
2284
                'sortable' => 'true',
2285
            ],
2286
            [
2287
                'name' => 'actions',
2288
                'index' => 'actions',
2289
                'align' => 'center',
2290
                'formatter' => 'action_formatter',
2291
                'sortable' => 'false',
2292
            ],
2293
        ];
2294
2295
        if ($this->type === 'user') {
2296
            $autoRemoveColumnModel = [
2297
                'name' => 'auto_remove',
2298
                'index' => 'auto_remove',
2299
                'align' => 'center',
2300
                'sortable' => 'true',
2301
            ];
2302
2303
            array_splice($columnModel, -1, 0, [$autoRemoveColumnModel]);
2304
        }
2305
2306
        return $columnModel;
2307
    }
2308
2309
    /**
2310
     * @param string $url
2311
     * @param string $action
2312
     *
2313
     * @return FormValidator
2314
     */
2315
    public function return_form($url, $action)
2316
    {
2317
        $form = new FormValidator($this->type.'_field', 'post', $url);
2318
2319
        $form->addHidden('type', $this->type);
2320
        $id = isset($_GET['id']) ? (int) $_GET['id'] : null;
2321
        $form->addHidden('id', $id);
2322
2323
        // Setting the form elements
2324
        $header = get_lang('Add');
2325
        $defaults = [];
2326
2327
        if ('edit' === $action) {
2328
            $header = get_lang('Edit');
2329
            // Setting the defaults
2330
            $defaults = $this->get($id);
2331
        }
2332
2333
        $form->addHeader($header);
2334
2335
        if ('edit' === $action) {
2336
            $translateUrl = Container::getRouter()->generate(
2337
                'legacy_main',
2338
                ['name' => 'extrafield/translate.php', 'extra_field' => $id]
2339
            );
2340
            $translateButton = Display::toolbarButton(
2341
                get_lang('Translate this term'),
2342
                $translateUrl,
2343
                'language',
2344
                'plain'
2345
            );
2346
2347
            $form->addElement('text', 'display_text', [get_lang('Name'), $translateButton]);
2348
        } else {
2349
            $form->addText('display_text', get_lang('Name'));
2350
        }
2351
2352
        $form->addHtmlEditor('description', get_lang('Description'), false);
2353
2354
        // Field type
2355
        $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

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