dolibarr 23.0.3
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: '".$object->id."',
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 }
1543
1544 $filter_categorie = false;
1545 if (count($InfoFieldList) > 5) {
1546 if ($InfoFieldList[0] == 'categorie') {
1547 $filter_categorie = true;
1548 }
1549 }
1550
1551 if (!$filter_categorie) {
1552 $fields_label = isset($InfoFieldList[1]) ? explode('|', $InfoFieldList[1]) : array();
1553 if (!empty($fields_label)) {
1554 $keyList .= ', ';
1555 $keyList .= implode(', ', $fields_label);
1556 }
1557
1558 // WARNING!! This code is duplicated into core/ajax/ajaxextrafield.php
1559
1560 $sqlwhere = '';
1561 $sql = "SELECT ".$keyList;
1562 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
1563
1564 // Add filter from 4th field
1565 if (!empty($InfoFieldList[4])) {
1566 // can use current entity filter
1567 if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
1568 $InfoFieldList[4] = str_replace('$ENTITY$', (string) $conf->entity, $InfoFieldList[4]);
1569 }
1570 // can use SELECT request
1571 if (!getDolGlobalString("MAIN_DISALLOW_UNSECURED_SELECT_INTO_EXTRAFIELDS_FILTER")) {
1572 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
1573 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
1574 }
1575 }
1576 // can use MODE parameter (list or view)
1577 if (strpos($InfoFieldList[4], '$MODE$') !== false) {
1578 $InfoFieldList[4] = str_replace('$MODE$', preg_replace('/[^a-z0-9_]/i', '', (string) $mode), $InfoFieldList[4]);
1579 }
1580
1581 // current object id can be use into filter
1582 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
1583 $InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
1584 } elseif (substr($_SERVER["PHP_SELF"], -8) == 'list.php') {
1585 // In filters of list views, we do not want $ID$ replaced by 0. So we remove the '=' condition.
1586 // Do nothing if condition is using 'IN' keyword
1587 // Replace 'column = $ID$' by "word"
1588 $word = '#\b([a-zA-Z0-9-\.-_]+)\b *= *\$ID\$#';
1589 $InfoFieldList[4] = preg_replace($word, '$1', $InfoFieldList[4]);
1590 // Replace '$ID$ = column' by "word"
1591 $word = '#\$ID\$ *= *\b([a-zA-Z0-9-\.-_]+)\b#';
1592 $InfoFieldList[4] = preg_replace($word, '$1', $InfoFieldList[4]);
1593 } else {
1594 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
1595 }
1596
1597 // can use filter on any field of object
1598 if (is_object($object)) {
1599 $tags = [];
1600 preg_match_all('/\$(.*?)\$/', $InfoFieldList[4], $tags); // Example: $InfoFieldList[4] is ($dateadh$:<=:CURRENT_DATE)
1601 foreach ($tags[0] as $keytag => $valuetag) {
1602 $property = preg_replace('/[^a-z0-9_]/', '', strtolower($tags[1][$keytag]));
1603 if (strpos($InfoFieldList[4], $valuetag) !== false && property_exists($object, $property) && !empty($object->$property)) {
1604 $InfoFieldList[4] = str_replace($valuetag, (string) $object->$property, $InfoFieldList[4]);
1605 } else {
1606 $InfoFieldList[4] = str_replace($valuetag, '0', $InfoFieldList[4]);
1607 }
1608 }
1609 }
1610
1611 // We have to join on extrafield table
1612 $errstr = '';
1613 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1614 $sql .= ' as main, '.$this->db->sanitize($this->db->prefix().$InfoFieldList[0]).'_extrafields as extra';
1615 $sqlwhere .= " WHERE extra.fk_object = main.".$this->db->sanitize($InfoFieldList[2]);
1616 $sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1617 } else {
1618 $sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1619 }
1620 } else {
1621 $sqlwhere .= ' WHERE 1=1';
1622 }
1623
1624 // Add Usf filter on second line
1625 /*
1626 if ($Usf) {
1627 $errorstr = '';
1628 $sqlusf .= forgeSQLFromUniversalSearchCriteria($Usf, $errorstr);
1629 if (!$errorstr) {
1630 $sqlwhere .= $sqlusf;
1631 } else {
1632 $sqlwhere .= " AND invalid_usf_filter_of_extrafield";
1633 }
1634 }
1635 */
1636
1637 // Some tables may have field, some other not. For the moment we disable it.
1638 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
1639 $sqlwhere .= ' AND entity = '.((int) $conf->entity);
1640 }
1641 $sql .= $sqlwhere;
1642
1643 $sql .= $this->db->order(implode(', ', $fields_label));
1644 $sql .= ' LIMIT ' . getDolGlobalInt('MAIN_EXTRAFIELDS_LIMIT_SELLIST_SQL', 1000);
1645 //print $sql;
1646
1647 dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
1648 $resql = $this->db->query($sql);
1649 if ($resql) {
1650 $out .= '<option value="0">&nbsp;</option>';
1651 $num = $this->db->num_rows($resql);
1652 $i = 0;
1653 while ($i < $num) {
1654 $labeltoshow = '';
1655 $obj = $this->db->fetch_object($resql);
1656
1657 $nameFields = $InfoFieldList[1];
1658 // If text is "field1|f(a,b,c) as xxx|field2", we must convert string into 'field1|xxx|field2'
1659 $nameFields = preg_replace('/[a-z_]+\‍([^\‍)]*\‍) as ([\w]+)/i', '\1', $nameFields);
1660 // Sanitize field names to avoid error when doing $obj->field
1661 $nameFields = preg_replace('/[^0-9a-z_\.\|]/i', '', $nameFields);
1662
1663 // Several fields into label (eq table:code|label:rowid)
1664 $notrans = false;
1665 $fields_label = explode('|', $nameFields);
1666 if (is_array($fields_label) && count($fields_label) > 1) {
1667 $notrans = true;
1668 foreach ($fields_label as $field_toshow) {
1669 $field_toshow = preg_replace('/^.*\./', '', $field_toshow);
1670 $labeltoshow .= $obj->$field_toshow.' ';
1671 }
1672 } else {
1673 $labeltoshow = $obj->$nameFields;
1674 }
1675
1676 if ($value == $obj->rowid) {
1677 if (!$notrans) {
1678 foreach ($fields_label as $field_toshow) {
1679 $translabel = $langs->trans($obj->$field_toshow);
1680 $labeltoshow = $translabel.' ';
1681 }
1682 }
1683 $out .= '<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
1684 } else {
1685 if (!$notrans) {
1686 $translabel = $langs->trans($obj->$nameFields);
1687 $labeltoshow = $translabel;
1688 }
1689 if (empty($labeltoshow)) {
1690 $labeltoshow = '(not defined)';
1691 }
1692
1693 if (!empty($InfoFieldList[3]) && $parentField) {
1694 // Sanitize parent field name to avoid when doing $obj->field
1695 $parentField = preg_replace('/[^a-zA-Z0-9_\-]/', '', $parentField);
1696 $parent = $parentName.':'.$obj->{$parentField};
1697 }
1698
1699 $out .= '<option value="'.$obj->rowid.'"';
1700 $out .= ($value == $obj->rowid ? ' selected' : '');
1701 $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
1702 $out .= '>'.$labeltoshow.'</option>';
1703 }
1704
1705 $i++;
1706 }
1707 $this->db->free($resql);
1708 } else {
1709 print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
1710 }
1711 } else {
1712 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
1713 $categcode = $InfoFieldList[5];
1714 if (is_numeric($categcode)) { // deprecated: must use the category code instead of id. For backward compatibility.
1715 $tmpcategory = new Categorie($this->db);
1716 $MAP_ID_TO_CODE = array_flip($tmpcategory->MAP_ID);
1717 $categcode = $MAP_ID_TO_CODE[(int) $categcode];
1718 }
1719
1720 $data = $form->select_all_categories($categcode, '', 'parent', 64, $InfoFieldList[6], 1, 1);
1721 $out .= '<option value="0">&nbsp;</option>';
1722 if (is_array($data)) {
1723 foreach ($data as $data_key => $data_value) {
1724 $out .= '<option value="'.$data_key.'"';
1725 $out .= ($value == $data_key ? ' selected' : '');
1726 $out .= '>'.$data_value.'</option>';
1727 }
1728 }
1729 }
1730 }
1731 $out .= '</select>';
1732 }
1733 } elseif ($type == 'checkbox') {
1734 $value_arr = $value;
1735 if (!is_array($value)) {
1736 $value_arr = explode(',', $value);
1737 }
1738 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ? null : $param['options']), $value_arr, 0, 0, '', 0, '100%');
1739 } elseif ($type == 'radio') {
1740 $out = '';
1741 foreach ($param['options'] as $keyopt => $val) {
1742 $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
1743 $out .= ' value="'.$keyopt.'"';
1744 $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
1745 $out .= ($value == $keyopt ? 'checked' : '');
1746 $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$langs->trans($val).'</label><br>';
1747 }
1748 } elseif ($type == 'chkbxlst') { // List of values selected from a table (n choices)
1749 if (is_array($value)) {
1750 $value_arr = $value;
1751 } else {
1752 $value_arr = explode(',', $value);
1753 }
1754
1755 if (is_array($param['options'])) {
1756 $tmpparamoptions = array_keys($param['options']);
1757 $paramoptions = preg_split('/[\r\n]+/', $tmpparamoptions[0]);
1758
1759 $InfoFieldList = explode(":", $paramoptions[0], 5); // We will extract field at position 6,7... later from the 5th one.
1760 // 0 : tableName
1761 // 1 : label field name
1762 // 2 : rowid field name (if different of rowid)
1763 // Optional parameters...
1764 // 3 : key field parent (for dependent lists). How this is used ?
1765 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value. Or use USF on the second line.
1766
1767 // To add a filter on category (not possible with USF restricted to table)
1768 // 5 : string category type ('product', 'customer', ...). This is added to the filter.
1769 // 6 : list of parent categories ids. This replace the filter.
1770 // 7 : sort field (not used here but used into format for commobject)
1771
1772 // For backward compatibility when tableName = 'category', so when we want a list of categories
1773 // 5 : string category type ('product', 'customer', ...). This replace the filter.
1774 // 6 : list of parent categories ids. This replace the filter.
1775 // 7 : sort field (not used here but used into format for commobject)
1776
1777 // Example with extrafield value = "societe:nom:rowid" or "categorie:label:rowid:::product"
1778
1779 // If there is a filter, we extract it by taking all content inside parenthesis.
1780 if (! empty($InfoFieldList[4])) {
1781 $pos = 0;
1782 $parenthesisopen = 0;
1783 while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
1784 if (substr($InfoFieldList[4], $pos, 1) == '(') {
1785 $parenthesisopen++;
1786 }
1787 if (substr($InfoFieldList[4], $pos, 1) == ')') {
1788 $parenthesisopen--;
1789 }
1790 $pos++;
1791 }
1792 $tmpbefore = substr($InfoFieldList[4], 0, $pos);
1793 $tmpafter = substr($InfoFieldList[4], $pos + 1);
1794 //var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
1795 $InfoFieldList[4] = $tmpbefore;
1796 if ($tmpafter !== '') {
1797 $InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
1798 }
1799
1800 // Fix better compatibility with some old extrafield syntax filter "(field_name=123)"
1801 $reg = array();
1802 if (preg_match('/^\‍(?([a-z0-9_]+)([=<>]+)(\d+)\‍)?$/i', $InfoFieldList[4], $reg)) {
1803 $InfoFieldList[4] = '('.$reg[1].':'.$reg[2].':'.$reg[3].')';
1804 }
1805
1806 //var_dump($InfoFieldList);
1807 }
1808
1809 //$Usf = empty($paramoptions[1]) ? '' :$paramoptions[1];
1810
1811
1812 $parentName = '';
1813 $parentField = '';
1814 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
1815
1816 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
1817 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1818 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
1819 } else {
1820 $keyList = $InfoFieldList[2].' as rowid';
1821 }
1822 }
1823 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
1824 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
1825 if (!empty($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra.') !== false) {
1826 $keyList .= ', main.'.$parentField;
1827 } else {
1828 $keyList .= ', '.$parentField;
1829 }
1830 }
1831
1832 $InfoFieldList[5] = (string) ($InfoFieldList[5]??'');
1833
1834 $filter_categorie = false;
1835 if (count($InfoFieldList) > 5 && ($InfoFieldList[5] != '')) {
1836 if ($InfoFieldList[0] == 'categorie') {
1837 $filter_categorie = true; // The combo list is a list of categories
1838 } else {
1839 $filter_categorie = false; // We have a filter on category for another table
1840 }
1841 }
1842
1843
1844 if (!$filter_categorie) {
1845 $fields_label = explode('|', $InfoFieldList[1]);
1846 if (is_array($fields_label)) {
1847 $keyList .= ', ';
1848 $keyList .= implode(', ', $fields_label);
1849 }
1850
1851 $sqlwhere = '';
1852 $sql = "SELECT ".$keyList;
1853 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
1854
1855 // Add filter from 4th field
1856 if (!empty($InfoFieldList[4])) {
1857 // can use current entity filter
1858 if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
1859 $InfoFieldList[4] = str_replace('$ENTITY$', (string) $conf->entity, $InfoFieldList[4]);
1860 }
1861 // can use SELECT request
1862 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
1863 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
1864 }
1865
1866 // current object id can be use into filter
1867 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
1868 $InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
1869 } elseif (substr($_SERVER["PHP_SELF"], -8) == 'list.php') {
1870 // In filters of list views, we do not want $ID$ replaced by 0. So we remove the '=' condition.
1871 // Do nothing if condition is using 'IN' keyword
1872 // Replace 'column = $ID$' by "word"
1873 $word = '#\b([a-zA-Z0-9-\.-_]+)\b *= *\$ID\$#';
1874 $InfoFieldList[4] = preg_replace($word, '$1', $InfoFieldList[4]);
1875 // Replace '$ID$ = column' by "word"
1876 $word = '#\$ID\$ *= *\b([a-zA-Z0-9-\.-_]+)\b#';
1877 $InfoFieldList[4] = preg_replace($word, '$1', $InfoFieldList[4]);
1878 } else {
1879 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
1880 }
1881
1882 // We have to join on extrafield table
1883 $errstr = '';
1884 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1885 $sql .= ' as main, '.$this->db->sanitize($this->db->prefix().$InfoFieldList[0]).'_extrafields as extra';
1886 $sqlwhere .= " WHERE extra.fk_object = main.".$this->db->sanitize($InfoFieldList[2]);
1887 $sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1888 } else {
1889 $sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1890 }
1891 } else {
1892 $sqlwhere .= ' WHERE 1=1';
1893 }
1894
1895
1896 if ($InfoFieldList[5] != '') {
1897 // We have a filter on category for another table
1898 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
1899 $tmpcategory = new Categorie($this->db);
1900 $tablesuffixcategory = empty($tmpcategory->MAP_CAT_TABLE[$InfoFieldList[5]]) ? $InfoFieldList[5] : $tmpcategory->MAP_CAT_TABLE[$InfoFieldList[5]];
1901 $fksuffixcategory = empty($tmpcategory->MAP_CAT_FK[$InfoFieldList[5]]) ? $InfoFieldList[5] : $tmpcategory->MAP_CAT_FK[$InfoFieldList[5]];
1902
1903 $sqlwhere .= ' AND EXISTS (SELECT fk_categorie as categid FROM '.MAIN_DB_PREFIX.'categorie_'.$this->db->sanitize($tablesuffixcategory);
1904 $sqlwhere .= ' WHERE fk_categorie IN ('.$this->db->sanitize($InfoFieldList[6]).')';
1905 $sqlwhere .= ' AND fk_'.$this->db->sanitize($fksuffixcategory).' = rowid)';
1906 //var_dump($sqlwhere);exit;
1907 }
1908
1909 // Add Usf filter on second line
1910 /*
1911 if ($Usf) {
1912 $errorstr = '';
1913 $sqlusf .= forgeSQLFromUniversalSearchCriteria($Usf, $errorstr);
1914 if (!$errorstr) {
1915 $sqlwhere .= $sqlusf;
1916 } else {
1917 $sqlwhere .= " AND invalid_usf_filter_of_extrafield";
1918 }
1919 }
1920 */
1921
1922 // Some tables may have field, some other not. For the moment we disable it.
1923 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
1924 $sqlwhere .= " AND entity = ".((int) $conf->entity);
1925 }
1926 // $sql.=preg_replace('/^ AND /','',$sqlwhere);
1927 // print $sql;
1928
1929 $sql .= $sqlwhere;
1930 $sql .= ' ORDER BY '.implode(', ', $fields_label);
1931
1932 dol_syslog(get_class($this).'::showInputField type=chkbxlst', LOG_DEBUG);
1933
1934 $resql = $this->db->query($sql);
1935 if ($resql) {
1936 $num = $this->db->num_rows($resql);
1937 $i = 0;
1938
1939 $data = array();
1940
1941 while ($i < $num) {
1942 $labeltoshow = '';
1943 $obj = $this->db->fetch_object($resql);
1944
1945 $notrans = false;
1946 // Several field into label (eq table:code|label:rowid)
1947 $fields_label = explode('|', $InfoFieldList[1]);
1948 if (is_array($fields_label)) {
1949 $notrans = true;
1950 foreach ($fields_label as $field_toshow) {
1951 $labeltoshow .= $obj->$field_toshow.' ';
1952 }
1953 } else {
1954 $labeltoshow = $obj->{$InfoFieldList[1]};
1955 }
1956 $labeltoshow = dol_trunc($labeltoshow, 45);
1957
1958 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
1959 $labeltoshow = '';
1960 foreach ($fields_label as $field_toshow) {
1961 $translabel = $langs->trans($obj->$field_toshow);
1962 if ($translabel != $obj->$field_toshow) {
1963 $labeltoshow .= ' '.dol_trunc($translabel, 18).' ';
1964 } else {
1965 $labeltoshow .= ' '.dol_trunc($obj->$field_toshow, 18).' ';
1966 }
1967 }
1968 $data[$obj->rowid] = $labeltoshow;
1969 } else {
1970 if (!$notrans) {
1971 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
1972 if ($translabel != $obj->{$InfoFieldList[1]}) {
1973 $labeltoshow = dol_trunc($translabel, 18);
1974 } else {
1975 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
1976 }
1977 }
1978 if (empty($labeltoshow)) {
1979 $labeltoshow = '(not defined)';
1980 }
1981
1982 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
1983 $data[$obj->rowid] = $labeltoshow;
1984 }
1985
1986 if (!empty($InfoFieldList[3]) && $parentField) {
1987 $parent = $parentName.':'.$obj->{$parentField};
1988 }
1989
1990 $data[$obj->rowid] = $labeltoshow;
1991 }
1992
1993 $i++;
1994 }
1995 $this->db->free($resql);
1996
1997 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, 0, 0, '', 0, '100%');
1998 } else {
1999 print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
2000 }
2001 } else {
2002 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2003 $categcode = $InfoFieldList[5];
2004 if (is_numeric($categcode)) { // deprecated: must use the category code instead of id. For backward compatibility.
2005 $tmpcategory = new Categorie($this->db);
2006 $MAP_ID_TO_CODE = array_flip($tmpcategory->MAP_ID);
2007 $categcode = $MAP_ID_TO_CODE[(int) $categcode];
2008 }
2009
2010 $data = $form->select_all_categories($categcode, '', 'parent', 64, $InfoFieldList[6], 1, 1);
2011 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, 0, 0, '', 0, '100%');
2012 }
2013 }
2014 } elseif ($type == 'link') {
2015 $param_list = array_keys($param['options']); // $param_list[0] = 'ObjectName:classPath' but can also be 'ObjectName:classPath:1:(status:=:1)'
2016 /* Removed.
2017 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
2018 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.
2019 if (strpos($param_list[0], '$ID$') !== false && !empty($objectid)) {
2020 $param_list[0] = str_replace('$ID$', $objectid, $param_list[0]);
2021 }*/
2022 $showempty = (($required && $default != '') ? 0 : 1);
2023
2024 $tmparray = explode(':', $param_list[0]);
2025
2026 $element = $extrafieldsobjectkey; // $extrafieldsobjectkey comes from $object->table_element but we need $object->element
2027 if ($element == 'socpeople') {
2028 $element = 'contact';
2029 } elseif ($element == 'projet') {
2030 $element = 'project';
2031 }
2032
2033 //$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
2034 $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.
2035 $objectfield = $element.':options_'.$key; // Example: 'actioncomm:options_fff' To be used in priority to know object linked with all its definition (including filters)
2036
2037 $out = $form->selectForForms($objectdesc, $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, '', 0, 0, '', $objectfield);
2038 } elseif (in_array($type, ['point', 'multipts', 'linestrg', 'polygon'])) {
2039 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
2040 $dolgeophp = new DolGeoPHP($this->db);
2041 $geojson = '{}';
2042 $centroidjson = getDolGlobalString('MAIN_INFO_SOCIETE_GEO_COORDINATES', '{}');
2043 if (!empty($value)) {
2044 $tmparray = $dolgeophp->parseGeoString($value);
2045 $geojson = $tmparray['geojson'];
2046 $centroidjson = $tmparray['centroidjson'];
2047 }
2048 if (!preg_match('/search_/', $keyprefix)) {
2049 require_once DOL_DOCUMENT_ROOT.'/core/class/geomapeditor.class.php';
2050 $geomapeditor = new GeoMapEditor();
2051 $out .= $geomapeditor->getHtml($keyprefix.$key.$keysuffix, $geojson, $centroidjson, $type);
2052 } else {
2053 // If keyprefix is search_ or search_options_, we must just use a simple text field
2054 $out = '';
2055 }
2056 } elseif ($type == 'password') {
2057 // If prefix is 'search_', field is used as a filter, we use a common text field.
2058 $out = '<input style="display:none" type="text" name="fakeusernameremembered">'; // Hidden field to reduce impact of evil Google Chrome autopopulate bug.
2059 $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 : '').'>';
2060 } elseif ($type == 'stars') {
2061 $out = '<input type="hidden" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').'>';
2062 $out .= '<div class="star-selection" id="'.$keyprefix.$key.$keysuffix.'_selection">';
2063 $i = 1;
2064 while ($i <= $size) {
2065 $out .= '<span class="star" data-value="'.$i.'">'.img_picto('', 'fontawesome_star_fas').'</span>';
2066 $i++;
2067 }
2068 $out .= '</div>';
2069 $out .= '<script>
2070 jQuery(function($) { /* extrafields.class.php 1 */
2071 let container = $("#'.$keyprefix.$key.$keysuffix.'_selection");
2072 let selectedStars = parseInt($("#'.$keyprefix.$key.$keysuffix.'").val()) || 0;
2073 container.find(".star").each(function() {
2074 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2075 });
2076 container.find(".star").on("mouseover", function() {
2077 let selectedStar = $(this).data("value");
2078 container.find(".star").each(function() {
2079 $(this).toggleClass("active", $(this).data("value") <= selectedStar);
2080 });
2081 });
2082 container.on("mouseout", function() {
2083 container.find(".star").each(function() {
2084 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2085 });
2086 });
2087 container.find(".star").off("click").on("click", function() {
2088 selectedStars = $(this).data("value");
2089 if (selectedStars === 1 && $("#'.$keyprefix.$key.$keysuffix.'").val() == 1) {
2090 selectedStars = 0;
2091 }
2092 $("#'.$keyprefix.$key.$keysuffix.'").val(selectedStars);
2093 container.find(".star").each(function() {
2094 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2095 });
2096 });
2097 });
2098 </script>';
2099 }
2100 if (!empty($hidden)) {
2101 $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
2102 }
2103
2104 // If alwayseditable is false, and object is not in draft, then showOutputField
2105 // @phan-suppress-next-line PhanUndeclaredConstantOfClass
2106 if ($alwayseditable == 0 && is_object($object) && isset($object->status) && defined(get_class($object)."::STATUS_DRAFT") && $object->status != $object::STATUS_DRAFT) {
2107 $out = $this->showOutputField($key, $value, $moreparam, $extrafieldsobjectkey, null, $object);
2108 }
2109 /* Add comments
2110 if ($type == 'date') $out.=' (YYYY-MM-DD)';
2111 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
2112 */
2113 /*if (!empty($help) && $keyprefix != 'search_options_') {
2114 $out .= $form->textwithpicto('', $help, 1, 'help', '', 0, 3);
2115 }*/
2116 return $out;
2117 }
2118
2119
2132 public function showOutputField($key, $value, $moreparam = '', $extrafieldsobjectkey = '', $outputlangs = null, $object = null, $mode = '')
2133 {
2134 global $conf, $langs, $hookmanager;
2135
2136 if (is_null($outputlangs) || !is_object($outputlangs)) {
2137 $outputlangs = $langs;
2138 }
2139
2140 if (empty($extrafieldsobjectkey)) {
2141 dol_syslog(get_class($this).'::showOutputField extrafieldsobjectkey required', LOG_ERR);
2142 return 'BadValueForParamExtraFieldsObjectKey';
2143 }
2144
2145 $parameters = array(
2146 'key' => $key,
2147 'value' => &$value,
2148 'moreparam' => $moreparam,
2149 'extrafieldsobjectkey' => $extrafieldsobjectkey,
2150 'outputlangs' => $outputlangs,
2151 'object' => $object
2152 );
2153 $action = '';
2154
2155 $reshook = $hookmanager->executeHooks('showOutputExtraField', $parameters, $this, $action); // Note that $action and $object may have been modified by hook
2156 if ($reshook > 0) {
2157 return $hookmanager->resPrint;
2158 }
2159
2160 $label = $this->attributes[$extrafieldsobjectkey]['label'][$key];
2161 $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
2162 $size = $this->attributes[$extrafieldsobjectkey]['size'][$key]; // Can be '255', '24,8'...
2163 $default = $this->attributes[$extrafieldsobjectkey]['default'][$key];
2164 $computed = $this->attributes[$extrafieldsobjectkey]['computed'][$key];
2165 $unique = $this->attributes[$extrafieldsobjectkey]['unique'][$key];
2166 $required = $this->attributes[$extrafieldsobjectkey]['required'][$key];
2167 $param = $this->attributes[$extrafieldsobjectkey]['param'][$key];
2168 $perms = (int) dol_eval((string) $this->attributes[$extrafieldsobjectkey]['perms'][$key], 1, 1, '2');
2169 $langfile = $this->attributes[$extrafieldsobjectkey]['langfile'][$key];
2170 $list = (string) dol_eval((string) $this->attributes[$extrafieldsobjectkey]['list'][$key], 1, 1, '2');
2171 $help = $this->attributes[$extrafieldsobjectkey]['help'][$key];
2172 $cssview = $this->attributes[$extrafieldsobjectkey]['cssview'][$key];
2173 $alwayseditable = $this->attributes[$extrafieldsobjectkey]['alwayseditable'][$key];
2174
2175 // If alwayseditable is false, and object is not in draft, then we show value instead of input field
2176 $showValueInsteadOfInputField = 0; // Variable used to disable update of fields via ajax
2177 // @phan-suppress-next-line PhanUndeclaredConstantOfClass
2178 if ($alwayseditable == 0 && is_object($object) && isset($object->status) && defined(get_class($object)."::STATUS_DRAFT") && $object->status != $object::STATUS_DRAFT) {
2179 $showValueInsteadOfInputField = 1;
2180 }
2181
2182 $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)
2183
2184 if ($hidden) {
2185 return ''; // This is a protection. If field is hidden, we should just not call this method.
2186 }
2187
2188 //print $key.' '.$type.'<br>';
2189
2190 $showsize = 0;
2191 if ($type == 'date') {
2192 $showsize = 10;
2193 if ($value !== '') {
2194 $value = dol_print_date($value, 'day'); // For date without hour, date is always GMT for storage and output
2195 }
2196 } elseif ($type == 'datetime') {
2197 $showsize = 19;
2198 if ($value !== '') {
2199 $value = dol_print_date($value, 'dayhour', 'tzuserrel');
2200 }
2201 } elseif ($type == 'duration') {
2202 $showsize = 10;
2203 if ($value !== '') {
2204 $value = intval($value);
2205 $value = convertSecondToTime($value);
2206 }
2207 } elseif ($type == 'datetimegmt') {
2208 $showsize = 19;
2209 if ($value !== '') {
2210 $value = dol_print_date($value, 'dayhour', 'gmt');
2211 }
2212 } elseif ($type == 'int') {
2213 $showsize = 10;
2214 } elseif ($type == 'double') {
2215 if (!empty($value)) {
2216 //$value=price($value);
2217 //$sizeparts = explode(",", $size);
2218 //$number_decimals = array_key_exists(1, $sizeparts) ? $sizeparts[1] : 0;
2219 $value = price($value, 0, $outputlangs, 0, 0, -2, '');
2220 }
2221 } elseif ($type == 'boolean') {
2222 $checked = '';
2223 if (!empty($value)) {
2224 $checked = ' checked ';
2225 }
2226 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
2227 $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
2228 } else {
2229 $value = yn($value ? 1 : 0);
2230 }
2231 } elseif ($type == 'mail' || $type == 'email') {
2232 $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
2233 } elseif ($type == 'ip') {
2234 $value = dol_print_ip($value, 0);
2235 } elseif ($type == 'icon') {
2236 $value = '<span class="'.$value.'"></span>';
2237 } elseif ($type == 'url') {
2238 $value = dol_print_url($value, '_blank', 32, 1);
2239 } elseif ($type == 'phone') {
2240 $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
2241 } elseif ($type == 'price') {
2242 //$value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
2243 if ($value || $value == '0') {
2244 $value = price($value, 0, $outputlangs, 0, getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'), -1).' '.$outputlangs->getCurrencySymbol($conf->currency);
2245 }
2246 } elseif ($type == 'pricecy') {
2247 $currency = $conf->currency;
2248 if (!empty($value)) {
2249 // $value in memory is a php string like '0.01:EUR'
2250 $pricetmp = explode(':', $value);
2251 $currency = !empty($pricetmp[1]) ? $pricetmp[1] : $conf->currency;
2252 $value = $pricetmp[0];
2253 }
2254 if ($value || $value == '0') {
2255 $value = price($value, 0, $outputlangs, 0, getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'), -1, $currency);
2256 }
2257 } elseif ($type == 'select') {
2258 $valstr = (!empty($param['options'][$value]) ? $param['options'][$value] : '');
2259 if (($pos = strpos($valstr, "|")) !== false) {
2260 $valstr = substr($valstr, 0, $pos);
2261 }
2262 if ($langfile && $valstr) {
2263 $value = $outputlangs->trans($valstr);
2264 } else {
2265 $value = $valstr;
2266 }
2267 } elseif ($type == 'sellist') {
2268 $param_list = array_keys($param['options']);
2269 $InfoFieldList = explode(":", $param_list[0]);
2270
2271 $selectkey = "rowid";
2272 $keyList = 'rowid';
2273
2274 if (count($InfoFieldList) >= 3) {
2275 $selectkey = $InfoFieldList[2];
2276 $keyList = $InfoFieldList[2].' as rowid';
2277 }
2278
2279 $fields_label = explode('|', $InfoFieldList[1]);
2280 if (is_array($fields_label)) {
2281 $keyList .= ', ';
2282 $keyList .= implode(', ', $fields_label);
2283 }
2284
2285 $filter_categorie = false;
2286 if (count($InfoFieldList) > 5) {
2287 if ($InfoFieldList[0] == 'categorie') {
2288 $filter_categorie = true;
2289 }
2290 }
2291
2292 $sql = "SELECT ".$keyList;
2293 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
2294 if (!empty($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra.') !== false) {
2295 $sql .= ' as main';
2296 }
2297 if ($selectkey == 'rowid' && empty($value)) {
2298 $sql .= " WHERE ".$this->db->sanitize($selectkey)." = 0";
2299 } elseif ($selectkey == 'rowid') {
2300 $sql .= " WHERE ".$this->db->sanitize($selectkey)." = ".((int) $value);
2301 } else {
2302 $sql .= " WHERE ".$this->db->sanitize($selectkey)." = '".$this->db->escape($value)."'";
2303 }
2304 //$sql.= ' AND entity = '.$conf->entity;
2305
2306 dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
2307
2308 $resql = $this->db->query($sql);
2309 if ($resql) {
2310 if (!$filter_categorie) {
2311 $value = ''; // value was used, so now we reset it to use it to build final output
2312
2313 $obj = $this->db->fetch_object($resql);
2314
2315 // Several field into label (eq table:code|label:rowid)
2316 $fields_label = explode('|', $InfoFieldList[1]);
2317
2318 if (is_array($fields_label) && count($fields_label) > 1) {
2319 foreach ($fields_label as $field_toshow) {
2320 $translabel = '';
2321 $field_toshow = preg_replace('/^.*\./', '', $field_toshow);
2322 if (!empty($obj->$field_toshow)) {
2323 $translabel = $outputlangs->trans($obj->$field_toshow);
2324
2325 if ($translabel != $obj->$field_toshow) {
2326 $value .= dol_trunc($translabel, 24) . ' ';
2327 } else {
2328 $value .= $obj->$field_toshow . ' ';
2329 }
2330 }
2331 }
2332 } else {
2333 $translabel = '';
2334 $tmppropname = $InfoFieldList[1];
2335 //$obj->$tmppropname = '';
2336 if (!empty(isset($obj->$tmppropname) ? $obj->$tmppropname : '')) {
2337 $translabel = $outputlangs->trans($obj->$tmppropname);
2338 }
2339 if ($translabel != (isset($obj->$tmppropname) ? $obj->$tmppropname : '')) {
2340 $value = dol_trunc($translabel, 18);
2341 } else {
2342 $value = isset($obj->$tmppropname) ? $obj->$tmppropname : '';
2343 }
2344 }
2345 } else {
2346 $toprint = array();
2347 $obj = $this->db->fetch_object($resql);
2348 if ($obj->rowid) {
2349 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
2350 $c = new Categorie($this->db);
2351 $result = $c->fetch($obj->rowid);
2352 if ($result > 0) {
2353 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
2354 foreach ($ways as $way) {
2355 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories'.($mode ? ' '.$mode : '').'"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #bbb"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
2356 }
2357 }
2358 }
2359 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
2360 }
2361 } else {
2362 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
2363 }
2364 } elseif ($type == 'radio') {
2365 if ($required && !isset($param['options'][$value])) {
2366 $outputlangs->load('errors');
2367 $value = '<span class="opacitymedium">'.$outputlangs->trans('ErrorNoValueForRadioType').'</span>';
2368 } else {
2369 if (isset($param['options'][$value])) {
2370 $value = $outputlangs->trans($param['options'][$value]);
2371 } else {
2372 $value = '';
2373 }
2374 }
2375 } elseif ($type == 'checkbox') {
2376 $value_arr = explode(',', $value);
2377 $value = '';
2378 $toprint = array();
2379 if (is_array($value_arr)) {
2380 foreach ($value_arr as $keyval => $valueval) {
2381 if (!empty($valueval)) {
2382 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories'.($mode ? ' '.$mode : '').'" style="background: #bbb">'.$param['options'][$valueval].'</li>';
2383 }
2384 }
2385 }
2386 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
2387 } elseif ($type == 'chkbxlst') { // Example with extrafield value = "societe:nom:rowid" or "categorie:label:rowid:::product"
2388 // Only if something to display (perf)
2389 if (!is_null($value) && $value != '') {
2390 $value_arr = explode(',', $value);
2391
2392 $param_list = array_keys($param['options']);
2393 $InfoFieldList = explode(":", $param_list[0]);
2394
2395 $selectkey = "rowid";
2396 $keyList = 'rowid';
2397
2398 if (count($InfoFieldList) >= 3) {
2399 $selectkey = $InfoFieldList[2];
2400 $keyList = $InfoFieldList[2].' as rowid';
2401 }
2402
2403 $fields_label = explode('|', $InfoFieldList[1]);
2404 if (is_array($fields_label)) {
2405 $keyList .= ', ';
2406 $keyList .= implode(', ', $fields_label);
2407 }
2408
2409 $filter_categorie = false;
2410 if (count($InfoFieldList) > 5 && ((string) $InfoFieldList[5] != '')) {
2411 if ($InfoFieldList[0] == 'categorie') {
2412 $filter_categorie = true;
2413 }
2414 }
2415
2416 $sql = "SELECT ".$keyList;
2417 $sql .= " FROM ".$this->db->prefix().$InfoFieldList[0];
2418 if (strpos($InfoFieldList[4], 'extra.') !== false) {
2419 $sql .= ' as main';
2420 }
2421 $sql .= " WHERE ".$this->db->sanitize($selectkey)." IN (".$this->db->sanitize(implode(',', $value_arr)).")";
2422
2423 dol_syslog(get_class($this).':showOutputField type=chkbxlst', LOG_DEBUG);
2424
2425 $resql = $this->db->query($sql);
2426
2427 if ($resql) {
2428 if (!$filter_categorie) {
2429 $value = ''; // value was used, so now we reset it to use it to build final output
2430 $toprint = array();
2431 $numOfElem = $this->db->num_rows($resql);
2432 while ($obj = $this->db->fetch_object($resql)) {
2433 // Several field into label (eq table:code|label:rowid)
2434 $fields_label = explode('|', $InfoFieldList[1]);
2435 $txtcolor = '#bbb';
2436 //var_dump($key, $type, $InfoFieldList, $fields_label, $object);
2437 if (count($fields_label) > 0) {
2438 if ($InfoFieldList[0] == 'societe' && $InfoFieldList[1] == 'nom' && $InfoFieldList[2] == 'rowid') {
2439 // Special cas for thirdparties
2440 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
2441 $tmpthirdpartystatic = new Societe($this->db);
2442 $tmpthirdpartystatic->id = $obj->rowid;
2443 $tmpthirdpartystatic->name = $obj->nom;
2444 if ($numOfElem >= 3) {
2445 $tmpthirdpartystatic->name = $obj->nom;
2446 $label = $tmpthirdpartystatic->getNomUrl(2);
2447 } elseif ($numOfElem >= 2) {
2448 $tmpthirdpartystatic->name = dol_trunc($obj->nom, 5);
2449 $label = $tmpthirdpartystatic->getNomUrl(1);
2450 } else {
2451 $tmpthirdpartystatic->name = $obj->nom;
2452 $label = $tmpthirdpartystatic->getNomUrl(1);
2453 }
2454 } else {
2455 $label = '<li class="select2-search-choice-dolibarr noborderoncategories'.($mode ? ' '.$mode : '').'" style="background: '.$txtcolor.'">';
2456 foreach ($fields_label as $field_toshow) {
2457 $translabel = '';
2458 if (!empty($obj->$field_toshow)) {
2459 $translabel = $outputlangs->trans($obj->$field_toshow);
2460 }
2461 if ($translabel != $field_toshow) {
2462 $label .= ' '.dol_trunc($translabel, 18);
2463 } else {
2464 $label .= ' '.$obj->$field_toshow;
2465 }
2466 }
2467 $label .= '</li>';
2468 }
2469 $toprint[] = $label;
2470 }
2471 }
2472 } else {
2473 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2474
2475 $toprint = array();
2476 $numElem = $this->db->num_rows($resql);
2477 while ($obj = $this->db->fetch_object($resql)) {
2478 $c = new Categorie($this->db);
2479 $c->fetch($obj->rowid);
2480 if ((string) $mode != 'list') {
2481 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
2482 foreach ($ways as $way) {
2483 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories'.($mode ? ' '.$mode : '').'"'.($c->color ? ' style="background: #'.$c->color.';"' : ' style="background: #bbb"').'>'.img_object('', 'category').' '.$way.'</li>';
2484 }
2485 } else {
2486 // Check contrast with background and correct text color
2487 $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.
2488 if ($c->color) {
2489 if (colorIsLight($c->color)) {
2490 $forced_color = 'categtextblack';
2491 }
2492 }
2493
2494 $s = '<li class="select2-search-choice-dolibarr noborderoncategories '.$forced_color.' list"'.($c->color ? ' style="background: #'.$c->color.';"' : ' style="background: #bbb"').'>';
2495 if ($numElem >= 2) {
2496 $s .= img_object($c->label, 'category', 'class="small"');
2497 } else {
2498 $s .= img_object($c->label, 'category', 'class="small"').' '.$c->label;
2499 }
2500 $s .= '</li>';
2501 $toprint[] = $s;
2502 }
2503 }
2504 }
2505 if (!empty($toprint)) {
2506 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
2507 }
2508 } else {
2509 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
2510 }
2511 }
2512 } elseif ($type == 'link') {
2513 // Only if something to display (perf)
2514 if ($value) { // If we have -1 here, pb is into insert, not into output (fix insert instead of changing code here to compensate)
2515 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
2516
2517 $InfoFieldList = explode(":", $param_list[0]);
2518 $classname = $InfoFieldList[0];
2519 $classpath = $InfoFieldList[1];
2520 if (!empty($classpath)) {
2521 dol_include_once($InfoFieldList[1]);
2522 if ($classname && class_exists($classname)) {
2523 $tmpobject = new $classname($this->db);
2524 '@phan-var-force CommonObject $tmpobject';
2525 $tmpobject->fetch($value);
2526
2527 if (get_class($tmpobject) == 'Categorie') {
2528 // For category object, rendering must use the same method than the one defined into showCategories()
2529 $color = $tmpobject->color;
2530 $sfortag = '<span class="noborderoncategories"' . ($color ? ' style="background: #' . $color . ';"' : ' style="background: #bbb"') . '>';
2531 $sfortag .= $tmpobject->getNomUrl(3);
2532 $sfortag .= '</span>';
2533 $value = $sfortag;
2534 } else {
2535 $value = $tmpobject->getNomUrl(3);
2536 }
2537 }
2538 } else {
2539 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
2540 return 'Error bad setup of extrafield';
2541 }
2542 }
2543 } elseif ($type == 'point') {
2544 if (!empty($value)) {
2545 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
2546 $dolgeophp = new DolGeoPHP($this->db);
2547 $value = $dolgeophp->getXYString($value);
2548 } else {
2549 $value = '';
2550 }
2551 } elseif (in_array($type, ['multipts', 'linestrg', 'polygon'])) {
2552 if (!empty($value)) {
2553 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
2554 $dolgeophp = new DolGeoPHP($this->db);
2555 $value = $dolgeophp->getPointString($value);
2556 } else {
2557 $value = '';
2558 }
2559 } elseif ($type == 'text') {
2560 $value = '<div class="'.($cssview ? $cssview : 'shortmessagecut').'">'.dol_htmlentitiesbr($value).'</div>';
2561 } elseif ($type == 'html') {
2562 $value = dol_htmlentitiesbr($value);
2563 } elseif ($type == 'password') {
2564 $value = dol_trunc(preg_replace('/./i', '*', $value), 8, 'right', 'UTF-8', 1);
2565 } elseif ($type == 'stars') {
2566 $objectid = (int) $object->id;
2567 $nbstars = (int) $value;
2568 if ($showValueInsteadOfInputField == 1) {
2569 $value = '<span style="display:none;" id="'.$key.$object->id.'">'.dol_escape_htmltag($value).'</span>';
2570 } else {
2571 $value = '<input type="hidden" class="flat" name="'.$key.'" id="'.$key.$objectid.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').'>';
2572 }
2573
2574 $value .= '<div class="star-selection" id="'.$key.$objectid.'_selection">';
2575 $i = 1;
2576 while ($i <= $size) {
2577 $value .= '<span class="star'.($i <= $nbstars ? ' active' : '').'" data-value="'.$i.'">'.img_picto('', 'fontawesome_star_fas').'</span>';
2578 $i++;
2579 }
2580 $value .= '</div>';
2581 $value .= '<script>
2582 $(document).ready(function() { /* extrafields.class.php 2 */
2583 let container = $("#'.$key.$objectid.'_selection");
2584 let selectedStars = parseInt($("#'.$key.$objectid.'").val() || $("#'.$key.$objectid.'").text()) || 0;
2585 ';
2586 if ($showValueInsteadOfInputField == 0) {
2587 $value .= '
2588 container.find(".star").on("mouseover", function() {
2589 console.log("Mouse over a star");
2590 let selectedStar = $(this).data("value");
2591 container.find(".star").each(function() {
2592 $(this).toggleClass("active", $(this).data("value") <= selectedStar);
2593 });
2594 });
2595 container.on("mouseout", function() {
2596 console.log("Mouse out of star");
2597 container.find(".star").each(function() {
2598 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2599 });
2600 });
2601 container.find(".star").off("click").on("click", function() {
2602 console.log("We click on star, we call the ajax core/ajax/updateextrafield.php");
2603 selectedStars = $(this).data("value");
2604 if (selectedStars == 1 && $("#'.$key.$objectid.'").val() == 1) {
2605 selectedStars = 0;
2606 }
2607 container.find("#'.$key.$objectid.'").val(selectedStars);
2608 container.find(".star").each(function() {
2609 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2610 });
2611 $.ajax({
2612 url: "'.DOL_URL_ROOT.'/core/ajax/updateextrafield.php",
2613 method: "POST",
2614 data: {
2615 objectType: \''.dol_escape_js($extrafieldsobjectkey).'\',
2616 objectId: '.((int) $objectid).',
2617 field: \''.dol_escape_js($key).'\',
2618 value: selectedStars,
2619 token: \''.newToken().'\'
2620 },
2621 success: function(response) {
2622 var res = JSON.parse(response);
2623 console[res.status === "success" ? "log" : "error"](res.message);
2624 },
2625 error: function(xhr, status, error) {
2626 console.log("Ajax request failed while updating '.$key.':", error);
2627 }
2628 });
2629 });';
2630 }
2631 $value .= '
2632 });
2633 </script>';
2634 } else {
2635 $showsize = round((float) $size);
2636 if ($showsize > 48) {
2637 $showsize = 48;
2638 }
2639 }
2640
2641 //print $type.'-'.$size;
2642 $out = $value;
2643
2644 return $out;
2645 }
2646
2655 public function getAlignFlag($key, $extrafieldsobjectkey = '')
2656 {
2657 return $this->getCSSClass($key, $extrafieldsobjectkey);
2658 }
2659
2668 public function getCSSClass($key, $extrafieldsobjectkey = '', $mode = 'csslist')
2669 {
2670 $type = 'varchar';
2671 if (!empty($extrafieldsobjectkey)) {
2672 $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
2673 }
2674
2675 $cssstring = '';
2676
2677 if ($mode == 'csslist') {
2678 if (in_array($type, array('date', 'datetime', 'datetimegmt',))) {
2679 $cssstring = "center";
2680 } elseif (in_array($type, array('int', 'price', 'double', 'duration'))) {
2681 $cssstring = "right";
2682 } elseif (in_array($type, array('boolean', 'radio', 'checkbox', 'ip', 'icon'))) {
2683 $cssstring = "center";
2684 }
2685
2686 if (!empty($this->attributes[$extrafieldsobjectkey][$mode][$key])) {
2687 $cssstring .= ($cssstring ? ' ' : '').$this->attributes[$extrafieldsobjectkey][$mode][$key];
2688 } else {
2689 if (in_array($type, array('ip'))) {
2690 $cssstring .= ($cssstring ? ' ' : '').'tdoverflowmax150';
2691 }
2692 }
2693 }
2694
2695 if ($mode == 'css' || $mode == 'cssview') {
2696 if (!empty($this->attributes[$extrafieldsobjectkey][$mode][$key])) {
2697 $cssstring = ($cssstring ? ' ' : '').$this->attributes[$extrafieldsobjectkey][$mode][$key];
2698 }
2699 }
2700
2701 return $cssstring;
2702 }
2703
2714 public function showSeparator($key, $object, $colspan = 2, $display_type = 'card', $mode = '')
2715 {
2716 global $conf, $langs;
2717
2718 $tagtype = 'tr';
2719 $tagtype_dyn = 'td';
2720
2721 if ($display_type == 'line') {
2722 $tagtype = 'div';
2723 $tagtype_dyn = 'span';
2724 $colspan = 0;
2725 }
2726
2727 $extrafield_param = $this->attributes[$object->table_element]['param'][$key];
2728 $extrafield_param_list = array();
2729 if (!empty($extrafield_param) && is_array($extrafield_param)) {
2730 $extrafield_param_list = array_keys($extrafield_param['options']);
2731 }
2732
2733 // Set $extrafield_collapse_display_value (do we have to collapse/expand the group after the separator)
2734 $extrafield_collapse_display_value = -1;
2735 $expand_display = false;
2736 if (is_array($extrafield_param_list) && count($extrafield_param_list) > 0) {
2737 $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
2738 $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));
2739 }
2740 $disabledcookiewrite = 0;
2741 if ($mode == 'create') {
2742 // On create mode, force separator group to not be collapsible
2743 $extrafield_collapse_display_value = 1;
2744 $expand_display = true; // We force group to be shown expanded
2745 $disabledcookiewrite = 1; // We keep status of group unchanged into the cookie
2746 }
2747
2748 $out = '<'.$tagtype.' id="trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'" class="trextrafieldseparator trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'">';
2749 $out .= '<'.$tagtype_dyn.' '.(!empty($colspan) ? 'colspan="' . $colspan . '"' : '').'>';
2750 // Some js code will be injected here to manage the collapsing of extrafields
2751 // Output the picto
2752 $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>';
2753 $out .= '&nbsp;';
2754 $out .= '<strong>';
2755 $out .= $langs->trans($this->attributes[$object->table_element]['label'][$key]);
2756 $out .= '</strong>';
2757 $out .= '</'.$tagtype_dyn.'>';
2758 $out .= '</'.$tagtype.'>';
2759
2760 $collapse_group = $key.(!empty($object->id) ? '_'.$object->id : '');
2761 //$extrafields_collapse_num = $this->attributes[$object->table_element]['pos'][$key].(!empty($object->id)?'_'.$object->id:'');
2762
2763 if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
2764 // Set the collapse_display status to cookie in priority or if ignorecollapsesetup is 1, if cookie and ignorecollapsesetup not defined, use the setup.
2765 $this->expand_display[$collapse_group] = $expand_display;
2766
2767 if (!empty($conf->use_javascript_ajax)) {
2768 $out .= '<!-- Add js script to manage the collapse/uncollapse of extrafields separators '.$key.' -->'."\n";
2769 $out .= '<script nonce="'.getNonce().'" type="text/javascript">'."\n";
2770 $out .= 'jQuery(document).ready(function(){'."\n";
2771 if (empty($disabledcookiewrite)) {
2772 if (!$expand_display) {
2773 $out .= ' console.log("Inject js for the collapsing of extrafield '.$key.' - hide");'."\n";
2774 $out .= ' jQuery(".trextrafields_collapse'.$collapse_group.'").hide();'."\n";
2775 } else {
2776 $out .= ' console.log("Inject js for collapsing of extrafield '.$key.' - keep visible and set cookie");'."\n";
2777 $out .= ' document.cookie = "DOLUSER_COLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=1; path='.$_SERVER["PHP_SELF"].'"'."\n";
2778 }
2779 }
2780 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'").click(function(){'."\n";
2781 $out .= ' console.log("We click on collapse/uncollapse to hide/show .trextrafields_collapse'.$collapse_group.'");'."\n";
2782 $out .= ' jQuery(".trextrafields_collapse'.$collapse_group.'").toggle(100, function(){'."\n";
2783 $out .= ' if (jQuery(".trextrafields_collapse'.$collapse_group.'").is(":hidden")) {'."\n";
2784 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').' '.$tagtype_dyn.' span").addClass("fa-plus-square").removeClass("fa-minus-square");'."\n";
2785 $out .= ' document.cookie = "DOLUSER_COLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=0; path='.$_SERVER["PHP_SELF"].'"'."\n";
2786 $out .= ' } else {'."\n";
2787 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').' '.$tagtype_dyn.' span").addClass("fa-minus-square").removeClass("fa-plus-square");'."\n";
2788 $out .= ' document.cookie = "DOLUSER_COLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=1; path='.$_SERVER["PHP_SELF"].'"'."\n";
2789 $out .= ' }'."\n";
2790 $out .= ' });'."\n";
2791 $out .= ' });'."\n";
2792 $out .= '});'."\n";
2793 $out .= '</script>'."\n";
2794 }
2795 } else {
2796 $this->expand_display[$collapse_group] = 1;
2797 }
2798
2799 return $out;
2800 }
2801
2813 public function setOptionalsFromPost($extralabels, $object, $onlykey = '', $todefaultifmissing = 0)
2814 {
2815 global $langs;
2816
2817 $nofillrequired = 0; // For error when required field left blank
2818 $error_field_required = array();
2819
2820 if (isset($this->attributes[$object->table_element]['label']) && is_array($this->attributes[$object->table_element]['label'])) {
2821 $extralabels = $this->attributes[$object->table_element]['label'];
2822 }
2823
2824 if (is_array($extralabels)) {
2825 // Get extra fields
2826 foreach ($extralabels as $key => $value) {
2827 if (!empty($onlykey) && $onlykey != '@GETPOSTISSET' && $key != $onlykey) {
2828 continue;
2829 }
2830
2831 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')))) {
2832 //when unticking boolean field, it's not set in POST
2833 continue;
2834 }
2835
2836 $key_type = $this->attributes[$object->table_element]['type'][$key];
2837 if ($key_type == 'separate') {
2838 continue;
2839 }
2840
2841 $enabled = 1;
2842 if (isset($this->attributes[$object->table_element]['enabled'][$key])) { // 'enabled' is often a condition on module enabled or not
2843 $enabled = (int) dol_eval((string) $this->attributes[$object->table_element]['enabled'][$key], 1, 1, '2');
2844 }
2845
2846 $visibility = 1;
2847 if (isset($this->attributes[$object->table_element]['list'][$key])) { // 'list' is option for visibility
2848 $visibility = (int) dol_eval((string) $this->attributes[$object->table_element]['list'][$key], 1, 1, '2');
2849 }
2850
2851 $perms = 1;
2852 if (isset($this->attributes[$object->table_element]['perms'][$key])) {
2853 $perms = (int) dol_eval((string) $this->attributes[$object->table_element]['perms'][$key], 1, 1, '2');
2854 }
2855 if (empty($enabled)
2856 || (
2857 $onlykey === '@GETPOSTISSET'
2858 && in_array($this->attributes[$object->table_element]['type'][$key], array('boolean', 'checkbox', 'chkbxlst'))
2859 && in_array(abs($enabled), array(2, 5))
2860 && ! GETPOSTISSET('options_' . $key) // Update hidden checkboxes and multiselect only if they are provided
2861 )
2862 ) {
2863 continue;
2864 }
2865
2866 $visibility_abs = abs($visibility);
2867 // not modify if extra field is not in update form (0 : never, 2 or -2 : list only, 5 or - 5 : list and view only)
2868 if (empty($visibility_abs) || $visibility_abs == 2 || $visibility_abs == 5) {
2869 continue;
2870 }
2871 if (empty($perms)) {
2872 continue;
2873 }
2874
2875 if ($this->attributes[$object->table_element]['required'][$key]) { // Value is required
2876 // Check if functionally empty without using GETPOST (depending on the type of extrafield, a
2877 // technically non-empty value may be treated as empty functionally).
2878 // value can be alpha, int, array, etc...
2879 $v = $_POST["options_".$key] ?? null;
2880 $type = $this->attributes[$object->table_element]['type'][$key];
2881 if (self::isEmptyValue($v, $type)) {
2882 //print 'ccc'.$value.'-'.$this->attributes[$object->table_element]['required'][$key];
2883
2884 // 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.
2885
2886 $nofillrequired++;
2887 if (!empty($this->attributes[$object->table_element]['langfile'][$key])) {
2888 $langs->load($this->attributes[$object->table_element]['langfile'][$key]);
2889 }
2890 $error_field_required[$key] = $langs->transnoentitiesnoconv($value);
2891 }
2892 }
2893
2894 if (in_array($key_type, array('date'))) {
2895 // Clean parameters
2896 $value_key = dol_mktime(12, 0, 0, GETPOSTINT("options_".$key."month"), GETPOSTINT("options_".$key."day"), GETPOSTINT("options_".$key."year"));
2897 } elseif (in_array($key_type, array('datetime'))) {
2898 // Clean parameters
2899 $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');
2900 } elseif (in_array($key_type, array('datetimegmt'))) {
2901 // Clean parameters
2902 $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');
2903 } elseif (in_array($key_type, array('duration'))) {
2904 $value_hours = GETPOSTINT("options_" . $key . "hour");
2905 $value_minutes = GETPOSTINT("options_" . $key . "min");
2906 $value_key = $value_hours * 3600 + $value_minutes * 60;
2907 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
2908 $value_arr = GETPOST("options_".$key, 'array'); // check if an array
2909 if (!empty($value_arr)) {
2910 $value_key = implode(',', $value_arr);
2911 } else {
2912 $value_key = '';
2913 }
2914 } elseif (in_array($key_type, array('price', 'double'))) {
2915 $value_arr = GETPOST("options_".$key, 'alpha');
2916 $value_key = price2num($value_arr);
2917 } elseif (in_array($key_type, array('pricecy'))) {
2918 $value_key = price2num(GETPOST("options_".$key, 'alpha')).':'.GETPOST("options_".$key."currency_id", 'alpha');
2919 } elseif (in_array($key_type, array('html'))) {
2920 $value_key = GETPOST("options_".$key, 'restricthtml');
2921 } elseif (in_array($key_type, ['point', 'multipts', 'linestrg', 'polygon'])) {
2922 // construct point
2923 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
2924 $geojson = GETPOST("options_".$key, 'restricthtml');
2925 if ($geojson != '{}') {
2926 $dolgeophp = new DolGeoPHP($this->db);
2927 $value_key = $dolgeophp->getWkt($geojson);
2928 } else {
2929 $value_key = '';
2930 }
2931 } elseif (in_array($key_type, array('text'))) {
2932 $label_security_check = 'alphanohtml';
2933 // by default 'alphanohtml' (better security); hidden conf MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML allows basic html
2934 if (getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_REF_LABELS')) {
2935 $label_security_check = 'nohtml';
2936 } else {
2937 $label_security_check = !getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML') ? 'alphanohtml' : 'restricthtml';
2938 }
2939 $value_key = GETPOST("options_".$key, $label_security_check);
2940 } else {
2941 $value_key = GETPOST("options_".$key);
2942 if (in_array($key_type, array('link')) && $value_key == '-1') {
2943 $value_key = '';
2944 }
2945 }
2946
2947 if (!empty($error_field_required[$key]) && $todefaultifmissing) {
2948 // Value is required but we have a default value and we asked to set empty value to the default value
2949 if (!empty($this->attributes[$object->table_element]['default']) && !is_null($this->attributes[$object->table_element]['default'][$key])) {
2950 $value_key = $this->attributes[$object->table_element]['default'][$key];
2951 unset($error_field_required[$key]);
2952 $nofillrequired--;
2953 }
2954 }
2955
2956 $object->array_options["options_".$key] = $value_key;
2957 }
2958
2959 if ($nofillrequired) {
2960 $langs->load('errors');
2961 $this->error = $langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required);
2962 setEventMessages($this->error, null, 'errors');
2963 return -1;
2964 } else {
2965 return 1;
2966 }
2967 } else {
2968 return 0;
2969 }
2970 }
2971
2980 public function getOptionalsFromPost($extrafieldsobjectkey, $keysuffix = '', $keyprefix = '')
2981 {
2982 global $_POST;
2983
2984 if (is_string($extrafieldsobjectkey) && !empty($this->attributes[$extrafieldsobjectkey]['label']) && is_array($this->attributes[$extrafieldsobjectkey]['label'])) {
2985 $extralabels = $this->attributes[$extrafieldsobjectkey]['label'];
2986 } else {
2987 $extralabels = $extrafieldsobjectkey;
2988 }
2989
2990 if (is_array($extralabels)) {
2991 $array_options = array();
2992
2993 // Get extra fields
2994 foreach ($extralabels as $key => $value) {
2995 $key_type = '';
2996 if (is_string($extrafieldsobjectkey)) {
2997 $key_type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
2998 }
2999
3000 if (in_array($key_type, array('date'))) {
3001 $dateparamname_start = $keyprefix . 'options_' . $key . $keysuffix . '_start';
3002 $dateparamname_end = $keyprefix . 'options_' . $key . $keysuffix . '_end';
3003
3004 if (GETPOST($dateparamname_start . 'year') || GETPOST($dateparamname_end . 'year')) {
3005 $value_key = array();
3006 // values provided as a component year, month, day, etc.
3007 if (GETPOST($dateparamname_start . 'year')) {
3008 $value_key['start'] = dol_mktime(0, 0, 0, GETPOSTINT($dateparamname_start . 'month'), GETPOSTINT($dateparamname_start . 'day'), GETPOSTINT($dateparamname_start . 'year'));
3009 }
3010 if (GETPOST($dateparamname_end . 'year')) {
3011 $value_key['end'] = dol_mktime(23, 59, 59, GETPOSTINT($dateparamname_end . 'month'), GETPOSTINT($dateparamname_end . 'day'), GETPOSTINT($dateparamname_end . 'year'));
3012 }
3013 } elseif (GETPOST($keyprefix."options_".$key.$keysuffix."year")) {
3014 // Clean parameters
3015 $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"));
3016 } else {
3017 continue; // Value was not provided, we should not set it.
3018 }
3019 } elseif (in_array($key_type, array('datetime', 'datetimegmt'))) {
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 // values provided as a date pair (start date + end date), each date being broken down as year, month, day, etc.
3025 $dateparamname_start_hour = GETPOSTINT($dateparamname_start . 'hour') != '-1' ? GETPOSTINT($dateparamname_start . 'hour') : '00';
3026 $dateparamname_start_min = GETPOSTINT($dateparamname_start . 'min') != '-1' ? GETPOSTINT($dateparamname_start . 'min') : '00';
3027 $dateparamname_start_sec = GETPOSTINT($dateparamname_start . 'sec') != '-1' ? GETPOSTINT($dateparamname_start . 'sec') : '00';
3028 $dateparamname_end_hour = GETPOSTINT($dateparamname_end . 'hour') != '-1' ? GETPOSTINT($dateparamname_end . 'hour') : '23';
3029 $dateparamname_end_min = GETPOSTINT($dateparamname_end . 'min') != '-1' ? GETPOSTINT($dateparamname_end . 'min') : '59';
3030 $dateparamname_end_sec = GETPOSTINT($dateparamname_end . 'sec') != '-1' ? GETPOSTINT($dateparamname_end . 'sec') : '59';
3031 if ($key_type == 'datetimegmt') {
3032 $value_key = array(
3033 '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'),
3034 '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')
3035 );
3036 } else {
3037 $value_key = array(
3038 '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'),
3039 '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')
3040 );
3041 }
3042 } elseif (GETPOST($keyprefix."options_".$key.$keysuffix."year")) {
3043 // Clean parameters
3044 if ($key_type == 'datetimegmt') {
3045 $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');
3046 } else {
3047 $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');
3048 }
3049 } else {
3050 continue; // Value was not provided, we should not set it.
3051 }
3052 } elseif ($key_type == 'select') {
3053 // to detect if we are in search context
3054 if (GETPOSTISARRAY($keyprefix."options_".$key.$keysuffix)) {
3055 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix, 'array:aZ09');
3056 // Make sure we get an array even if there's only one selected
3057 $value_arr = (array) $value_arr;
3058 $value_key = implode(',', $value_arr);
3059 } else {
3060 $value_key = GETPOST($keyprefix."options_".$key.$keysuffix);
3061 }
3062 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
3063 // We test on a hidden field named "..._multiselect" that is always set to 1 if param is in form so
3064 // when nothing is provided we can make a difference between noparam in the form and param was set to nothing.
3065 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix.'_multiselect')) {
3066 continue; // Value was not provided, we should not set it.
3067 }
3068 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
3069 // Make sure we get an array even if there's only one checkbox
3070 $value_arr = (array) $value_arr;
3071 $value_key = implode(',', $value_arr);
3072 } elseif (in_array($key_type, array('pricecy'))) {
3073 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
3074 continue; // Value was not provided, we should not set it.
3075 }
3076 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
3077 if ($keyprefix != 'search_') { // If value is for a search, we must keep complex string like '>100 <=150'
3078 $value_key = price2num($value_arr).':'.GETPOST($keyprefix."options_".$key.$keysuffix."currency_id", 'alpha');
3079 } else {
3080 $value_key = $value_arr;
3081 }
3082 } elseif (in_array($key_type, array('price', 'double', 'int'))) {
3083 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
3084 continue; // Value was not provided, we should not set it.
3085 }
3086 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
3087 if ($keyprefix != 'search_') { // If value is for a search, we must keep complex string like '>100 <=150'
3088 $value_key = price2num($value_arr);
3089 } else {
3090 $value_key = $value_arr;
3091 }
3092 } elseif (in_array($key_type, array('boolean'))) {
3093 // We test on a hidden field named "..._boolean" that is always set to 1 if param is in form so
3094 // when nothing is provided we can make a difference between noparam in the form and param was set to nothing.
3095 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix."_boolean")) {
3096 $value_key = '';
3097 } else {
3098 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
3099 $value_key = $value_arr;
3100 }
3101 } elseif (in_array($key_type, array('html'))) {
3102 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
3103 continue; // Value was not provided, we should not set it.
3104 }
3105 $value_key = dol_htmlcleanlastbr(GETPOST($keyprefix."options_".$key.$keysuffix, 'restricthtml'));
3106 } else {
3107 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
3108 continue; // Value was not provided, we should not set it.
3109 }
3110
3111 $value_key = GETPOST($keyprefix."options_".$key.$keysuffix);
3112 if ($value_key === '') {
3113 $value_key = null;
3114 }
3115 }
3116
3117 $array_options[$keyprefix."options_".$key] = $value_key; // No keyprefix here. keyprefix is used only for read.
3118 }
3119
3120 return $array_options;
3121 }
3122
3123 return 0;
3124 }
3125
3131 public static function getListOfTypesLabels()
3132 {
3133 global $langs;
3134
3135 $arraytype2label = array('');
3136
3137 $tmptype2label = ExtraFields::$type2label;
3138 foreach ($tmptype2label as $key => $val) {
3139 $arraytype2label[$key] = $langs->transnoentitiesnoconv($val);
3140 }
3141
3142 if (!getDolGlobalString('MAIN_USE_EXTRAFIELDS_ICON')) {
3143 unset($arraytype2label['icon']);
3144 }
3145 if (!getDolGlobalString('MAIN_USE_GEOPHP')) {
3146 unset($arraytype2label['point']);
3147 unset($arraytype2label['multipts']);
3148 unset($arraytype2label['linestrg']);
3149 unset($arraytype2label['polygon']);
3150 }
3151
3152 return $arraytype2label;
3153 }
3154
3162 public static function isEmptyValue($v, string $type)
3163 {
3164 if ($v === null || $v === '') {
3165 return true;
3166 }
3167 if (is_array($v) || $type == 'select') {
3168 return empty($v);
3169 }
3170 if ($type == 'link') {
3171 return ($v == '-1');
3172 }
3173 if ($type == 'sellist') {
3174 return ($v == '0');
3175 }
3176 return empty($v); // Note empty('0') is also true, tested 7.0 up to 8.3.12
3177 }
3178}
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:247
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...
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.
dol_print_email($email, $contactid=0, $socid=0, $addlink=0, $max=0, $showinvalid=1, $withpicto=0, $morecss='paddingrightonly')
Show EMail link formatted for HTML output.
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:464
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:125