ExtraFields::showInputField()   F
last analyzed

Complexity

Conditions 191
Paths > 20000

Size

Total Lines 699
Code Lines 462

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 191
eloc 462
nc 11576900
nop 9
dl 0
loc 699
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

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