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