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