Passed
Pull Request — master (#6238)
by
unknown
09:30
created

ExtraField::get_max_field_order()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

2092
        /** @scrutinizer ignore-call */ 
2093
        $fieldInfo = self::get_handler_field_info_by_field_variable($params['variable']);
Loading history...
2093
        $params = $this->clean_parameters($params);
2094
        $params['item_type'] = $this->itemType;
2095
2096
        if ($fieldInfo) {
2097
            return $fieldInfo['id'];
2098
        } else {
2099
            $id = parent::save($params, $showQuery);
2100
            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...
2101
                $fieldOption = new ExtraFieldOption($this->type);
2102
                $params['field_id'] = $id;
2103
                $fieldOption->save($params);
2104
            }
2105
2106
            return $id;
2107
        }
2108
    }
2109
2110
    /**
2111
     * @param string $variable
2112
     *
2113
     * @return array|bool
2114
     */
2115
    public function get_handler_field_info_by_field_variable($variable)
2116
    {
2117
        $variable = Database::escape_string($variable);
2118
        $sql = "SELECT * FROM {$this->table}
2119
                WHERE
2120
                    variable = '$variable' AND
2121
                    item_type = $this->itemType";
2122
        $result = Database::query($sql);
2123
        if (Database::num_rows($result)) {
2124
            $extraFieldRepo = Container::getExtraFieldRepository();
2125
            $row = Database::fetch_assoc($result);
2126
            if ($row) {
2127
                $extraFieldId = $row['id'];
2128
                /** @var EntityExtraField $extraField */
2129
                $field = $extraFieldRepo->find($extraFieldId);
2130
                $row['display_text'] = $field->getDisplayText();
2131
2132
                // All the options of the field
2133
                $sql = "SELECT * FROM $this->table_field_options
2134
                        WHERE field_id='".$extraFieldId."'
2135
                        ORDER BY option_order ASC";
2136
                $result = Database::query($sql);
2137
                while ($option = Database::fetch_array($result)) {
2138
                    $row['options'][$option['id']] = $option;
2139
                }
2140
2141
                return $row;
2142
            }
2143
        }
2144
2145
        return false;
2146
    }
2147
2148
    /**
2149
     * @param array $params
2150
     *
2151
     * @return array
2152
     */
2153
    public function clean_parameters($params)
2154
    {
2155
        if (!isset($params['variable']) || empty($params['variable'])) {
2156
            $params['variable'] = $params['display_text'];
2157
        }
2158
2159
        $params['variable'] = trim(strtolower(str_replace(' ', '_', $params['variable'])));
2160
2161
        if (!isset($params['field_order'])) {
2162
            $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

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

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