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

ExtraField::set_extra_fields_in_form()   F

Complexity

Conditions 144
Paths 3

Size

Total Lines 895
Code Lines 561

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 144
eloc 561
nc 3
nop 16
dl 0
loc 895
rs 3.3333
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\Asset;
6
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
7
use Chamilo\CoreBundle\Entity\ExtraFieldRelTag;
8
use Chamilo\CoreBundle\Entity\Tag;
9
use Chamilo\CoreBundle\Framework\Container;
10
use Chamilo\CoreBundle\Component\Utils\ActionIcon;
11
class ExtraField extends Model
12
{
13
    public const FIELD_TYPE_TEXT = 1;
14
    public const FIELD_TYPE_TEXTAREA = 2;
15
    public const FIELD_TYPE_RADIO = 3;
16
    public const FIELD_TYPE_SELECT = 4;
17
    public const FIELD_TYPE_SELECT_MULTIPLE = 5;
18
    public const FIELD_TYPE_DATE = 6;
19
    public const FIELD_TYPE_DATETIME = 7;
20
    public const FIELD_TYPE_DOUBLE_SELECT = 8;
21
    public const FIELD_TYPE_DIVIDER = 9;
22
    public const FIELD_TYPE_TAG = 10;
23
    public const FIELD_TYPE_TIMEZONE = 11;
24
    public const FIELD_TYPE_SOCIAL_PROFILE = 12;
25
    public const FIELD_TYPE_CHECKBOX = 13;
26
    public const FIELD_TYPE_MOBILE_PHONE_NUMBER = 14;
27
    public const FIELD_TYPE_INTEGER = 15;
28
    public const FIELD_TYPE_FILE_IMAGE = 16;
29
    public const FIELD_TYPE_FLOAT = 17;
30
    public const FIELD_TYPE_FILE = 18;
31
    public const FIELD_TYPE_VIDEO_URL = 19;
32
    public const FIELD_TYPE_LETTERS_ONLY = 20;
33
    public const FIELD_TYPE_ALPHANUMERIC = 21;
34
    public const FIELD_TYPE_LETTERS_SPACE = 22;
35
    public const FIELD_TYPE_ALPHANUMERIC_SPACE = 23;
36
    public const FIELD_TYPE_GEOLOCALIZATION = 24;
37
    public const FIELD_TYPE_GEOLOCALIZATION_COORDINATES = 25;
38
    public const FIELD_TYPE_SELECT_WITH_TEXT_FIELD = 26;
39
    public const FIELD_TYPE_TRIPLE_SELECT = 27;
40
    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