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