dolibarr 23.0.4
extrafields.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2002-2003 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2002-2003 Jean-Louis Bergamo <jlb@j1b.org>
4 * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
5 * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
6 * Copyright (C) 2009-2012 Laurent Destailleur <eldy@users.sourceforge.net>
7 * Copyright (C) 2009-2012 Regis Houssin <regis.houssin@inodbox.com>
8 * Copyright (C) 2013 Florian Henry <forian.henry@open-concept.pro>
9 * Copyright (C) 2015-2025 Charlene BENKE <charlene@patas-monkey.com>
10 * Copyright (C) 2016 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
11 * Copyright (C) 2017 Nicolas ZABOURI <info@inovea-conseil.com>
12 * Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
13 * Copyright (C) 2022 Antonin MARCHAL <antonin@letempledujeu.fr>
14 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
15 * Copyright (C) 2024 Benoît PASCAL <contact@p-ben.com>
16 * Copyright (C) 2024 Joachim Kueter <git-jk@bloxera.com>
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
43{
47 public $db;
48
52 public $attributes = array();
53
57 public $expand_display;
58
62 public $error = '';
63
67 public $errors = array();
68
72 public $errno;
73
77 public static $type2label = array(
78 'varchar' => 'String1Line',
79 'text' => 'TextLongNLines',
80 'html' => 'HtmlText',
81 'int' => 'Int',
82 'double' => 'Float',
83 'date' => 'Date',
84 'datetime' => 'DateAndTime',
85 'duration' => 'Duration',
86 //'datetimegmt'=>'DateAndTimeUTC',
87 'boolean' => 'Boolean',
88 'price' => 'ExtrafieldPrice',
89 'pricecy' => 'ExtrafieldPriceWithCurrency',
90 'phone' => 'ExtrafieldPhone',
91 'email' => 'ExtrafieldMail',
92 'url' => 'ExtrafieldUrl',
93 'ip' => 'ExtrafieldIP',
94 'icon' => 'Icon',
95 'password' => 'ExtrafieldPassword',
96 'radio' => 'ExtrafieldRadio',
97 'select' => 'ExtrafieldSelect',
98 'sellist' => 'ExtrafieldSelectList',
99 'checkbox' => 'ExtrafieldCheckBox',
100 'chkbxlst' => 'ExtrafieldCheckBoxFromList',
101 'link' => 'ExtrafieldLink',
102 'point' => 'ExtrafieldPointGeo',
103 'multipts' => 'ExtrafieldMultiPointGeo',
104 'linestrg' => 'ExtrafieldLinestringGeo',
105 'polygon' => 'ExtrafieldPolygonGeo',
106 'separate' => 'ExtrafieldSeparator',
107 'stars' => 'ExtrafieldStars',
108 );
109
111 public static $geoDataTypes = array(
112 'point' => array(
113 'ST_Function' => 'ST_PointFromText',
114 'shortname' => 'point'
115 ),
116 'multipts' => array(
117 'ST_Function' => 'ST_MultiPointFromText',
118 'shortname' => 'multipoint'
119 ),
120 'linestrg' => array(
121 'ST_Function' => 'ST_LineFromText',
122 'shortname' => 'line'
123 ),
124 'polygon' => array(
125 'ST_Function' => 'ST_PolyFromText',
126 'shortname' => 'polygon'
127 )
128 );
129
135 public function __construct($db)
136 {
137 $this->db = $db;
138 }
139
168 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(), $aiprompt = "", $emptyonclone = 0)
169 {
170 if (empty($attrname)) {
171 return -1;
172 }
173 if (empty($label)) {
174 return -1;
175 }
176
177 $result = 0;
178
179 // Clean properties
180 if ($type == 'separator' || $type == 'separate') {
181 $type = 'separate';
182 $unique = 0;
183 $required = 0;
184 } // Force unique and not required if this is a separator field to avoid troubles.
185 if ($elementtype == 'thirdparty') {
186 $elementtype = 'societe';
187 }
188 if ($elementtype == 'contact') {
189 $elementtype = 'socpeople';
190 }
191 // If property has a computed formula, it must not be a required or unique field
192 if (!empty($computed)) {
193 $required = 0;
194 $unique = 0;
195 }
196
197 // Create field into database except for separator type which is not stored in database
198 if ($type != 'separate') {
199 $result = $this->create($attrname, $type, $size, $elementtype, $unique, $required, $default_value, $param, $perms, $list, $computed, $help, $moreparams);
200 }
201 $err1 = $this->errno;
202 if ($result > 0 || $err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' || $type == 'separate') {
203 // Add declaration of field into table
204 $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, $aiprompt, $emptyonclone);
205 $err2 = $this->errno;
206 if ($result2 > 0
207 || ($err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' && $err2 == 'DB_ERROR_RECORD_ALREADY_EXISTS')
208 || ($type == 'separate' && $err2 == 'DB_ERROR_RECORD_ALREADY_EXISTS')) {
209 $this->error = '';
210 $this->errno = '0';
211 return 1;
212 } else {
213 return -2;
214 }
215 } else {
216 return -1;
217 }
218 }
219
247 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(), $emptyonclone = 0)
248 {
249 if (empty($attrname)) {
250 return -1;
251 }
252 if (empty($label)) {
253 return -1;
254 }
255
256 $result = 0;
257
258 if ($type == 'separator' || $type == 'separate') {
259 $type = 'separate';
260 $unique = 0;
261 $required = 0;
262 } // Force unique and not required if this is a separator field to avoid troubles.
263 if ($elementtype == 'thirdparty') {
264 $elementtype = 'societe';
265 }
266 if ($elementtype == 'contact') {
267 $elementtype = 'socpeople';
268 }
269
270 // Create field into database except for separator type which is not stored in database
271 if ($type != 'separate') {
272 dol_syslog(get_class($this).'::thisupdate', LOG_DEBUG);
273 $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, '', $emptyonclone);
274 }
275 $err1 = $this->errno;
276 if ($result > 0 || $err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' || $type == 'separate') {
277 // Add declaration of field into table
278 dol_syslog(get_class($this).'::thislabel', LOG_DEBUG);
279 $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, '', $emptyonclone);
280 $err2 = $this->errno;
281 if ($result2 > 0 || ($err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' && $err2 == 'DB_ERROR_RECORD_ALREADY_EXISTS')) {
282 $this->error = '';
283 $this->errno = '0';
284 return 1;
285 } else {
286 return -2;
287 }
288 } else {
289 return -1;
290 }
291 }
292
312 private function create($attrname, $type = 'varchar', $length = '255', $elementtype = '', $unique = 0, $required = 0, $default_value = '', $param = array(), $perms = '', $list = '0', $computed = '', $help = '', $moreparams = array())
313 {
314 if ($elementtype == 'thirdparty') {
315 $elementtype = 'societe';
316 }
317 if ($elementtype == 'contact') {
318 $elementtype = 'socpeople';
319 }
320
321 $table = $elementtype.'_extrafields';
322 if ($elementtype == 'categorie') {
323 $table = 'categories_extrafields';
324 }
325
326 if (!empty($attrname) && preg_match("/^\w[a-zA-Z0-9_]*$/", $attrname) && !is_numeric($attrname)) {
327 if ($type == 'boolean') {
328 $typedb = 'int';
329 $lengthdb = '1';
330 } elseif ($type == 'price') {
331 $typedb = 'double';
332 $lengthdb = '24,8';
333 } elseif ($type == 'pricecy') {
334 $typedb = 'varchar';
335 $lengthdb = '64';
336 } elseif ($type == 'phone') {
337 $typedb = 'varchar';
338 $lengthdb = '20';
339 } elseif ($type == 'email' || $type == 'mail' || $type == 'ip' || $type == 'icon') {
340 $typedb = 'varchar';
341 $lengthdb = '128';
342 } elseif ($type == 'url') {
343 $typedb = 'varchar';
344 $lengthdb = '255';
345 } elseif (($type == 'select') || ($type == 'sellist') || ($type == 'radio') || ($type == 'checkbox') || ($type == 'chkbxlst')) {
346 $typedb = 'varchar';
347 $lengthdb = '255';
348 } elseif ($type == 'link') {
349 $typedb = 'int';
350 $lengthdb = '11';
351 } elseif ($type == 'duration') {
352 $typedb = 'int';
353 $lengthdb = '11';
354 } elseif ($type == 'point') {
355 $typedb = 'point';
356 $lengthdb = '';
357 } elseif ($type == 'multipts') {
358 $typedb = 'multipoint';
359 $lengthdb = '';
360 } elseif ($type == 'linestrg') {
361 $typedb = 'linestring';
362 $lengthdb = '';
363 } elseif ($type == 'polygon') {
364 $typedb = 'polygon';
365 $lengthdb = '';
366 } elseif ($type == 'html') {
367 $typedb = 'text';
368 $lengthdb = $length;
369 } elseif ($type == 'password') {
370 $typedb = 'varchar';
371 $lengthdb = '128';
372 } elseif ($type == 'stars') {
373 $typedb = 'int';
374 $lengthdb = $length;
375 } else {
376 $typedb = $type;
377 $lengthdb = $length;
378 if ($type == 'varchar' && empty($lengthdb)) {
379 $lengthdb = '255';
380 }
381 }
382 $field_desc = array(
383 'type' => $typedb,
384 'value' => $lengthdb,
385 'null' => ($required ? 'NOT NULL' : 'NULL'),
386 'default' => $default_value
387 );
388
389 $result = $this->db->DDLAddField($this->db->prefix().$this->db->sanitize($table), $attrname, $field_desc);
390 if ($result > 0) {
391 if ($unique) {
392 $sql = "ALTER TABLE ".$this->db->prefix().$this->db->sanitize($table)." ADD UNIQUE INDEX uk_".$this->db->sanitize($table)."_".$attrname." (".$attrname.")";
393 $resql = $this->db->query($sql, 1, 'dml');
394 }
395 return 1;
396 } else {
397 $this->error = $this->db->lasterror();
398 $this->errno = $this->db->lasterrno();
399 return -1;
400 }
401 } else {
402 return 0;
403 }
404 }
405
406 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
436 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(), $aiprompt = "", $emptyonclone = 0)
437 {
438 // phpcs:enable
439 global $conf, $user;
440
441 if ($elementtype == 'thirdparty') {
442 $elementtype = 'societe';
443 }
444 if ($elementtype == 'contact') {
445 $elementtype = 'socpeople';
446 }
447
448 // Clean parameters
449 if (empty($pos)) {
450 $pos = 0;
451 }
452 if (empty($list)) {
453 $list = '0';
454 }
455 if (empty($required)) {
456 $required = 0;
457 }
458 if (empty($unique)) {
459 $unique = 0;
460 }
461 if (empty($printable)) {
462 $printable = 0;
463 }
464 if (empty($alwayseditable)) {
465 $alwayseditable = 0;
466 }
467 if (empty($emptyonclone)) {
468 $emptyonclone = 0;
469 }
470 if (empty($totalizable)) {
471 $totalizable = 0;
472 }
473
474 $css = '';
475 if (!empty($moreparams) && !empty($moreparams['css'])) {
476 $css = $moreparams['css'];
477 }
478 $csslist = '';
479 if (!empty($moreparams) && !empty($moreparams['csslist'])) {
480 $csslist = $moreparams['csslist'];
481 }
482 $cssview = '';
483 if (!empty($moreparams) && !empty($moreparams['cssview'])) {
484 $cssview = $moreparams['cssview'];
485 }
486
487 if (!empty($attrname) && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname) && !is_numeric($attrname)) {
488 if (is_array($param) && count($param) > 0) {
489 $params = serialize($param);
490 } elseif (strlen($param) > 0) {
491 $params = trim($param);
492 } else {
493 $params = '';
494 }
495
496 $sql = "INSERT INTO ".$this->db->prefix()."extrafields(";
497 $sql .= " name,";
498 $sql .= " label,";
499 $sql .= " type,";
500 $sql .= " pos,";
501 $sql .= " size,";
502 $sql .= " entity,";
503 $sql .= " elementtype,";
504 $sql .= " fieldunique,";
505 $sql .= " fieldrequired,";
506 $sql .= " param,";
507 $sql .= " alwayseditable,";
508 $sql .= " perms,";
509 $sql .= " langs,";
510 $sql .= " list,";
511 $sql .= " printable,";
512 $sql .= " fielddefault,";
513 $sql .= " fieldcomputed,";
514 $sql .= " fk_user_author,";
515 $sql .= " fk_user_modif,";
516 $sql .= " datec,";
517 $sql .= " enabled,";
518 $sql .= " help,";
519 $sql .= " totalizable,";
520 $sql .= " css,";
521 $sql .= " csslist,";
522 $sql .= " cssview,";
523 $sql .= " aiprompt,";
524 $sql .= " emptyonclone";
525 $sql .= " )";
526 $sql .= " VALUES('".$this->db->escape($attrname)."',";
527 $sql .= " '".$this->db->escape($label)."',";
528 $sql .= " '".$this->db->escape($type)."',";
529 $sql .= " ".((int) $pos).",";
530 $sql .= " '".$this->db->escape($size)."',";
531 $sql .= " ".($entity === '' ? $conf->entity : $entity).",";
532 $sql .= " '".$this->db->escape($elementtype)."',";
533 $sql .= " ".((int) $unique).",";
534 $sql .= " ".((int) $required).",";
535 $sql .= " '".$this->db->escape($params)."',";
536 $sql .= " ".((int) $alwayseditable).",";
537 $sql .= " ".($perms ? "'".$this->db->escape($perms)."'" : "null").",";
538 $sql .= " ".($langfile ? "'".$this->db->escape($langfile)."'" : "null").",";
539 $sql .= " '".$this->db->escape($list)."',";
540 $sql .= " '".$this->db->escape((string) $printable)."',";
541 $sql .= " ".($default ? "'".$this->db->escape($default)."'" : "null").",";
542 $sql .= " ".($computed ? "'".$this->db->escape($computed)."'" : "null").",";
543 $sql .= " ".(is_object($user) ? $user->id : 0).",";
544 $sql .= " ".(is_object($user) ? $user->id : 0).",";
545 $sql .= "'".$this->db->idate(dol_now())."',";
546 $sql .= " ".($enabled ? "'".$this->db->escape($enabled)."'" : "1").",";
547 $sql .= " ".($help ? "'".$this->db->escape($help)."'" : "null").",";
548 $sql .= " ".($totalizable ? 'TRUE' : 'FALSE').",";
549 $sql .= " ".($css ? "'".$this->db->escape($css)."'" : "null").",";
550 $sql .= " ".($csslist ? "'".$this->db->escape($csslist)."'" : "null").",";
551 $sql .= " ".($cssview ? "'".$this->db->escape($cssview)."'" : "null").",";
552 $sql .= " '".$this->db->escape($aiprompt)."',";
553 $sql .= " ".((int) $emptyonclone);
554 $sql .= ')';
555
556 if ($this->db->query($sql)) {
557 dol_syslog(get_class($this)."::create_label_success", LOG_DEBUG);
558 return 1;
559 } else {
560 dol_syslog(get_class($this)."::create_label_error", LOG_DEBUG);
561 $this->error = $this->db->lasterror();
562 $this->errno = $this->db->lasterrno();
563 return -1;
564 }
565 }
566 return -1;
567 }
568
576 public function delete($attrname, $elementtype = '')
577 {
578 if ($elementtype == 'thirdparty') {
579 $elementtype = 'societe';
580 }
581 if ($elementtype == 'contact') {
582 $elementtype = 'socpeople';
583 }
584
585 $table = $elementtype.'_extrafields';
586 if ($elementtype == 'categorie') {
587 $table = 'categories_extrafields';
588 }
589
590 $error = 0;
591
592 if (!empty($attrname) && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
593 $result = $this->delete_label($attrname, $elementtype);
594 if ($result < 0) {
595 $this->error = $this->db->lasterror();
596 $this->errors[] = $this->db->lasterror();
597 $error++;
598 }
599
600 if (!$error) {
601 $sql = "SELECT COUNT(rowid) as nb";
602 $sql .= " FROM ".$this->db->prefix()."extrafields";
603 $sql .= " WHERE elementtype = '".$this->db->escape($elementtype)."'";
604 $sql .= " AND name = '".$this->db->escape($attrname)."'";
605 //$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
606 $resql = $this->db->query($sql);
607 if ($resql) {
608 $obj = $this->db->fetch_object($resql);
609 if ($obj->nb <= 0) {
610 $result = $this->db->DDLDropField($this->db->prefix().$table, $attrname); // This also drop the unique key
611 if ($result < 0) {
612 $this->error = $this->db->lasterror();
613 $this->errors[] = $this->db->lasterror();
614 $error++;
615 }
616 }
617 } else {
618 $this->error = $this->db->lasterror();
619 $this->errors[] = $this->db->lasterror();
620 $error++;
621 }
622 }
623 if (empty($error)) {
624 return $result;
625 } else {
626 return $error * -1;
627 }
628 } else {
629 return 0;
630 }
631 }
632
633 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
641 private function delete_label($attrname, $elementtype = '')
642 {
643 // phpcs:enable
644 global $conf;
645
646 if ($elementtype == 'thirdparty') {
647 $elementtype = 'societe';
648 }
649 if ($elementtype == 'contact') {
650 $elementtype = 'socpeople';
651 }
652
653 if (isset($attrname) && $attrname != '' && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
654 $sql = "DELETE FROM ".$this->db->prefix()."extrafields";
655 $sql .= " WHERE name = '".$this->db->escape($attrname)."'";
656 $sql .= " AND entity IN (0,".$conf->entity.')';
657 if (!empty($elementtype)) {
658 $sql .= " AND elementtype = '".$this->db->escape($elementtype)."'";
659 }
660
661 dol_syslog(get_class($this)."::delete_label", LOG_DEBUG);
662 $resql = $this->db->query($sql);
663 if ($resql) {
664 return 1;
665 } else {
666 dol_print_error($this->db);
667 return -1;
668 }
669 } else {
670 return 0;
671 }
672 }
673
703 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(), $aiprompt = "", $emptyonclone = 0)
704 {
705 global $action, $hookmanager;
706
707 $result = 0;
708
709 if ($elementtype == 'thirdparty') {
710 $elementtype = 'societe';
711 }
712 if ($elementtype == 'contact') {
713 $elementtype = 'socpeople';
714 }
715
716 $table = $elementtype.'_extrafields';
717 if ($elementtype == 'categorie') {
718 $table = 'categories_extrafields';
719 }
720
721 if (isset($attrname) && $attrname != '' && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
722 // Clean parameters
723 if ($type == 'boolean') {
724 $typedb = 'int';
725 $lengthdb = '1';
726 } elseif ($type == 'price') {
727 $typedb = 'double';
728 $lengthdb = '24,8';
729 } elseif ($type == 'pricecy') {
730 $typedb = 'varchar';
731 $lengthdb = '64';
732 } elseif ($type == 'phone') {
733 $typedb = 'varchar';
734 $lengthdb = '20';
735 } elseif ($type == 'mail' || $type == 'email' || $type == 'ip' || $type == 'icon') {
736 $typedb = 'varchar';
737 $lengthdb = '128';
738 } elseif ($type == 'url') {
739 $typedb = 'varchar';
740 $lengthdb = '255';
741 } elseif (($type == 'select') || ($type == 'sellist') || ($type == 'radio') || ($type == 'checkbox') || ($type == 'chkbxlst')) {
742 $typedb = 'varchar';
743 $lengthdb = '255';
744 } elseif ($type == 'html') {
745 $typedb = 'text';
746 $lengthdb = $length;
747 } elseif ($type == 'link') {
748 $typedb = 'int';
749 $lengthdb = '11';
750 } elseif ($type == 'duration') {
751 $typedb = 'int';
752 $lengthdb = '11';
753 } elseif ($type == 'point') {
754 $typedb = 'point';
755 $lengthdb = '';
756 } elseif ($type == 'multipts') {
757 $typedb = 'multipoint';
758 $lengthdb = '';
759 } elseif ($type == 'linestrg') {
760 $typedb = 'linestring';
761 $lengthdb = '';
762 } elseif ($type == 'polygon') {
763 $typedb = 'polygon';
764 $lengthdb = '';
765 } elseif ($type == 'password') {
766 $typedb = 'varchar';
767 $lengthdb = '128';
768 } elseif ($type == 'stars') {
769 $typedb = 'int';
770 $lengthdb = $length;
771 } else {
772 $typedb = $type;
773 $lengthdb = $length;
774 }
775 $field_desc = array('type' => $typedb, 'value' => $lengthdb, 'null' => ($required ? 'NOT NULL' : 'NULL'), 'default' => $default);
776
777 // If property has a computed formula, it must not be a required or unique field
778 if (!empty($computed)) {
779 $required = 0;
780 $unique = 0;
781 }
782
783 if (is_object($hookmanager)) {
784 $hookmanager->initHooks(array('extrafieldsdao'));
785 $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, 'emptyonclone' => $emptyonclone, 'perms' => $perms, 'list' => $list, 'help' => $help, 'default' => $default, 'computed' => $computed, 'entity' => $entity, 'langfile' => $langfile, 'enabled' => $enabled, 'totalizable' => $totalizable, 'printable' => $printable);
786 $reshook = $hookmanager->executeHooks('updateExtrafields', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
787
788 if ($reshook < 0) {
789 $this->error = $this->db->lasterror();
790 return -1;
791 }
792 }
793
794 dol_syslog(get_class($this).'::DDLUpdateField', LOG_DEBUG);
795 if ($type != 'separate') { // No table update when separate type
796 // TODO: Verify, adjust - field_desc has 'value' (not expected), and is missing 'label','enabled','position','visible'
797 $result = $this->db->DDLUpdateField($this->db->prefix().$table, $attrname, $field_desc);
798 }
799 if ($result > 0 || $type == 'separate') {
800 if ($label) {
801 dol_syslog(get_class($this).'::update_label', LOG_DEBUG);
802 $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, $aiprompt, $emptyonclone);
803 }
804 if ($result > 0) {
805 $sql = '';
806 if ($unique) {
807 dol_syslog(get_class($this).'::update_unique', LOG_DEBUG);
808 $sql = "ALTER TABLE ".$this->db->prefix().$table." ADD UNIQUE INDEX uk_".$table."_".$this->db->sanitize($attrname)." (".$this->db->sanitize($attrname).")";
809 } else {
810 dol_syslog(get_class($this).'::update_common', LOG_DEBUG);
811 $sql = "ALTER TABLE ".$this->db->prefix().$table." DROP INDEX uk_".$table."_".$this->db->sanitize($attrname);
812 }
813 dol_syslog(get_class($this).'::update', LOG_DEBUG);
814 $resql = $this->db->query($sql, 1, 'dml');
815 /*if ($resql < 0) {
816 $this->error = $this->db->lasterror();
817 return -1;
818 }*/
819 return 1;
820 } else {
821 $this->error = $this->db->lasterror();
822 return -1;
823 }
824 } else {
825 $this->error = $this->db->lasterror();
826 return -1;
827 }
828 } else {
829 return 0;
830 }
831 }
832
833 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
864 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(), $aiprompt = "", $emptyonclone = 0)
865 {
866 // phpcs:enable
867 global $conf, $user;
868 dol_syslog(get_class($this)."::update_label ".$attrname.", ".$label.", ".$type.", ".$size.", ".$elementtype.", ".$unique.", ".$required.", ".$pos.", ".$alwayseditable.", ".$perms.", ".$list.", ".$default.", ".$computed.", ".$entity.", ".$langfile.", ".$enabled.", ".$totalizable.", ".$printable.", ".$aiprompt);
869
870 // Clean parameters
871 if ($elementtype == 'thirdparty') {
872 $elementtype = 'societe';
873 }
874 if ($elementtype == 'contact') {
875 $elementtype = 'socpeople';
876 }
877
878 if (empty($pos)) {
879 $pos = 0;
880 }
881 if (empty($list)) {
882 $list = '0';
883 }
884 if (empty($totalizable)) {
885 $totalizable = 0;
886 }
887 if (empty($required)) {
888 $required = 0;
889 }
890 if (empty($unique)) {
891 $unique = 0;
892 }
893 if (empty($alwayseditable)) {
894 $alwayseditable = 0;
895 }
896 if (empty($emptyonclone)) {
897 $emptyonclone = 0;
898 }
899
900 $css = '';
901 if (!empty($moreparams) && !empty($moreparams['css'])) {
902 $css = $moreparams['css'];
903 }
904 $csslist = '';
905 if (!empty($moreparams) && !empty($moreparams['csslist'])) {
906 $csslist = $moreparams['csslist'];
907 }
908 $cssview = '';
909 if (!empty($moreparams) && !empty($moreparams['cssview'])) {
910 $cssview = $moreparams['cssview'];
911 }
912
913 if (isset($attrname) && $attrname != '' && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
914 $this->db->begin();
915
916 if (is_array($param) && count($param) > 0) {
917 $params = serialize($param);
918 } elseif (is_array($param)) {
919 $params = '';
920 } elseif (strlen($param) > 0) {
921 $params = trim($param);
922 } else {
923 $params = '';
924 }
925
926 if ($entity === '' || $entity != '0') {
927 // We don't want on all entities, we delete all and current
928 $sql_del = "DELETE FROM ".$this->db->prefix()."extrafields";
929 $sql_del .= " WHERE name = '".$this->db->escape($attrname)."'";
930 $sql_del .= " AND entity IN (0, ".($entity === '' ? $conf->entity : $entity).")";
931 $sql_del .= " AND elementtype = '".$this->db->escape($elementtype)."'";
932 } else {
933 // We want on all entities ($entities = '0'), we delete on all only (we keep setup specific to each entity)
934 $sql_del = "DELETE FROM ".$this->db->prefix()."extrafields";
935 $sql_del .= " WHERE name = '".$this->db->escape($attrname)."'";
936 $sql_del .= " AND entity = 0";
937 $sql_del .= " AND elementtype = '".$this->db->escape($elementtype)."'";
938 }
939 $resql1 = $this->db->query($sql_del);
940
941 $sql = "INSERT INTO ".$this->db->prefix()."extrafields(";
942 $sql .= " name,"; // This is code
943 $sql .= " entity,";
944 $sql .= " label,";
945 $sql .= " type,";
946 $sql .= " size,";
947 $sql .= " elementtype,";
948 $sql .= " fieldunique,";
949 $sql .= " fieldrequired,";
950 $sql .= " perms,";
951 $sql .= " langs,";
952 $sql .= " pos,";
953 $sql .= " alwayseditable,";
954 $sql .= " param,";
955 $sql .= " list,";
956 $sql .= " printable,";
957 $sql .= " totalizable,";
958 $sql .= " fielddefault,";
959 $sql .= " fieldcomputed,";
960 $sql .= " fk_user_author,";
961 $sql .= " fk_user_modif,";
962 $sql .= " datec,";
963 $sql .= " enabled,";
964 $sql .= " help,";
965 $sql .= " css,";
966 $sql .= " csslist,";
967 $sql .= " cssview,";
968 $sql .= " aiprompt,";
969 $sql .= " emptyonclone";
970 $sql .= ") VALUES (";
971 $sql .= "'".$this->db->escape($attrname)."',";
972 $sql .= " ".($entity === '' ? $conf->entity : $entity).",";
973 $sql .= " '".$this->db->escape($label)."',";
974 $sql .= " '".$this->db->escape($type)."',";
975 $sql .= " '".$this->db->escape($size)."',";
976 $sql .= " '".$this->db->escape($elementtype)."',";
977 $sql .= " ".((int) $unique).",";
978 $sql .= " ".((int) $required).",";
979 $sql .= " ".($perms ? "'".$this->db->escape($perms)."'" : "null").",";
980 $sql .= " ".($langfile ? "'".$this->db->escape($langfile)."'" : "null").",";
981 $sql .= " ".((int) $pos).",";
982 $sql .= " ".((int) $alwayseditable).",";
983 $sql .= " '".$this->db->escape($params)."',";
984 $sql .= " '".$this->db->escape($list)."',";
985 $sql .= " ".((int) $printable).",";
986 $sql .= " ".($totalizable ? 'TRUE' : 'FALSE').",";
987 $sql .= " ".(($default != '') ? "'".$this->db->escape($default)."'" : "null").",";
988 $sql .= " ".($computed ? "'".$this->db->escape($computed)."'" : "null").",";
989 $sql .= " ".((int) $user->id).",";
990 $sql .= " ".((int) $user->id).",";
991 $sql .= "'".$this->db->idate(dol_now())."',";
992 $sql .= "'".$this->db->escape($enabled)."',";
993 $sql .= " ".($help ? "'".$this->db->escape($help)."'" : "null").",";
994 $sql .= " ".($css ? "'".$this->db->escape($css)."'" : "null").",";
995 $sql .= " ".($csslist ? "'".$this->db->escape($csslist)."'" : "null").",";
996 $sql .= " ".($cssview ? "'".$this->db->escape($cssview)."'" : "null").",";
997 $sql .= " '".$this->db->escape($aiprompt)."',";
998 $sql .= " ".((int) $emptyonclone);
999 $sql .= ")";
1000
1001 $resql2 = $this->db->query($sql);
1002
1003 if ($resql1 && $resql2) {
1004 $this->db->commit();
1005 return 1;
1006 } else {
1007 $this->db->rollback();
1008 dol_print_error($this->db);
1009 return -1;
1010 }
1011 } else {
1012 return 0;
1013 }
1014 }
1015
1016 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1026 public function fetch_name_optionals_label($elementtype, $forceload = false, $attrname = '')
1027 {
1028 // phpcs:enable
1029 global $conf;
1030
1031 if (empty($elementtype)) {
1032 return array();
1033 }
1034
1035 if ($elementtype == 'thirdparty') {
1036 $elementtype = 'societe';
1037 }
1038 if ($elementtype == 'contact') {
1039 $elementtype = 'socpeople';
1040 }
1041 if ($elementtype == 'order_supplier') {
1042 $elementtype = 'commande_fournisseur';
1043 }
1044
1045 if ($elementtype != 'all' && isset($this->attributes[$elementtype]) && $this->attributes[$elementtype]['loaded'] == 1 && !$forceload && isset($this->attributes[$elementtype]['label'])) {
1046 return $this->attributes[$elementtype]['label'];
1047 }
1048
1049 $array_name_label = array();
1050
1051 // 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
1052 $sql = "SELECT rowid, name, label, type, size, elementtype, fieldunique, fieldrequired, param, pos, alwayseditable, emptyonclone, perms, langs, list, printable, totalizable, fielddefault, fieldcomputed, entity, enabled, help, aiprompt,";
1053 $sql .= " css, cssview, csslist";
1054 $sql .= " FROM ".$this->db->prefix()."extrafields";
1055 //$sql.= " WHERE entity IN (0,".$conf->entity.")"; // Filter is done later
1056 if ($elementtype && $elementtype != 'all') {
1057 $sql .= " WHERE elementtype = '".$this->db->escape($elementtype)."'"; // Filed with object->table_element
1058 }
1059 if ($attrname && $elementtype && $elementtype != 'all') {
1060 $sql .= " AND name = '".$this->db->escape($attrname)."'";
1061 }
1062 $sql .= " ORDER BY pos";
1063
1064 $resql = $this->db->query($sql);
1065 if ($resql) {
1066 $count = 0;
1067 if ($this->db->num_rows($resql)) {
1068 while ($tab = $this->db->fetch_object($resql)) {
1069 if ($tab->entity != 0 && $tab->entity != $conf->entity) {
1070 // 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
1071 if ($tab->fieldrequired && is_null($tab->fielddefault)) {
1072 $this->attributes[$tab->elementtype]['mandatoryfieldsofotherentities'][$tab->name] = $tab->type;
1073 }
1074 continue;
1075 }
1076
1077 // We can add this attribute to object. TODO Remove this and return $this->attributes[$elementtype]['label']
1078 if ($tab->type != 'separate') {
1079 $array_name_label[$tab->name] = $tab->label;
1080 }
1081
1082
1083 $this->attributes[$tab->elementtype]['type'][$tab->name] = $tab->type;
1084 $this->attributes[$tab->elementtype]['label'][$tab->name] = $tab->label;
1085 $this->attributes[$tab->elementtype]['size'][$tab->name] = $tab->size;
1086 $this->attributes[$tab->elementtype]['elementtype'][$tab->name] = $tab->elementtype;
1087 $this->attributes[$tab->elementtype]['default'][$tab->name] = $tab->fielddefault;
1088 $this->attributes[$tab->elementtype]['computed'][$tab->name] = $tab->fieldcomputed;
1089 $this->attributes[$tab->elementtype]['unique'][$tab->name] = $tab->fieldunique;
1090 $this->attributes[$tab->elementtype]['required'][$tab->name] = $tab->fieldrequired;
1091 $this->attributes[$tab->elementtype]['param'][$tab->name] = ($tab->param ? jsonOrUnserialize($tab->param) : '');
1092 $this->attributes[$tab->elementtype]['pos'][$tab->name] = $tab->pos;
1093 $this->attributes[$tab->elementtype]['alwayseditable'][$tab->name] = $tab->alwayseditable;
1094 $this->attributes[$tab->elementtype]['emptyonclone'][$tab->name] = $tab->emptyonclone;
1095 $this->attributes[$tab->elementtype]['perms'][$tab->name] = $tab->perms;
1096 $this->attributes[$tab->elementtype]['langfile'][$tab->name] = $tab->langs;
1097 $this->attributes[$tab->elementtype]['list'][$tab->name] = $tab->list;
1098 $this->attributes[$tab->elementtype]['printable'][$tab->name] = $tab->printable;
1099 $this->attributes[$tab->elementtype]['totalizable'][$tab->name] = ($tab->totalizable ? 1 : 0);
1100 $this->attributes[$tab->elementtype]['entityid'][$tab->name] = $tab->entity;
1101 $this->attributes[$tab->elementtype]['enabled'][$tab->name] = $tab->enabled;
1102 $this->attributes[$tab->elementtype]['help'][$tab->name] = $tab->help;
1103 $this->attributes[$tab->elementtype]['aiprompt'][$tab->name] = $tab->aiprompt;
1104 $this->attributes[$tab->elementtype]['css'][$tab->name] = $tab->css;
1105 $this->attributes[$tab->elementtype]['cssview'][$tab->name] = $tab->cssview;
1106 $this->attributes[$tab->elementtype]['csslist'][$tab->name] = $tab->csslist;
1107
1108 $this->attributes[$tab->elementtype]['loaded'] = 1;
1109 $count++;
1110 }
1111 }
1112 if ($elementtype) {
1113 $this->attributes[$elementtype]['loaded'] = 1; // Note: If nothing is found, we also set the key 'loaded' to 1.
1114 $this->attributes[$elementtype]['count'] = $count;
1115 }
1116 } else {
1117 $this->error = $this->db->lasterror();
1118 dol_syslog(get_class($this)."::fetch_name_optionals_label ".$this->error, LOG_ERR);
1119 }
1120
1121 return $array_name_label;
1122 }
1123
1124
1140 public function showInputField($key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '', $object = 0, $extrafieldsobjectkey = '', $mode = 0)
1141 {
1142 global $conf, $langs, $form, $hookmanager;
1143
1144 if (!is_object($form)) {
1145 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
1146 $form = new Form($this->db);
1147 }
1148
1149 if ($mode == 1) { // When field is used for search input into a list, we limit its size.
1150 $morecss = 'maxwidth125';
1151 }
1152
1153 $parameters = array(
1154 'key' => $key,
1155 'value' => &$value,
1156 'moreparam' => $moreparam,
1157 'keysuffix' => $keysuffix,
1158 'keyprefix' => $keyprefix,
1159 'morecss' => $morecss,
1160 'object' => $object,
1161 'extrafieldsobjectkey' => $extrafieldsobjectkey,
1162 'mode' => $mode
1163 );
1164 $action = '';
1165
1166 $reshook = $hookmanager->executeHooks('showInputExtraField', $parameters, $this, $action); // Note that $action and $object may have been modified by hook
1167 if ($reshook > 0) {
1168 return $hookmanager->resPrint;
1169 }
1170
1171 $objectid = (is_numeric($object) ? $object : $object->id);
1172
1173 $out = '';
1174
1175 if (!preg_match('/options_$/', $keyprefix)) { // Because we work on extrafields, we add 'options_' to prefix if not already added
1176 $keyprefix .= 'options_';
1177 }
1178
1179 if (empty($extrafieldsobjectkey)) {
1180 dol_syslog(get_class($this).'::showInputField extrafieldsobjectkey required', LOG_ERR);
1181 return 'BadValueForParamExtraFieldsObjectKey';
1182 }
1183
1184 $label = $this->attributes[$extrafieldsobjectkey]['label'][$key];
1185 $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
1186 $size = $this->attributes[$extrafieldsobjectkey]['size'][$key];
1187 $default = $this->attributes[$extrafieldsobjectkey]['default'][$key];
1188 $computed = $this->attributes[$extrafieldsobjectkey]['computed'][$key];
1189 $unique = $this->attributes[$extrafieldsobjectkey]['unique'][$key];
1190 $required = $this->attributes[$extrafieldsobjectkey]['required'][$key];
1191 $param = $this->attributes[$extrafieldsobjectkey]['param'][$key];
1192 $perms = (int) dol_eval((string) $this->attributes[$extrafieldsobjectkey]['perms'][$key], 1, 1, '2');
1193 $langfile = $this->attributes[$extrafieldsobjectkey]['langfile'][$key];
1194 $list = (string) dol_eval((string) $this->attributes[$extrafieldsobjectkey]['list'][$key], 1, 1, '2');
1195 $totalizable = $this->attributes[$extrafieldsobjectkey]['totalizable'][$key];
1196 $help = $this->attributes[$extrafieldsobjectkey]['help'][$key];
1197 $alwayseditable = $this->attributes[$extrafieldsobjectkey]['alwayseditable'][$key];
1198 $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)
1199
1200 //var_dump('key='.$key.' '.$value.' '.$moreparam.' '.$keysuffix.' '.$keyprefix.' '.$objectid.' '.$extrafieldsobjectkey.' '.$mode);
1201 //var_dump('label='.$label.' type='.$type.' param='.var_export($param, 1));
1202
1203 if ($computed) {
1204 if (!preg_match('/^search_/', $keyprefix)) {
1205 return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
1206 } else {
1207 return '';
1208 }
1209 }
1210
1211 //
1212 // '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'
1213 if (empty($morecss)) {
1214 // Add automatic css
1215 if ($type == 'date') {
1216 $morecss = 'minwidth100imp';
1217 } elseif ($type == 'datetime' || $type == 'datetimegmt' || $type == 'link') {
1218 $morecss = 'minwidth200imp';
1219 } elseif (in_array($type, array('int', 'integer', 'double', 'price'))) {
1220 $morecss = 'maxwidth75';
1221 } elseif ($type == 'password') {
1222 $morecss = 'maxwidth100';
1223 } elseif ($type == 'url') {
1224 $morecss = 'minwidth400';
1225 } elseif ($type == 'boolean') {
1226 $morecss = '';
1227 } elseif ($type == 'radio') {
1228 $morecss = 'width25';
1229 } else {
1230 if (empty($size) || round((float) $size) < 12) {
1231 $morecss = 'minwidth100';
1232 } elseif (round((float) $size) <= 48) {
1233 $morecss = 'minwidth200';
1234 } else {
1235 $morecss = 'minwidth400';
1236 }
1237 }
1238 // If css forced in attribute, we use this one
1239 if (!empty($this->attributes[$extrafieldsobjectkey]['css'][$key])) {
1240 $morecss = $this->attributes[$extrafieldsobjectkey]['css'][$key];
1241 }
1242 }
1243
1244 if (in_array($type, array('date'))) {
1245 $tmp = explode(',', $size);
1246 $newsize = $tmp[0];
1247 $showtime = 0;
1248
1249 // Do not show current date when field not required (see selectDate() method)
1250 if (!$required && $value == '') {
1251 $value = '-1';
1252 }
1253
1254 if ($mode == 1) {
1255 // mode input for a search filter, we show two inputs to select a date range
1256 $prefill = array(
1257 'start' => isset($value['start']) ? $value['start'] : '',
1258 'end' => isset($value['end']) ? $value['end'] : ''
1259 );
1260 $out = '<div ' . ($moreparam ? $moreparam : '') . '><div class="nowrap">';
1261 $out .= $form->selectDate($prefill['start'], $keyprefix.$key.$keysuffix.'_start', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("From"));
1262 $out .= '</div><div class="nowrap">';
1263 $out .= $form->selectDate($prefill['end'], $keyprefix.$key.$keysuffix.'_end', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"));
1264 $out .= '</div></div>';
1265 } else {
1266 // mode input into a create/update form
1267 // TODO Must also support $moreparam
1268 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required ? 0 : 2, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
1269 }
1270 } elseif (in_array($type, array('datetime', 'datetimegmt'))) {
1271 $tmp = explode(',', $size);
1272 $newsize = $tmp[0];
1273 $showtime = 1;
1274
1275 // Do not show current date when field not required (see selectDate() method)
1276 if (!$required && $value == '') {
1277 $value = '-1';
1278 }
1279
1280 if ($mode == 1) {
1281 // mode input for a search filter, we show two inputs to select a date range
1282 $prefill = array(
1283 'start' => isset($value['start']) ? $value['start'] : '',
1284 'end' => isset($value['end']) ? $value['end'] : ''
1285 );
1286 $out = '<div ' . ($moreparam ? $moreparam : '') . '><div class="nowrap">';
1287 $out .= $form->selectDate($prefill['start'], $keyprefix.$key.$keysuffix.'_start', 1, 1, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("From"), 'tzuserrel');
1288 $out .= '</div><div class="nowrap">';
1289 $out .= $form->selectDate($prefill['end'], $keyprefix.$key.$keysuffix.'_end', 1, 1, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
1290 $out .= '</div></div>';
1291 } else {
1292 // mode input into a create/update form
1293 // TODO Must also support $moreparam
1294 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required ? 0 : 2, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
1295 }
1296 } elseif (in_array($type, array('int', 'integer'))) {
1297 $tmp = explode(',', $size);
1298 $newsize = $tmp[0];
1299 $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 : '').'>';
1300 } elseif (preg_match('/varchar/', $type)) {
1301 $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 : '').'>';
1302 } elseif (in_array($type, array('email', 'mail', 'ip', 'phone', 'url'))) {
1303 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
1304 } elseif ($type == 'icon') {
1305 /* External lib inclusion are not allowed in backoffice. Also lib is included several time if there is several icon file.
1306 Some code must be added into main when MAIN_ADD_ICONPICKER_JS is set to add of lib in html header
1307 $out ='<link rel="stylesheet" href="'.dol_buildpath('/myfield/css/fontawesome-iconpicker.min.css', 1).'">';
1308 $out.='<script src="'.dol_buildpath('/myfield/js/fontawesome-iconpicker.min.js', 1).'"></script>';
1309 */
1310 $out .= '<input type="text" class="form-control icp icp-auto iconpicker-element iconpicker-input flat '.$morecss.' maxwidthonsmartphone"';
1311 $out .= ' name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
1312 if (getDolGlobalInt('MAIN_ADD_ICONPICKER_JS')) {
1313 $out .= '<script>';
1314 $options = "{ title: '<b>".$langs->trans("IconFieldSelector")."</b>', placement: 'right', showFooter: false, templates: {";
1315 $options .= "iconpicker: '<div class=\"iconpicker\"><div style=\"background-color:#EFEFEF;\" class=\"iconpicker-items\"></div></div>',";
1316 $options .= "iconpickerItem: '<a role=\"button\" href=\"#\" class=\"iconpicker-item\" style=\"background-color:#DDDDDD;\"><i></i></a>',";
1317 // $options.="buttons: '<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-cancel btn btn-default btn-sm\">".$langs->trans("Cancel")."</button>";
1318 // $options.="<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-accept btn btn-primary btn-sm\">".$langs->trans("Save")."</button>',";
1319 $options .= "footer: '<div class=\"popover-footer\" style=\"background-color:#EFEFEF;\"></div>',";
1320 $options .= "search: '<input type=\"search\" class\"form-control iconpicker-search\" placeholder=\"".$langs->trans("TypeToFilter")."\" />',";
1321 $options .= "popover: '<div class=\"iconpicker-popover popover\">";
1322 $options .= " <div class=\"arrow\" ></div>";
1323 $options .= " <div class=\"popover-title\" style=\"text-align:center;background-color:#EFEFEF;\"></div>";
1324 $options .= " <div class=\"popover-content \" ></div>";
1325 $options .= "</div>'}}";
1326 $out .= "$('#".$keyprefix.$key.$keysuffix."').iconpicker(".$options.");";
1327 $out .= '</script>';
1328 }
1329 } elseif ($type == 'text') {
1330 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
1331 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
1332 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
1333 $out = (string) $doleditor->Create(1);
1334 } else {
1335 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
1336 }
1337 } elseif ($type == 'html') {
1338 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
1339 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
1340 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && getDolGlobalInt('FCKEDITOR_ENABLE_SOCIETE'), ROWS_5, '90%');
1341 $out = (string) $doleditor->Create(1);
1342 } else {
1343 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
1344 }
1345 } elseif ($type == 'boolean') {
1346 if (empty($mode)) {
1347 $checked = '';
1348 if (!empty($value)) {
1349 $checked = ' checked value="1" ';
1350 } else {
1351 $checked = ' value="1" ';
1352 }
1353 $out = '<input type="checkbox" class="flat valignmiddle'.($morecss ? ' '.$morecss : '').' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>';
1354 } else {
1355 $out = $form->selectyesno($keyprefix.$key.$keysuffix, $value, 1, false, 1, 1, 'width75 yesno');
1356 }
1357 $out .= '<input type="hidden" name="'.$keyprefix.$key.$keysuffix.'_boolean" value="1">'; // A hidden field ending with "_boolean" that is always set to 1.
1358 } elseif ($type == 'price') {
1359 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
1360 $value = price($value);
1361 }
1362 $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).'">';
1363 } elseif ($type == 'pricecy') {
1364 $currency = $conf->currency;
1365 if (!empty($value)) {
1366 // $value in memory is a php string like '10.01:USD'
1367 $pricetmp = explode(':', $value);
1368 $currency = !empty($pricetmp[1]) ? $pricetmp[1] : $conf->currency;
1369 $value = price($pricetmp[0]);
1370 }
1371 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
1372 $out .= $form->selectCurrency($currency, $keyprefix.$key.$keysuffix.'currency_id');
1373 } elseif ($type == 'duration') {
1374 $value = intval($value);
1375 $out = $form->select_duration($keyprefix . $key, $value, 0, 'text', 0, 1);
1376 } elseif ($type == 'double') {
1377 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
1378 $value = price($value);
1379 }
1380 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
1381 } elseif ($type == 'select') {
1382 $out = '';
1383 if ($mode) {
1384 $options = array();
1385 foreach ($param['options'] as $okey => $val) {
1386 if ((string) $okey == '') {
1387 continue;
1388 }
1389
1390 $valarray = explode('|', $val);
1391 $val = $valarray[0];
1392
1393 if ($langfile && $val) {
1394 $options[$okey] = $langs->trans($val);
1395 } else {
1396 $options[$okey] = $val;
1397 }
1398 }
1399 $selected = array();
1400 if (!is_array($value)) {
1401 $selected = explode(',', $value);
1402 }
1403
1404 $out .= $form->multiselectarray($keyprefix.$key.$keysuffix, $options, $selected, 0, 0, $morecss, 0, 0, '', '', '', (int) (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')));
1405 } else {
1406 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
1407 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1408 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
1409 }
1410
1411 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
1412 $out .= '<option value="0">&nbsp;</option>';
1413 foreach ($param['options'] as $key2 => $val2) {
1414 if ((string) $key2 == '') {
1415 continue;
1416 }
1417 $valarray = explode('|', $val2);
1418 $val2 = $valarray[0];
1419 $parent = '';
1420 if (!empty($valarray[1])) {
1421 $parent = $valarray[1];
1422 }
1423 $out .= '<option value="'.$key2.'"';
1424 $out .= (((string) $value == (string) $key2) ? ' selected' : '');
1425 $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
1426 $out .= '>';
1427 if ($langfile && $val2) {
1428 $out .= $langs->trans($val2);
1429 } else {
1430 $out .= $val2;
1431 }
1432 $out .= '</option>';
1433 }
1434 $out .= '</select>';
1435 }
1436 } elseif ($type == 'sellist') { // List of values selected from a table (1 choice)
1437 $out = '';
1438 if (!empty($conf->use_javascript_ajax)) {
1439 if (getDolGlobalString('MAIN_EXTRAFIELDS_ENABLE_NEW_SELECT2')) {
1440 $out .= "
1441 <script>
1442 $(document).ready(function () {
1443 $('#".$keyprefix.$key.$keysuffix."').select2({
1444 ajax: {
1445 url: '".DOL_URL_ROOT.'/core/ajax/ajaxextrafield.php'."',
1446 dataType: 'json',
1447 delay: 250, // wait 250 milliseconds before triggering the request
1448 data: function (params) {
1449 var query = {
1450 search: params.term,
1451 page: params.page || 1,
1452 objecttype: '".$extrafieldsobjectkey."',
1453 objectid: '".$objectid."',
1454 objectkey: '".$key."',
1455 mode: '".$mode."',
1456 value: '".$value."'
1457 }
1458 return query;
1459 }
1460 }
1461 })
1462 });
1463 </script>";
1464 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
1465 $out .= ' <option value="'.$value.'" selected>'.$this->showOutputField($key, $value, $moreparam, $extrafieldsobjectkey).'</option>';
1466 $out .= '</select>';
1467 } elseif (!getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
1468 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1469 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
1470 }
1471 }
1472 if (!getDolGlobalString('MAIN_EXTRAFIELDS_ENABLE_NEW_SELECT2')) {
1473 //$out .= '<!-- type = sellist -->';
1474 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
1475 if (is_array($param['options'])) {
1476 // WARNING!! @FIXME This code is duplicated into core/class/extrafields.class.php
1477
1478 $tmpparamoptions = array_keys($param['options']);
1479 $paramoptions = preg_split('/[\r\n]+/', $tmpparamoptions[0]);
1480
1481 $InfoFieldList = explode(":", $paramoptions[0], 5);
1482 // 0 : tableName
1483 // 1 : label field name
1484 // 2 : key fields name (if different of rowid)
1485 // optional parameters...
1486 // 3 : key field parent (for dependent lists). How this is used ?
1487 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value. Or use USF on the second line.
1488 // 5 : string category type. This replace the filter.
1489 // 6 : ids categories list separated by comma for category root. This replace the filter.
1490 // 7 : sort field (not used here but used into format for commobject)
1491
1492 // If there is a filter, we extract it by taking all content inside parenthesis.
1493 if (! empty($InfoFieldList[4])) {
1494 $pos = 0; // $pos will be position of ending filter
1495 $parenthesisopen = 0;
1496 while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
1497 if (substr($InfoFieldList[4], $pos, 1) == '(') {
1498 $parenthesisopen++;
1499 }
1500 if (substr($InfoFieldList[4], $pos, 1) == ')') {
1501 $parenthesisopen--;
1502 }
1503 $pos++;
1504 }
1505 $tmpbefore = substr($InfoFieldList[4], 0, $pos);
1506 $tmpafter = substr($InfoFieldList[4], $pos + 1);
1507 //var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
1508 $InfoFieldList[4] = $tmpbefore;
1509 if ($tmpafter !== '') {
1510 $InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
1511 }
1512
1513 // Fix better compatibility with some old extrafield syntax filter "(field=123)"
1514 $reg = array();
1515 if (preg_match('/^\‍(?([a-z0-9]+)([=<>]+)(\d+)\‍)?$/i', $InfoFieldList[4], $reg)) {
1516 $InfoFieldList[4] = '('.$reg[1].':'.$reg[2].':'.$reg[3].')';
1517 }
1518
1519 //var_dump($InfoFieldList);
1520 }
1521
1522 //$Usf = empty($paramoptions[1]) ? '' :$paramoptions[1];
1523
1524 $parentName = '';
1525 $parentField = '';
1526 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
1527
1528 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
1529 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1530 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
1531 } else {
1532 $keyList = $InfoFieldList[2].' as rowid';
1533 }
1534 }
1535 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
1536 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
1537 if (!empty($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra.') !== false) {
1538 $keyList .= ', main.'.$parentField;
1539 } else {
1540 $keyList .= ', '.$parentField;
1541 }
1542 // Re-add parent field that was removed by keyList reset above
1543 if (!empty($parentField)) {
1544 $keyList .= ', '.$parentField;
1545 }
1546 }
1547
1548 $filter_categorie = false;
1549 if (count($InfoFieldList) > 5) {
1550 if ($InfoFieldList[0] == 'categorie') {
1551 $filter_categorie = true;
1552 }
1553 }
1554
1555 if (!$filter_categorie) {
1556 $fields_label = isset($InfoFieldList[1]) ? explode('|', $InfoFieldList[1]) : array();
1557 if (!empty($fields_label)) {
1558 $keyList .= ', ';
1559 $keyList .= implode(', ', $fields_label);
1560 }
1561
1562 // WARNING!! This code is duplicated into core/ajax/ajaxextrafield.php
1563
1564 $sqlwhere = '';
1565 $sql = "SELECT ".$keyList;
1566 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
1567
1568 // Add filter from 4th field
1569 if (!empty($InfoFieldList[4])) {
1570 // can use current entity filter
1571 if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
1572 $InfoFieldList[4] = str_replace('$ENTITY$', (string) $conf->entity, $InfoFieldList[4]);
1573 }
1574 // can use SELECT request
1575 if (!getDolGlobalString("MAIN_DISALLOW_UNSECURED_SELECT_INTO_EXTRAFIELDS_FILTER")) {
1576 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
1577 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
1578 }
1579 }
1580 // can use MODE parameter (list or view)
1581 if (strpos($InfoFieldList[4], '$MODE$') !== false) {
1582 $InfoFieldList[4] = str_replace('$MODE$', preg_replace('/[^a-z0-9_]/i', '', (string) $mode), $InfoFieldList[4]);
1583 }
1584
1585 // current object id can be use into filter
1586 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
1587 $InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
1588 } elseif (substr($_SERVER["PHP_SELF"], -8) == 'list.php') {
1589 // In filters of list views, we do not want $ID$ replaced by 0. So we remove the '=' condition.
1590 // Do nothing if condition is using 'IN' keyword
1591 // Replace 'column = $ID$' by an always true test, the parent object is unknown in a list
1592 $word = '#\b([a-zA-Z0-9-\.-_]+)\b *= *\$ID\$#';
1593 $InfoFieldList[4] = preg_replace($word, '1 = 1', $InfoFieldList[4]);
1594 // Replace '$ID$ = column' by an always true test
1595 $word = '#\$ID\$ *= *\b([a-zA-Z0-9-\.-_]+)\b#';
1596 $InfoFieldList[4] = preg_replace($word, '1 = 1', $InfoFieldList[4]);
1597 // Same with the Universal Search Filter syntax, '(column:=:$ID$)'
1598 $word = '#\b([a-zA-Z0-9-\.-_]+)\b *: *[<>!=]?= *: *\$ID\$#';
1599 $InfoFieldList[4] = preg_replace($word, '1 = 1', $InfoFieldList[4]);
1600 } else {
1601 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
1602 }
1603
1604 // can use filter on any field of object
1605 if (is_object($object)) {
1606 $tags = [];
1607 preg_match_all('/\$(.*?)\$/', $InfoFieldList[4], $tags); // Example: $InfoFieldList[4] is ($dateadh$:<=:CURRENT_DATE)
1608 foreach ($tags[0] as $keytag => $valuetag) {
1609 $property = preg_replace('/[^a-z0-9_]/', '', strtolower($tags[1][$keytag]));
1610 if (strpos($InfoFieldList[4], $valuetag) !== false && property_exists($object, $property) && !empty($object->$property)) {
1611 $InfoFieldList[4] = str_replace($valuetag, (string) $object->$property, $InfoFieldList[4]);
1612 } else {
1613 $InfoFieldList[4] = str_replace($valuetag, '0', $InfoFieldList[4]);
1614 }
1615 }
1616 }
1617
1618 // We have to join on extrafield table
1619 $errstr = '';
1620 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1621 $sql .= ' as main, '.$this->db->sanitize($this->db->prefix().$InfoFieldList[0]).'_extrafields as extra';
1622 $sqlwhere .= " WHERE extra.fk_object = main.".$this->db->sanitize($InfoFieldList[2]);
1623 $sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1624 } else {
1625 $sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1626 }
1627 } else {
1628 $sqlwhere .= ' WHERE 1=1';
1629 }
1630
1631 // Add Usf filter on second line
1632 /*
1633 if ($Usf) {
1634 $errorstr = '';
1635 $sqlusf .= forgeSQLFromUniversalSearchCriteria($Usf, $errorstr);
1636 if (!$errorstr) {
1637 $sqlwhere .= $sqlusf;
1638 } else {
1639 $sqlwhere .= " AND invalid_usf_filter_of_extrafield";
1640 }
1641 }
1642 */
1643
1644 // Some tables may have field, some other not. For the moment we disable it.
1645 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
1646 $sqlwhere .= ' AND entity = '.((int) $conf->entity);
1647 }
1648 $sql .= $sqlwhere;
1649
1650 $sql .= $this->db->order(implode(', ', $fields_label));
1651 $sql .= ' LIMIT ' . getDolGlobalInt('MAIN_EXTRAFIELDS_LIMIT_SELLIST_SQL', 1000);
1652 //print $sql;
1653
1654 dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
1655 $resql = $this->db->query($sql);
1656 if ($resql) {
1657 $out .= '<option value="0">&nbsp;</option>';
1658 $num = $this->db->num_rows($resql);
1659 $i = 0;
1660 while ($i < $num) {
1661 $labeltoshow = '';
1662 $obj = $this->db->fetch_object($resql);
1663
1664 $nameFields = $InfoFieldList[1];
1665 // If text is "field1|f(a,b,c) as xxx|field2", we must convert string into 'field1|xxx|field2'
1666 $nameFields = preg_replace('/[a-z_]+\‍([^\‍)]*\‍) as ([\w]+)/i', '\1', $nameFields);
1667 // Sanitize field names to avoid error when doing $obj->field
1668 $nameFields = preg_replace('/[^0-9a-z_\.\|]/i', '', $nameFields);
1669
1670 // Several fields into label (eq table:code|label:rowid)
1671 $notrans = false;
1672 $fields_label = explode('|', $nameFields);
1673 if (is_array($fields_label) && count($fields_label) > 1) {
1674 $notrans = true;
1675 foreach ($fields_label as $field_toshow) {
1676 $field_toshow = preg_replace('/^.*\./', '', $field_toshow);
1677 $labeltoshow .= $obj->$field_toshow.' ';
1678 }
1679 } else {
1680 $labeltoshow = $obj->$nameFields;
1681 }
1682
1683 if ($value == $obj->rowid) {
1684 if (!$notrans) {
1685 foreach ($fields_label as $field_toshow) {
1686 $translabel = $langs->trans($obj->$field_toshow);
1687 $labeltoshow = $translabel.' ';
1688 }
1689 }
1690 $out .= '<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
1691 } else {
1692 if (!$notrans) {
1693 $translabel = $langs->trans($obj->$nameFields);
1694 $labeltoshow = $translabel;
1695 }
1696 if (empty($labeltoshow)) {
1697 $labeltoshow = '(not defined)';
1698 }
1699
1700 if (!empty($InfoFieldList[3]) && $parentField) {
1701 // Sanitize parent field name to avoid when doing $obj->field
1702 $parentField = preg_replace('/[^a-zA-Z0-9_\-]/', '', $parentField);
1703 $parent = $parentName.':'.$obj->{$parentField};
1704 }
1705
1706 $out .= '<option value="'.$obj->rowid.'"';
1707 $out .= ($value == $obj->rowid ? ' selected' : '');
1708 $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
1709 $out .= '>'.$labeltoshow.'</option>';
1710 }
1711
1712 $i++;
1713 }
1714 $this->db->free($resql);
1715 } else {
1716 print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
1717 }
1718 } else {
1719 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
1720 $categcode = $InfoFieldList[5];
1721 if (is_numeric($categcode)) { // deprecated: must use the category code instead of id. For backward compatibility.
1722 $tmpcategory = new Categorie($this->db);
1723 $MAP_ID_TO_CODE = array_flip($tmpcategory->MAP_ID);
1724 $categcode = $MAP_ID_TO_CODE[(int) $categcode];
1725 }
1726
1727 $data = $form->select_all_categories($categcode, '', 'parent', 64, $InfoFieldList[6], 1, 1);
1728 $out .= '<option value="0">&nbsp;</option>';
1729 if (is_array($data)) {
1730 foreach ($data as $data_key => $data_value) {
1731 $out .= '<option value="'.$data_key.'"';
1732 $out .= ($value == $data_key ? ' selected' : '');
1733 $out .= '>'.$data_value.'</option>';
1734 }
1735 }
1736 }
1737 }
1738 $out .= '</select>';
1739 }
1740 } elseif ($type == 'checkbox') {
1741 $value_arr = $value;
1742 if (!is_array($value)) {
1743 $value_arr = explode(',', $value);
1744 }
1745 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ? null : $param['options']), $value_arr, 0, 0, '', 0, '100%');
1746 } elseif ($type == 'radio') {
1747 $out = '';
1748 foreach ($param['options'] as $keyopt => $val) {
1749 $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
1750 $out .= ' value="'.$keyopt.'"';
1751 $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
1752 $out .= ($value == $keyopt ? 'checked' : '');
1753 $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$langs->trans($val).'</label><br>';
1754 }
1755 } elseif ($type == 'chkbxlst') { // List of values selected from a table (n choices)
1756 if (is_array($value)) {
1757 $value_arr = $value;
1758 } else {
1759 $value_arr = explode(',', $value);
1760 }
1761
1762 if (is_array($param['options'])) {
1763 $tmpparamoptions = array_keys($param['options']);
1764 $paramoptions = preg_split('/[\r\n]+/', $tmpparamoptions[0]);
1765
1766 $InfoFieldList = explode(":", $paramoptions[0], 5); // We will extract field at position 6,7... later from the 5th one.
1767 // 0 : tableName
1768 // 1 : label field name
1769 // 2 : rowid field name (if different of rowid)
1770 // Optional parameters...
1771 // 3 : key field parent (for dependent lists). How this is used ?
1772 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value. Or use USF on the second line.
1773
1774 // To add a filter on category (not possible with USF restricted to table)
1775 // 5 : string category type ('product', 'customer', ...). This is added to the filter.
1776 // 6 : list of parent categories ids. This replace the filter.
1777 // 7 : sort field (not used here but used into format for commobject)
1778
1779 // For backward compatibility when tableName = 'category', so when we want a list of categories
1780 // 5 : string category type ('product', 'customer', ...). This replace the filter.
1781 // 6 : list of parent categories ids. This replace the filter.
1782 // 7 : sort field (not used here but used into format for commobject)
1783
1784 // Example with extrafield value = "societe:nom:rowid" or "categorie:label:rowid:::product"
1785
1786 // If there is a filter, we extract it by taking all content inside parenthesis.
1787 if (! empty($InfoFieldList[4])) {
1788 $pos = 0;
1789 $parenthesisopen = 0;
1790 while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
1791 if (substr($InfoFieldList[4], $pos, 1) == '(') {
1792 $parenthesisopen++;
1793 }
1794 if (substr($InfoFieldList[4], $pos, 1) == ')') {
1795 $parenthesisopen--;
1796 }
1797 $pos++;
1798 }
1799 $tmpbefore = substr($InfoFieldList[4], 0, $pos);
1800 $tmpafter = substr($InfoFieldList[4], $pos + 1);
1801 //var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
1802 $InfoFieldList[4] = $tmpbefore;
1803 if ($tmpafter !== '') {
1804 $InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
1805 }
1806
1807 // Fix better compatibility with some old extrafield syntax filter "(field_name=123)"
1808 $reg = array();
1809 if (preg_match('/^\‍(?([a-z0-9_]+)([=<>]+)(\d+)\‍)?$/i', $InfoFieldList[4], $reg)) {
1810 $InfoFieldList[4] = '('.$reg[1].':'.$reg[2].':'.$reg[3].')';
1811 }
1812
1813 //var_dump($InfoFieldList);
1814 }
1815
1816 //$Usf = empty($paramoptions[1]) ? '' :$paramoptions[1];
1817
1818
1819 $parentName = '';
1820 $parentField = '';
1821 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
1822
1823 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
1824 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1825 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
1826 } else {
1827 $keyList = $InfoFieldList[2].' as rowid';
1828 }
1829 }
1830 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
1831 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
1832 if (!empty($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra.') !== false) {
1833 $keyList .= ', main.'.$parentField;
1834 } else {
1835 $keyList .= ', '.$parentField;
1836 }
1837 // Re-add parent field that was removed by keyList reset above
1838 if (!empty($parentField)) {
1839 $keyList .= ', '.$parentField;
1840 }
1841 }
1842
1843 $InfoFieldList[5] = (string) ($InfoFieldList[5]??'');
1844
1845 $filter_categorie = false;
1846 if (count($InfoFieldList) > 5 && ($InfoFieldList[5] != '')) {
1847 if ($InfoFieldList[0] == 'categorie') {
1848 $filter_categorie = true; // The combo list is a list of categories
1849 } else {
1850 $filter_categorie = false; // We have a filter on category for another table
1851 }
1852 }
1853
1854
1855 if (!$filter_categorie) {
1856 $fields_label = explode('|', $InfoFieldList[1]);
1857 if (is_array($fields_label)) {
1858 $keyList .= ', ';
1859 $keyList .= implode(', ', $fields_label);
1860 }
1861
1862 $sqlwhere = '';
1863 $sql = "SELECT ".$keyList;
1864 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
1865
1866 // Add filter from 4th field
1867 if (!empty($InfoFieldList[4])) {
1868 // can use current entity filter
1869 if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
1870 $InfoFieldList[4] = str_replace('$ENTITY$', (string) $conf->entity, $InfoFieldList[4]);
1871 }
1872 // can use SELECT request
1873 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
1874 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
1875 }
1876
1877 // current object id can be use into filter
1878 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
1879 $InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
1880 } elseif (substr($_SERVER["PHP_SELF"], -8) == 'list.php') {
1881 // In filters of list views, we do not want $ID$ replaced by 0. So we remove the '=' condition.
1882 // Do nothing if condition is using 'IN' keyword
1883 // Replace 'column = $ID$' by an always true test, the parent object is unknown in a list
1884 $word = '#\b([a-zA-Z0-9-\.-_]+)\b *= *\$ID\$#';
1885 $InfoFieldList[4] = preg_replace($word, '1 = 1', $InfoFieldList[4]);
1886 // Replace '$ID$ = column' by an always true test
1887 $word = '#\$ID\$ *= *\b([a-zA-Z0-9-\.-_]+)\b#';
1888 $InfoFieldList[4] = preg_replace($word, '1 = 1', $InfoFieldList[4]);
1889 // Same with the Universal Search Filter syntax, '(column:=:$ID$)'
1890 $word = '#\b([a-zA-Z0-9-\.-_]+)\b *: *[<>!=]?= *: *\$ID\$#';
1891 $InfoFieldList[4] = preg_replace($word, '1 = 1', $InfoFieldList[4]);
1892 } else {
1893 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
1894 }
1895
1896 // We have to join on extrafield table
1897 $errstr = '';
1898 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1899 $sql .= ' as main, '.$this->db->sanitize($this->db->prefix().$InfoFieldList[0]).'_extrafields as extra';
1900 $sqlwhere .= " WHERE extra.fk_object = main.".$this->db->sanitize($InfoFieldList[2]);
1901 $sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1902 } else {
1903 $sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1904 }
1905 } else {
1906 $sqlwhere .= ' WHERE 1=1';
1907 }
1908
1909
1910 if ($InfoFieldList[5] != '') {
1911 // We have a filter on category for another table
1912 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
1913 $tmpcategory = new Categorie($this->db);
1914 $tablesuffixcategory = empty($tmpcategory->MAP_CAT_TABLE[$InfoFieldList[5]]) ? $InfoFieldList[5] : $tmpcategory->MAP_CAT_TABLE[$InfoFieldList[5]];
1915 $fksuffixcategory = empty($tmpcategory->MAP_CAT_FK[$InfoFieldList[5]]) ? $InfoFieldList[5] : $tmpcategory->MAP_CAT_FK[$InfoFieldList[5]];
1916
1917 $sqlwhere .= ' AND EXISTS (SELECT fk_categorie as categid FROM '.MAIN_DB_PREFIX.'categorie_'.$this->db->sanitize($tablesuffixcategory);
1918 $sqlwhere .= ' WHERE fk_categorie IN ('.$this->db->sanitize($InfoFieldList[6]).')';
1919 $sqlwhere .= ' AND fk_'.$this->db->sanitize($fksuffixcategory).' = rowid)';
1920 //var_dump($sqlwhere);exit;
1921 }
1922
1923 // Add Usf filter on second line
1924 /*
1925 if ($Usf) {
1926 $errorstr = '';
1927 $sqlusf .= forgeSQLFromUniversalSearchCriteria($Usf, $errorstr);
1928 if (!$errorstr) {
1929 $sqlwhere .= $sqlusf;
1930 } else {
1931 $sqlwhere .= " AND invalid_usf_filter_of_extrafield";
1932 }
1933 }
1934 */
1935
1936 // Some tables may have field, some other not. For the moment we disable it.
1937 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
1938 $sqlwhere .= " AND entity = ".((int) $conf->entity);
1939 }
1940 // $sql.=preg_replace('/^ AND /','',$sqlwhere);
1941 // print $sql;
1942
1943 $sql .= $sqlwhere;
1944 $sql .= ' ORDER BY '.implode(', ', $fields_label);
1945
1946 dol_syslog(get_class($this).'::showInputField type=chkbxlst', LOG_DEBUG);
1947
1948 $resql = $this->db->query($sql);
1949 if ($resql) {
1950 $num = $this->db->num_rows($resql);
1951 $i = 0;
1952
1953 $data = array();
1954
1955 while ($i < $num) {
1956 $labeltoshow = '';
1957 $obj = $this->db->fetch_object($resql);
1958
1959 $notrans = false;
1960 // Several field into label (eq table:code|label:rowid)
1961 $fields_label = explode('|', $InfoFieldList[1]);
1962 if (is_array($fields_label)) {
1963 $notrans = true;
1964 foreach ($fields_label as $field_toshow) {
1965 $labeltoshow .= $obj->$field_toshow.' ';
1966 }
1967 } else {
1968 $labeltoshow = $obj->{$InfoFieldList[1]};
1969 }
1970 $labeltoshow = dol_trunc($labeltoshow, 45);
1971
1972 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
1973 $labeltoshow = '';
1974 foreach ($fields_label as $field_toshow) {
1975 $translabel = $langs->trans($obj->$field_toshow);
1976 if ($translabel != $obj->$field_toshow) {
1977 $labeltoshow .= ' '.dol_trunc($translabel, 18).' ';
1978 } else {
1979 $labeltoshow .= ' '.dol_trunc($obj->$field_toshow, 18).' ';
1980 }
1981 }
1982 $data[$obj->rowid] = $labeltoshow;
1983 } else {
1984 if (!$notrans) {
1985 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
1986 if ($translabel != $obj->{$InfoFieldList[1]}) {
1987 $labeltoshow = dol_trunc($translabel, 18);
1988 } else {
1989 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
1990 }
1991 }
1992 if (empty($labeltoshow)) {
1993 $labeltoshow = '(not defined)';
1994 }
1995
1996 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
1997 $data[$obj->rowid] = $labeltoshow;
1998 }
1999
2000 if (!empty($InfoFieldList[3]) && $parentField) {
2001 $parent = $parentName.':'.$obj->{$parentField};
2002 }
2003
2004 $data[$obj->rowid] = $labeltoshow;
2005 }
2006
2007 $i++;
2008 }
2009 $this->db->free($resql);
2010
2011 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, 0, 0, '', 0, '100%');
2012 } else {
2013 print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
2014 }
2015 } else {
2016 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2017 $categcode = $InfoFieldList[5];
2018 if (is_numeric($categcode)) { // deprecated: must use the category code instead of id. For backward compatibility.
2019 $tmpcategory = new Categorie($this->db);
2020 $MAP_ID_TO_CODE = array_flip($tmpcategory->MAP_ID);
2021 $categcode = $MAP_ID_TO_CODE[(int) $categcode];
2022 }
2023
2024 $data = $form->select_all_categories($categcode, '', 'parent', 64, $InfoFieldList[6], 1, 1);
2025 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, 0, 0, '', 0, '100%');
2026 }
2027 }
2028 } elseif ($type == 'link') {
2029 $param_list = array_keys($param['options']); // $param_list[0] = 'ObjectName:classPath' but can also be 'ObjectName:classPath:1:(status:=:1)'
2030 /* Removed.
2031 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
2032 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.
2033 if (strpos($param_list[0], '$ID$') !== false && !empty($objectid)) {
2034 $param_list[0] = str_replace('$ID$', $objectid, $param_list[0]);
2035 }*/
2036 $showempty = (($required && $default != '') ? 0 : 1);
2037
2038 $tmparray = explode(':', $param_list[0]);
2039
2040 $element = $extrafieldsobjectkey; // $extrafieldsobjectkey comes from $object->table_element but we need $object->element
2041 if ($element == 'socpeople') {
2042 $element = 'contact';
2043 } elseif ($element == 'projet') {
2044 $element = 'project';
2045 }
2046
2047 //$objectdesc = $param_list[0]; // Example: 'ObjectName:classPath:1:(status:=:1)' Replaced by next line: old line propagated also the filter to ajax call that was blocked by some WAF
2048 $objectdesc = $tmparray[0].(empty($tmparray[1]) ? "" : ":".$tmparray[1]); // 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 filter into the definition in the ->fields of $elem if found.
2049 $objectfield = $element.':options_'.$key; // Example: 'actioncomm:options_fff' To be used in priority to know object linked with all its definition (including filters)
2050
2051 $out = $form->selectForForms($objectdesc, $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, '', 0, 0, '', $objectfield);
2052 } elseif (in_array($type, ['point', 'multipts', 'linestrg', 'polygon'])) {
2053 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
2054 $dolgeophp = new DolGeoPHP($this->db);
2055 $geojson = '{}';
2056 $centroidjson = getDolGlobalString('MAIN_INFO_SOCIETE_GEO_COORDINATES', '{}');
2057 if (!empty($value)) {
2058 $tmparray = $dolgeophp->parseGeoString($value);
2059 $geojson = $tmparray['geojson'];
2060 $centroidjson = $tmparray['centroidjson'];
2061 }
2062 if (!preg_match('/search_/', $keyprefix)) {
2063 require_once DOL_DOCUMENT_ROOT.'/core/class/geomapeditor.class.php';
2064 $geomapeditor = new GeoMapEditor();
2065 $out .= $geomapeditor->getHtml($keyprefix.$key.$keysuffix, $geojson, $centroidjson, $type);
2066 } else {
2067 // If keyprefix is search_ or search_options_, we must just use a simple text field
2068 $out = '';
2069 }
2070 } elseif ($type == 'password') {
2071 // If prefix is 'search_', field is used as a filter, we use a common text field.
2072 $out = '<input style="display:none" type="text" name="fakeusernameremembered">'; // Hidden field to reduce impact of evil Google Chrome autopopulate bug.
2073 $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 : '').'>';
2074 } elseif ($type == 'stars') {
2075 $out = '<input type="hidden" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').'>';
2076 $out .= '<div class="star-selection" id="'.$keyprefix.$key.$keysuffix.'_selection">';
2077 $i = 1;
2078 while ($i <= $size) {
2079 $out .= '<span class="star" data-value="'.$i.'">'.img_picto('', 'fontawesome_star_fas').'</span>';
2080 $i++;
2081 }
2082 $out .= '</div>';
2083 $out .= '<script>
2084 jQuery(function($) { /* extrafields.class.php 1 */
2085 let container = $("#'.$keyprefix.$key.$keysuffix.'_selection");
2086 let selectedStars = parseInt($("#'.$keyprefix.$key.$keysuffix.'").val()) || 0;
2087 container.find(".star").each(function() {
2088 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2089 });
2090 container.find(".star").on("mouseover", function() {
2091 let selectedStar = $(this).data("value");
2092 container.find(".star").each(function() {
2093 $(this).toggleClass("active", $(this).data("value") <= selectedStar);
2094 });
2095 });
2096 container.on("mouseout", function() {
2097 container.find(".star").each(function() {
2098 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2099 });
2100 });
2101 container.find(".star").off("click").on("click", function() {
2102 selectedStars = $(this).data("value");
2103 if (selectedStars === 1 && $("#'.$keyprefix.$key.$keysuffix.'").val() == 1) {
2104 selectedStars = 0;
2105 }
2106 $("#'.$keyprefix.$key.$keysuffix.'").val(selectedStars);
2107 container.find(".star").each(function() {
2108 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2109 });
2110 });
2111 });
2112 </script>';
2113 }
2114 if (!empty($hidden)) {
2115 $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
2116 }
2117
2118 // If alwayseditable is false, and object is not in draft, then showOutputField
2119 // @phan-suppress-next-line PhanUndeclaredConstantOfClass
2120 if ($alwayseditable == 0 && is_object($object) && isset($object->status) && defined(get_class($object)."::STATUS_DRAFT") && $object->status != $object::STATUS_DRAFT) {
2121 $out = $this->showOutputField($key, $value, $moreparam, $extrafieldsobjectkey, null, $object);
2122 }
2123 /* Add comments
2124 if ($type == 'date') $out.=' (YYYY-MM-DD)';
2125 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
2126 */
2127 /*if (!empty($help) && $keyprefix != 'search_options_') {
2128 $out .= $form->textwithpicto('', $help, 1, 'help', '', 0, 3);
2129 }*/
2130 return $out;
2131 }
2132
2133
2146 public function showOutputField($key, $value, $moreparam = '', $extrafieldsobjectkey = '', $outputlangs = null, $object = null, $mode = '')
2147 {
2148 global $conf, $langs, $hookmanager;
2149
2150 if (is_null($outputlangs) || !is_object($outputlangs)) {
2151 $outputlangs = $langs;
2152 }
2153
2154 if (empty($extrafieldsobjectkey)) {
2155 dol_syslog(get_class($this).'::showOutputField extrafieldsobjectkey required', LOG_ERR);
2156 return 'BadValueForParamExtraFieldsObjectKey';
2157 }
2158
2159 $parameters = array(
2160 'key' => $key,
2161 'value' => &$value,
2162 'moreparam' => $moreparam,
2163 'extrafieldsobjectkey' => $extrafieldsobjectkey,
2164 'outputlangs' => $outputlangs,
2165 'object' => $object
2166 );
2167 $action = '';
2168
2169 $reshook = $hookmanager->executeHooks('showOutputExtraField', $parameters, $this, $action); // Note that $action and $object may have been modified by hook
2170 if ($reshook > 0) {
2171 return $hookmanager->resPrint;
2172 }
2173
2174 $label = $this->attributes[$extrafieldsobjectkey]['label'][$key];
2175 $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
2176 $size = $this->attributes[$extrafieldsobjectkey]['size'][$key]; // Can be '255', '24,8'...
2177 $default = $this->attributes[$extrafieldsobjectkey]['default'][$key];
2178 $computed = $this->attributes[$extrafieldsobjectkey]['computed'][$key];
2179 $unique = $this->attributes[$extrafieldsobjectkey]['unique'][$key];
2180 $required = $this->attributes[$extrafieldsobjectkey]['required'][$key];
2181 $param = $this->attributes[$extrafieldsobjectkey]['param'][$key];
2182 $perms = (int) dol_eval((string) $this->attributes[$extrafieldsobjectkey]['perms'][$key], 1, 1, '2');
2183 $langfile = $this->attributes[$extrafieldsobjectkey]['langfile'][$key];
2184 $list = (string) dol_eval((string) $this->attributes[$extrafieldsobjectkey]['list'][$key], 1, 1, '2');
2185 $help = $this->attributes[$extrafieldsobjectkey]['help'][$key];
2186 $cssview = $this->attributes[$extrafieldsobjectkey]['cssview'][$key];
2187 $alwayseditable = $this->attributes[$extrafieldsobjectkey]['alwayseditable'][$key];
2188
2189 // If alwayseditable is false, and object is not in draft, then we show value instead of input field
2190 $showValueInsteadOfInputField = 0; // Variable used to disable update of fields via ajax
2191 // @phan-suppress-next-line PhanUndeclaredConstantOfClass
2192 if ($alwayseditable == 0 && is_object($object) && isset($object->status) && defined(get_class($object)."::STATUS_DRAFT") && $object->status != $object::STATUS_DRAFT) {
2193 $showValueInsteadOfInputField = 1;
2194 }
2195
2196 $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)
2197
2198 if ($hidden) {
2199 return ''; // This is a protection. If field is hidden, we should just not call this method.
2200 }
2201
2202 //print $key.' '.$type.'<br>';
2203
2204 $showsize = 0;
2205 if ($type == 'date') {
2206 $showsize = 10;
2207 if ($value !== '') {
2208 $value = dol_print_date($value, 'day'); // For date without hour, date is always GMT for storage and output
2209 }
2210 } elseif ($type == 'datetime') {
2211 $showsize = 19;
2212 if ($value !== '') {
2213 $value = dol_print_date($value, 'dayhour', 'tzuserrel');
2214 }
2215 } elseif ($type == 'duration') {
2216 $showsize = 10;
2217 if ($value !== '') {
2218 $value = intval($value);
2219 $value = convertSecondToTime($value);
2220 }
2221 } elseif ($type == 'datetimegmt') {
2222 $showsize = 19;
2223 if ($value !== '') {
2224 $value = dol_print_date($value, 'dayhour', 'gmt');
2225 }
2226 } elseif ($type == 'int') {
2227 $showsize = 10;
2228 } elseif ($type == 'double') {
2229 if (!empty($value)) {
2230 //$value=price($value);
2231 //$sizeparts = explode(",", $size);
2232 //$number_decimals = array_key_exists(1, $sizeparts) ? $sizeparts[1] : 0;
2233 $value = price($value, 0, $outputlangs, 0, 0, -2, '');
2234 }
2235 } elseif ($type == 'boolean') {
2236 $checked = '';
2237 if (!empty($value)) {
2238 $checked = ' checked ';
2239 }
2240 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
2241 $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
2242 } else {
2243 $value = yn($value ? 1 : 0);
2244 }
2245 } elseif ($type == 'mail' || $type == 'email') {
2246 $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
2247 } elseif ($type == 'ip') {
2248 $value = dol_print_ip($value, 0);
2249 } elseif ($type == 'icon') {
2250 $value = '<span class="'.$value.'"></span>';
2251 } elseif ($type == 'url') {
2252 $value = dol_print_url($value, '_blank', 32, 1);
2253 } elseif ($type == 'phone') {
2254 $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
2255 } elseif ($type == 'price') {
2256 //$value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
2257 if ($value || $value == '0') {
2258 $value = price($value, 0, $outputlangs, 0, getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'), -1).' '.$outputlangs->getCurrencySymbol($conf->currency);
2259 }
2260 } elseif ($type == 'pricecy') {
2261 $currency = $conf->currency;
2262 if (!empty($value)) {
2263 // $value in memory is a php string like '0.01:EUR'
2264 $pricetmp = explode(':', $value);
2265 $currency = !empty($pricetmp[1]) ? $pricetmp[1] : $conf->currency;
2266 $value = $pricetmp[0];
2267 }
2268 if ($value || $value == '0') {
2269 $value = price($value, 0, $outputlangs, 0, getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'), -1, $currency);
2270 }
2271 } elseif ($type == 'select') {
2272 $valstr = (!empty($param['options'][$value]) ? $param['options'][$value] : '');
2273 if (($pos = strpos($valstr, "|")) !== false) {
2274 $valstr = substr($valstr, 0, $pos);
2275 }
2276 if ($langfile && $valstr) {
2277 $value = $outputlangs->trans($valstr);
2278 } else {
2279 $value = $valstr;
2280 }
2281 } elseif ($type == 'sellist') {
2282 $param_list = array_keys($param['options']);
2283 $InfoFieldList = explode(":", $param_list[0]);
2284
2285 $selectkey = "rowid";
2286 $keyList = 'rowid';
2287
2288 if (count($InfoFieldList) >= 3) {
2289 $selectkey = $InfoFieldList[2];
2290 $keyList = $InfoFieldList[2].' as rowid';
2291 }
2292
2293 $fields_label = explode('|', $InfoFieldList[1]);
2294 if (is_array($fields_label)) {
2295 $keyList .= ', ';
2296 $keyList .= implode(', ', $fields_label);
2297 }
2298
2299 $filter_categorie = false;
2300 if (count($InfoFieldList) > 5) {
2301 if ($InfoFieldList[0] == 'categorie') {
2302 $filter_categorie = true;
2303 }
2304 }
2305
2306 $sql = "SELECT ".$keyList;
2307 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
2308 if (!empty($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra.') !== false) {
2309 $sql .= ' as main';
2310 }
2311 if ($selectkey == 'rowid' && empty($value)) {
2312 $sql .= " WHERE ".$this->db->sanitize($selectkey)." = 0";
2313 } elseif ($selectkey == 'rowid') {
2314 $sql .= " WHERE ".$this->db->sanitize($selectkey)." = ".((int) $value);
2315 } else {
2316 $sql .= " WHERE ".$this->db->sanitize($selectkey)." = '".$this->db->escape($value)."'";
2317 }
2318 //$sql.= ' AND entity = '.$conf->entity;
2319
2320 dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
2321
2322 $resql = $this->db->query($sql);
2323 if ($resql) {
2324 if (!$filter_categorie) {
2325 $value = ''; // value was used, so now we reset it to use it to build final output
2326
2327 $obj = $this->db->fetch_object($resql);
2328
2329 // Several field into label (eq table:code|label:rowid)
2330 $fields_label = explode('|', $InfoFieldList[1]);
2331
2332 if (is_array($fields_label) && count($fields_label) > 1) {
2333 foreach ($fields_label as $field_toshow) {
2334 $translabel = '';
2335 $field_toshow = preg_replace('/^.*\./', '', $field_toshow);
2336 if (!empty($obj->$field_toshow)) {
2337 $translabel = $outputlangs->trans($obj->$field_toshow);
2338
2339 if ($translabel != $obj->$field_toshow) {
2340 $value .= dol_trunc($translabel, 24) . ' ';
2341 } else {
2342 $value .= $obj->$field_toshow . ' ';
2343 }
2344 }
2345 }
2346 } else {
2347 $translabel = '';
2348 $tmppropname = $InfoFieldList[1];
2349 //$obj->$tmppropname = '';
2350 if (!empty(isset($obj->$tmppropname) ? $obj->$tmppropname : '')) {
2351 $translabel = $outputlangs->trans($obj->$tmppropname);
2352 }
2353 if ($translabel != (isset($obj->$tmppropname) ? $obj->$tmppropname : '')) {
2354 $value = dol_trunc($translabel, 18);
2355 } else {
2356 $value = isset($obj->$tmppropname) ? $obj->$tmppropname : '';
2357 }
2358 }
2359 } else {
2360 $toprint = array();
2361 $obj = $this->db->fetch_object($resql);
2362 if ($obj->rowid) {
2363 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
2364 $c = new Categorie($this->db);
2365 $result = $c->fetch($obj->rowid);
2366 if ($result > 0) {
2367 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
2368 foreach ($ways as $way) {
2369 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories'.($mode ? ' '.$mode : '').'"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #bbb"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
2370 }
2371 }
2372 }
2373 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
2374 }
2375 } else {
2376 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
2377 }
2378 } elseif ($type == 'radio') {
2379 if ($required && !isset($param['options'][$value])) {
2380 $outputlangs->load('errors');
2381 $value = '<span class="opacitymedium">'.$outputlangs->trans('ErrorNoValueForRadioType').'</span>';
2382 } else {
2383 if (isset($param['options'][$value])) {
2384 $value = $outputlangs->trans($param['options'][$value]);
2385 } else {
2386 $value = '';
2387 }
2388 }
2389 } elseif ($type == 'checkbox') {
2390 $value_arr = explode(',', $value);
2391 $value = '';
2392 $toprint = array();
2393 if (is_array($value_arr)) {
2394 foreach ($value_arr as $keyval => $valueval) {
2395 if (!empty($valueval)) {
2396 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories'.($mode ? ' '.$mode : '').'" style="background: #bbb">'.$param['options'][$valueval].'</li>';
2397 }
2398 }
2399 }
2400 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
2401 } elseif ($type == 'chkbxlst') { // Example with extrafield value = "societe:nom:rowid" or "categorie:label:rowid:::product"
2402 // Only if something to display (perf)
2403 if (!is_null($value) && $value != '') {
2404 $value_arr = explode(',', $value);
2405
2406 $param_list = array_keys($param['options']);
2407 $InfoFieldList = explode(":", $param_list[0]);
2408
2409 $selectkey = "rowid";
2410 $keyList = 'rowid';
2411
2412 if (count($InfoFieldList) >= 3) {
2413 $selectkey = $InfoFieldList[2];
2414 $keyList = $InfoFieldList[2].' as rowid';
2415 }
2416
2417 $fields_label = explode('|', $InfoFieldList[1]);
2418 if (is_array($fields_label)) {
2419 $keyList .= ', ';
2420 $keyList .= implode(', ', $fields_label);
2421 }
2422
2423 $filter_categorie = false;
2424 if (count($InfoFieldList) > 5 && ((string) $InfoFieldList[5] != '')) {
2425 if ($InfoFieldList[0] == 'categorie') {
2426 $filter_categorie = true;
2427 }
2428 }
2429
2430 $sql = "SELECT ".$keyList;
2431 $sql .= " FROM ".$this->db->prefix().$InfoFieldList[0];
2432 if (strpos($InfoFieldList[4], 'extra.') !== false) {
2433 $sql .= ' as main';
2434 }
2435 $sql .= " WHERE ".$this->db->sanitize($selectkey)." IN (".$this->db->sanitize(implode(',', $value_arr)).")";
2436
2437 dol_syslog(get_class($this).':showOutputField type=chkbxlst', LOG_DEBUG);
2438
2439 $resql = $this->db->query($sql);
2440
2441 if ($resql) {
2442 if (!$filter_categorie) {
2443 $value = ''; // value was used, so now we reset it to use it to build final output
2444 $toprint = array();
2445 $numOfElem = $this->db->num_rows($resql);
2446 while ($obj = $this->db->fetch_object($resql)) {
2447 // Several field into label (eq table:code|label:rowid)
2448 $fields_label = explode('|', $InfoFieldList[1]);
2449 $txtcolor = '#bbb';
2450 //var_dump($key, $type, $InfoFieldList, $fields_label, $object);
2451 if (count($fields_label) > 0) {
2452 if ($InfoFieldList[0] == 'societe' && $InfoFieldList[1] == 'nom' && $InfoFieldList[2] == 'rowid') {
2453 // Special cas for thirdparties
2454 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
2455 $tmpthirdpartystatic = new Societe($this->db);
2456 $tmpthirdpartystatic->id = $obj->rowid;
2457 $tmpthirdpartystatic->name = $obj->nom;
2458 if ($numOfElem >= 3) {
2459 $tmpthirdpartystatic->name = $obj->nom;
2460 $label = $tmpthirdpartystatic->getNomUrl(2);
2461 } elseif ($numOfElem >= 2) {
2462 $tmpthirdpartystatic->name = dol_trunc($obj->nom, 5);
2463 $label = $tmpthirdpartystatic->getNomUrl(1);
2464 } else {
2465 $tmpthirdpartystatic->name = $obj->nom;
2466 $label = $tmpthirdpartystatic->getNomUrl(1);
2467 }
2468 } else {
2469 $label = '<li class="select2-search-choice-dolibarr noborderoncategories'.($mode ? ' '.$mode : '').'" style="background: '.$txtcolor.'">';
2470 foreach ($fields_label as $field_toshow) {
2471 $translabel = '';
2472 if (!empty($obj->$field_toshow)) {
2473 $translabel = $outputlangs->trans($obj->$field_toshow);
2474 }
2475 if ($translabel != $field_toshow) {
2476 $label .= ' '.dol_trunc($translabel, 18);
2477 } else {
2478 $label .= ' '.$obj->$field_toshow;
2479 }
2480 }
2481 $label .= '</li>';
2482 }
2483 $toprint[] = $label;
2484 }
2485 }
2486 } else {
2487 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2488
2489 $toprint = array();
2490 $numElem = $this->db->num_rows($resql);
2491 while ($obj = $this->db->fetch_object($resql)) {
2492 $c = new Categorie($this->db);
2493 $c->fetch($obj->rowid);
2494 if ((string) $mode != 'list') {
2495 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
2496 foreach ($ways as $way) {
2497 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories'.($mode ? ' '.$mode : '').'"'.($c->color ? ' style="background: #'.$c->color.';"' : ' style="background: #bbb"').'>'.img_object('', 'category').' '.$way.'</li>';
2498 }
2499 } else {
2500 // Check contrast with background and correct text color
2501 $forced_color = 'categtextwhite'; // We want color white because the getNomUrl of a tag is always called inside a dark background like '<span color="bbb"></span>' to show it as a tag. TODO Add this in param to force when called outside of span.
2502 if ($c->color) {
2503 if (colorIsLight($c->color)) {
2504 $forced_color = 'categtextblack';
2505 }
2506 }
2507
2508 $s = '<li class="select2-search-choice-dolibarr noborderoncategories '.$forced_color.' list"'.($c->color ? ' style="background: #'.$c->color.';"' : ' style="background: #bbb"').'>';
2509 if ($numElem >= 2) {
2510 $s .= img_object($c->label, 'category', 'class="small"');
2511 } else {
2512 $s .= img_object($c->label, 'category', 'class="small"').' '.$c->label;
2513 }
2514 $s .= '</li>';
2515 $toprint[] = $s;
2516 }
2517 }
2518 }
2519 if (!empty($toprint)) {
2520 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
2521 }
2522 } else {
2523 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
2524 }
2525 }
2526 } elseif ($type == 'link') {
2527 // Only if something to display (perf)
2528 if ($value) { // If we have -1 here, pb is into insert, not into output (fix insert instead of changing code here to compensate)
2529 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
2530
2531 $InfoFieldList = explode(":", $param_list[0]);
2532 $classname = $InfoFieldList[0];
2533 $classpath = $InfoFieldList[1];
2534 if (!empty($classpath)) {
2535 dol_include_once($InfoFieldList[1]);
2536 if (!$classname || !class_exists($classname)) {
2537 // Without this, the raw id is printed with nothing telling why, which is very
2538 // hard to diagnose. Most often the class path stored in the definition is wrong.
2539 dol_syslog('Extrafields::showOutputField the class '.$classname.' of the link field '.$key.' could not be loaded from '.$classpath.', check the extrafield definition', LOG_WARNING);
2540 }
2541 if ($classname && class_exists($classname)) {
2542 $tmpobject = new $classname($this->db);
2543 '@phan-var-force CommonObject $tmpobject';
2544 $tmpobject->fetch($value);
2545
2546 if (get_class($tmpobject) == 'Categorie') {
2547 // For category object, rendering must use the same method than the one defined into showCategories()
2548 $color = $tmpobject->color;
2549 $sfortag = '<span class="noborderoncategories"' . ($color ? ' style="background: #' . $color . ';"' : ' style="background: #bbb"') . '>';
2550 $sfortag .= $tmpobject->getNomUrl(3);
2551 $sfortag .= '</span>';
2552 $value = $sfortag;
2553 } else {
2554 $value = $tmpobject->getNomUrl(3);
2555 }
2556 }
2557 } else {
2558 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
2559 return 'Error bad setup of extrafield';
2560 }
2561 }
2562 } elseif ($type == 'point') {
2563 if (!empty($value)) {
2564 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
2565 $dolgeophp = new DolGeoPHP($this->db);
2566 $value = $dolgeophp->getXYString($value);
2567 } else {
2568 $value = '';
2569 }
2570 } elseif (in_array($type, ['multipts', 'linestrg', 'polygon'])) {
2571 if (!empty($value)) {
2572 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
2573 $dolgeophp = new DolGeoPHP($this->db);
2574 $value = $dolgeophp->getPointString($value);
2575 } else {
2576 $value = '';
2577 }
2578 } elseif ($type == 'text') {
2579 $value = '<div class="'.($cssview ? $cssview : 'shortmessagecut').'">'.dol_htmlentitiesbr($value).'</div>';
2580 } elseif ($type == 'html') {
2581 $value = dol_htmlentitiesbr($value);
2582 } elseif ($type == 'password') {
2583 $value = dol_trunc(preg_replace('/./i', '*', $value), 8, 'right', 'UTF-8', 1);
2584 } elseif ($type == 'stars') {
2585 $objectid = (int) $object->id;
2586 $nbstars = (int) $value;
2587 if ($showValueInsteadOfInputField == 1) {
2588 $value = '<span style="display:none;" id="'.$key.$object->id.'">'.dol_escape_htmltag($value).'</span>';
2589 } else {
2590 $value = '<input type="hidden" class="flat" name="'.$key.'" id="'.$key.$objectid.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').'>';
2591 }
2592
2593 $value .= '<div class="star-selection" id="'.$key.$objectid.'_selection">';
2594 $i = 1;
2595 while ($i <= $size) {
2596 $value .= '<span class="star'.($i <= $nbstars ? ' active' : '').'" data-value="'.$i.'">'.img_picto('', 'fontawesome_star_fas').'</span>';
2597 $i++;
2598 }
2599 $value .= '</div>';
2600 $value .= '<script>
2601 $(document).ready(function() { /* extrafields.class.php 2 */
2602 let container = $("#'.$key.$objectid.'_selection");
2603 let selectedStars = parseInt($("#'.$key.$objectid.'").val() || $("#'.$key.$objectid.'").text()) || 0;
2604 ';
2605 if ($showValueInsteadOfInputField == 0) {
2606 $value .= '
2607 container.find(".star").on("mouseover", function() {
2608 console.log("Mouse over a star");
2609 let selectedStar = $(this).data("value");
2610 container.find(".star").each(function() {
2611 $(this).toggleClass("active", $(this).data("value") <= selectedStar);
2612 });
2613 });
2614 container.on("mouseout", function() {
2615 console.log("Mouse out of star");
2616 container.find(".star").each(function() {
2617 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2618 });
2619 });
2620 container.find(".star").off("click").on("click", function() {
2621 console.log("We click on star, we call the ajax core/ajax/updateextrafield.php");
2622 selectedStars = $(this).data("value");
2623 if (selectedStars == 1 && $("#'.$key.$objectid.'").val() == 1) {
2624 selectedStars = 0;
2625 }
2626 container.find("#'.$key.$objectid.'").val(selectedStars);
2627 container.find(".star").each(function() {
2628 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2629 });
2630 $.ajax({
2631 url: "'.DOL_URL_ROOT.'/core/ajax/updateextrafield.php",
2632 method: "POST",
2633 data: {
2634 objectType: \''.dol_escape_js($extrafieldsobjectkey).'\',
2635 objectId: '.((int) $objectid).',
2636 field: \''.dol_escape_js($key).'\',
2637 value: selectedStars,
2638 token: \''.newToken().'\'
2639 },
2640 success: function(response) {
2641 var res = JSON.parse(response);
2642 console[res.status === "success" ? "log" : "error"](res.message);
2643 },
2644 error: function(xhr, status, error) {
2645 console.log("Ajax request failed while updating '.$key.':", error);
2646 }
2647 });
2648 });';
2649 }
2650 $value .= '
2651 });
2652 </script>';
2653 } else {
2654 $showsize = round((float) $size);
2655 if ($showsize > 48) {
2656 $showsize = 48;
2657 }
2658 }
2659
2660 //print $type.'-'.$size;
2661 $out = $value;
2662
2663 return $out;
2664 }
2665
2674 public function getAlignFlag($key, $extrafieldsobjectkey = '')
2675 {
2676 return $this->getCSSClass($key, $extrafieldsobjectkey);
2677 }
2678
2687 public function getCSSClass($key, $extrafieldsobjectkey = '', $mode = 'csslist')
2688 {
2689 $type = 'varchar';
2690 if (!empty($extrafieldsobjectkey)) {
2691 $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
2692 }
2693
2694 $cssstring = '';
2695
2696 if ($mode == 'csslist') {
2697 if (in_array($type, array('date', 'datetime', 'datetimegmt',))) {
2698 $cssstring = "center";
2699 } elseif (in_array($type, array('int', 'price', 'double', 'duration'))) {
2700 $cssstring = "right";
2701 } elseif (in_array($type, array('boolean', 'radio', 'checkbox', 'ip', 'icon'))) {
2702 $cssstring = "center";
2703 }
2704
2705 if (!empty($this->attributes[$extrafieldsobjectkey][$mode][$key])) {
2706 $cssstring .= ($cssstring ? ' ' : '').$this->attributes[$extrafieldsobjectkey][$mode][$key];
2707 } else {
2708 if (in_array($type, array('ip'))) {
2709 $cssstring .= ($cssstring ? ' ' : '').'tdoverflowmax150';
2710 }
2711 }
2712 }
2713
2714 if ($mode == 'css' || $mode == 'cssview') {
2715 if (!empty($this->attributes[$extrafieldsobjectkey][$mode][$key])) {
2716 $cssstring = ($cssstring ? ' ' : '').$this->attributes[$extrafieldsobjectkey][$mode][$key];
2717 }
2718 }
2719
2720 return $cssstring;
2721 }
2722
2733 public function showSeparator($key, $object, $colspan = 2, $display_type = 'card', $mode = '')
2734 {
2735 global $conf, $langs;
2736
2737 $tagtype = 'tr';
2738 $tagtype_dyn = 'td';
2739
2740 if ($display_type == 'line') {
2741 $tagtype = 'div';
2742 $tagtype_dyn = 'span';
2743 $colspan = 0;
2744 }
2745
2746 $extrafield_param = $this->attributes[$object->table_element]['param'][$key];
2747 $extrafield_param_list = array();
2748 if (!empty($extrafield_param) && is_array($extrafield_param)) {
2749 $extrafield_param_list = array_keys($extrafield_param['options']);
2750 }
2751
2752 // Set $extrafield_collapse_display_value (do we have to collapse/expand the group after the separator)
2753 $extrafield_collapse_display_value = -1;
2754 $expand_display = false;
2755 if (is_array($extrafield_param_list) && count($extrafield_param_list) > 0) {
2756 $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
2757 $expand_display = ((isset($_COOKIE['DOLUSER_COLLAPSE_'.$object->table_element.'_extrafields_'.$key]) || GETPOSTINT('ignorecollapsesetup')) ? (!empty($_COOKIE['DOLUSER_COLLAPSE_'.$object->table_element.'_extrafields_'.$key])) : !($extrafield_collapse_display_value == 2));
2758 }
2759 $disabledcookiewrite = 0;
2760 if ($mode == 'create') {
2761 // On create mode, force separator group to not be collapsible
2762 $extrafield_collapse_display_value = 1;
2763 $expand_display = true; // We force group to be shown expanded
2764 $disabledcookiewrite = 1; // We keep status of group unchanged into the cookie
2765 }
2766
2767 $out = '<'.$tagtype.' id="trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'" class="trextrafieldseparator trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'">';
2768 $out .= '<'.$tagtype_dyn.' '.(!empty($colspan) ? 'colspan="' . $colspan . '"' : '').'>';
2769 // Some js code will be injected here to manage the collapsing of extrafields
2770 // Output the picto
2771 $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>';
2772 $out .= '&nbsp;';
2773 $out .= '<strong>';
2774 $out .= $langs->trans($this->attributes[$object->table_element]['label'][$key]);
2775 $out .= '</strong>';
2776 $out .= '</'.$tagtype_dyn.'>';
2777 $out .= '</'.$tagtype.'>';
2778
2779 $collapse_group = $key.(!empty($object->id) ? '_'.$object->id : '');
2780 //$extrafields_collapse_num = $this->attributes[$object->table_element]['pos'][$key].(!empty($object->id)?'_'.$object->id:'');
2781
2782 if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
2783 // Set the collapse_display status to cookie in priority or if ignorecollapsesetup is 1, if cookie and ignorecollapsesetup not defined, use the setup.
2784 $this->expand_display[$collapse_group] = $expand_display;
2785
2786 if (!empty($conf->use_javascript_ajax)) {
2787 $out .= '<!-- Add js script to manage the collapse/uncollapse of extrafields separators '.$key.' -->'."\n";
2788 $out .= '<script nonce="'.getNonce().'" type="text/javascript">'."\n";
2789 $out .= 'jQuery(document).ready(function(){'."\n";
2790 if (empty($disabledcookiewrite)) {
2791 if (!$expand_display) {
2792 $out .= ' console.log("Inject js for the collapsing of extrafield '.$key.' - hide");'."\n";
2793 $out .= ' jQuery(".trextrafields_collapse'.$collapse_group.'").hide();'."\n";
2794 } else {
2795 $out .= ' console.log("Inject js for collapsing of extrafield '.$key.' - keep visible and set cookie");'."\n";
2796 $out .= ' document.cookie = "DOLUSER_COLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=1; path='.$_SERVER["PHP_SELF"].'"'."\n";
2797 }
2798 }
2799 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'").click(function(){'."\n";
2800 $out .= ' console.log("We click on collapse/uncollapse to hide/show .trextrafields_collapse'.$collapse_group.'");'."\n";
2801 $out .= ' jQuery(".trextrafields_collapse'.$collapse_group.'").toggle(100, function(){'."\n";
2802 $out .= ' if (jQuery(".trextrafields_collapse'.$collapse_group.'").is(":hidden")) {'."\n";
2803 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').' '.$tagtype_dyn.' span").addClass("fa-plus-square").removeClass("fa-minus-square");'."\n";
2804 $out .= ' document.cookie = "DOLUSER_COLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=0; path='.$_SERVER["PHP_SELF"].'"'."\n";
2805 $out .= ' } else {'."\n";
2806 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').' '.$tagtype_dyn.' span").addClass("fa-minus-square").removeClass("fa-plus-square");'."\n";
2807 $out .= ' document.cookie = "DOLUSER_COLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=1; path='.$_SERVER["PHP_SELF"].'"'."\n";
2808 $out .= ' }'."\n";
2809 $out .= ' });'."\n";
2810 $out .= ' });'."\n";
2811 $out .= '});'."\n";
2812 $out .= '</script>'."\n";
2813 }
2814 } else {
2815 $this->expand_display[$collapse_group] = 1;
2816 }
2817
2818 return $out;
2819 }
2820
2832 public function setOptionalsFromPost($extralabels, $object, $onlykey = '', $todefaultifmissing = 0)
2833 {
2834 global $langs;
2835
2836 $nofillrequired = 0; // For error when required field left blank
2837 $error_field_required = array();
2838
2839 if (isset($this->attributes[$object->table_element]['label']) && is_array($this->attributes[$object->table_element]['label'])) {
2840 $extralabels = $this->attributes[$object->table_element]['label'];
2841 }
2842
2843 if (is_array($extralabels)) {
2844 // Get extra fields
2845 foreach ($extralabels as $key => $value) {
2846 if (!empty($onlykey) && $onlykey != '@GETPOSTISSET' && $key != $onlykey) {
2847 continue;
2848 }
2849
2850 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', 'duration')))) {
2851 //when unticking boolean field, it's not set in POST
2852 continue;
2853 }
2854
2855 $key_type = $this->attributes[$object->table_element]['type'][$key];
2856 if ($key_type == 'separate') {
2857 continue;
2858 }
2859
2860 $enabled = 1;
2861 if (isset($this->attributes[$object->table_element]['enabled'][$key])) { // 'enabled' is often a condition on module enabled or not
2862 $enabled = (int) dol_eval((string) $this->attributes[$object->table_element]['enabled'][$key], 1, 1, '2');
2863 }
2864
2865 $visibility = 1;
2866 if (isset($this->attributes[$object->table_element]['list'][$key])) { // 'list' is option for visibility
2867 $visibility = (int) dol_eval((string) $this->attributes[$object->table_element]['list'][$key], 1, 1, '2');
2868 }
2869
2870 $perms = 1;
2871 if (isset($this->attributes[$object->table_element]['perms'][$key])) {
2872 $perms = (int) dol_eval((string) $this->attributes[$object->table_element]['perms'][$key], 1, 1, '2');
2873 }
2874 if (empty($enabled)
2875 || (
2876 $onlykey === '@GETPOSTISSET'
2877 && in_array($this->attributes[$object->table_element]['type'][$key], array('boolean', 'checkbox', 'chkbxlst'))
2878 && in_array(abs($enabled), array(2, 5))
2879 && ! GETPOSTISSET('options_' . $key) // Update hidden checkboxes and multiselect only if they are provided
2880 )
2881 ) {
2882 continue;
2883 }
2884
2885 $visibility_abs = abs($visibility);
2886 // not modify if extra field is not in update form (0 : never, 2 or -2 : list only, 5 or - 5 : list and view only)
2887 if (empty($visibility_abs) || $visibility_abs == 2 || $visibility_abs == 5) {
2888 continue;
2889 }
2890 if (empty($perms)) {
2891 continue;
2892 }
2893
2894 if ($this->attributes[$object->table_element]['required'][$key]) { // Value is required
2895 // Check if functionally empty without using GETPOST (depending on the type of extrafield, a
2896 // technically non-empty value may be treated as empty functionally).
2897 // value can be alpha, int, array, etc...
2898 $v = $_POST["options_".$key] ?? null;
2899 $type = $this->attributes[$object->table_element]['type'][$key];
2900 if (self::isEmptyValue($v, $type)) {
2901 //print 'ccc'.$value.'-'.$this->attributes[$object->table_element]['required'][$key];
2902
2903 // 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.
2904
2905 $nofillrequired++;
2906 if (!empty($this->attributes[$object->table_element]['langfile'][$key])) {
2907 $langs->load($this->attributes[$object->table_element]['langfile'][$key]);
2908 }
2909 $error_field_required[$key] = $langs->transnoentitiesnoconv($value);
2910 }
2911 }
2912
2913 if (in_array($key_type, array('date'))) {
2914 // Clean parameters
2915 $value_key = dol_mktime(12, 0, 0, GETPOSTINT("options_".$key."month"), GETPOSTINT("options_".$key."day"), GETPOSTINT("options_".$key."year"));
2916 } elseif (in_array($key_type, array('datetime'))) {
2917 // Clean parameters
2918 $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');
2919 } elseif (in_array($key_type, array('datetimegmt'))) {
2920 // Clean parameters
2921 $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');
2922 } elseif (in_array($key_type, array('duration'))) {
2923 $value_hours = GETPOSTINT("options_" . $key . "hour");
2924 $value_minutes = GETPOSTINT("options_" . $key . "min");
2925 $value_key = $value_hours * 3600 + $value_minutes * 60;
2926 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
2927 $value_arr = GETPOST("options_".$key, 'array'); // check if an array
2928 if (!empty($value_arr)) {
2929 $value_key = implode(',', $value_arr);
2930 } else {
2931 $value_key = '';
2932 }
2933 } elseif (in_array($key_type, array('price', 'double'))) {
2934 $value_arr = GETPOST("options_".$key, 'alpha');
2935 $value_key = price2num($value_arr);
2936 } elseif (in_array($key_type, array('pricecy'))) {
2937 $value_key = price2num(GETPOST("options_".$key, 'alpha')).':'.GETPOST("options_".$key."currency_id", 'alpha');
2938 } elseif (in_array($key_type, array('html'))) {
2939 $value_key = GETPOST("options_".$key, 'restricthtml');
2940 } elseif (in_array($key_type, ['point', 'multipts', 'linestrg', 'polygon'])) {
2941 // construct point
2942 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
2943 $geojson = GETPOST("options_".$key, 'restricthtml');
2944 if ($geojson != '{}') {
2945 $dolgeophp = new DolGeoPHP($this->db);
2946 $value_key = $dolgeophp->getWkt($geojson);
2947 } else {
2948 $value_key = '';
2949 }
2950 } elseif (in_array($key_type, array('text'))) {
2951 $label_security_check = 'alphanohtml';
2952 // by default 'alphanohtml' (better security); hidden conf MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML allows basic html
2953 if (getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_REF_LABELS')) {
2954 $label_security_check = 'nohtml';
2955 } else {
2956 $label_security_check = !getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML') ? 'alphanohtml' : 'restricthtml';
2957 }
2958 $value_key = GETPOST("options_".$key, $label_security_check);
2959 } else {
2960 $value_key = GETPOST("options_".$key);
2961 if (in_array($key_type, array('link')) && $value_key == '-1') {
2962 $value_key = '';
2963 }
2964 }
2965
2966 if (!empty($error_field_required[$key]) && $todefaultifmissing) {
2967 // Value is required but we have a default value and we asked to set empty value to the default value
2968 if (!empty($this->attributes[$object->table_element]['default']) && !is_null($this->attributes[$object->table_element]['default'][$key])) {
2969 $value_key = $this->attributes[$object->table_element]['default'][$key];
2970 unset($error_field_required[$key]);
2971 $nofillrequired--;
2972 }
2973 }
2974
2975 $object->array_options["options_".$key] = $value_key;
2976 }
2977
2978 if ($nofillrequired) {
2979 $langs->load('errors');
2980 $this->error = $langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required);
2981 setEventMessages($this->error, null, 'errors');
2982 return -1;
2983 } else {
2984 return 1;
2985 }
2986 } else {
2987 return 0;
2988 }
2989 }
2990
2999 public function getOptionalsFromPost($extrafieldsobjectkey, $keysuffix = '', $keyprefix = '')
3000 {
3001 global $_POST;
3002
3003 if (is_string($extrafieldsobjectkey) && !empty($this->attributes[$extrafieldsobjectkey]['label']) && is_array($this->attributes[$extrafieldsobjectkey]['label'])) {
3004 $extralabels = $this->attributes[$extrafieldsobjectkey]['label'];
3005 } else {
3006 $extralabels = $extrafieldsobjectkey;
3007 }
3008
3009 if (is_array($extralabels)) {
3010 $array_options = array();
3011
3012 // Get extra fields
3013 foreach ($extralabels as $key => $value) {
3014 $key_type = '';
3015 if (is_string($extrafieldsobjectkey)) {
3016 $key_type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
3017 }
3018
3019 if (in_array($key_type, array('date'))) {
3020 $dateparamname_start = $keyprefix . 'options_' . $key . $keysuffix . '_start';
3021 $dateparamname_end = $keyprefix . 'options_' . $key . $keysuffix . '_end';
3022
3023 if (GETPOST($dateparamname_start . 'year') || GETPOST($dateparamname_end . 'year')) {
3024 $value_key = array();
3025 // values provided as a component year, month, day, etc.
3026 if (GETPOST($dateparamname_start . 'year')) {
3027 $value_key['start'] = dol_mktime(0, 0, 0, GETPOSTINT($dateparamname_start . 'month'), GETPOSTINT($dateparamname_start . 'day'), GETPOSTINT($dateparamname_start . 'year'));
3028 }
3029 if (GETPOST($dateparamname_end . 'year')) {
3030 $value_key['end'] = dol_mktime(23, 59, 59, GETPOSTINT($dateparamname_end . 'month'), GETPOSTINT($dateparamname_end . 'day'), GETPOSTINT($dateparamname_end . 'year'));
3031 }
3032 } elseif (GETPOST($keyprefix."options_".$key.$keysuffix."year")) {
3033 // Clean parameters
3034 $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"));
3035 } else {
3036 continue; // Value was not provided, we should not set it.
3037 }
3038 } elseif (in_array($key_type, array('datetime', 'datetimegmt'))) {
3039 $dateparamname_start = $keyprefix . 'options_' . $key . $keysuffix . '_start';
3040 $dateparamname_end = $keyprefix . 'options_' . $key . $keysuffix . '_end';
3041
3042 if (GETPOST($dateparamname_start . 'year') || GETPOST($dateparamname_end . 'year')) {
3043 // values provided as a date pair (start date + end date), each date being broken down as year, month, day, etc.
3044 $dateparamname_start_hour = GETPOSTINT($dateparamname_start . 'hour') != '-1' ? GETPOSTINT($dateparamname_start . 'hour') : '00';
3045 $dateparamname_start_min = GETPOSTINT($dateparamname_start . 'min') != '-1' ? GETPOSTINT($dateparamname_start . 'min') : '00';
3046 $dateparamname_start_sec = GETPOSTINT($dateparamname_start . 'sec') != '-1' ? GETPOSTINT($dateparamname_start . 'sec') : '00';
3047 $dateparamname_end_hour = GETPOSTINT($dateparamname_end . 'hour') != '-1' ? GETPOSTINT($dateparamname_end . 'hour') : '23';
3048 $dateparamname_end_min = GETPOSTINT($dateparamname_end . 'min') != '-1' ? GETPOSTINT($dateparamname_end . 'min') : '59';
3049 $dateparamname_end_sec = GETPOSTINT($dateparamname_end . 'sec') != '-1' ? GETPOSTINT($dateparamname_end . 'sec') : '59';
3050 if ($key_type == 'datetimegmt') {
3051 $value_key = array(
3052 'start' => dol_mktime($dateparamname_start_hour, $dateparamname_start_min, $dateparamname_start_sec, GETPOSTINT($dateparamname_start . 'month'), GETPOSTINT($dateparamname_start . 'day'), GETPOSTINT($dateparamname_start . 'year'), 'gmt'),
3053 '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')
3054 );
3055 } else {
3056 $value_key = array(
3057 'start' => dol_mktime($dateparamname_start_hour, $dateparamname_start_min, $dateparamname_start_sec, GETPOSTINT($dateparamname_start . 'month'), GETPOSTINT($dateparamname_start . 'day'), GETPOSTINT($dateparamname_start . 'year'), 'tzuserrel'),
3058 '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')
3059 );
3060 }
3061 } elseif (GETPOST($keyprefix."options_".$key.$keysuffix."year")) {
3062 // Clean parameters
3063 if ($key_type == 'datetimegmt') {
3064 $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');
3065 } else {
3066 $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');
3067 }
3068 } else {
3069 continue; // Value was not provided, we should not set it.
3070 }
3071 } elseif ($key_type == 'select') {
3072 // to detect if we are in search context
3073 if (GETPOSTISARRAY($keyprefix."options_".$key.$keysuffix)) {
3074 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix, 'array:aZ09');
3075 // Make sure we get an array even if there's only one selected
3076 $value_arr = (array) $value_arr;
3077 $value_key = implode(',', $value_arr);
3078 } else {
3079 $value_key = GETPOST($keyprefix."options_".$key.$keysuffix);
3080 }
3081 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
3082 // We test on a hidden field named "..._multiselect" that is always set to 1 if param is in form so
3083 // when nothing is provided we can make a difference between noparam in the form and param was set to nothing.
3084 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix.'_multiselect')) {
3085 continue; // Value was not provided, we should not set it.
3086 }
3087 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
3088 // Make sure we get an array even if there's only one checkbox
3089 $value_arr = (array) $value_arr;
3090 $value_key = implode(',', $value_arr);
3091 } elseif (in_array($key_type, array('pricecy'))) {
3092 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
3093 continue; // Value was not provided, we should not set it.
3094 }
3095 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
3096 if ($keyprefix != 'search_') { // If value is for a search, we must keep complex string like '>100 <=150'
3097 $value_key = price2num($value_arr).':'.GETPOST($keyprefix."options_".$key.$keysuffix."currency_id", 'alpha');
3098 } else {
3099 $value_key = $value_arr;
3100 }
3101 } elseif (in_array($key_type, array('price', 'double', 'int'))) {
3102 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
3103 continue; // Value was not provided, we should not set it.
3104 }
3105 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
3106 if ($keyprefix != 'search_') { // If value is for a search, we must keep complex string like '>100 <=150'
3107 $value_key = price2num($value_arr);
3108 } else {
3109 $value_key = $value_arr;
3110 }
3111 } elseif (in_array($key_type, array('boolean'))) {
3112 // We test on a hidden field named "..._boolean" that is always set to 1 if param is in form so
3113 // when nothing is provided we can make a difference between noparam in the form and param was set to nothing.
3114 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix."_boolean")) {
3115 $value_key = '';
3116 } else {
3117 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
3118 $value_key = $value_arr;
3119 }
3120 } elseif (in_array($key_type, array('html'))) {
3121 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
3122 continue; // Value was not provided, we should not set it.
3123 }
3124 $value_key = dol_htmlcleanlastbr(GETPOST($keyprefix."options_".$key.$keysuffix, 'restricthtml'));
3125 } else {
3126 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
3127 continue; // Value was not provided, we should not set it.
3128 }
3129
3130 $value_key = GETPOST($keyprefix."options_".$key.$keysuffix);
3131 if ($value_key === '') {
3132 $value_key = null;
3133 }
3134 }
3135
3136 $array_options[$keyprefix."options_".$key] = $value_key; // No keyprefix here. keyprefix is used only for read.
3137 }
3138
3139 return $array_options;
3140 }
3141
3142 return 0;
3143 }
3144
3150 public static function getListOfTypesLabels()
3151 {
3152 global $langs;
3153
3154 $arraytype2label = array('');
3155
3156 $tmptype2label = ExtraFields::$type2label;
3157 foreach ($tmptype2label as $key => $val) {
3158 $arraytype2label[$key] = $langs->transnoentitiesnoconv($val);
3159 }
3160
3161 if (!getDolGlobalString('MAIN_USE_EXTRAFIELDS_ICON')) {
3162 unset($arraytype2label['icon']);
3163 }
3164 if (!getDolGlobalString('MAIN_USE_GEOPHP')) {
3165 unset($arraytype2label['point']);
3166 unset($arraytype2label['multipts']);
3167 unset($arraytype2label['linestrg']);
3168 unset($arraytype2label['polygon']);
3169 }
3170
3171 return $arraytype2label;
3172 }
3173
3181 public static function isEmptyValue($v, string $type)
3182 {
3183 if ($v === null || $v === '') {
3184 return true;
3185 }
3186 if (is_array($v) || $type == 'select') {
3187 return empty($v);
3188 }
3189 if ($type == 'link') {
3190 return ($v == '-1');
3191 }
3192 if ($type == 'sellist') {
3193 return ($v == '0');
3194 }
3195 return empty($v); // Note empty('0') is also true, tested 7.0 up to 8.3.12
3196 }
3197}
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition ajax.lib.php:475
$c
Definition line.php:331
Class to manage categories.
Class to manage a WYSIWYG editor.
Class to manage Geo processing Usage: $dolgeophp=new DolGeoPHP($db);.
Class to manage standard extra fields.
showOutputField($key, $value, $moreparam='', $extrafieldsobjectkey='', $outputlangs=null, $object=null, $mode='')
Return HTML string to put an output field into a page.
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(), $aiprompt="", $emptyonclone=0)
Modify description of personalized attribute This is a private method.
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(), $aiprompt="", $emptyonclone=0)
Modify type of a personalized attribute.
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(), $aiprompt="", $emptyonclone=0)
Add description of a new optional attribute.
getOptionalsFromPost($extrafieldsobjectkey, $keysuffix='', $keyprefix='')
return array_options array of data of extrafields value of object sent by a search form
fetch_name_optionals_label($elementtype, $forceload=false, $attrname='')
Load the array of extrafields definition $this->attributes.
create($attrname, $type='varchar', $length='255', $elementtype='', $unique=0, $required=0, $default_value='', $param=array(), $perms='', $list='0', $computed='', $help='', $moreparams=array())
Add a new optional attribute.
static getListOfTypesLabels()
Return array with all possible types and labels of extrafields.
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(), $aiprompt="", $emptyonclone=0)
Add a new extra field parameter.
showInputField($key, $value, $moreparam='', $keysuffix='', $keyprefix='', $morecss='', $object=0, $extrafieldsobjectkey='', $mode=0)
Return HTML string to put an input field into a page Code very similar with showInputField of common ...
__construct($db)
Constructor.
delete_label($attrname, $elementtype='')
Delete description of an optional attribute.
static isEmptyValue($v, string $type)
Return if a value is "empty" for a mandatory vision.
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(), $emptyonclone=0)
Update an existing extra field parameter.
Class to manage generation of HTML components Only common components must be here.
Class to manage a Leaflet map width geometrics objects.
Class to manage third parties objects (customers, suppliers, prospects...)
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:171
convertSecondToTime($iSecond, $format='all', $lengthOfDay=86400, $lengthOfWeek=7)
Return, in clear text, value of a number of seconds in days, hours and minutes.
Definition date.lib.php:262
dol_now($mode='gmt')
Return date for now.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
dol_print_email($email, $contactid=0, $socid=0, $addlink=0, $max=0, $showinvalid=2, $withpicto=0, $morecss='paddingrightonly')
Show EMail link formatted for HTML output.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
dol_print_ip($ip, $mode=0, $showname=0)
Return an IP formatted to be shown on screen.
dol_print_phone($phone, $countrycode='', $contactid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0, $morecss='paddingright')
Format phone numbers according to country.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_print_url($url, $target='_blank', $max=32, $withpicto=0, $morecss='')
Show Url link.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
jsonOrUnserialize($stringtodecode, $assoc=true)
Decode an encoded string.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
yn($yesno, $format=1, $color=0)
Return yes or no in current language.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
colorIsLight($stringcolor)
Return true if the color is light.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
This function is called to encode a string into a HTML string but differs from htmlentities because a...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
if(getDolGlobalString( 'TAKEPOS_SHOW_CUSTOMER')) print $langs trans('Date')." left Label right Qty right Price right TotalHT right TotalTTC right right right right right right right right right centpercent right TotalHT right n right VAT right n right TotalVAT right n No sujeto a RE IRPF right TotalLT1 right n right TotalLT2 right n right TotalTTC right n takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency right TotalTTC takeposcustomercurrency right takeposcustomercurrency n right PaymentTypeShortLIQ right SELECT p pos_change as p datep as date
Definition receipt.php:469
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:132