Passed
Push — main ( f1540e...02d90d )
by Rafael
45:15
created

ExtraFields   F

Complexity

Total Complexity 599

Size/Duplication

Total Lines 2445
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 1465
c 0
b 0
f 0
dl 0
loc 2445
rs 0.8
wmc 599

16 Methods

Rating   Name   Duplication   Size   Complexity  
F create() 0 73 28
B getAlignFlag() 0 28 9
F showOutputField() 0 345 102
B delete() 0 47 11
F update() 0 94 32
F getOptionalsFromPost() 0 125 34
B isEmptyValue() 0 15 8
B delete_label() 0 28 7
F update_label() 0 142 37
F showInputField() 0 677 188
F showSeparator() 0 86 26
C addExtraField() 0 41 14
A __construct() 0 6 1
F setOptionalsFromPost() 0 143 46
D fetch_name_optionals_label() 0 89 20
F create_label() 0 123 36

How to fix   Complexity   

Complex Class

Complex classes like ExtraFields often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ExtraFields, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/* Copyright (C) 2002-2003  Rodolphe Quiedeville    <[email protected]>
4
 * Copyright (C) 2002-2003  Jean-Louis Bergamo      <[email protected]>
5
 * Copyright (C) 2004       Sebastien Di Cintio     <[email protected]>
6
 * Copyright (C) 2004       Benoit Mortier          <[email protected]>
7
 * Copyright (C) 2009-2012  Laurent Destailleur     <[email protected]>
8
 * Copyright (C) 2009-2012  Regis Houssin           <[email protected]>
9
 * Copyright (C) 2013       Florian Henry           <[email protected]>
10
 * Copyright (C) 2015-2023  Charlene BENKE          <[email protected]>
11
 * Copyright (C) 2016       Raphaël Doursenaud      <[email protected]>
12
 * Copyright (C) 2017       Nicolas ZABOURI         <[email protected]>
13
 * Copyright (C) 2018-2022  Frédéric France         <[email protected]>
14
 * Copyright (C) 2022 		Antonin MARCHAL         <[email protected]>
15
 * Copyright (C) 2024		MDW						<[email protected]>
16
 * Copyright (C) 2024       Rafael San José         <[email protected]>
17
 *
18
 * This program is free software; you can redistribute it and/or modify
19
 * it under the terms of the GNU General Public License as published by
20
 * the Free Software Foundation; either version 3 of the License, or
21
 * (at your option) any later version.
22
 *
23
 * This program is distributed in the hope that it will be useful,
24
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26
 * GNU General Public License for more details.
27
 *
28
 * You should have received a copy of the GNU General Public License
29
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
30
 */
31
32
/**
33
 *  \file       htdocs/core/class/extrafields.class.php
34
 *  \ingroup    core
35
 *  \brief      File of class to manage extra fields
36
 */
37
38
39
namespace DoliCore\Lib;
40
41
use DoliDB;
42
43
/**
44
 *  Class to manage standard extra fields
45
 */
46
class ExtraFields
47
{
48
    /**
49
     * @var DoliDB Database handler.
50
     */
51
    public $db;
52
53
    /**
54
     * @var array<string,array{label:array<string,string>,type:array<string,string>,size:array<string,string>,default:array<string,string>,computed:array<string,string>,unique:array<string,int>,required:array<string,int>,param:array<string,mixed>,perms:array<string,mixed[]>,list:array<string,mixed[]>,totalizable:array<string,int>,help:array<string,string>,printable:array<string,int>,enabled:array<string,int>,langfile:array<string,string>,css:array<string,string>,csslist:array<string,string>,hidden:array<string,int>,mandatoryfieldsofotherentities:array<string,string>}>
55
     *      New array to store extrafields definition
56
     */
57
    public $attributes;
58
59
    /**
60
     * @var array<string,bool>  Array with boolean of status of groups
61
     */
62
    public $expand_display;
63
64
    /**
65
     * @var string Error code (or message)
66
     */
67
    public $error = '';
68
69
    /**
70
     * @var string[] Array of Error code (or message)
71
     */
72
    public $errors = [];
73
74
    /**
75
     * @var string  DB Error number
76
     */
77
    public $errno;
78
79
    /**
80
     * @var array<string,string>    Array of type to label
81
     */
82
    public static $type2label = [
83
        'varchar' => 'String1Line',
84
        'text' => 'TextLongNLines',
85
        'html' => 'HtmlText',
86
        'int' => 'Int',
87
        'double' => 'Float',
88
        'date' => 'Date',
89
        'datetime' => 'DateAndTime',
90
        //'datetimegmt'=>'DateAndTimeUTC',
91
        'boolean' => 'Boolean',
92
        'price' => 'ExtrafieldPrice',
93
        'pricecy' => 'ExtrafieldPriceWithCurrency',
94
        'phone' => 'ExtrafieldPhone',
95
        'mail' => 'ExtrafieldMail',
96
        'url' => 'ExtrafieldUrl',
97
        'ip' => 'ExtrafieldIP',
98
        'icon' => 'Icon',
99
        'password' => 'ExtrafieldPassword',
100
        'select' => 'ExtrafieldSelect',
101
        'sellist' => 'ExtrafieldSelectList',
102
        'radio' => 'ExtrafieldRadio',
103
        'checkbox' => 'ExtrafieldCheckBox',
104
        'chkbxlst' => 'ExtrafieldCheckBoxFromList',
105
        'link' => 'ExtrafieldLink',
106
        'separate' => 'ExtrafieldSeparator',
107
    ];
108
109
    /**
110
     *  Constructor
111
     *
112
     * @param DoliDB $db Database handler
113
     */
114
    public function __construct(DoliDB $db)
115
    {
116
        $this->db = $db;
117
        $this->error = '';
118
        $this->errors = [];
119
        $this->attributes = [];
120
    }
121
122
    /**
123
     *  Add a new extra field parameter
124
     *
125
     * @param string       $attrname       Code of attribute
126
     * @param string       $label          label of attribute
127
     * @param string       $type           Type of attribute
128
     *                                     ('boolean','int','varchar','text','html','date','datetime','price',
129
     *                                     'pricecy',
130
     *                                     'phone','mail','password','url','select','checkbox','separate',...)
131
     * @param int          $pos            Position of attribute
132
     * @param string       $size           Size/length definition of attribute ('5', '24,8', ...). For float, it
133
     *                                     contains 2 numeric separated with a comma.
134
     * @param string       $elementtype    Element type. Same value than object->table_element (Example 'member',
135
     *                                     'product', 'thirdparty', ...)
136
     * @param int          $unique         Is field unique or not
137
     * @param int          $required       Is field required or not
138
     * @param string       $default_value  Defaulted value (In database. use the default_value feature for default
139
     *                                     value on screen. Example: '', '0', 'null', 'avalue')
140
     * @param array|string $param          Params for field (ex for select list : array('options' =>
141
     *                                     array(value'=>'label of option')) )
142
     * @param int          $alwayseditable Is attribute always editable regardless of the document status
143
     * @param string       $perms          Permission to check
144
     * @param string       $list           Visibility ('0'=never visible, '1'=visible on list+forms, '2'=list only,
145
     *                                     '3'=form only or 'eval string')
146
     * @param string       $help           Text with help tooltip
147
     * @param string       $computed       Computed value
148
     * @param string       $entity         Entity of extrafields (for multicompany modules)
149
     * @param string       $langfile       Language file
150
     * @param string       $enabled        Condition to have the field enabled or not
151
     * @param int          $totalizable    Is a measure. Must show a total on lists
152
     * @param int          $printable      Is extrafield displayed on PDF
153
     * @param array        $moreparams     More parameters. Example: array('css'=>, 'csslist'=>Css on list,
154
     *                                     'cssview'=>...)
155
     *
156
     * @return int                                 Return integer <=0 if KO, >0 if OK
157
     */
158
    public function addExtraField($attrname, $label, $type, $pos, $size, $elementtype, $unique = 0, $required = 0, $default_value = '', $param = '', $alwayseditable = 0, $perms = '', $list = '-1', $help = '', $computed = '', $entity = '', $langfile = '', $enabled = '1', $totalizable = 0, $printable = 0, $moreparams = [])
159
    {
160
        if (empty($attrname)) {
161
            return -1;
162
        }
163
        if (empty($label)) {
164
            return -1;
165
        }
166
167
        $result = 0;
168
169
        if ($type == 'separator' || $type == 'separate') {
170
            $type = 'separate';
171
            $unique = 0;
172
            $required = 0;
173
        }   // Force unique and not required if this is a separator field to avoid troubles.
174
        if ($elementtype == 'thirdparty') {
175
            $elementtype = 'societe';
176
        }
177
        if ($elementtype == 'contact') {
178
            $elementtype = 'socpeople';
179
        }
180
181
        // Create field into database except for separator type which is not stored in database
182
        if ($type != 'separate') {
183
            $result = $this->create($attrname, $type, $size, $elementtype, $unique, $required, $default_value, $param, $perms, $list, $computed, $help, $moreparams);
184
        }
185
        $err1 = $this->errno;
186
        if ($result > 0 || $err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' || $type == 'separate') {
187
            // Add declaration of field into table
188
            $result2 = $this->create_label($attrname, $label, $type, $pos, $size, $elementtype, $unique, $required, $param, $alwayseditable, $perms, $list, $help, $default_value, $computed, $entity, $langfile, $enabled, $totalizable, $printable, $moreparams);
189
            $err2 = $this->errno;
190
            if ($result2 > 0 || ($err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' && $err2 == 'DB_ERROR_RECORD_ALREADY_EXISTS')) {
191
                $this->error = '';
192
                $this->errno = '0';
193
                return 1;
194
            } else {
195
                return -2;
196
            }
197
        } else {
198
            return -1;
199
        }
200
    }
201
202
    /**
203
     *  Add a new optional attribute.
204
     *  This is a private method. For public method, use addExtraField.
205
     *
206
     * @param string $attrname      code of attribute
207
     * @param string $type          Type of attribute ('boolean', 'int', 'varchar', 'text', 'html', 'date', 'datetime',
208
     *                              'price', 'pricecy', 'phone', 'mail', 'password', 'url', 'select', 'checkbox', ...)
209
     * @param string $length        Size/length of attribute ('5', '24,8', ...)
210
     * @param string $elementtype   Element type ('member', 'product', 'thirdparty', 'contact', ...)
211
     * @param int    $unique        Is field unique or not
212
     * @param int    $required      Is field required or not
213
     * @param string $default_value Default value for field (in database)
214
     * @param array  $param         Params for field  (ex for select list : array('options'=>array('value'=>'label of
215
     *                              option'))
216
     * @param string $perms         Permission
217
     * @param string $list          Into list view by default
218
     * @param string $computed      Computed value
219
     * @param string $help          Help on tooltip
220
     * @param array  $moreparams    More parameters. Example: array('css'=>, 'csslist'=>, 'cssview'=>...)
221
     *
222
     * @return int                         Return integer <=0 if KO, >0 if OK
223
     */
224
    private function create($attrname, $type = 'varchar', $length = '255', $elementtype = 'member', $unique = 0, $required = 0, $default_value = '', $param = [], $perms = '', $list = '0', $computed = '', $help = '', $moreparams = [])
225
    {
226
        if ($elementtype == 'thirdparty') {
227
            $elementtype = 'societe';
228
        }
229
        if ($elementtype == 'contact') {
230
            $elementtype = 'socpeople';
231
        }
232
233
        $table = $elementtype . '_extrafields';
234
        if ($elementtype == 'categorie') {
235
            $table = 'categories_extrafields';
236
        }
237
238
        if (!empty($attrname) && preg_match("/^\w[a-zA-Z0-9_]*$/", $attrname) && !is_numeric($attrname)) {
239
            if ($type == 'boolean') {
240
                $typedb = 'int';
241
                $lengthdb = '1';
242
            } elseif ($type == 'price') {
243
                $typedb = 'double';
244
                $lengthdb = '24,8';
245
            } elseif ($type == 'pricecy') {
246
                $typedb = 'varchar';
247
                $lengthdb = '64';
248
            } elseif ($type == 'phone') {
249
                $typedb = 'varchar';
250
                $lengthdb = '20';
251
            } elseif ($type == 'mail' || $type == 'ip' || $type == 'icon') {
252
                $typedb = 'varchar';
253
                $lengthdb = '128';
254
            } elseif ($type == 'url') {
255
                $typedb = 'varchar';
256
                $lengthdb = '255';
257
            } elseif (($type == 'select') || ($type == 'sellist') || ($type == 'radio') || ($type == 'checkbox') || ($type == 'chkbxlst')) {
258
                $typedb = 'varchar';
259
                $lengthdb = '255';
260
            } elseif ($type == 'link') {
261
                $typedb = 'int';
262
                $lengthdb = '11';
263
            } elseif ($type == 'html') {
264
                $typedb = 'text';
265
                $lengthdb = $length;
266
            } elseif ($type == 'password') {
267
                $typedb = 'varchar';
268
                $lengthdb = '128';
269
            } else {
270
                $typedb = $type;
271
                $lengthdb = $length;
272
                if ($type == 'varchar' && empty($lengthdb)) {
273
                    $lengthdb = '255';
274
                }
275
            }
276
            $field_desc = [
277
                'type' => $typedb,
278
                'value' => $lengthdb,
279
                'null' => ($required ? 'NOT NULL' : 'NULL'),
280
                'default' => $default_value,
281
            ];
282
283
            $result = $this->db->DDLAddField($this->db->prefix() . $table, $attrname, $field_desc);
284
            if ($result > 0) {
285
                if ($unique) {
286
                    $sql = "ALTER TABLE " . $this->db->prefix() . $table . " ADD UNIQUE INDEX uk_" . $table . "_" . $attrname . " (" . $attrname . ")";
287
                    $resql = $this->db->query($sql, 1, 'dml');
288
                }
289
                return 1;
290
            } else {
291
                $this->error = $this->db->lasterror();
292
                $this->errno = $this->db->lasterrno();
293
                return -1;
294
            }
295
        } else {
296
            return 0;
297
        }
298
    }
299
300
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
301
302
    /**
303
     *  Add description of a new optional attribute
304
     *
305
     * @param string       $attrname       code of attribute
306
     * @param string       $label          label of attribute
307
     * @param string       $type           Type of attribute ('int', 'varchar', 'text', 'html', 'date', 'datehour',
308
     *                                     'float')
309
     * @param int          $pos            Position of attribute
310
     * @param string       $size           Size/length of attribute ('5', '24,8', ...)
311
     * @param string       $elementtype    Element type ('member', 'product', 'thirdparty', ...)
312
     * @param int          $unique         Is field unique or not
313
     * @param int          $required       Is field required or not
314
     * @param array|string $param          Params for field  (ex for select list : array('options' =>
315
     *                                     array(value'=>'label of option')) )
316
     * @param int          $alwayseditable Is attribute always editable regardless of the document status
317
     * @param string       $perms          Permission to check
318
     * @param string       $list           Visibility
319
     * @param string       $help           Help on tooltip
320
     * @param string       $default        Default value (in database. use the default_value feature for default value
321
     *                                     on screen).
322
     * @param string       $computed       Computed value
323
     * @param string       $entity         Entity of extrafields
324
     * @param string       $langfile       Language file
325
     * @param string       $enabled        Condition to have the field enabled or not
326
     * @param int          $totalizable    Is a measure. Must show a total on lists
327
     * @param int          $printable      Is extrafield displayed on PDF
328
     * @param array        $moreparams     More parameters. Example: array('css'=>, 'csslist'=>, 'cssview'=>...)
329
     *
330
     * @return int                             Return integer <=0 if KO, >0 if OK
331
     * @throws Exception
332
     */
333
    private function create_label($attrname, $label = '', $type = '', $pos = 0, $size = '', $elementtype = 'member', $unique = 0, $required = 0, $param = '', $alwayseditable = 0, $perms = '', $list = '-1', $help = '', $default = '', $computed = '', $entity = '', $langfile = '', $enabled = '1', $totalizable = 0, $printable = 0, $moreparams = [])
334
    {
335
        // phpcs:enable
336
        global $conf, $user;
337
338
        if ($elementtype == 'thirdparty') {
339
            $elementtype = 'societe';
340
        }
341
        if ($elementtype == 'contact') {
342
            $elementtype = 'socpeople';
343
        }
344
345
        // Clean parameters
346
        if (empty($pos)) {
347
            $pos = 0;
348
        }
349
        if (empty($list)) {
350
            $list = '0';
351
        }
352
        if (empty($required)) {
353
            $required = 0;
354
        }
355
        if (empty($unique)) {
356
            $unique = 0;
357
        }
358
        if (empty($printable)) {
359
            $printable = 0;
360
        }
361
        if (empty($alwayseditable)) {
362
            $alwayseditable = 0;
363
        }
364
        if (empty($totalizable)) {
365
            $totalizable = 0;
366
        }
367
368
        $css = '';
369
        if (!empty($moreparams) && !empty($moreparams['css'])) {
370
            $css = $moreparams['css'];
371
        }
372
        $csslist = '';
373
        if (!empty($moreparams) && !empty($moreparams['csslist'])) {
374
            $csslist = $moreparams['csslist'];
375
        }
376
        $cssview = '';
377
        if (!empty($moreparams) && !empty($moreparams['cssview'])) {
378
            $cssview = $moreparams['cssview'];
379
        }
380
381
        if (!empty($attrname) && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname) && !is_numeric($attrname)) {
382
            if (is_array($param) && count($param) > 0) {
383
                $params = serialize($param);
384
            } elseif (strlen($param) > 0) {
385
                $params = trim($param);
386
            } else {
387
                $params = '';
388
            }
389
390
            $sql = "INSERT INTO " . $this->db->prefix() . "extrafields(";
391
            $sql .= " name,";
392
            $sql .= " label,";
393
            $sql .= " type,";
394
            $sql .= " pos,";
395
            $sql .= " size,";
396
            $sql .= " entity,";
397
            $sql .= " elementtype,";
398
            $sql .= " fieldunique,";
399
            $sql .= " fieldrequired,";
400
            $sql .= " param,";
401
            $sql .= " alwayseditable,";
402
            $sql .= " perms,";
403
            $sql .= " langs,";
404
            $sql .= " list,";
405
            $sql .= " printable,";
406
            $sql .= " fielddefault,";
407
            $sql .= " fieldcomputed,";
408
            $sql .= " fk_user_author,";
409
            $sql .= " fk_user_modif,";
410
            $sql .= " datec,";
411
            $sql .= " enabled,";
412
            $sql .= " help,";
413
            $sql .= " totalizable,";
414
            $sql .= " css,";
415
            $sql .= " csslist,";
416
            $sql .= " cssview";
417
            $sql .= " )";
418
            $sql .= " VALUES('" . $this->db->escape($attrname) . "',";
419
            $sql .= " '" . $this->db->escape($label) . "',";
420
            $sql .= " '" . $this->db->escape($type) . "',";
421
            $sql .= " " . ((int) $pos) . ",";
422
            $sql .= " '" . $this->db->escape($size) . "',";
423
            $sql .= " " . ($entity === '' ? $conf->entity : $entity) . ",";
424
            $sql .= " '" . $this->db->escape($elementtype) . "',";
425
            $sql .= " " . ((int) $unique) . ",";
426
            $sql .= " " . ((int) $required) . ",";
427
            $sql .= " '" . $this->db->escape($params) . "',";
428
            $sql .= " " . ((int) $alwayseditable) . ",";
429
            $sql .= " " . ($perms ? "'" . $this->db->escape($perms) . "'" : "null") . ",";
430
            $sql .= " " . ($langfile ? "'" . $this->db->escape($langfile) . "'" : "null") . ",";
431
            $sql .= " '" . $this->db->escape($list) . "',";
432
            $sql .= " '" . $this->db->escape($printable) . "',";
433
            $sql .= " " . ($default ? "'" . $this->db->escape($default) . "'" : "null") . ",";
434
            $sql .= " " . ($computed ? "'" . $this->db->escape($computed) . "'" : "null") . ",";
435
            $sql .= " " . (is_object($user) ? $user->id : 0) . ",";
436
            $sql .= " " . (is_object($user) ? $user->id : 0) . ",";
437
            $sql .= "'" . $this->db->idate(dol_now()) . "',";
438
            $sql .= " " . ($enabled ? "'" . $this->db->escape($enabled) . "'" : "1") . ",";
439
            $sql .= " " . ($help ? "'" . $this->db->escape($help) . "'" : "null") . ",";
440
            $sql .= " " . ($totalizable ? 'TRUE' : 'FALSE') . ",";
441
            $sql .= " " . ($css ? "'" . $this->db->escape($css) . "'" : "null") . ",";
442
            $sql .= " " . ($csslist ? "'" . $this->db->escape($csslist) . "'" : "null") . ",";
443
            $sql .= " " . ($cssview ? "'" . $this->db->escape($cssview) . "'" : "null");
444
            $sql .= ')';
445
446
            dol_syslog(get_class($this) . "::create_label", LOG_DEBUG);
447
            if ($this->db->query($sql)) {
448
                return 1;
449
            } else {
450
                $this->error = $this->db->lasterror();
451
                $this->errno = $this->db->lasterrno();
452
                return -1;
453
            }
454
        }
455
        return -1;
456
    }
457
458
    /**
459
     *  Delete an optional attribute
460
     *
461
     * @param string $attrname    Code of attribute to delete
462
     * @param string $elementtype Element type ('member', 'product', 'thirdparty', 'contact', ...)
463
     *
464
     * @return int                     Return integer < 0 if KO, 0 if nothing is done, 1 if OK
465
     */
466
    public function delete($attrname, $elementtype = 'member')
467
    {
468
        if ($elementtype == 'thirdparty') {
469
            $elementtype = 'societe';
470
        }
471
        if ($elementtype == 'contact') {
472
            $elementtype = 'socpeople';
473
        }
474
475
        $table = $elementtype . '_extrafields';
476
        if ($elementtype == 'categorie') {
477
            $table = 'categories_extrafields';
478
        }
479
480
        $error = 0;
481
482
        if (!empty($attrname) && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
483
            $result = $this->delete_label($attrname, $elementtype);
484
            if ($result < 0) {
485
                $this->error = $this->db->lasterror();
486
                $this->errors[] = $this->db->lasterror();
487
                $error++;
488
            }
489
490
            if (!$error) {
491
                $sql = "SELECT COUNT(rowid) as nb";
492
                $sql .= " FROM " . $this->db->prefix() . "extrafields";
493
                $sql .= " WHERE elementtype = '" . $this->db->escape($elementtype) . "'";
494
                $sql .= " AND name = '" . $this->db->escape($attrname) . "'";
495
                //$sql.= " AND entity IN (0,".$conf->entity.")";      Do not test on entity here. We want to see if there is still on field remaining in other entities before deleting field in table
496
                $resql = $this->db->query($sql);
497
                if ($resql) {
498
                    $obj = $this->db->fetch_object($resql);
499
                    if ($obj->nb <= 0) {
500
                        $result = $this->db->DDLDropField($this->db->prefix() . $table, $attrname); // This also drop the unique key
501
                        if ($result < 0) {
502
                            $this->error = $this->db->lasterror();
503
                            $this->errors[] = $this->db->lasterror();
504
                            $error++;
505
                        }
506
                    }
507
                }
508
            }
509
510
            return $result;
511
        } else {
512
            return 0;
513
        }
514
    }
515
516
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
517
518
    /**
519
     *  Delete description of an optional attribute
520
     *
521
     * @param string $attrname    Code of attribute to delete
522
     * @param string $elementtype Element type ('member', 'product', 'thirdparty', ...)
523
     *
524
     * @return int                         Return integer < 0 if KO, 0 if nothing is done, 1 if OK
525
     */
526
    private function delete_label($attrname, $elementtype = 'member')
527
    {
528
        // phpcs:enable
529
        global $conf;
530
531
        if ($elementtype == 'thirdparty') {
532
            $elementtype = 'societe';
533
        }
534
        if ($elementtype == 'contact') {
535
            $elementtype = 'socpeople';
536
        }
537
538
        if (isset($attrname) && $attrname != '' && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
539
            $sql = "DELETE FROM " . $this->db->prefix() . "extrafields";
540
            $sql .= " WHERE name = '" . $this->db->escape($attrname) . "'";
541
            $sql .= " AND entity IN  (0," . $conf->entity . ')';
542
            $sql .= " AND elementtype = '" . $this->db->escape($elementtype) . "'";
543
544
            dol_syslog(get_class($this) . "::delete_label", LOG_DEBUG);
545
            $resql = $this->db->query($sql);
546
            if ($resql) {
547
                return 1;
548
            } else {
549
                dol_print_error($this->db);
550
                return -1;
551
            }
552
        } else {
553
            return 0;
554
        }
555
    }
556
557
    /**
558
     *  Modify type of a personalized attribute
559
     *
560
     * @param string $attrname       Name of attribute
561
     * @param string $label          Label of attribute
562
     * @param string $type           Type of attribute ('boolean', 'int', 'varchar', 'text', 'html', 'date',
563
     *                               'datetime','price','phone','mail','password','url','select','checkbox', ...)
564
     * @param int    $length         Length of attribute
565
     * @param string $elementtype    Element type ('member', 'product', 'thirdparty', 'contact', ...)
566
     * @param int    $unique         Is field unique or not
567
     * @param int    $required       Is field required or not
568
     * @param int    $pos            Position of attribute
569
     * @param array  $param          Params for field (ex for select list : array('options' => array(value'=>'label of
570
     *                               option')) )
571
     * @param int    $alwayseditable Is attribute always editable regardless of the document status
572
     * @param string $perms          Permission to check
573
     * @param string $list           Visibility
574
     * @param string $help           Help on tooltip
575
     * @param string $default        Default value (in database. use the default_value feature for default value on
576
     *                               screen).
577
     * @param string $computed       Computed value
578
     * @param string $entity         Entity of extrafields
579
     * @param string $langfile       Language file
580
     * @param string $enabled        Condition to have the field enabled or not
581
     * @param int    $totalizable    Is extrafield totalizable on list
582
     * @param int    $printable      Is extrafield displayed on PDF
583
     * @param array  $moreparams     More parameters. Example: array('css'=>, 'csslist'=>, 'cssview'=>...)
584
     *
585
     * @return int                         >0 if OK, <=0 if KO
586
     * @throws Exception
587
     */
588
    public function update($attrname, $label, $type, $length, $elementtype, $unique = 0, $required = 0, $pos = 0, $param = [], $alwayseditable = 0, $perms = '', $list = '', $help = '', $default = '', $computed = '', $entity = '', $langfile = '', $enabled = '1', $totalizable = 0, $printable = 0, $moreparams = [])
589
    {
590
        global $action, $hookmanager;
591
592
        if ($elementtype == 'thirdparty') {
593
            $elementtype = 'societe';
594
        }
595
        if ($elementtype == 'contact') {
596
            $elementtype = 'socpeople';
597
        }
598
599
        $table = $elementtype . '_extrafields';
600
        if ($elementtype == 'categorie') {
601
            $table = 'categories_extrafields';
602
        }
603
604
        if (isset($attrname) && $attrname != '' && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
605
            if ($type == 'boolean') {
606
                $typedb = 'int';
607
                $lengthdb = '1';
608
            } elseif ($type == 'price') {
609
                $typedb = 'double';
610
                $lengthdb = '24,8';
611
            } elseif ($type == 'pricecy') {
612
                $typedb = 'varchar';
613
                $lengthdb = '64';
614
            } elseif ($type == 'phone') {
615
                $typedb = 'varchar';
616
                $lengthdb = '20';
617
            } elseif ($type == 'mail' || $type == 'ip' || $type == 'icon') {
618
                $typedb = 'varchar';
619
                $lengthdb = '128';
620
            } elseif ($type == 'url') {
621
                $typedb = 'varchar';
622
                $lengthdb = '255';
623
            } elseif (($type == 'select') || ($type == 'sellist') || ($type == 'radio') || ($type == 'checkbox') || ($type == 'chkbxlst')) {
624
                $typedb = 'varchar';
625
                $lengthdb = '255';
626
            } elseif ($type == 'html') {
627
                $typedb = 'text';
628
            } elseif ($type == 'link') {
629
                $typedb = 'int';
630
                $lengthdb = '11';
631
            } elseif ($type == 'password') {
632
                $typedb = 'varchar';
633
                $lengthdb = '128';
634
            } else {
635
                $typedb = $type;
636
                $lengthdb = $length;
637
            }
638
            $field_desc = ['type' => $typedb, 'value' => $lengthdb, 'null' => ($required ? 'NOT NULL' : 'NULL'), 'default' => $default];
639
640
            if (is_object($hookmanager)) {
641
                $hookmanager->initHooks(['extrafieldsdao']);
642
                $parameters = ['field_desc' => &$field_desc, 'table' => $table, 'attr_name' => $attrname, 'label' => $label, 'type' => $type, 'length' => $length, 'unique' => $unique, 'required' => $required, 'pos' => $pos, 'param' => $param, 'alwayseditable' => $alwayseditable, 'perms' => $perms, 'list' => $list, 'help' => $help, 'default' => $default, 'computed' => $computed, 'entity' => $entity, 'langfile' => $langfile, 'enabled' => $enabled, 'totalizable' => $totalizable, 'printable' => $printable];
643
                $reshook = $hookmanager->executeHooks('updateExtrafields', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
644
645
                if ($reshook < 0) {
646
                    $this->error = $this->db->lasterror();
647
                    return -1;
648
                }
649
            }
650
651
            if ($type != 'separate') { // No table update when separate type
652
                $result = $this->db->DDLUpdateField($this->db->prefix() . $table, $attrname, $field_desc);
653
            }
654
            if ($result > 0 || $type == 'separate') {
655
                if ($label) {
656
                    $result = $this->update_label($attrname, $label, $type, $length, $elementtype, $unique, $required, $pos, $param, $alwayseditable, $perms, $list, $help, $default, $computed, $entity, $langfile, $enabled, $totalizable, $printable, $moreparams);
657
                }
658
                if ($result > 0) {
659
                    $sql = '';
660
                    if ($unique) {
661
                        $sql = "ALTER TABLE " . $this->db->prefix() . $table . " ADD UNIQUE INDEX uk_" . $table . "_" . $attrname . " (" . $attrname . ")";
662
                    } else {
663
                        $sql = "ALTER TABLE " . $this->db->prefix() . $table . " DROP INDEX IF EXISTS uk_" . $table . "_" . $attrname;
664
                    }
665
                    dol_syslog(get_class($this) . '::update', LOG_DEBUG);
666
                    $resql = $this->db->query($sql, 1, 'dml');
667
                    /*if ($resql < 0) {
668
                        $this->error = $this->db->lasterror();
669
                        return -1;
670
                    }*/
671
                    return 1;
672
                } else {
673
                    $this->error = $this->db->lasterror();
674
                    return -1;
675
                }
676
            } else {
677
                $this->error = $this->db->lasterror();
678
                return -1;
679
            }
680
        } else {
681
            return 0;
682
        }
683
    }
684
685
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
686
687
    /**
688
     *  Modify description of personalized attribute
689
     *
690
     * @param string $attrname       Name of attribute
691
     * @param string $label          Label of attribute
692
     * @param string $type           Type of attribute
693
     * @param int    $size           Length of attribute
694
     * @param string $elementtype    Element type ('member', 'product', 'thirdparty', ...)
695
     * @param int    $unique         Is field unique or not
696
     * @param int    $required       Is field required or not
697
     * @param int    $pos            Position of attribute
698
     * @param array  $param          Params for field  (ex for select list : array('options' => array(value'=>'label of
699
     *                               option')) )
700
     * @param int    $alwayseditable Is attribute always editable regardless of the document status
701
     * @param string $perms          Permission to check
702
     * @param string $list           Visibility
703
     * @param string $help           Help on tooltip.
704
     * @param string $default        Default value (in database. use the default_value feature for default value on
705
     *                               screen).
706
     * @param string $computed       Computed value
707
     * @param string $entity         Entity of extrafields
708
     * @param string $langfile       Language file
709
     * @param string $enabled        Condition to have the field enabled or not
710
     * @param int    $totalizable    Is extrafield totalizable on list
711
     * @param int    $printable      Is extrafield displayed on PDF
712
     * @param array  $moreparams     More parameters. Example: array('css'=>, 'csslist'=>, 'cssview'=>...)
713
     *
714
     * @return int                         Return integer <=0 if KO, >0 if OK
715
     * @throws Exception
716
     */
717
    private function update_label($attrname, $label, $type, $size, $elementtype, $unique = 0, $required = 0, $pos = 0, $param = [], $alwayseditable = 0, $perms = '', $list = '0', $help = '', $default = '', $computed = '', $entity = '', $langfile = '', $enabled = '1', $totalizable = 0, $printable = 0, $moreparams = [])
718
    {
719
        // phpcs:enable
720
        global $conf, $user;
721
        dol_syslog(get_class($this) . "::update_label " . $attrname . ", " . $label . ", " . $type . ", " . $size . ", " . $elementtype . ", " . $unique . ", " . $required . ", " . $pos . ", " . $alwayseditable . ", " . $perms . ", " . $list . ", " . $default . ", " . $computed . ", " . $entity . ", " . $langfile . ", " . $enabled . ", " . $totalizable . ", " . $printable);
722
723
        // Clean parameters
724
        if ($elementtype == 'thirdparty') {
725
            $elementtype = 'societe';
726
        }
727
        if ($elementtype == 'contact') {
728
            $elementtype = 'socpeople';
729
        }
730
731
        if (empty($pos)) {
732
            $pos = 0;
733
        }
734
        if (empty($list)) {
735
            $list = '0';
736
        }
737
        if (empty($totalizable)) {
738
            $totalizable = 0;
739
        }
740
        if (empty($required)) {
741
            $required = 0;
742
        }
743
        if (empty($unique)) {
744
            $unique = 0;
745
        }
746
        if (empty($alwayseditable)) {
747
            $alwayseditable = 0;
748
        }
749
750
        $css = '';
751
        if (!empty($moreparams) && !empty($moreparams['css'])) {
752
            $css = $moreparams['css'];
753
        }
754
        $csslist = '';
755
        if (!empty($moreparams) && !empty($moreparams['csslist'])) {
756
            $csslist = $moreparams['csslist'];
757
        }
758
        $cssview = '';
759
        if (!empty($moreparams) && !empty($moreparams['cssview'])) {
760
            $cssview = $moreparams['cssview'];
761
        }
762
763
        if (isset($attrname) && $attrname != '' && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
764
            $this->db->begin();
765
766
            if (is_array($param) && count($param) > 0) {
767
                $params = serialize($param);
768
            } elseif (is_array($param)) {
769
                $params = '';
770
            } elseif (strlen($param) > 0) {
771
                $params = trim($param);
772
            } else {
773
                $params = '';
774
            }
775
776
            if ($entity === '' || $entity != '0') {
777
                // We don't want on all entities, we delete all and current
778
                $sql_del = "DELETE FROM " . $this->db->prefix() . "extrafields";
779
                $sql_del .= " WHERE name = '" . $this->db->escape($attrname) . "'";
780
                $sql_del .= " AND entity IN (0, " . ($entity === '' ? $conf->entity : $entity) . ")";
781
                $sql_del .= " AND elementtype = '" . $this->db->escape($elementtype) . "'";
782
            } else {
783
                // We want on all entities ($entities = '0'), we delete on all only (we keep setup specific to each entity)
784
                $sql_del = "DELETE FROM " . $this->db->prefix() . "extrafields";
785
                $sql_del .= " WHERE name = '" . $this->db->escape($attrname) . "'";
786
                $sql_del .= " AND entity = 0";
787
                $sql_del .= " AND elementtype = '" . $this->db->escape($elementtype) . "'";
788
            }
789
            $resql1 = $this->db->query($sql_del);
790
791
            $sql = "INSERT INTO " . $this->db->prefix() . "extrafields(";
792
            $sql .= " name,"; // This is code
793
            $sql .= " entity,";
794
            $sql .= " label,";
795
            $sql .= " type,";
796
            $sql .= " size,";
797
            $sql .= " elementtype,";
798
            $sql .= " fieldunique,";
799
            $sql .= " fieldrequired,";
800
            $sql .= " perms,";
801
            $sql .= " langs,";
802
            $sql .= " pos,";
803
            $sql .= " alwayseditable,";
804
            $sql .= " param,";
805
            $sql .= " list,";
806
            $sql .= " printable,";
807
            $sql .= " totalizable,";
808
            $sql .= " fielddefault,";
809
            $sql .= " fieldcomputed,";
810
            $sql .= " fk_user_author,";
811
            $sql .= " fk_user_modif,";
812
            $sql .= " datec,";
813
            $sql .= " enabled,";
814
            $sql .= " help,";
815
            $sql .= " css,";
816
            $sql .= " csslist,";
817
            $sql .= " cssview";
818
            $sql .= ") VALUES (";
819
            $sql .= "'" . $this->db->escape($attrname) . "',";
820
            $sql .= " " . ($entity === '' ? $conf->entity : $entity) . ",";
821
            $sql .= " '" . $this->db->escape($label) . "',";
822
            $sql .= " '" . $this->db->escape($type) . "',";
823
            $sql .= " '" . $this->db->escape($size) . "',";
824
            $sql .= " '" . $this->db->escape($elementtype) . "',";
825
            $sql .= " " . $unique . ",";
826
            $sql .= " " . $required . ",";
827
            $sql .= " " . ($perms ? "'" . $this->db->escape($perms) . "'" : "null") . ",";
828
            $sql .= " " . ($langfile ? "'" . $this->db->escape($langfile) . "'" : "null") . ",";
829
            $sql .= " " . $pos . ",";
830
            $sql .= " '" . $this->db->escape($alwayseditable) . "',";
831
            $sql .= " '" . $this->db->escape($params) . "',";
832
            $sql .= " '" . $this->db->escape($list) . "', ";
833
            $sql .= " '" . $this->db->escape($printable) . "', ";
834
            $sql .= " " . ($totalizable ? 'TRUE' : 'FALSE') . ",";
835
            $sql .= " " . (($default != '') ? "'" . $this->db->escape($default) . "'" : "null") . ",";
836
            $sql .= " " . ($computed ? "'" . $this->db->escape($computed) . "'" : "null") . ",";
837
            $sql .= " " . $user->id . ",";
838
            $sql .= " " . $user->id . ",";
839
            $sql .= "'" . $this->db->idate(dol_now()) . "',";
840
            $sql .= "'" . $this->db->escape($enabled) . "',";
841
            $sql .= " " . ($help ? "'" . $this->db->escape($help) . "'" : "null") . ",";
842
            $sql .= " " . ($css ? "'" . $this->db->escape($css) . "'" : "null") . ",";
843
            $sql .= " " . ($csslist ? "'" . $this->db->escape($csslist) . "'" : "null") . ",";
844
            $sql .= " " . ($cssview ? "'" . $this->db->escape($cssview) . "'" : "null");
845
            $sql .= ")";
846
847
            $resql2 = $this->db->query($sql);
848
849
            if ($resql1 && $resql2) {
850
                $this->db->commit();
851
                return 1;
852
            } else {
853
                $this->db->rollback();
854
                dol_print_error($this->db);
855
                return -1;
856
            }
857
        } else {
858
            return 0;
859
        }
860
    }
861
862
863
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
864
865
    /**
866
     *  Load the array of extrafields definition $this->attributes
867
     *
868
     * @param string  $elementtype Type of element ('all' = all or $object->table_element like 'adherent', 'commande',
869
     *                             'thirdparty', 'facture', 'propal', 'product', ...).
870
     * @param boolean $forceload   Force load of extra fields whatever is status of cache.
871
     *
872
     * @return array                           Array of attributes keys+label for all extra fields.
873
     */
874
    public function fetch_name_optionals_label($elementtype, $forceload = false)
875
    {
876
        // phpcs:enable
877
        global $conf;
878
879
        if (empty($elementtype)) {
880
            return [];
881
        }
882
883
        if ($elementtype == 'thirdparty') {
884
            $elementtype = 'societe';
885
        }
886
        if ($elementtype == 'contact') {
887
            $elementtype = 'socpeople';
888
        }
889
        if ($elementtype == 'order_supplier') {
890
            $elementtype = 'commande_fournisseur';
891
        }
892
893
        // Test cache $this->attributes[$elementtype]['loaded'] to see if we must do something
894
        // TODO
895
896
        $array_name_label = [];
897
898
        // We should not have several time this request. If we have, there is some optimization to do by calling a simple $extrafields->fetch_optionals() in top of code and not into subcode
899
        $sql = "SELECT rowid, name, label, type, size, elementtype, fieldunique, fieldrequired, param, pos, alwayseditable, perms, langs, list, printable, totalizable, fielddefault, fieldcomputed, entity, enabled, help,";
900
        $sql .= " css, cssview, csslist";
901
        $sql .= " FROM " . $this->db->prefix() . "extrafields";
902
        //$sql.= " WHERE entity IN (0,".$conf->entity.")";    // Filter is done later
903
        if ($elementtype && $elementtype != 'all') {
904
            $sql .= " WHERE elementtype = '" . $this->db->escape($elementtype) . "'"; // Filed with object->table_element
905
        }
906
        $sql .= " ORDER BY pos";
907
908
        $resql = $this->db->query($sql);
909
        if ($resql) {
910
            $count = 0;
911
            if ($this->db->num_rows($resql)) {
912
                while ($tab = $this->db->fetch_object($resql)) {
913
                    if ($tab->entity != 0 && $tab->entity != $conf->entity) {
914
                        // This field is not in current entity. We discard but before we save it into the array of mandatory fields if it is a mandatory field without default value
915
                        if ($tab->fieldrequired && is_null($tab->fielddefault)) {
916
                            $this->attributes[$tab->elementtype]['mandatoryfieldsofotherentities'][$tab->name] = $tab->type;
917
                        }
918
                        continue;
919
                    }
920
921
                    // We can add this attribute to object. TODO Remove this and return $this->attributes[$elementtype]['label']
922
                    if ($tab->type != 'separate') {
923
                        $array_name_label[$tab->name] = $tab->label;
924
                    }
925
926
                    $this->attributes[$tab->elementtype]['type'][$tab->name] = $tab->type;
927
                    $this->attributes[$tab->elementtype]['label'][$tab->name] = $tab->label;
928
                    $this->attributes[$tab->elementtype]['size'][$tab->name] = $tab->size;
929
                    $this->attributes[$tab->elementtype]['elementtype'][$tab->name] = $tab->elementtype;
930
                    $this->attributes[$tab->elementtype]['default'][$tab->name] = $tab->fielddefault;
931
                    $this->attributes[$tab->elementtype]['computed'][$tab->name] = $tab->fieldcomputed;
932
                    $this->attributes[$tab->elementtype]['unique'][$tab->name] = $tab->fieldunique;
933
                    $this->attributes[$tab->elementtype]['required'][$tab->name] = $tab->fieldrequired;
934
                    $this->attributes[$tab->elementtype]['param'][$tab->name] = ($tab->param ? jsonOrUnserialize($tab->param) : '');
935
                    $this->attributes[$tab->elementtype]['pos'][$tab->name] = $tab->pos;
936
                    $this->attributes[$tab->elementtype]['alwayseditable'][$tab->name] = $tab->alwayseditable;
937
                    $this->attributes[$tab->elementtype]['perms'][$tab->name] = ((is_null($tab->perms) || strlen($tab->perms) == 0) ? 1 : $tab->perms);
938
                    $this->attributes[$tab->elementtype]['langfile'][$tab->name] = $tab->langs;
939
                    $this->attributes[$tab->elementtype]['list'][$tab->name] = $tab->list;
940
                    $this->attributes[$tab->elementtype]['printable'][$tab->name] = $tab->printable;
941
                    $this->attributes[$tab->elementtype]['totalizable'][$tab->name] = ($tab->totalizable ? 1 : 0);
942
                    $this->attributes[$tab->elementtype]['entityid'][$tab->name] = $tab->entity;
943
                    $this->attributes[$tab->elementtype]['enabled'][$tab->name] = $tab->enabled;
944
                    $this->attributes[$tab->elementtype]['help'][$tab->name] = $tab->help;
945
                    $this->attributes[$tab->elementtype]['css'][$tab->name] = $tab->css;
946
                    $this->attributes[$tab->elementtype]['cssview'][$tab->name] = $tab->cssview;
947
                    $this->attributes[$tab->elementtype]['csslist'][$tab->name] = $tab->csslist;
948
949
                    $this->attributes[$tab->elementtype]['loaded'] = 1;
950
                    $count++;
951
                }
952
            }
953
            if ($elementtype) {
954
                $this->attributes[$elementtype]['loaded'] = 1; // Note: If nothing is found, we also set the key 'loaded' to 1.
955
                $this->attributes[$elementtype]['count'] = $count;
956
            }
957
        } else {
958
            $this->error = $this->db->lasterror();
959
            dol_syslog(get_class($this) . "::fetch_name_optionals_label " . $this->error, LOG_ERR);
960
        }
961
962
        return $array_name_label;
963
    }
964
965
966
    /**
967
     * Return HTML string to put an input field into a page
968
     * Code very similar with showInputField of common object
969
     *
970
     * @param string       $key                  Key of attribute
971
     * @param string|array $value                Preselected value to show (for date type it must be in timestamp
972
     *                                           format, for amount or price it must be a php numeric value); for dates
973
     *                                           in filter mode, a range array('start'=><timestamp>,
974
     *                                           'end'=><timestamp>) should be provided
975
     * @param string       $moreparam            To add more parameters on html input tag
976
     * @param string       $keysuffix            Prefix string to add after name and id of field (can be used to avoid
977
     *                                           duplicate names)
978
     * @param string       $keyprefix            Suffix string to add before name and id of field (can be used to avoid
979
     *                                           duplicate names)
980
     * @param string       $morecss              More css (to defined size of field. Old behaviour: may also be a
981
     *                                           numeric)
982
     * @param int          $objectid             Current object id
983
     * @param string       $extrafieldsobjectkey The key to use to store retrieved data (commonly
984
     *                                           $object->table_element)
985
     * @param int          $mode                 1=Used for search filters
986
     *
987
     * @return string
988
     */
989
    public function showInputField($key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '', $objectid = 0, $extrafieldsobjectkey = '', $mode = 0)
990
    {
991
        global $conf, $langs, $form;
992
993
        if (!is_object($form)) {
994
            $form = new Form($this->db);
995
        }
996
997
        $out = '';
998
999
        if (!preg_match('/options_$/', $keyprefix)) {   // Because we work on extrafields, we add 'options_' to prefix if not already added
1000
            $keyprefix = $keyprefix . 'options_';
1001
        }
1002
1003
        if (empty($extrafieldsobjectkey)) {
1004
            dol_syslog(get_class($this) . '::showInputField extrafieldsobjectkey required', LOG_ERR);
1005
            return 'BadValueForParamExtraFieldsObjectKey';
1006
        }
1007
1008
        $label = $this->attributes[$extrafieldsobjectkey]['label'][$key];
1009
        $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
1010
        $size = $this->attributes[$extrafieldsobjectkey]['size'][$key];
1011
        $default = $this->attributes[$extrafieldsobjectkey]['default'][$key];
1012
        $computed = $this->attributes[$extrafieldsobjectkey]['computed'][$key];
1013
        $unique = $this->attributes[$extrafieldsobjectkey]['unique'][$key];
1014
        $required = $this->attributes[$extrafieldsobjectkey]['required'][$key];
1015
        $param = $this->attributes[$extrafieldsobjectkey]['param'][$key];
1016
        $perms = (int) dol_eval($this->attributes[$extrafieldsobjectkey]['perms'][$key], 1, 1, '2');
1017
        $langfile = $this->attributes[$extrafieldsobjectkey]['langfile'][$key];
1018
        $list = (string) dol_eval($this->attributes[$extrafieldsobjectkey]['list'][$key], 1, 1, '2');
1019
        $totalizable = $this->attributes[$extrafieldsobjectkey]['totalizable'][$key];
1020
        $help = $this->attributes[$extrafieldsobjectkey]['help'][$key];
1021
        $hidden = (empty($list) ? 1 : 0); // If empty, we are sure it is hidden, otherwise we show. If it depends on mode (view/create/edit form or list, this must be filtered by caller)
1022
1023
        if ($computed) {
1024
            if (!preg_match('/^search_/', $keyprefix)) {
1025
                return '<span class="opacitymedium">' . $langs->trans("AutomaticallyCalculated") . '</span>';
1026
            } else {
1027
                return '';
1028
            }
1029
        }
1030
1031
        //
1032
        // 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'css'=>'minwidth300 maxwidth500 widthcentpercentminusx', 'cssview'=>'wordbreak', 'csslist'=>'tdoverflowmax200'
1033
        if (empty($morecss)) {
1034
            // Add automatic css
1035
            if ($type == 'date') {
1036
                $morecss = 'minwidth100imp';
1037
            } elseif ($type == 'datetime' || $type == 'datetimegmt' || $type == 'link') {
1038
                $morecss = 'minwidth200imp';
1039
            } elseif (in_array($type, ['int', 'integer', 'double', 'price'])) {
1040
                $morecss = 'maxwidth75';
1041
            } elseif ($type == 'password') {
1042
                $morecss = 'maxwidth100';
1043
            } elseif ($type == 'url') {
1044
                $morecss = 'minwidth400';
1045
            } elseif ($type == 'boolean') {
1046
                $morecss = '';
1047
            } elseif ($type == 'radio') {
1048
                $morecss = 'width25';
1049
            } else {
1050
                if (empty($size) || round((float) $size) < 12) {
1051
                    $morecss = 'minwidth100';
1052
                } elseif (round((float) $size) <= 48) {
1053
                    $morecss = 'minwidth200';
1054
                } else {
1055
                    $morecss = 'minwidth400';
1056
                }
1057
            }
1058
            // If css forced in attribute, we use this one
1059
            if (!empty($this->attributes[$extrafieldsobjectkey]['css'][$key])) {
1060
                $morecss = $this->attributes[$extrafieldsobjectkey]['css'][$key];
1061
            }
1062
        }
1063
1064
        if (in_array($type, ['date'])) {
1065
            $tmp = explode(',', $size);
1066
            $newsize = $tmp[0];
1067
            $showtime = 0;
1068
1069
            // Do not show current date when field not required (see selectDate() method)
1070
            if (!$required && $value == '') {
1071
                $value = '-1';
1072
            }
1073
1074
            if ($mode == 1) {
1075
                // search filter on a date extrafield shows two inputs to select a date range
1076
                $prefill = [
1077
                    'start' => isset($value['start']) ? $value['start'] : '',
1078
                    'end' => isset($value['end']) ? $value['end'] : '',
1079
                ];
1080
                $out = '<div ' . ($moreparam ? $moreparam : '') . '><div class="nowrap">';
1081
                $out .= $form->selectDate($prefill['start'], $keyprefix . $key . $keysuffix . '_start', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("From"));
1082
                $out .= '</div><div class="nowrap">';
1083
                $out .= $form->selectDate($prefill['end'], $keyprefix . $key . $keysuffix . '_end', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"));
1084
                $out .= '</div></div>';
1085
            } else {
1086
                // TODO Must also support $moreparam
1087
                $out = $form->selectDate($value, $keyprefix . $key . $keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
1088
            }
1089
        } elseif (in_array($type, ['datetime', 'datetimegmt'])) {
1090
            $tmp = explode(',', $size);
1091
            $newsize = $tmp[0];
1092
            $showtime = 1;
1093
1094
            // Do not show current date when field not required (see selectDate() method)
1095
            if (!$required && $value == '') {
1096
                $value = '-1';
1097
            }
1098
1099
            if ($mode == 1) {
1100
                // search filter on a date extrafield shows two inputs to select a date range
1101
                $prefill = [
1102
                    'start' => isset($value['start']) ? $value['start'] : '',
1103
                    'end' => isset($value['end']) ? $value['end'] : '',
1104
                ];
1105
                $out = '<div ' . ($moreparam ? $moreparam : '') . '><div class="nowrap">';
1106
                $out .= $form->selectDate($prefill['start'], $keyprefix . $key . $keysuffix . '_start', 1, 1, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("From"), 'tzuserrel');
1107
                $out .= '</div><div class="nowrap">';
1108
                $out .= $form->selectDate($prefill['end'], $keyprefix . $key . $keysuffix . '_end', 1, 1, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
1109
                $out .= '</div></div>';
1110
            } else {
1111
                // TODO Must also support $moreparam
1112
                $out = $form->selectDate($value, $keyprefix . $key . $keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
1113
            }
1114
        } elseif (in_array($type, ['int', 'integer'])) {
1115
            $tmp = explode(',', $size);
1116
            $newsize = $tmp[0];
1117
            $out = '<input type="text" class="flat ' . $morecss . ' maxwidthonsmartphone" name="' . $keyprefix . $key . $keysuffix . '" id="' . $keyprefix . $key . $keysuffix . '" maxlength="' . $newsize . '" value="' . dol_escape_htmltag($value) . '"' . ($moreparam ? $moreparam : '') . '>';
1118
        } elseif (preg_match('/varchar/', $type)) {
1119
            $out = '<input type="text" class="flat ' . $morecss . ' maxwidthonsmartphone" name="' . $keyprefix . $key . $keysuffix . '" id="' . $keyprefix . $key . $keysuffix . '" maxlength="' . $size . '" value="' . dol_escape_htmltag($value) . '"' . ($moreparam ? $moreparam : '') . '>';
1120
        } elseif (in_array($type, ['mail', 'ip', 'phone', 'url'])) {
1121
            $out = '<input type="text" class="flat ' . $morecss . ' maxwidthonsmartphone" name="' . $keyprefix . $key . $keysuffix . '" id="' . $keyprefix . $key . $keysuffix . '" value="' . dol_escape_htmltag($value) . '" ' . ($moreparam ? $moreparam : '') . '>';
1122
        } elseif ($type == 'icon') {
1123
            /* External lib inclusion are not allowed in backoffice. Also lib is included several time if there is several icon file.
1124
            Some code must be added into main when MAIN_ADD_ICONPICKER_JS is set to add of lib in html header
1125
            $out ='<link rel="stylesheet" href="'.dol_buildpath('/myfield/css/fontawesome-iconpicker.min.css', 1).'">';
1126
            $out.='<script src="'.dol_buildpath('/myfield/js/fontawesome-iconpicker.min.js', 1).'"></script>';
1127
            */
1128
            $out .= '<input type="text" class="form-control icp icp-auto iconpicker-element iconpicker-input flat ' . $morecss . ' maxwidthonsmartphone"';
1129
            $out .= ' name="' . $keyprefix . $key . $keysuffix . '" id="' . $keyprefix . $key . $keysuffix . '" value="' . dol_escape_htmltag($value) . '" ' . ($moreparam ? $moreparam : '') . '>';
1130
            if (getDolGlobalInt('MAIN_ADD_ICONPICKER_JS')) {
1131
                $out .= '<script>';
1132
                $options = "{ title: '<b>" . $langs->trans("IconFieldSelector") . "</b>', placement: 'right', showFooter: false, templates: {";
1133
                $options .= "iconpicker: '<div class=\"iconpicker\"><div style=\"background-color:#EFEFEF;\" class=\"iconpicker-items\"></div></div>',";
1134
                $options .= "iconpickerItem: '<a role=\"button\" href=\"#\" class=\"iconpicker-item\" style=\"background-color:#DDDDDD;\"><i></i></a>',";
1135
                // $options.="buttons: '<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-cancel btn btn-default btn-sm\">".$langs->trans("Cancel")."</button>";
1136
                // $options.="<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-accept btn btn-primary btn-sm\">".$langs->trans("Save")."</button>',";
1137
                $options .= "footer: '<div class=\"popover-footer\" style=\"background-color:#EFEFEF;\"></div>',";
1138
                $options .= "search: '<input type=\"search\" class\"form-control iconpicker-search\" placeholder=\"" . $langs->trans("TypeToFilter") . "\" />',";
1139
                $options .= "popover: '<div class=\"iconpicker-popover popover\">";
1140
                $options .= "   <div class=\"arrow\" ></div>";
1141
                $options .= "   <div class=\"popover-title\" style=\"text-align:center;background-color:#EFEFEF;\"></div>";
1142
                $options .= "   <div class=\"popover-content \" ></div>";
1143
                $options .= "</div>'}}";
1144
                $out .= "$('#" . $keyprefix . $key . $keysuffix . "').iconpicker(" . $options . ");";
1145
                $out .= '</script>';
1146
            }
1147
        } elseif ($type == 'text') {
1148
            if (!preg_match('/search_/', $keyprefix)) {     // If keyprefix is search_ or search_options_, we must just use a simple text field
1149
                require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
1150
                $doleditor = new DolEditor($keyprefix . $key . $keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
0 ignored issues
show
Bug introduced by
The type DoliCore\Lib\DolEditor was not found. Did you mean DolEditor? If so, make sure to prefix the type with \.
Loading history...
1151
                $out = $doleditor->Create(1);
1152
            } else {
1153
                $out = '<input type="text" class="flat ' . $morecss . ' maxwidthonsmartphone" name="' . $keyprefix . $key . $keysuffix . '" id="' . $keyprefix . $key . $keysuffix . '" value="' . dol_escape_htmltag($value) . '" ' . ($moreparam ? $moreparam : '') . '>';
1154
            }
1155
        } elseif ($type == 'html') {
1156
            if (!preg_match('/search_/', $keyprefix)) {     // If keyprefix is search_ or search_options_, we must just use a simple text field
1157
                require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
1158
                $doleditor = new DolEditor($keyprefix . $key . $keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_5, '90%');
1159
                $out = $doleditor->Create(1);
1160
            } else {
1161
                $out = '<input type="text" class="flat ' . $morecss . ' maxwidthonsmartphone" name="' . $keyprefix . $key . $keysuffix . '" id="' . $keyprefix . $key . $keysuffix . '" value="' . dol_escape_htmltag($value) . '" ' . ($moreparam ? $moreparam : '') . '>';
1162
            }
1163
        } elseif ($type == 'boolean') {
1164
            if (empty($mode)) {
1165
                $checked = '';
1166
                if (!empty($value)) {
1167
                    $checked = ' checked value="1" ';
1168
                } else {
1169
                    $checked = ' value="1" ';
1170
                }
1171
                $out = '<input type="checkbox" class="flat valignmiddle' . ($morecss ? ' ' . $morecss : '') . ' maxwidthonsmartphone" name="' . $keyprefix . $key . $keysuffix . '" id="' . $keyprefix . $key . $keysuffix . '" ' . $checked . ' ' . ($moreparam ? $moreparam : '') . '>';
1172
            } else {
1173
                $out = $form->selectyesno($keyprefix . $key . $keysuffix, $value, 1, false, 1);
1174
            }
1175
            $out .= '<input type="hidden" name="' . $keyprefix . $key . $keysuffix . '_boolean" value="1">';    // A hidden field ending with "_boolean" that is always set to 1.
1176
        } elseif ($type == 'price') {
1177
            if (!empty($value)) {       // $value in memory is a php numeric, we format it into user number format.
1178
                $value = price($value);
1179
            }
1180
            $out = '<input type="text" class="flat ' . $morecss . ' maxwidthonsmartphone" name="' . $keyprefix . $key . $keysuffix . '" id="' . $keyprefix . $key . $keysuffix . '" value="' . $value . '" ' . ($moreparam ? $moreparam : '') . '> ' . $langs->getCurrencySymbol($conf->currency);
1181
        } elseif ($type == 'pricecy') {
1182
            $currency = $conf->currency;
1183
            if (!empty($value)) {
1184
                // $value in memory is a php string like '10.01:USD'
1185
                $pricetmp = explode(':', $value);
1186
                $currency = !empty($pricetmp[1]) ? $pricetmp[1] : $conf->currency;
1187
                $value = price($pricetmp[0]);
1188
            }
1189
            $out = '<input type="text" class="flat ' . $morecss . ' maxwidthonsmartphone" name="' . $keyprefix . $key . $keysuffix . '" id="' . $keyprefix . $key . $keysuffix . '" value="' . $value . '" ' . ($moreparam ? $moreparam : '') . '> ';
1190
            $out .= $form->selectCurrency($currency, $keyprefix . $key . $keysuffix . 'currency_id');
1191
        } elseif ($type == 'double') {
1192
            if (!empty($value)) {       // $value in memory is a php numeric, we format it into user number format.
1193
                $value = price($value);
1194
            }
1195
            $out = '<input type="text" class="flat ' . $morecss . ' maxwidthonsmartphone" name="' . $keyprefix . $key . $keysuffix . '" id="' . $keyprefix . $key . $keysuffix . '" value="' . $value . '" ' . ($moreparam ? $moreparam : '') . '> ';
1196
        } elseif ($type == 'select') {
1197
            $out = '';  // @phan-suppress-current-line PhanPluginRedundantAssignment
1198
            if ($mode) {
1199
                $options = [];
1200
                foreach ($param['options'] as $okey => $val) {
1201
                    if ((string) $okey == '') {
1202
                        continue;
1203
                    }
1204
1205
                    $valarray = explode('|', $val);
1206
                    $val = $valarray[0];
1207
1208
                    if ($langfile && $val) {
1209
                        $options[$okey] = $langs->trans($val);
1210
                    } else {
1211
                        $options[$okey] = $val;
1212
                    }
1213
                }
1214
                $selected = [];
1215
                if (!is_array($value)) {
1216
                    $selected = explode(',', $value);
1217
                }
1218
1219
                $out .= $form->multiselectarray($keyprefix . $key . $keysuffix, $options, $selected, 0, 0, $morecss, 0, 0, '', '', '', !empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2'));
1220
            } else {
1221
                if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
1222
                    include_once BASE_PATH . '/../Dolibarr/Lib/Ajax.php';
1223
                    $out .= ajax_combobox($keyprefix . $key . $keysuffix, [], 0);
1224
                }
1225
1226
                $out .= '<select class="flat ' . $morecss . ' maxwidthonsmartphone" name="' . $keyprefix . $key . $keysuffix . '" id="' . $keyprefix . $key . $keysuffix . '" ' . ($moreparam ? $moreparam : '') . '>';
1227
                $out .= '<option value="0">&nbsp;</option>';
1228
                foreach ($param['options'] as $key2 => $val2) {
1229
                    if ((string) $key2 == '') {
1230
                        continue;
1231
                    }
1232
                    $valarray = explode('|', $val2);
1233
                    $val2 = $valarray[0];
1234
                    $parent = '';
1235
                    if (!empty($valarray[1])) {
1236
                        $parent = $valarray[1];
1237
                    }
1238
                    $out .= '<option value="' . $key2 . '"';
1239
                    $out .= (((string) $value == (string) $key2) ? ' selected' : '');
1240
                    $out .= (!empty($parent) ? ' parent="' . $parent . '"' : '');
1241
                    $out .= '>';
1242
                    if ($langfile && $val2) {
1243
                        $out .= $langs->trans($val2);
1244
                    } else {
1245
                        $out .= $val2;
1246
                    }
1247
                    $out .= '</option>';
1248
                }
1249
                $out .= '</select>';
1250
            }
1251
        } elseif ($type == 'sellist') {
1252
            $out = '';  // @phan-suppress-current-line PhanPluginRedundantAssignment
1253
            if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
1254
                include_once BASE_PATH . '/../Dolibarr/Lib/Ajax.php';
1255
                $out .= ajax_combobox($keyprefix . $key . $keysuffix, [], 0);
1256
            }
1257
1258
            $out .= '<select class="flat ' . $morecss . ' maxwidthonsmartphone" name="' . $keyprefix . $key . $keysuffix . '" id="' . $keyprefix . $key . $keysuffix . '" ' . ($moreparam ? $moreparam : '') . '>';
1259
            if (is_array($param['options'])) {
1260
                $param_list = array_keys($param['options']);
1261
                $InfoFieldList = explode(":", $param_list[0]);
1262
                $parentName = '';
1263
                $parentField = '';
1264
                // 0 : tableName
1265
                // 1 : label field name
1266
                // 2 : key fields name (if differ of rowid)
1267
                // 3 : key field parent (for dependent lists)
1268
                // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
1269
                // 5 : id category type
1270
                // 6 : ids categories list separated by comma for category root
1271
                // 7 : sort by (to be close to common object)
1272
                $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2] . ' as rowid');
1273
1274
1275
                if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
1276
                    if (strpos($InfoFieldList[4], 'extra.') !== false) {
1277
                        $keyList = 'main.' . $InfoFieldList[2] . ' as rowid';
1278
                    } else {
1279
                        $keyList = $InfoFieldList[2] . ' as rowid';
1280
                    }
1281
                }
1282
                if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
1283
                    [$parentName, $parentField] = explode('|', $InfoFieldList[3]);
1284
                    $keyList .= ', ' . $parentField;
1285
                }
1286
1287
                $filter_categorie = false;
1288
                if (count($InfoFieldList) > 5) {
1289
                    if ($InfoFieldList[0] == 'categorie') {
1290
                        $filter_categorie = true;
1291
                    }
1292
                }
1293
1294
                if ($filter_categorie === false) {
1295
                    $fields_label = explode('|', $InfoFieldList[1]);
1296
                    if (is_array($fields_label)) {
1297
                        $keyList .= ', ';
1298
                        $keyList .= implode(', ', $fields_label);
1299
                    }
1300
1301
                    $sqlwhere = '';
1302
                    $sql = "SELECT " . $keyList;
1303
                    $sql .= ' FROM ' . $this->db->prefix() . $InfoFieldList[0];
1304
                    if (!empty($InfoFieldList[4])) {
1305
                        // can use current entity filter
1306
                        if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
1307
                            $InfoFieldList[4] = str_replace('$ENTITY$', $conf->entity, $InfoFieldList[4]);
1308
                        }
1309
                        // can use SELECT request
1310
                        if (strpos($InfoFieldList[4], '$SEL$') !== false) {
1311
                            $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
1312
                        }
1313
1314
                        // current object id can be use into filter
1315
                        if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
1316
                            $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
1317
                        } else {
1318
                            $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
1319
                        }
1320
                        //We have to join on extrafield table
1321
                        if (strpos($InfoFieldList[4], 'extra.') !== false) {
1322
                            $sql .= ' as main, ' . $this->db->prefix() . $InfoFieldList[0] . '_extrafields as extra';
1323
                            $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2] . " AND " . $InfoFieldList[4];
1324
                        } else {
1325
                            $sqlwhere .= " WHERE " . $InfoFieldList[4];
1326
                        }
1327
                    } else {
1328
                        $sqlwhere .= ' WHERE 1=1';
1329
                    }
1330
                    // Some tables may have field, some other not. For the moment we disable it.
1331
                    if (in_array($InfoFieldList[0], ['tablewithentity'])) {
1332
                        $sqlwhere .= ' AND entity = ' . ((int) $conf->entity);
1333
                    }
1334
                    $sql .= $sqlwhere;
1335
                    //print $sql;
1336
1337
                    $sql .= ' ORDER BY ' . implode(', ', $fields_label);
1338
1339
                    dol_syslog(get_class($this) . '::showInputField type=sellist', LOG_DEBUG);
1340
                    $resql = $this->db->query($sql);
1341
                    if ($resql) {
1342
                        $out .= '<option value="0">&nbsp;</option>';
1343
                        $num = $this->db->num_rows($resql);
1344
                        $i = 0;
1345
                        while ($i < $num) {
1346
                            $labeltoshow = '';
1347
                            $obj = $this->db->fetch_object($resql);
1348
1349
                            // Several field into label (eq table:code|label:rowid)
1350
                            $notrans = false;
1351
                            $fields_label = explode('|', $InfoFieldList[1]);
1352
                            if (is_array($fields_label) && count($fields_label) > 1) {
1353
                                $notrans = true;
1354
                                foreach ($fields_label as $field_toshow) {
1355
                                    $labeltoshow .= $obj->$field_toshow . ' ';
1356
                                }
1357
                            } else {
1358
                                $labeltoshow = $obj->{$InfoFieldList[1]};
1359
                            }
1360
1361
                            if ($value == $obj->rowid) {
1362
                                if (!$notrans) {
1363
                                    foreach ($fields_label as $field_toshow) {
1364
                                        $translabel = $langs->trans($obj->$field_toshow);
1365
                                        $labeltoshow = $translabel . ' ';
1366
                                    }
1367
                                }
1368
                                $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
1369
                            } else {
1370
                                if (!$notrans) {
1371
                                    $translabel = $langs->trans($obj->{$InfoFieldList[1]});
1372
                                    $labeltoshow = $translabel;
1373
                                }
1374
                                if (empty($labeltoshow)) {
1375
                                    $labeltoshow = '(not defined)';
1376
                                }
1377
1378
                                if (!empty($InfoFieldList[3]) && $parentField) {
1379
                                    $parent = $parentName . ':' . $obj->{$parentField};
1380
                                }
1381
1382
                                $out .= '<option value="' . $obj->rowid . '"';
1383
                                $out .= ($value == $obj->rowid ? ' selected' : '');
1384
                                $out .= (!empty($parent) ? ' parent="' . $parent . '"' : '');
1385
                                $out .= '>' . $labeltoshow . '</option>';
1386
                            }
1387
1388
                            $i++;
1389
                        }
1390
                        $this->db->free($resql);
1391
                    } else {
1392
                        print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
1393
                    }
1394
                } else {
1395
                    $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
1396
                    $out .= '<option value="0">&nbsp;</option>';
1397
                    if (is_array($data)) {
1398
                        foreach ($data as $data_key => $data_value) {
1399
                            $out .= '<option value="' . $data_key . '"';
1400
                            $out .= ($value == $data_key ? ' selected' : '');
1401
                            $out .= '>' . $data_value . '</option>';
1402
                        }
1403
                    }
1404
                }
1405
            }
1406
            $out .= '</select>';
1407
        } elseif ($type == 'checkbox') {
1408
            $value_arr = $value;
1409
            if (!is_array($value)) {
1410
                $value_arr = explode(',', $value);
1411
            }
1412
            $out = $form->multiselectarray($keyprefix . $key . $keysuffix, (empty($param['options']) ? null : $param['options']), $value_arr, '', 0, '', 0, '100%');
1413
        } elseif ($type == 'radio') {
1414
            $out = '';  // @phan-suppress-current-line PhanPluginRedundantAssignment
1415
            foreach ($param['options'] as $keyopt => $val) {
1416
                $out .= '<input class="flat ' . $morecss . '" type="radio" name="' . $keyprefix . $key . $keysuffix . '" id="' . $keyprefix . $key . $keysuffix . '" ' . ($moreparam ? $moreparam : '');
1417
                $out .= ' value="' . $keyopt . '"';
1418
                $out .= ' id="' . $keyprefix . $key . $keysuffix . '_' . $keyopt . '"';
1419
                $out .= ($value == $keyopt ? 'checked' : '');
1420
                $out .= '/><label for="' . $keyprefix . $key . $keysuffix . '_' . $keyopt . '">' . $langs->trans($val) . '</label><br>';
1421
            }
1422
        } elseif ($type == 'chkbxlst') {
1423
            if (is_array($value)) {
1424
                $value_arr = $value;
1425
            } else {
1426
                $value_arr = explode(',', $value);
1427
            }
1428
1429
            if (is_array($param['options'])) {
1430
                $param_list = array_keys($param['options']);
1431
                $InfoFieldList = explode(":", $param_list[0]);
1432
                $parentName = '';
1433
                $parentField = '';
1434
                // 0 : tableName
1435
                // 1 : label field name
1436
                // 2 : key fields name (if differ of rowid)
1437
                // 3 : key field parent (for dependent lists)
1438
                // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
1439
                // 5 : id category type
1440
                // 6 : ids categories list separated by comma for category root
1441
                // 7 : sort by (to be close to common object)
1442
                $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2] . ' as rowid');
1443
1444
                if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
1445
                    [$parentName, $parentField] = explode('|', $InfoFieldList[3]);
1446
                    $keyList .= ', ' . $parentField;
1447
                }
1448
                if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
1449
                    if (strpos($InfoFieldList[4], 'extra.') !== false) {
1450
                        $keyList = 'main.' . $InfoFieldList[2] . ' as rowid';
1451
                    } else {
1452
                        $keyList = $InfoFieldList[2] . ' as rowid';
1453
                    }
1454
                }
1455
1456
                $filter_categorie = false;
1457
                if (count($InfoFieldList) > 5) {
1458
                    if ($InfoFieldList[0] == 'categorie') {
1459
                        $filter_categorie = true;
1460
                    }
1461
                }
1462
1463
                if ($filter_categorie === false) {
1464
                    $fields_label = explode('|', $InfoFieldList[1]);
1465
                    if (is_array($fields_label)) {
1466
                        $keyList .= ', ';
1467
                        $keyList .= implode(', ', $fields_label);
1468
                    }
1469
1470
                    $sqlwhere = '';
1471
                    $sql = "SELECT " . $keyList;
1472
                    $sql .= ' FROM ' . $this->db->prefix() . $InfoFieldList[0];
1473
                    if (!empty($InfoFieldList[4])) {
1474
                        // can use current entity filter
1475
                        if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
1476
                            $InfoFieldList[4] = str_replace('$ENTITY$', $conf->entity, $InfoFieldList[4]);
1477
                        }
1478
                        // can use SELECT request
1479
                        if (strpos($InfoFieldList[4], '$SEL$') !== false) {
1480
                            $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
1481
                        }
1482
1483
                        // current object id can be use into filter
1484
                        if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
1485
                            $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
1486
                        } elseif (preg_match("#^.*list.php$#", $_SERVER['PHP_SELF'])) {
1487
                            // Pattern for word=$ID$
1488
                            $word = '\b[a-zA-Z0-9-\.-_]+\b=\$ID\$';
1489
1490
                            // Removing spaces around =, ( and )
1491
                            $InfoFieldList[4] = preg_replace('# *(=|\(|\)) *#', '$1', $InfoFieldList[4]);
1492
1493
                            $nbPreg = 1;
1494
                            // While we have parenthesis
1495
                            while ($nbPreg != 0) {
1496
                                // Initialise counters
1497
                                $nbPregRepl = $nbPregSel = 0;
1498
                                // Remove all parenthesis not preceded with '=' sign
1499
                                $InfoFieldList[4] = preg_replace('#([^=])(\([^)^(]*(' . $word . ')[^)^(]*\))#', '$1 $3 ', $InfoFieldList[4], -1, $nbPregRepl);
1500
                                // Remove all escape characters around '=' and parenthesis
1501
                                $InfoFieldList[4] = preg_replace('# *(=|\(|\)) *#', '$1', $InfoFieldList[4]);
1502
                                // Remove all parentheses preceded with '='
1503
                                $InfoFieldList[4] = preg_replace('#\b[a-zA-Z0-9-\.-_]+\b=\([^)^(]*(' . $word . ')[^)^(]*\)#', '$1 ', $InfoFieldList[4], -1, $nbPregSel);
1504
                                // On retire les escapes autour des = et parenthèses
1505
                                $InfoFieldList[4] = preg_replace('# *(=|\(|\)) *#', '$1', $InfoFieldList[4]);
1506
1507
                                // UPdate the totals counter for the loop
1508
                                $nbPreg = $nbPregRepl + $nbPregSel;
1509
                            }
1510
1511
                            // In case there is AND ou OR, before or after
1512
                            $matchCondition = [];
1513
                            preg_match('#(AND|OR|) *(' . $word . ') *(AND|OR|)#', $InfoFieldList[4], $matchCondition);
1514
                            while (!empty($matchCondition[0])) {
1515
                                // If the two sides differ but are not empty
1516
                                if (!empty($matchCondition[1]) && !empty($matchCondition[3]) && $matchCondition[1] != $matchCondition[3]) {
1517
                                    // Nobody sain would do that without parentheses
1518
                                    $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
1519
                                } else {
1520
                                    if (!empty($matchCondition[1])) {
1521
                                        $boolCond = (($matchCondition[1] == "AND") ? ' AND TRUE ' : ' OR FALSE ');
1522
                                        $InfoFieldList[4] = str_replace($matchCondition[0], $boolCond . $matchCondition[3], $InfoFieldList[4]);
1523
                                    } elseif (!empty($matchCondition[3])) {
1524
                                        $boolCond = (($matchCondition[3] == "AND") ? ' TRUE AND ' : ' FALSE OR');
1525
                                        $InfoFieldList[4] = str_replace($matchCondition[0], $boolCond, $InfoFieldList[4]);
1526
                                    } else {
1527
                                        $InfoFieldList[4] = " TRUE ";
1528
                                    }
1529
                                }
1530
1531
                                // In case there is AND ou OR, before or after
1532
                                preg_match('#(AND|OR|) *(' . $word . ') *(AND|OR|)#', $InfoFieldList[4], $matchCondition);
1533
                            }
1534
                        } else {
1535
                            $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
1536
                        }
1537
1538
                        // We have to join on extrafield table
1539
                        if (strpos($InfoFieldList[4], 'extra.') !== false) {
1540
                            $sql .= ' as main, ' . $this->db->prefix() . $InfoFieldList[0] . '_extrafields as extra';
1541
                            $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2] . " AND " . $InfoFieldList[4];
1542
                        } else {
1543
                            $sqlwhere .= " WHERE " . $InfoFieldList[4];
1544
                        }
1545
                    } else {
1546
                        $sqlwhere .= ' WHERE 1=1';
1547
                    }
1548
                    // Some tables may have field, some other not. For the moment we disable it.
1549
                    if (in_array($InfoFieldList[0], ['tablewithentity'])) {
1550
                        $sqlwhere .= " AND entity = " . ((int) $conf->entity);
1551
                    }
1552
                    // $sql.=preg_replace('/^ AND /','',$sqlwhere);
1553
                    // print $sql;
1554
1555
                    $sql .= $sqlwhere;
1556
                    dol_syslog(get_class($this) . '::showInputField type=chkbxlst', LOG_DEBUG);
1557
                    $resql = $this->db->query($sql);
1558
                    if ($resql) {
1559
                        $num = $this->db->num_rows($resql);
1560
                        $i = 0;
1561
1562
                        $data = [];
1563
1564
                        while ($i < $num) {
1565
                            $labeltoshow = '';
1566
                            $obj = $this->db->fetch_object($resql);
1567
1568
                            $notrans = false;
1569
                            // Several field into label (eq table:code|label:rowid)
1570
                            $fields_label = explode('|', $InfoFieldList[1]);
1571
                            if (is_array($fields_label)) {
1572
                                $notrans = true;
1573
                                foreach ($fields_label as $field_toshow) {
1574
                                    $labeltoshow .= $obj->$field_toshow . ' ';
1575
                                }
1576
                            } else {
1577
                                $labeltoshow = $obj->{$InfoFieldList[1]};
1578
                            }
1579
                            $labeltoshow = dol_trunc($labeltoshow, 45);
1580
1581
                            if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
1582
                                $labeltoshow = '';
1583
                                foreach ($fields_label as $field_toshow) {
1584
                                    $translabel = $langs->trans($obj->$field_toshow);
1585
                                    if ($translabel != $obj->$field_toshow) {
1586
                                        $labeltoshow .= ' ' . dol_trunc($translabel, 18) . ' ';
1587
                                    } else {
1588
                                        $labeltoshow .= ' ' . dol_trunc($obj->$field_toshow, 18) . ' ';
1589
                                    }
1590
                                }
1591
                                $data[$obj->rowid] = $labeltoshow;
1592
                            } else {
1593
                                if (!$notrans) {
1594
                                    $translabel = $langs->trans($obj->{$InfoFieldList[1]});
1595
                                    if ($translabel != $obj->{$InfoFieldList[1]}) {
1596
                                        $labeltoshow = dol_trunc($translabel, 18);
1597
                                    } else {
1598
                                        $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
1599
                                    }
1600
                                }
1601
                                if (empty($labeltoshow)) {
1602
                                    $labeltoshow = '(not defined)';
1603
                                }
1604
1605
                                if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
1606
                                    $data[$obj->rowid] = $labeltoshow;
1607
                                }
1608
1609
                                if (!empty($InfoFieldList[3]) && $parentField) {
1610
                                    $parent = $parentName . ':' . $obj->{$parentField};
1611
                                }
1612
1613
                                $data[$obj->rowid] = $labeltoshow;
1614
                            }
1615
1616
                            $i++;
1617
                        }
1618
                        $this->db->free($resql);
1619
1620
                        $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, '', 0, '', 0, '100%');
1621
                    } else {
1622
                        print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
1623
                    }
1624
                } else {
1625
                    $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
1626
                    $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, '', 0, '', 0, '100%');
1627
                }
1628
            }
1629
        } elseif ($type == 'link') {
1630
            $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
1631
            /* Removed.
1632
            The selectForForms is called with parameter $objectfield defined, so that the app can retrieve the filter inside the ajax component instead of being provided as parameters. The
1633
            filter was used to pass SQL requests leading to serious SQL injection problems. This should not be possible. Also the call of the ajax was broken by some WAF.
1634
            if (strpos($param_list[0], '$ID$') !== false && !empty($objectid)) {
1635
                $param_list[0] = str_replace('$ID$', $objectid, $param_list[0]);
1636
            }*/
1637
            $showempty = (($required && $default != '') ? 0 : 1);
1638
1639
            $tmparray = explode(':', $param_list[0]);
1640
1641
            $element = $extrafieldsobjectkey;       // $extrafieldsobjectkey comes from $object->table_element but we need $object->element
1642
            if ($element == 'socpeople') {
1643
                $element = 'contact';
1644
            } elseif ($element == 'projet') {
1645
                $element = 'project';
1646
            }
1647
1648
            //$out = $form->selectForForms($param_list[0], $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, '', 0, 0, '');
1649
            $out = $form->selectForForms($tmparray[0], $keyprefix . $key . $keysuffix, $value, $showempty, '', '', $morecss, '', 0, 0, '', $element . ':options_' . $key);
1650
        } elseif ($type == 'password') {
1651
            // If prefix is 'search_', field is used as a filter, we use a common text field.
1652
            $out = '<input style="display:none" type="text" name="fakeusernameremembered">'; // Hidden field to reduce impact of evil Google Chrome autopopulate bug.
1653
            $out .= '<input autocomplete="new-password" type="' . ($keyprefix == 'search_' ? 'text' : 'password') . '" class="flat ' . $morecss . '" name="' . $keyprefix . $key . $keysuffix . '" id="' . $keyprefix . $key . $keysuffix . '" value="' . $value . '" ' . ($moreparam ? $moreparam : '') . '>';
1654
        }
1655
        if (!empty($hidden)) {
1656
            $out = '<input type="hidden" value="' . $value . '" name="' . $keyprefix . $key . $keysuffix . '" id="' . $keyprefix . $key . $keysuffix . '"/>';
1657
        }
1658
        /* Add comments
1659
         if ($type == 'date') $out.=' (YYYY-MM-DD)';
1660
         elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
1661
         */
1662
        /*if (!empty($help) && $keyprefix != 'search_options_') {
1663
            $out .= $form->textwithpicto('', $help, 1, 'help', '', 0, 3);
1664
        }*/
1665
        return $out;
1666
    }
1667
1668
1669
    /**
1670
     * Return HTML string to put an output field into a page
1671
     *
1672
     * @param string $key                  Key of attribute
1673
     * @param string $value                Value to show
1674
     * @param string $moreparam            To add more parameters on html input tag (only checkbox use html input for
1675
     *                                     output rendering)
1676
     * @param string $extrafieldsobjectkey Required (for example $object->table_element).
1677
     *
1678
     * @return  string                          Formatted value
1679
     */
1680
    public function showOutputField($key, $value, $moreparam = '', $extrafieldsobjectkey = '')
1681
    {
1682
        global $conf, $langs;
1683
1684
        if (empty($extrafieldsobjectkey)) {
1685
            dol_syslog(get_class($this) . '::showOutputField extrafieldsobjectkey required', LOG_ERR);
1686
            return 'BadValueForParamExtraFieldsObjectKey';
1687
        }
1688
1689
        $label = $this->attributes[$extrafieldsobjectkey]['label'][$key];
1690
        $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
1691
        $size = $this->attributes[$extrafieldsobjectkey]['size'][$key];         // Can be '255', '24,8'...
1692
        $default = $this->attributes[$extrafieldsobjectkey]['default'][$key];
1693
        $computed = $this->attributes[$extrafieldsobjectkey]['computed'][$key];
1694
        $unique = $this->attributes[$extrafieldsobjectkey]['unique'][$key];
1695
        $required = $this->attributes[$extrafieldsobjectkey]['required'][$key];
1696
        $param = $this->attributes[$extrafieldsobjectkey]['param'][$key];
1697
        $perms = (int) dol_eval($this->attributes[$extrafieldsobjectkey]['perms'][$key], 1, 1, '2');
1698
        $langfile = $this->attributes[$extrafieldsobjectkey]['langfile'][$key];
1699
        $list = (string) dol_eval($this->attributes[$extrafieldsobjectkey]['list'][$key], 1, 1, '2');
1700
        $help = $this->attributes[$extrafieldsobjectkey]['help'][$key];
1701
        $hidden = (empty($list) ? 1 : 0); // If $list empty, we are sure it is hidden, otherwise we show. If it depends on mode (view/create/edit form or list, this must be filtered by caller)
1702
1703
        if ($hidden) {
1704
            return ''; // This is a protection. If field is hidden, we should just not call this method.
1705
        }
1706
1707
        //if ($computed) $value =       // $value is already calculated into $value before calling this method
1708
1709
        $showsize = 0;
1710
        if ($type == 'date') {
1711
            $showsize = 10;
1712
            if ($value !== '') {
1713
                $value = dol_print_date($value, 'day'); // For date without hour, date is always GMT for storage and output
1714
            }
1715
        } elseif ($type == 'datetime') {
1716
            $showsize = 19;
1717
            if ($value !== '') {
1718
                $value = dol_print_date($value, 'dayhour', 'tzuserrel');
1719
            }
1720
        } elseif ($type == 'datetimegmt') {
1721
            $showsize = 19;
1722
            if ($value !== '') {
1723
                $value = dol_print_date($value, 'dayhour', 'gmt');
1724
            }
1725
        } elseif ($type == 'int') {
1726
            $showsize = 10;
1727
        } elseif ($type == 'double') {
1728
            if (!empty($value)) {
1729
                //$value=price($value);
1730
                $sizeparts = explode(",", $size);
1731
                $number_decimals = array_key_exists(1, $sizeparts) ? $sizeparts[1] : 0;
1732
                $value = price($value, 0, $langs, 0, 0, $number_decimals, '');
1733
            }
1734
        } elseif ($type == 'boolean') {
1735
            $checked = '';
1736
            if (!empty($value)) {
1737
                $checked = ' checked ';
1738
            }
1739
            if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
1740
                $value = '<input type="checkbox" ' . $checked . ' ' . ($moreparam ? $moreparam : '') . ' readonly disabled>';
1741
            } else {
1742
                $value = yn($value ? 1 : 0);
1743
            }
1744
        } elseif ($type == 'mail') {
1745
            $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
1746
        } elseif ($type == 'ip') {
1747
            $value = dol_print_ip($value, 0);
1748
        } elseif ($type == 'icon') {
1749
            $value = '<span class="' . $value . '"></span>';
1750
        } elseif ($type == 'url') {
1751
            $value = dol_print_url($value, '_blank', 32, 1);
1752
        } elseif ($type == 'phone') {
1753
            $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
1754
        } elseif ($type == 'price') {
1755
            //$value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
1756
            if ($value || $value == '0') {
1757
                $value = price($value, 0, $langs, 0, $conf->global->MAIN_MAX_DECIMALS_TOT, -1) . ' ' . $langs->getCurrencySymbol($conf->currency);
1758
            }
1759
        } elseif ($type == 'pricecy') {
1760
            $currency = $conf->currency;
1761
            if (!empty($value)) {
1762
                // $value in memory is a php string like '0.01:EUR'
1763
                $pricetmp = explode(':', $value);
1764
                $currency = !empty($pricetmp[1]) ? $pricetmp[1] : $conf->currency;
1765
                $value = $pricetmp[0];
1766
            }
1767
            if ($value || $value == '0') {
1768
                $value = price($value, 0, $langs, 0, $conf->global->MAIN_MAX_DECIMALS_TOT, -1, $currency);
1769
            }
1770
        } elseif ($type == 'select') {
1771
            $valstr = (!empty($param['options'][$value]) ? $param['options'][$value] : '');
1772
            if (($pos = strpos($valstr, "|")) !== false) {
1773
                $valstr = substr($valstr, 0, $pos);
1774
            }
1775
            if ($langfile && $valstr) {
1776
                $value = $langs->trans($valstr);
1777
            } else {
1778
                $value = $valstr;
1779
            }
1780
        } elseif ($type == 'sellist') {
1781
            $param_list = array_keys($param['options']);
1782
            $InfoFieldList = explode(":", $param_list[0]);
1783
1784
            $selectkey = "rowid";
1785
            $keyList = 'rowid';
1786
1787
            if (count($InfoFieldList) >= 3) {
1788
                $selectkey = $InfoFieldList[2];
1789
                $keyList = $InfoFieldList[2] . ' as rowid';
1790
            }
1791
1792
            $fields_label = explode('|', $InfoFieldList[1]);
1793
            if (is_array($fields_label)) {
1794
                $keyList .= ', ';
1795
                $keyList .= implode(', ', $fields_label);
1796
            }
1797
1798
            $filter_categorie = false;
1799
            if (count($InfoFieldList) > 5) {
1800
                if ($InfoFieldList[0] == 'categorie') {
1801
                    $filter_categorie = true;
1802
                }
1803
            }
1804
1805
            $sql = "SELECT " . $keyList;
1806
            $sql .= ' FROM ' . $this->db->prefix() . $InfoFieldList[0];
1807
            if (!empty($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra.') !== false) {
1808
                $sql .= ' as main';
1809
            }
1810
            if ($selectkey == 'rowid' && empty($value)) {
1811
                $sql .= " WHERE " . $selectkey . " = 0";
1812
            } elseif ($selectkey == 'rowid') {
1813
                $sql .= " WHERE " . $selectkey . " = " . ((int) $value);
1814
            } else {
1815
                $sql .= " WHERE " . $selectkey . " = '" . $this->db->escape($value) . "'";
1816
            }
1817
1818
            //$sql.= ' AND entity = '.$conf->entity;
1819
1820
            dol_syslog(get_class($this) . ':showOutputField:$type=sellist', LOG_DEBUG);
1821
            $resql = $this->db->query($sql);
1822
            if ($resql) {
1823
                if ($filter_categorie === false) {
1824
                    $value = ''; // value was used, so now we reste it to use it to build final output
1825
1826
                    $obj = $this->db->fetch_object($resql);
1827
1828
                    // Several field into label (eq table:code|label:rowid)
1829
                    $fields_label = explode('|', $InfoFieldList[1]);
1830
1831
                    if (is_array($fields_label) && count($fields_label) > 1) {
1832
                        foreach ($fields_label as $field_toshow) {
1833
                            $translabel = '';
1834
                            if (!empty($obj->$field_toshow)) {
1835
                                $translabel = $langs->trans($obj->$field_toshow);
1836
1837
                                if ($translabel != $obj->$field_toshow) {
1838
                                    $value .= dol_trunc($translabel, 24) . ' ';
1839
                                } else {
1840
                                    $value .= $obj->$field_toshow . ' ';
1841
                                }
1842
                            }
1843
                        }
1844
                    } else {
1845
                        $translabel = '';
1846
                        $tmppropname = $InfoFieldList[1];
1847
                        //$obj->$tmppropname = '';
1848
                        if (!empty(isset($obj->$tmppropname) ? $obj->$tmppropname : '')) {
1849
                            $translabel = $langs->trans($obj->$tmppropname);
1850
                        }
1851
                        if ($translabel != (isset($obj->$tmppropname) ? $obj->$tmppropname : '')) {
1852
                            $value = dol_trunc($translabel, 18);
1853
                        } else {
1854
                            $value = isset($obj->$tmppropname) ? $obj->$tmppropname : '';
1855
                        }
1856
                    }
1857
                } else {
1858
                    $toprint = [];
1859
                    $obj = $this->db->fetch_object($resql);
1860
                    if ($obj->rowid) {
1861
                        $c = new Categorie($this->db);
1862
                        $result = $c->fetch($obj->rowid);
1863
                        if ($result > 0) {
1864
                            $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
1865
                            foreach ($ways as $way) {
1866
                                $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #bbb"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
1867
                            }
1868
                        }
1869
                    }
1870
                    $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
1871
                }
1872
            } else {
1873
                dol_syslog(get_class($this) . '::showOutputField error ' . $this->db->lasterror(), LOG_WARNING);
1874
            }
1875
        } elseif ($type == 'radio') {
1876
            if (!isset($param['options'][$value])) {
1877
                $langs->load('errors');
1878
                $value = $langs->trans('ErrorNoValueForRadioType');
1879
            } else {
1880
                $value = $langs->trans($param['options'][$value]);
1881
            }
1882
        } elseif ($type == 'checkbox') {
1883
            $value_arr = explode(',', $value);
1884
            $value = '';
1885
            $toprint = [];
1886
            if (is_array($value_arr)) {
1887
                foreach ($value_arr as $keyval => $valueval) {
1888
                    if (!empty($valueval)) {
1889
                        $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $param['options'][$valueval] . '</li>';
1890
                    }
1891
                }
1892
            }
1893
            $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
1894
        } elseif ($type == 'chkbxlst') {
1895
            $value_arr = explode(',', $value);
1896
1897
            $param_list = array_keys($param['options']);
1898
            $InfoFieldList = explode(":", $param_list[0]);
1899
1900
            $selectkey = "rowid";
1901
            $keyList = 'rowid';
1902
1903
            if (count($InfoFieldList) >= 3) {
1904
                $selectkey = $InfoFieldList[2];
1905
                $keyList = $InfoFieldList[2] . ' as rowid';
1906
            }
1907
1908
            $fields_label = explode('|', $InfoFieldList[1]);
1909
            if (is_array($fields_label)) {
1910
                $keyList .= ', ';
1911
                $keyList .= implode(', ', $fields_label);
1912
            }
1913
1914
            $filter_categorie = false;
1915
            if (count($InfoFieldList) > 5) {
1916
                if ($InfoFieldList[0] == 'categorie') {
1917
                    $filter_categorie = true;
1918
                }
1919
            }
1920
1921
            $sql = "SELECT " . $keyList;
1922
            $sql .= " FROM " . $this->db->prefix() . $InfoFieldList[0];
1923
            if (strpos($InfoFieldList[4], 'extra.') !== false) {
1924
                $sql .= ' as main';
1925
            }
1926
            // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
1927
            // $sql.= ' AND entity = '.$conf->entity;
1928
1929
            dol_syslog(get_class($this) . ':showOutputField:$type=chkbxlst', LOG_DEBUG);
1930
            $resql = $this->db->query($sql);
1931
            if ($resql) {
1932
                if ($filter_categorie === false) {
1933
                    $value = ''; // value was used, so now we reste it to use it to build final output
1934
                    $toprint = [];
1935
                    while ($obj = $this->db->fetch_object($resql)) {
1936
                        // Several field into label (eq table:code|label:rowid)
1937
                        $fields_label = explode('|', $InfoFieldList[1]);
1938
                        if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
1939
                            if (is_array($fields_label) && count($fields_label) > 1) {
1940
                                $label = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">';
1941
                                foreach ($fields_label as $field_toshow) {
1942
                                    $translabel = '';
1943
                                    if (!empty($obj->$field_toshow)) {
1944
                                        $translabel = $langs->trans($obj->$field_toshow);
1945
                                    }
1946
                                    if ($translabel != $field_toshow) {
1947
                                        $label .= ' ' . dol_trunc($translabel, 18);
1948
                                    } else {
1949
                                        $label .= ' ' . $obj->$field_toshow;
1950
                                    }
1951
                                }
1952
                                $label .= '</li>';
1953
                                $toprint[] = $label;
1954
                            } else {
1955
                                $translabel = '';
1956
                                if (!empty($obj->{$InfoFieldList[1]})) {
1957
                                    $translabel = $langs->trans($obj->{$InfoFieldList[1]});
1958
                                }
1959
                                if ($translabel != $obj->{$InfoFieldList[1]}) {
1960
                                    $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
1961
                                } else {
1962
                                    $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->{$InfoFieldList[1]} . '</li>';
1963
                                }
1964
                            }
1965
                        }
1966
                    }
1967
                } else {
1968
                    $toprint = [];
1969
                    while ($obj = $this->db->fetch_object($resql)) {
1970
                        if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
1971
                            $c = new Categorie($this->db);
1972
                            $c->fetch($obj->rowid);
1973
                            $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
1974
                            foreach ($ways as $way) {
1975
                                $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #bbb"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
1976
                            }
1977
                        }
1978
                    }
1979
                }
1980
                if (!empty($toprint)) {
1981
                    $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
1982
                }
1983
            } else {
1984
                dol_syslog(get_class($this) . '::showOutputField error ' . $this->db->lasterror(), LOG_WARNING);
1985
            }
1986
        } elseif ($type == 'link') {
1987
            $out = '';
1988
1989
            // Only if something to display (perf)
1990
            if ($value) {       // If we have -1 here, pb is into insert, not into output (fix insert instead of changing code here to compensate)
1991
                $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
1992
1993
                $InfoFieldList = explode(":", $param_list[0]);
1994
                $classname = $InfoFieldList[0];
1995
                $classpath = $InfoFieldList[1];
1996
                if (!empty($classpath)) {
1997
                    dol_include_once($InfoFieldList[1]);
1998
                    if ($classname && class_exists($classname)) {
1999
                        $object = new $classname($this->db);
2000
                        $object->fetch($value);
2001
                        $value = $object->getNomUrl(3);
2002
                    }
2003
                } else {
2004
                    dol_syslog('Error bad setup of extrafield', LOG_WARNING);
2005
                    return 'Error bad setup of extrafield';
2006
                }
2007
            }
2008
        } elseif ($type == 'text') {
2009
            $value = dol_htmlentitiesbr($value);
2010
        } elseif ($type == 'html') {
2011
            $value = dol_htmlentitiesbr($value);
2012
        } elseif ($type == 'password') {
2013
            $value = dol_trunc(preg_replace('/./i', '*', $value), 8, 'right', 'UTF-8', 1);
2014
        } else {
2015
            $showsize = round((float) $size);
2016
            if ($showsize > 48) {
2017
                $showsize = 48;
2018
            }
2019
        }
2020
2021
        //print $type.'-'.$size;
2022
        $out = $value;
2023
2024
        return $out;
2025
    }
2026
2027
    /**
2028
     * Return the CSS to use for this extrafield into list
2029
     *
2030
     * @param string $key                  Key of attribute
2031
     * @param string $extrafieldsobjectkey If defined, use the new method to get extrafields data
2032
     *
2033
     * @return  string                          Formatted value
2034
     */
2035
    public function getAlignFlag($key, $extrafieldsobjectkey = '')
2036
    {
2037
        global $conf, $langs;
2038
2039
        $type = 'varchar';
2040
        if (!empty($extrafieldsobjectkey)) {
2041
            $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
2042
        }
2043
2044
        $cssstring = '';
2045
2046
        if (in_array($type, ['date', 'datetime', 'datetimegmt',])) {
2047
            $cssstring = "center";
2048
        } elseif (in_array($type, ['int', 'price', 'double'])) {
2049
            $cssstring = "right";
2050
        } elseif (in_array($type, ['boolean', 'radio', 'checkbox', 'ip', 'icon'])) {
2051
            $cssstring = "center";
2052
        }
2053
2054
        if (!empty($this->attributes[$extrafieldsobjectkey]['csslist'][$key])) {
2055
            $cssstring .= ($cssstring ? ' ' : '') . $this->attributes[$extrafieldsobjectkey]['csslist'][$key];
2056
        } else {
2057
            if (in_array($type, ['ip'])) {
2058
                $cssstring .= ($cssstring ? ' ' : '') . 'tdoverflowmax150';
2059
            }
2060
        }
2061
2062
        return $cssstring;
2063
    }
2064
2065
    /**
2066
     * Return HTML string to print separator extrafield
2067
     *
2068
     * @param string $key          Key of attribute
2069
     * @param object $object       Object
2070
     * @param int    $colspan      Value of colspan to use (it must includes the first column with title)
2071
     * @param string $display_type "card" for form display, "line" for document line display (extrafields on propal
2072
     *                             line, order line, etc...)
2073
     * @param string $mode         Show output ('view') or input ('create' or 'edit') for extrafield
2074
     *
2075
     * @return  string                  HTML code with line for separator
2076
     */
2077
    public function showSeparator($key, $object, $colspan = 2, $display_type = 'card', $mode = '')
2078
    {
2079
        global $conf, $langs;
2080
2081
        $tagtype = 'tr';
2082
        $tagtype_dyn = 'td';
2083
2084
        if ($display_type == 'line') {
2085
            $tagtype = 'div';
2086
            $tagtype_dyn = 'span';
2087
            $colspan = 0;
2088
        }
2089
2090
        $extrafield_param = $this->attributes[$object->table_element]['param'][$key];
2091
        $extrafield_param_list = [];
2092
        if (!empty($extrafield_param) && is_array($extrafield_param)) {
2093
            $extrafield_param_list = array_keys($extrafield_param['options']);
2094
        }
2095
2096
        // Set $extrafield_collapse_display_value (do we have to collapse/expand the group after the separator)
2097
        $extrafield_collapse_display_value = -1;
2098
        $expand_display = false;
2099
        if (is_array($extrafield_param_list) && count($extrafield_param_list) > 0) {
2100
            $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
2101
            $expand_display = ((isset($_COOKIE['DOLCOLLAPSE_' . $object->table_element . '_extrafields_' . $key]) || GETPOSTINT('ignorecollapsesetup')) ? (empty($_COOKIE['DOLCOLLAPSE_' . $object->table_element . '_extrafields_' . $key]) ? false : true) : ($extrafield_collapse_display_value == 2 ? false : true));
2102
        }
2103
        $disabledcookiewrite = 0;
2104
        if ($mode == 'create') {
2105
            // On create mode, force separator group to not be collapsible
2106
            $extrafield_collapse_display_value = 1;
2107
            $expand_display = true; // We force group to be shown expanded
2108
            $disabledcookiewrite = 1; // We keep status of group unchanged into the cookie
2109
        }
2110
2111
        $out = '<' . $tagtype . ' id="trextrafieldseparator' . $key . (!empty($object->id) ? '_' . $object->id : '') . '" class="trextrafieldseparator trextrafieldseparator' . $key . (!empty($object->id) ? '_' . $object->id : '') . '">';
2112
        $out .= '<' . $tagtype_dyn . ' ' . (!empty($colspan) ? 'colspan="' . $colspan . '"' : '') . '>';
2113
        // Some js code will be injected here to manage the collapsing of extrafields
2114
        // Output the picto
2115
        $out .= '<span class="' . ($extrafield_collapse_display_value ? 'cursorpointer ' : '') . ($extrafield_collapse_display_value == 0 ? 'fas fa-square opacitymedium' : 'far fa-' . (($expand_display ? 'minus' : 'plus') . '-square')) . '"></span>';
2116
        $out .= '&nbsp;';
2117
        $out .= '<strong>';
2118
        $out .= $langs->trans($this->attributes[$object->table_element]['label'][$key]);
2119
        $out .= '</strong>';
2120
        $out .= '</' . $tagtype_dyn . '>';
2121
        $out .= '</' . $tagtype . '>';
2122
2123
        $collapse_group = $key . (!empty($object->id) ? '_' . $object->id : '');
2124
        //$extrafields_collapse_num = $this->attributes[$object->table_element]['pos'][$key].(!empty($object->id)?'_'.$object->id:'');
2125
2126
        if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
2127
            // Set the collapse_display status to cookie in priority or if ignorecollapsesetup is 1, if cookie and ignorecollapsesetup not defined, use the setup.
2128
            $this->expand_display[$collapse_group] = $expand_display;
2129
2130
            if (!empty($conf->use_javascript_ajax)) {
2131
                $out .= '<!-- Add js script to manage the collapse/uncollapse of extrafields separators ' . $key . ' -->' . "\n";
2132
                $out .= '<script nonce="' . getNonce() . '" type="text/javascript">' . "\n";
2133
                $out .= 'jQuery(document).ready(function(){' . "\n";
2134
                if (empty($disabledcookiewrite)) {
2135
                    if ($expand_display === false) {
2136
                        $out .= '   console.log("Inject js for the collapsing of extrafield ' . $key . ' - hide");' . "\n";
2137
                        $out .= '   jQuery(".trextrafields_collapse' . $collapse_group . '").hide();' . "\n";
2138
                    } else {
2139
                        $out .= '   console.log("Inject js for collapsing of extrafield ' . $key . ' - keep visible and set cookie");' . "\n";
2140
                        $out .= '   document.cookie = "DOLCOLLAPSE_' . $object->table_element . '_extrafields_' . $key . '=1; path=' . $_SERVER['PHP_SELF'] . '"' . "\n";
2141
                    }
2142
                }
2143
                $out .= '   jQuery("#trextrafieldseparator' . $key . (!empty($object->id) ? '_' . $object->id : '') . '").click(function(){' . "\n";
2144
                $out .= '       console.log("We click on collapse/uncollapse to hide/show .trextrafields_collapse' . $collapse_group . '");' . "\n";
2145
                $out .= '       jQuery(".trextrafields_collapse' . $collapse_group . '").toggle(100, function(){' . "\n";
2146
                $out .= '           if (jQuery(".trextrafields_collapse' . $collapse_group . '").is(":hidden")) {' . "\n";
2147
                $out .= '               jQuery("#trextrafieldseparator' . $key . (!empty($object->id) ? '_' . $object->id : '') . ' ' . $tagtype_dyn . ' span").addClass("fa-plus-square").removeClass("fa-minus-square");' . "\n";
2148
                $out .= '               document.cookie = "DOLCOLLAPSE_' . $object->table_element . '_extrafields_' . $key . '=0; path=' . $_SERVER['PHP_SELF'] . '"' . "\n";
2149
                $out .= '           } else {' . "\n";
2150
                $out .= '               jQuery("#trextrafieldseparator' . $key . (!empty($object->id) ? '_' . $object->id : '') . ' ' . $tagtype_dyn . ' span").addClass("fa-minus-square").removeClass("fa-plus-square");' . "\n";
2151
                $out .= '               document.cookie = "DOLCOLLAPSE_' . $object->table_element . '_extrafields_' . $key . '=1; path=' . $_SERVER['PHP_SELF'] . '"' . "\n";
2152
                $out .= '           }' . "\n";
2153
                $out .= '       });' . "\n";
2154
                $out .= '   });' . "\n";
2155
                $out .= '});' . "\n";
2156
                $out .= '</script>' . "\n";
2157
            }
2158
        } else {
2159
            $this->expand_display[$collapse_group] = 1;
2160
        }
2161
2162
        return $out;
2163
    }
2164
2165
    /**
2166
     * Fill array_options property of object by extrafields value (using for data sent by forms)
2167
     *
2168
     * @param array|null $extralabels           Deprecated (old $array of extrafields, now set this to null)
2169
     * @param object     $object                Object
2170
     * @param string     $onlykey               Only some keys are filled:
2171
     *                                          'string' => When we make update of only one extrafield ($action =
2172
     *                                          'update_extras'), calling page can set this to avoid to have other
2173
     *                                          extrafields being reset.
2174
     *                                          '@GETPOSTISSET' => When we make update of several extrafields ($action
2175
     *                                          = 'update'), calling page can set this to avoid to have fields not into
2176
     *                                          POST being reset.
2177
     * @param int        $todefaultifmissing    1=Set value to the default value in database if value is mandatory and
2178
     *                                          missing
2179
     *
2180
     * @return  int                             1 if array_options set, 0 if no value, -1 if error (field required
2181
     *                                          missing for example)
2182
     */
2183
    public function setOptionalsFromPost($extralabels, &$object, $onlykey = '', $todefaultifmissing = 0)
2184
    {
2185
        global $conf, $_POST, $langs;
2186
2187
        $nofillrequired = 0; // For error when required field left blank
2188
        $error_field_required = [];
2189
2190
        if (isset($this->attributes[$object->table_element]['label']) && is_array($this->attributes[$object->table_element]['label'])) {
2191
            $extralabels = $this->attributes[$object->table_element]['label'];
2192
        }
2193
2194
        if (is_array($extralabels)) {
2195
            // Get extra fields
2196
            foreach ($extralabels as $key => $value) {
2197
                if (!empty($onlykey) && $onlykey != '@GETPOSTISSET' && $key != $onlykey) {
2198
                    continue;
2199
                }
2200
2201
                if (!empty($onlykey) && $onlykey == '@GETPOSTISSET' && !GETPOSTISSET('options_' . $key) && (!in_array($this->attributes[$object->table_element]['type'][$key], ['boolean', 'checkbox', 'chkbxlst']))) {
2202
                    //when unticking boolean field, it's not set in POST
2203
                    continue;
2204
                }
2205
2206
                $key_type = $this->attributes[$object->table_element]['type'][$key];
2207
                if ($key_type == 'separate') {
2208
                    continue;
2209
                }
2210
2211
                $enabled = 1;
2212
                if (isset($this->attributes[$object->table_element]['enabled'][$key])) {    // 'enabled' is often a condition on module enabled or not
2213
                    $enabled = (int) dol_eval($this->attributes[$object->table_element]['enabled'][$key], 1, 1, '2');
2214
                }
2215
2216
                $visibility = 1;
2217
                if (isset($this->attributes[$object->table_element]['list'][$key])) {       // 'list' is option for visibility
2218
                    $visibility = (int) dol_eval($this->attributes[$object->table_element]['list'][$key], 1, 1, '2');
2219
                }
2220
2221
                $perms = 1;
2222
                if (isset($this->attributes[$object->table_element]['perms'][$key])) {
2223
                    $perms = (int) dol_eval($this->attributes[$object->table_element]['perms'][$key], 1, 1, '2');
2224
                }
2225
                if (
2226
                    empty($enabled)
2227
                    || (
2228
                        $onlykey === '@GETPOSTISSET'
2229
                        && in_array($this->attributes[$object->table_element]['type'][$key], ['boolean', 'checkbox', 'chkbxlst'])
2230
                        && in_array(abs($enabled), [2, 5])
2231
                        && !GETPOSTISSET('options_' . $key) // Update hidden checkboxes and multiselect only if they are provided
2232
                    )
2233
                ) {
2234
                    continue;
2235
                }
2236
2237
                $visibility_abs = abs($visibility);
2238
                // not modify if extra field is not in update form (0 : never, 2 or -2 : list only, 5 or - 5 : list and view only)
2239
                if (empty($visibility_abs) || $visibility_abs == 2 || $visibility_abs == 5) {
2240
                    continue;
2241
                }
2242
                if (empty($perms)) {
2243
                    continue;
2244
                }
2245
2246
                if ($this->attributes[$object->table_element]['required'][$key]) {  // Value is required
2247
                    // Check if functionally empty without using GETPOST (depending on the type of extrafield, a
2248
                    // technically non-empty value may be treated as empty functionally).
2249
                    // value can be alpha, int, array, etc...
2250
                    $v = $_POST["options_" . $key] ?? null;
2251
                    $type = $this->attributes[$object->table_element]['type'][$key];
2252
                    if (self::isEmptyValue($v, $type)) {
2253
                        //print 'ccc'.$value.'-'.$this->attributes[$object->table_element]['required'][$key];
2254
2255
                        // Field is not defined. We mark this as an error. We may fix it later if there is a default value and $todefaultifmissing is set.
2256
2257
                        $nofillrequired++;
2258
                        if (!empty($this->attributes[$object->table_element]['langfile'][$key])) {
2259
                            $langs->load($this->attributes[$object->table_element]['langfile'][$key]);
2260
                        }
2261
                        $error_field_required[$key] = $langs->transnoentitiesnoconv($value);
2262
                    }
2263
                }
2264
2265
                if (in_array($key_type, ['date'])) {
2266
                    // Clean parameters
2267
                    $value_key = dol_mktime(12, 0, 0, GETPOSTINT("options_" . $key . "month"), GETPOSTINT("options_" . $key . "day"), GETPOSTINT("options_" . $key . "year"));
2268
                } elseif (in_array($key_type, ['datetime'])) {
2269
                    // Clean parameters
2270
                    $value_key = dol_mktime(GETPOSTINT("options_" . $key . "hour"), GETPOSTINT("options_" . $key . "min"), GETPOSTINT("options_" . $key . "sec"), GETPOSTINT("options_" . $key . "month"), GETPOSTINT("options_" . $key . "day"), GETPOSTINT("options_" . $key . "year"), 'tzuserrel');
2271
                } elseif (in_array($key_type, ['datetimegmt'])) {
2272
                    // Clean parameters
2273
                    $value_key = dol_mktime(GETPOSTINT("options_" . $key . "hour"), GETPOSTINT("options_" . $key . "min"), GETPOSTINT("options_" . $key . "sec"), GETPOSTINT("options_" . $key . "month"), GETPOSTINT("options_" . $key . "day"), GETPOSTINT("options_" . $key . "year"), 'gmt');
2274
                } elseif (in_array($key_type, ['checkbox', 'chkbxlst'])) {
2275
                    $value_arr = GETPOST("options_" . $key, 'array'); // check if an array
2276
                    if (!empty($value_arr)) {
2277
                        $value_key = implode(',', $value_arr);
2278
                    } else {
2279
                        $value_key = '';
2280
                    }
2281
                } elseif (in_array($key_type, ['price', 'double'])) {
2282
                    $value_arr = GETPOST("options_" . $key, 'alpha');
2283
                    $value_key = price2num($value_arr);
2284
                } elseif (in_array($key_type, ['pricecy', 'double'])) {
2285
                    $value_key = price2num(GETPOST("options_" . $key, 'alpha')) . ':' . GETPOST("options_" . $key . "currency_id", 'alpha');
2286
                } elseif (in_array($key_type, ['html'])) {
2287
                    $value_key = GETPOST("options_" . $key, 'restricthtml');
2288
                } elseif (in_array($key_type, ['text'])) {
2289
                    $label_security_check = 'alphanohtml';
2290
                    // by default 'alphanohtml' (better security); hidden conf MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML allows basic html
2291
                    if (getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_REF_LABELS')) {
2292
                        $label_security_check = 'nohtml';
2293
                    } else {
2294
                        $label_security_check = !getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML') ? 'alphanohtml' : 'restricthtml';
2295
                    }
2296
                    $value_key = GETPOST("options_" . $key, $label_security_check);
2297
                } else {
2298
                    $value_key = GETPOST("options_" . $key);
2299
                    if (in_array($key_type, ['link']) && $value_key == '-1') {
2300
                        $value_key = '';
2301
                    }
2302
                }
2303
2304
                if (!empty($error_field_required[$key]) && $todefaultifmissing) {
2305
                    // Value is required but we have a default value and we asked to set empty value to the default value
2306
                    if (!empty($this->attributes[$object->table_element]['default']) && !is_null($this->attributes[$object->table_element]['default'][$key])) {
2307
                        $value_key = $this->attributes[$object->table_element]['default'][$key];
2308
                        unset($error_field_required[$key]);
2309
                        $nofillrequired--;
2310
                    }
2311
                }
2312
2313
                $object->array_options["options_" . $key] = $value_key;
2314
            }
2315
2316
            if ($nofillrequired) {
2317
                $langs->load('errors');
2318
                $this->error = $langs->trans('ErrorFieldsRequired') . ' : ' . implode(', ', $error_field_required);
2319
                setEventMessages($this->error, null, 'errors');
2320
                return -1;
2321
            } else {
2322
                return 1;
2323
            }
2324
        } else {
2325
            return 0;
2326
        }
2327
    }
2328
2329
    /**
2330
     * return array_options array of data of extrafields value of object sent by a search form
2331
     *
2332
     * @param array|string $extrafieldsobjectkey array of extrafields (old usage) or value of object->table_element
2333
     *                                           (new usage)
2334
     * @param string       $keyprefix            Prefix string to add into name and id of field (can be used to avoid
2335
     *                                           duplicate names)
2336
     * @param string       $keysuffix            Suffix string to add into name and id of field (can be used to avoid
2337
     *                                           duplicate names)
2338
     *
2339
     * @return array|int                                array_options set or 0 if no value
2340
     */
2341
    public function getOptionalsFromPost($extrafieldsobjectkey, $keyprefix = '', $keysuffix = '')
2342
    {
2343
        global $_POST;
2344
2345
        if (is_string($extrafieldsobjectkey) && !empty($this->attributes[$extrafieldsobjectkey]['label']) && is_array($this->attributes[$extrafieldsobjectkey]['label'])) {
2346
            $extralabels = $this->attributes[$extrafieldsobjectkey]['label'];
2347
        } else {
2348
            $extralabels = $extrafieldsobjectkey;
2349
        }
2350
2351
        if (is_array($extralabels)) {
2352
            $array_options = [];
2353
2354
            // Get extra fields
2355
            foreach ($extralabels as $key => $value) {
2356
                $key_type = '';
2357
                if (is_string($extrafieldsobjectkey)) {
2358
                    $key_type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
2359
                }
2360
2361
                if (in_array($key_type, ['date'])) {
2362
                    $dateparamname_start = $keysuffix . 'options_' . $key . $keyprefix . '_start';
2363
                    $dateparamname_end = $keysuffix . 'options_' . $key . $keyprefix . '_end';
2364
                    if (GETPOST($dateparamname_start . 'year') || GETPOST($dateparamname_end . 'year')) {
2365
                        $value_key = [];
2366
                        // values provided as a component year, month, day, etc.
2367
                        if (GETPOST($dateparamname_start . 'year')) {
2368
                            $value_key['start'] = dol_mktime(0, 0, 0, GETPOSTINT($dateparamname_start . 'month'), GETPOSTINT($dateparamname_start . 'day'), GETPOSTINT($dateparamname_start . 'year'));
2369
                        }
2370
                        if (GETPOST($dateparamname_start . 'year')) {
2371
                            $value_key['end'] = dol_mktime(23, 59, 59, GETPOSTINT($dateparamname_end . 'month'), GETPOSTINT($dateparamname_end . 'day'), GETPOSTINT($dateparamname_end . 'year'));
2372
                        }
2373
                    } elseif (GETPOST($keysuffix . "options_" . $key . $keyprefix . "year")) {
2374
                        // Clean parameters
2375
                        $value_key = dol_mktime(12, 0, 0, GETPOSTINT($keysuffix . "options_" . $key . $keyprefix . "month"), GETPOSTINT($keysuffix . "options_" . $key . $keyprefix . "day"), GETPOSTINT($keysuffix . "options_" . $key . $keyprefix . "year"));
2376
                    } else {
2377
                        continue; // Value was not provided, we should not set it.
2378
                    }
2379
                } elseif (in_array($key_type, ['datetime', 'datetimegmt'])) {
2380
                    $dateparamname_start = $keysuffix . 'options_' . $key . $keyprefix . '_start';
2381
                    $dateparamname_end = $keysuffix . 'options_' . $key . $keyprefix . '_end';
2382
                    if (GETPOST($dateparamname_start . 'year') && GETPOST($dateparamname_end . 'year')) {
2383
                        // values provided as a date pair (start date + end date), each date being broken down as year, month, day, etc.
2384
                        $dateparamname_end_hour = GETPOSTINT($dateparamname_end . 'hour') != '-1' ? GETPOSTINT($dateparamname_end . 'hour') : '23';
2385
                        $dateparamname_end_min = GETPOSTINT($dateparamname_end . 'min') != '-1' ? GETPOSTINT($dateparamname_end . 'min') : '59';
2386
                        $dateparamname_end_sec = GETPOSTINT($dateparamname_end . 'sec') != '-1' ? GETPOSTINT($dateparamname_end . 'sec') : '59';
2387
                        if ($key_type == 'datetimegmt') {
2388
                            $value_key = [
2389
                                'start' => dol_mktime(GETPOSTINT($dateparamname_start . 'hour'), GETPOSTINT($dateparamname_start . 'min'), GETPOSTINT($dateparamname_start . 'sec'), GETPOSTINT($dateparamname_start . 'month'), GETPOSTINT($dateparamname_start . 'day'), GETPOSTINT($dateparamname_start . 'year'), 'gmt'),
2390
                                'end' => dol_mktime($dateparamname_end_hour, $dateparamname_end_min, $dateparamname_end_sec, GETPOSTINT($dateparamname_end . 'month'), GETPOSTINT($dateparamname_end . 'day'), GETPOSTINT($dateparamname_end . 'year'), 'gmt'),
2391
                            ];
2392
                        } else {
2393
                            $value_key = [
2394
                                'start' => dol_mktime(GETPOSTINT($dateparamname_start . 'hour'), GETPOSTINT($dateparamname_start . 'min'), GETPOSTINT($dateparamname_start . 'sec'), GETPOSTINT($dateparamname_start . 'month'), GETPOSTINT($dateparamname_start . 'day'), GETPOSTINT($dateparamname_start . 'year'), 'tzuserrel'),
2395
                                'end' => dol_mktime($dateparamname_end_hour, $dateparamname_end_min, $dateparamname_end_sec, GETPOSTINT($dateparamname_end . 'month'), GETPOSTINT($dateparamname_end . 'day'), GETPOSTINT($dateparamname_end . 'year'), 'tzuserrel'),
2396
                            ];
2397
                        }
2398
                    } elseif (GETPOST($keysuffix . "options_" . $key . $keyprefix . "year")) {
2399
                        // Clean parameters
2400
                        if ($key_type == 'datetimegmt') {
2401
                            $value_key = dol_mktime(GETPOSTINT($keysuffix . "options_" . $key . $keyprefix . "hour"), GETPOSTINT($keysuffix . "options_" . $key . $keyprefix . "min"), GETPOSTINT($keysuffix . "options_" . $key . $keyprefix . "sec"), GETPOSTINT($keysuffix . "options_" . $key . $keyprefix . "month"), GETPOSTINT($keysuffix . "options_" . $key . $keyprefix . "day"), GETPOSTINT($keysuffix . "options_" . $key . $keyprefix . "year"), 'gmt');
2402
                        } else {
2403
                            $value_key = dol_mktime(GETPOSTINT($keysuffix . "options_" . $key . $keyprefix . "hour"), GETPOSTINT($keysuffix . "options_" . $key . $keyprefix . "min"), GETPOSTINT($keysuffix . "options_" . $key . $keyprefix . "sec"), GETPOSTINT($keysuffix . "options_" . $key . $keyprefix . "month"), GETPOSTINT($keysuffix . "options_" . $key . $keyprefix . "day"), GETPOSTINT($keysuffix . "options_" . $key . $keyprefix . "year"), 'tzuserrel');
2404
                        }
2405
                    } else {
2406
                        continue; // Value was not provided, we should not set it.
2407
                    }
2408
                } elseif ($key_type == 'select') {
2409
                    // to detect if we are in search context
2410
                    if (GETPOSTISARRAY($keysuffix . "options_" . $key . $keyprefix)) {
2411
                        $value_arr = GETPOST($keysuffix . "options_" . $key . $keyprefix, 'array:aZ09');
2412
                        // Make sure we get an array even if there's only one selected
2413
                        $value_arr = (array) $value_arr;
2414
                        $value_key = implode(',', $value_arr);
2415
                    } else {
2416
                        $value_key = GETPOST($keysuffix . "options_" . $key . $keyprefix);
2417
                    }
2418
                } elseif (in_array($key_type, ['checkbox', 'chkbxlst'])) {
2419
                    // We test on a hidden field named "..._multiselect" that is always set to 1 if param is in form so
2420
                    // when nothing is provided we can make a difference between noparam in the form and param was set to nothing.
2421
                    if (!GETPOSTISSET($keysuffix . "options_" . $key . $keyprefix . '_multiselect')) {
2422
                        continue; // Value was not provided, we should not set it.
2423
                    }
2424
                    $value_arr = GETPOST($keysuffix . "options_" . $key . $keyprefix);
2425
                    // Make sure we get an array even if there's only one checkbox
2426
                    $value_arr = (array) $value_arr;
2427
                    $value_key = implode(',', $value_arr);
2428
                } elseif (in_array($key_type, ['price', 'double', 'int'])) {
2429
                    if (!GETPOSTISSET($keysuffix . "options_" . $key . $keyprefix)) {
2430
                        continue; // Value was not provided, we should not set it.
2431
                    }
2432
                    $value_arr = GETPOST($keysuffix . "options_" . $key . $keyprefix);
2433
                    if ($keysuffix != 'search_') {    // If value is for a search, we must keep complex string like '>100 <=150'
2434
                        $value_key = price2num($value_arr);
2435
                    } else {
2436
                        $value_key = $value_arr;
2437
                    }
2438
                } elseif (in_array($key_type, ['boolean'])) {
2439
                    // We test on a hidden field named "..._boolean" that is always set to 1 if param is in form so
2440
                    // when nothing is provided we can make a difference between noparam in the form and param was set to nothing.
2441
                    if (!GETPOSTISSET($keysuffix . "options_" . $key . $keyprefix . "_boolean")) {
2442
                        $value_key = '';
2443
                    } else {
2444
                        $value_arr = GETPOST($keysuffix . "options_" . $key . $keyprefix);
2445
                        $value_key = $value_arr;
2446
                    }
2447
                } elseif (in_array($key_type, ['html'])) {
2448
                    if (!GETPOSTISSET($keysuffix . "options_" . $key . $keyprefix)) {
2449
                        continue; // Value was not provided, we should not set it.
2450
                    }
2451
                    $value_key = dol_htmlcleanlastbr(GETPOST($keysuffix . "options_" . $key . $keyprefix, 'restricthtml'));
2452
                } else {
2453
                    if (!GETPOST($keysuffix . "options_" . $key . $keyprefix)) {
2454
                        continue; // Value was not provided, we should not set it.
2455
                    }
2456
                    $value_key = GETPOST($keysuffix . "options_" . $key . $keyprefix);
2457
                }
2458
2459
                $array_options[$keysuffix . "options_" . $key] = $value_key; // No keyprefix here. keyprefix is used only for read.
2460
            }
2461
2462
            return $array_options;
2463
        }
2464
2465
        return 0;
2466
    }
2467
2468
    /**
2469
     * Return if a value is "empty" for a mandatory vision.
2470
     *
2471
     * @param mixed  $v    Value to test
2472
     * @param string $type Type of extrafield 'sellist', 'link', 'select', ...
2473
     *
2474
     * @return  boolean         True is value is an empty value, not allowed for a mandatory field.
2475
     */
2476
    public static function isEmptyValue($v, string $type)
2477
    {
2478
        if ($v === null || $v === '') {
2479
            return true;
2480
        }
2481
        if (is_array($v) || $type == 'select') {
2482
            return empty($v);
2483
        }
2484
        if ($type == 'link') {
2485
            return ($v == '-1');
2486
        }
2487
        if ($type == 'sellist') {
2488
            return ($v == '0');
2489
        }
2490
        return (empty($v) && $v != '0');
2491
    }
2492
}
2493