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 foreach ($param['options'] as $keyopt => $val) {
1802 $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
1803 $out .= ' value="'.$keyopt.'"';
1804 $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
1805 $out .= ($value == $keyopt ? 'checked' : '');
1806 $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$langs->trans($val).'</label><br>';
1807 }
1808 } elseif ($type == 'chkbxlst') { // List of values selected from a table (n choices)
1809 $out = '';
1810 if (getDolGlobalString('MAIN_EXTRAFIELDS_ENABLE_NEW_SELECT2')) {
1811 if (is_array($value)) {
1812 $value_arr = $value;
1813 } else {
1814 $value_arr = explode(',', $value);
1815 }
1816 $out .= "
1817 <script>
1818 $(document).ready(function () {
1819 $('#".$keyprefix.$key.$keysuffix."').select2({
1820 ajax: {
1821 url: '".DOL_URL_ROOT.'/core/ajax/ajaxextrafield.php'."',
1822 dataType: 'json',
1823 multiple: true,
1824 delay: 250, // wait 250 milliseconds before triggering the request
1825 data: function (params) {
1826 var query = {
1827 search: params.term,
1828 page: params.page || 1,
1829 objecttype: '".$extrafieldsobjectkey."',
1830 objectid: '".$object->id."',
1831 objectkey: '".$key."',
1832 mode: '".$mode."',
1833 value: '".$value."'
1834 }
1835 return query;
1836 }
1837 }
1838 })
1839 });
1840 </script>";
1841 // We need a hidden field because when using the multiselect, if we unselect all, there is no
1842 // variable submitted at all, so no way to make a difference between variable not submitted and variable
1843 // submitted to nothing.
1844 $out .= '<input type="hidden" name="'.$keyprefix.$key.$keysuffix.'_multiselect" value="1">';
1845 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'[]" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').' multiple="multiple">';
1846 foreach ($value_arr as $value) {
1847 $out .= ' <option value="'.$value.'" selected>'.$this->showOutputField($key, $value, $moreparam, $extrafieldsobjectkey).'</option>';
1848 }
1849 $out .= '</select>';
1850 } elseif (!getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
1851 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1852 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
1853 }
1854 if (!getDolGlobalString('MAIN_EXTRAFIELDS_ENABLE_NEW_SELECT2')) {
1855 if (is_array($value)) {
1856 $value_arr = $value;
1857 } else {
1858 $value_arr = explode(',', $value);
1859 }
1860
1861 if (is_array($param['options'])) {
1862 $tmpparamoptions = array_keys($param['options']);
1863 $paramoptions = preg_split('/[\r\n]+/', $tmpparamoptions[0]);
1864
1865 $InfoFieldList = explode(":", $paramoptions[0], 5); // We will extract field at position 6,7... later from the 5th one.
1866 // 0 : tableName
1867 // 1 : label field name
1868 // 2 : rowid field name (if different of rowid)
1869 // Optional parameters...
1870 // 3 : key field parent (for dependent lists). How this is used ?
1871 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value. Or use USF on the second line.
1872
1873 // To add a filter on category (not possible with USF restricted to table)
1874 // 5 : string category type ('product', 'customer', ...). This is added to the filter.
1875 // 6 : list of parent categories ids. This replace the filter.
1876 // 7 : sort field (not used here but used into format for commobject)
1877
1878 // For backward compatibility when tableName = 'category', so when we want a list of categories
1879 // 5 : string category type ('product', 'customer', ...). This replace the filter.
1880 // 6 : list of parent categories ids. This replace the filter.
1881 // 7 : sort field (not used here but used into format for commobject)
1882
1883 // Example with extrafield value = "societe:nom:rowid" or "categorie:label:rowid:::product"
1884
1885 // If there is a filter, we extract it by taking all content inside parenthesis.
1886 if (! empty($InfoFieldList[4])) {
1887 $pos = 0;
1888 $parenthesisopen = 0;
1889 while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
1890 if (substr($InfoFieldList[4], $pos, 1) == '(') {
1891 $parenthesisopen++;
1892 }
1893 if (substr($InfoFieldList[4], $pos, 1) == ')') {
1894 $parenthesisopen--;
1895 }
1896 $pos++;
1897 }
1898 $tmpbefore = substr($InfoFieldList[4], 0, $pos);
1899 $tmpafter = substr($InfoFieldList[4], $pos + 1);
1900 //var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
1901 $InfoFieldList[4] = $tmpbefore;
1902 if ($tmpafter !== '') {
1903 $InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
1904 }
1905
1906 // Fix better compatibility with some old extrafield syntax filter "(field=123)"
1907 $reg = array();
1908 if (preg_match('/^\‍(?([a-z0-9]+)([=<>]+)(\d+)\‍)?$/i', $InfoFieldList[4], $reg)) {
1909 $InfoFieldList[4] = '('.$reg[1].':'.$reg[2].':'.$reg[3].')';
1910 }
1911 }
1912
1913 // $Usf = empty($paramoptions[1]) ? '' :$paramoptions[1];
1914
1915 $parentName = '';
1916 $parentField = '';
1917 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
1918
1919 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
1920 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1921 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
1922 } else {
1923 $keyList = $InfoFieldList[2].' as rowid';
1924 }
1925 }
1926 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
1927 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
1928 if (!empty($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra.') !== false) {
1929 $keyList .= ', main.'.$parentField;
1930 } else {
1931 $keyList .= ', '.$parentField;
1932 }
1933 }
1934
1935
1936 $filter_categorie = false;
1937 if (count($InfoFieldList) > 5 && ($InfoFieldList[5] != '')) {
1938 $InfoFieldList[5] = (string) $InfoFieldList[5];
1939 if ($InfoFieldList[0] == 'categorie') {
1940 $filter_categorie = true; // The combo list is a list of categories
1941 } else {
1942 $filter_categorie = false; // We have a filter on category for another table
1943 }
1944 }
1945
1946
1947 if (!$filter_categorie) {
1948 $fields_label = explode('|', $InfoFieldList[1]);
1949 if (is_array($fields_label)) {
1950 $keyList .= ', ';
1951 $keyList .= implode(', ', $fields_label);
1952 }
1953
1954 $sqlwhere = '';
1955 $sql = "SELECT ".$this->db->sanitize($keyList, 0, 0, 1);
1956 $sql .= ' FROM '.$this->db->prefix().$this->db->sanitize($InfoFieldList[0]);
1957
1958 // Add filter from 4th field
1959 if (!empty($InfoFieldList[4])) {
1960 // can use current entity filter
1961 if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
1962 $InfoFieldList[4] = str_replace('$ENTITY$', (string) $conf->entity, $InfoFieldList[4]);
1963 }
1964 // can use SELECT request
1965 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
1966 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
1967 }
1968
1969 // current object id can be use into filter
1970 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
1971 $InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
1972 } elseif (substr($_SERVER["PHP_SELF"], -8) == 'list.php') {
1973 // In filters of list views, we do not want $ID$ replaced by 0. So we remove the '=' condition.
1974 // Do nothing if condition is using 'IN' keyword
1975 // Replace 'column = $ID$' by "word"
1976 $word = '#\b([a-zA-Z0-9-\.-_]+)\b *= *\$ID\$#';
1977 $InfoFieldList[4] = preg_replace($word, '$1', $InfoFieldList[4]);
1978 // Replace '$ID$ = column' by "word"
1979 $word = '#\$ID\$ *= *\b([a-zA-Z0-9-\.-_]+)\b#';
1980 $InfoFieldList[4] = preg_replace($word, '$1', $InfoFieldList[4]);
1981 } else {
1982 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
1983 }
1984
1985 // We have to join on extrafield table
1986 $errstr = '';
1987 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1988 $sql .= ' as main, '.$this->db->sanitize($this->db->prefix().$InfoFieldList[0]).'_extrafields as extra';
1989 $sqlwhere .= " WHERE extra.fk_object = main.".$this->db->sanitize($InfoFieldList[2]);
1990 $sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1991 } else {
1992 $sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1993 }
1994 } else {
1995 $sqlwhere .= ' WHERE 1=1';
1996 }
1997
1998 if (count($InfoFieldList) > 5 && ($InfoFieldList[5] != '')) {
1999 // We have a filter on category for another table
2000 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2001 $tmpcategory = new Categorie($this->db);
2002 $tablesuffixcategory = empty($tmpcategory->MAP_CAT_TABLE[$InfoFieldList[5]]) ? $InfoFieldList[5] : $tmpcategory->MAP_CAT_TABLE[$InfoFieldList[5]];
2003 $fksuffixcategory = empty($tmpcategory->MAP_CAT_FK[$InfoFieldList[5]]) ? $InfoFieldList[5] : $tmpcategory->MAP_CAT_FK[$InfoFieldList[5]];
2004
2005 $sqlwhere .= ' AND EXISTS (SELECT fk_categorie as categid FROM '.MAIN_DB_PREFIX.'categorie_'.$this->db->sanitize($tablesuffixcategory);
2006 $sqlwhere .= ' WHERE fk_categorie IN ('.$this->db->sanitize($InfoFieldList[6]).')';
2007 $sqlwhere .= ' AND fk_'.$this->db->sanitize($fksuffixcategory).' = rowid)';
2008 //var_dump($sqlwhere);exit;
2009 }
2010
2011 // Add Usf filter on second line
2012 /*
2013 if ($Usf) {
2014 $errorstr = '';
2015 $sqlusf .= forgeSQLFromUniversalSearchCriteria($Usf, $errorstr);
2016 if (!$errorstr) {
2017 $sqlwhere .= $sqlusf;
2018 } else {
2019 $sqlwhere .= " AND invalid_usf_filter_of_extrafield";
2020 }
2021 }
2022 */
2023
2024 // Some tables may have field, some other not. For the moment we disable it.
2025 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
2026 $sqlwhere .= " AND entity = ".((int) $conf->entity);
2027 }
2028 // $sql.=preg_replace('/^ AND /','',$sqlwhere);
2029 // print $sql;
2030
2031 $sql .= $sqlwhere;
2032 $sql .= ' ORDER BY '.implode(', ', $fields_label);
2033
2034 dol_syslog(get_class($this).'::showInputField type=chkbxlst', LOG_DEBUG);
2035
2036 $resql = $this->db->query($sql);
2037 if ($resql) {
2038 $num = $this->db->num_rows($resql);
2039 $i = 0;
2040
2041 $data = array();
2042
2043 while ($i < $num) {
2044 $labeltoshow = '';
2045 $obj = $this->db->fetch_object($resql);
2046
2047 $notrans = false;
2048 // Several field into label (eq table:code|label:rowid)
2049 $fields_label = explode('|', $InfoFieldList[1]);
2050 if (is_array($fields_label)) {
2051 $notrans = true;
2052 foreach ($fields_label as $field_toshow) {
2053 $labeltoshow .= $obj->$field_toshow.' ';
2054 }
2055 } else {
2056 $labeltoshow = $obj->{$InfoFieldList[1]};
2057 }
2058 $labeltoshow = dol_trunc($labeltoshow, 45);
2059
2060 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
2061 $labeltoshow = '';
2062 foreach ($fields_label as $field_toshow) {
2063 $translabel = $langs->trans($obj->$field_toshow);
2064 if ($translabel != $obj->$field_toshow) {
2065 $labeltoshow .= ' '.dol_trunc($translabel, 18).' ';
2066 } else {
2067 $labeltoshow .= ' '.dol_trunc($obj->$field_toshow, 18).' ';
2068 }
2069 }
2070 $data[$obj->rowid] = $labeltoshow;
2071 } else {
2072 if (!$notrans) {
2073 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
2074 if ($translabel != $obj->{$InfoFieldList[1]}) {
2075 $labeltoshow = dol_trunc($translabel, 18);
2076 } else {
2077 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
2078 }
2079 }
2080 if (empty($labeltoshow)) {
2081 $labeltoshow = '(not defined)';
2082 }
2083
2084 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
2085 $data[$obj->rowid] = $labeltoshow;
2086 }
2087
2088 if (!empty($InfoFieldList[3]) && $parentField) {
2089 $parent = $parentName.':'.$obj->{$parentField};
2090 }
2091
2092 $data[$obj->rowid] = $labeltoshow;
2093 }
2094
2095 $i++;
2096 }
2097 $this->db->free($resql);
2098
2099 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, 0, 0, '', 0, '100%');
2100 } else {
2101 print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
2102 }
2103 } else {
2104 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2105 $categcode = $InfoFieldList[5];
2106 if (is_numeric($categcode)) { // deprecated: must use the category code instead of id. For backward compatibility.
2107 $tmpcategory = new Categorie($this->db);
2108 $MAP_ID_TO_CODE = array_flip($tmpcategory->MAP_ID);
2109 $categcode = $MAP_ID_TO_CODE[(int) $categcode];
2110 }
2111
2112 $data = $form->select_all_categories($categcode, '', 'parent', 64, $InfoFieldList[6], 1, 1);
2113 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, 0, 0, '', 0, '100%');
2114 }
2115 }
2116 }
2117 } elseif ($type == 'link') {
2118 $param_list = array_keys($param['options']); // $param_list[0] = 'ObjectName:classPath' but can also be 'ObjectName:classPath:1:(status:=:1)'
2119 /* Removed.
2120 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
2121 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.
2122 if (strpos($param_list[0], '$ID$') !== false && !empty($objectid)) {
2123 $param_list[0] = str_replace('$ID$', $objectid, $param_list[0]);
2124 }*/
2125 $showempty = (($required && $default != '') ? 0 : 1);
2126
2127 $tmparray = explode(':', $param_list[0]);
2128
2129 $element = $extrafieldsobjectkey; // $extrafieldsobjectkey comes from $object->table_element but we need $object->element
2130 if ($element == 'socpeople') {
2131 $element = 'contact';
2132 } elseif ($element == 'projet') {
2133 $element = 'project';
2134 }
2135
2136 //$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
2137 $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.
2138 $objectfield = $element.':options_'.$key; // Example: 'actioncomm:options_fff' To be used in priority to know object linked with all its definition (including filters)
2139
2140 $out = $form->selectForForms($objectdesc, $keyprefix.$key.$keysuffix, (int) $value, $showempty, '', '', $morecss, '', 0, 0, '', $objectfield);
2141 } elseif (in_array($type, ['point', 'multipts', 'linestrg', 'polygon'])) {
2142 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
2143 $dolgeophp = new DolGeoPHP($this->db);
2144 $geojson = '{}';
2145 $centroidjson = getDolGlobalString('MAIN_INFO_SOCIETE_GEO_COORDINATES', '{}');
2146 if (!empty($value)) {
2147 $tmparray = $dolgeophp->parseGeoString($value);
2148 $geojson = $tmparray['geojson'];
2149 $centroidjson = $tmparray['centroidjson'];
2150 }
2151 if (!preg_match('/search_/', $keyprefix)) {
2152 require_once DOL_DOCUMENT_ROOT.'/core/class/geomapeditor.class.php';
2153 $geomapeditor = new GeoMapEditor();
2154 $out .= $geomapeditor->getHtml($keyprefix.$key.$keysuffix, $geojson, $centroidjson, $type);
2155 } else {
2156 // If keyprefix is search_ or search_options_, we must just use a simple text field
2157 $out = '';
2158 }
2159 } elseif ($type == 'password') {
2160 // If prefix is 'search_', field is used as a filter, we use a common text field.
2161 $out = '<input style="display:none" type="text" name="fakeusernameremembered">'; // Hidden field to reduce impact of evil Google Chrome autopopulate bug.
2162 $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 : '').'>';
2163 } elseif ($type == 'stars') {
2164 $out = '<input type="hidden" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').'>';
2165 $out .= '<div class="star-selection" id="'.$keyprefix.$key.$keysuffix.'_selection">';
2166 $i = 1;
2167 while ($i <= $size) {
2168 $out .= '<span class="star" data-value="'.$i.'">'.img_picto('', 'fontawesome_star_fas').'</span>';
2169 $i++;
2170 }
2171 $out .= '</div>';
2172 $out .= '<script>
2173 jQuery(function($) { /* extrafields.class.php 1 */
2174 let container = $("#'.$keyprefix.$key.$keysuffix.'_selection");
2175 let selectedStars = parseInt($("#'.$keyprefix.$key.$keysuffix.'").val()) || 0;
2176 container.find(".star").each(function() {
2177 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2178 });
2179 container.find(".star").on("mouseover", function() {
2180 let selectedStar = $(this).data("value");
2181 container.find(".star").each(function() {
2182 $(this).toggleClass("active", $(this).data("value") <= selectedStar);
2183 });
2184 });
2185 container.on("mouseout", function() {
2186 container.find(".star").each(function() {
2187 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2188 });
2189 });
2190 container.find(".star").off("click").on("click", function() {
2191 selectedStars = $(this).data("value");
2192 if (selectedStars === 1 && $("#'.$keyprefix.$key.$keysuffix.'").val() == 1) {
2193 selectedStars = 0;
2194 }
2195 $("#'.$keyprefix.$key.$keysuffix.'").val(selectedStars);
2196 container.find(".star").each(function() {
2197 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2198 });
2199 });
2200 });
2201 </script>';
2202 }
2203 if (!empty($hidden)) {
2204 $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
2205 }
2206
2207 // If alwayseditable is false, and object is not in draft, then showOutputField
2208 // @phan-suppress-next-line PhanUndeclaredConstantOfClass
2209 if ($alwayseditable == 0 && is_object($object) && isset($object->status) && defined(get_class($object)."::STATUS_DRAFT") && $object->status != $object::STATUS_DRAFT) {
2210 $out = $this->showOutputField($key, $value, $moreparam, $extrafieldsobjectkey, null, $object);
2211 }
2212 /* Add comments
2213 if ($type == 'date') $out.=' (YYYY-MM-DD)';
2214 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
2215 */
2216 /*if (!empty($help) && $keyprefix != 'search_options_') {
2217 $out .= $form->textwithpicto('', $help, 1, 'help', '', 0, 3);
2218 }*/
2219 return $out;
2220 }
2221
2222
2235 public function showOutputField($key, $value, $moreparam = '', $extrafieldsobjectkey = '', $outputlangs = null, $object = null, $mode = '')
2236 {
2237 global $conf, $langs, $hookmanager;
2238
2239 if (is_null($outputlangs) || !is_object($outputlangs)) {
2240 $outputlangs = $langs;
2241 }
2242
2243 if (empty($extrafieldsobjectkey)) {
2244 dol_syslog(get_class($this).'::showOutputField extrafieldsobjectkey required', LOG_ERR);
2245 return 'BadValueForParamExtraFieldsObjectKey';
2246 }
2247
2248 $parameters = array(
2249 'key' => $key,
2250 'value' => &$value,
2251 'moreparam' => $moreparam,
2252 'extrafieldsobjectkey' => $extrafieldsobjectkey,
2253 'outputlangs' => $outputlangs,
2254 'object' => $object
2255 );
2256 $action = '';
2257
2258 $reshook = $hookmanager->executeHooks('showOutputExtraField', $parameters, $this, $action); // Note that $action and $object may have been modified by hook
2259 if ($reshook > 0) {
2260 return $hookmanager->resPrint;
2261 }
2262
2263 $label = $this->attributes[$extrafieldsobjectkey]['label'][$key];
2264 $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
2265 $size = $this->attributes[$extrafieldsobjectkey]['size'][$key]; // Can be '255', '24,8'...
2266 $default = $this->attributes[$extrafieldsobjectkey]['default'][$key];
2267 $computed = $this->attributes[$extrafieldsobjectkey]['computed'][$key];
2268 $unique = $this->attributes[$extrafieldsobjectkey]['unique'][$key];
2269 $required = $this->attributes[$extrafieldsobjectkey]['required'][$key];
2270 $param = $this->attributes[$extrafieldsobjectkey]['param'][$key];
2271 $perms = (int) dol_eval((string) $this->attributes[$extrafieldsobjectkey]['perms'][$key], 1, 1, '2');
2272 $langfile = $this->attributes[$extrafieldsobjectkey]['langfile'][$key];
2273 $list = (string) dol_eval((string) $this->attributes[$extrafieldsobjectkey]['list'][$key], 1, 1, '2');
2274 $help = $this->attributes[$extrafieldsobjectkey]['help'][$key];
2275 $cssview = $this->attributes[$extrafieldsobjectkey]['cssview'][$key];
2276 $alwayseditable = $this->attributes[$extrafieldsobjectkey]['alwayseditable'][$key];
2277
2278 // If alwayseditable is false, and object is not in draft, then we show value instead of input field
2279 $showValueInsteadOfInputField = 0; // Variable used to disable update of fields via ajax
2280 // @phan-suppress-next-line PhanUndeclaredConstantOfClass
2281 if ($alwayseditable == 0 && is_object($object) && isset($object->status) && defined(get_class($object)."::STATUS_DRAFT") && $object->status != $object::STATUS_DRAFT) {
2282 $showValueInsteadOfInputField = 1;
2283 }
2284
2285 $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)
2286
2287 if ($hidden) {
2288 return ''; // This is a protection. If field is hidden, we should just not call this method.
2289 }
2290
2291 //print $key.' '.$type.'<br>';
2292
2293 $showsize = 0;
2294 if ($type == 'date') {
2295 $showsize = 10;
2296 if ($value !== '') {
2297 $value = dol_print_date($value, 'day'); // For date without hour, date is always GMT for storage and output
2298 }
2299 } elseif ($type == 'datetime') {
2300 $showsize = 19;
2301 if ($value !== '') {
2302 $value = dol_print_date($value, 'dayhour', 'tzuserrel');
2303 }
2304 } elseif ($type == 'duration') {
2305 $showsize = 10;
2306 if ($value !== '') {
2307 $value = intval($value);
2308 $value = convertSecondToTime($value);
2309 }
2310 } elseif ($type == 'datetimegmt') {
2311 $showsize = 19;
2312 if ($value !== '') {
2313 $value = dol_print_date($value, 'dayhour', 'gmt');
2314 }
2315 } elseif ($type == 'int') {
2316 $showsize = 10;
2317 } elseif ($type == 'double') {
2318 if (!empty($value)) {
2319 //$value=price($value);
2320 //$sizeparts = explode(",", $size);
2321 //$number_decimals = array_key_exists(1, $sizeparts) ? $sizeparts[1] : 0;
2322 $value = price($value, 0, $outputlangs, 0, 0, -2, '');
2323 }
2324 } elseif ($type == 'boolean') {
2325 $checked = '';
2326 if (!empty($value)) {
2327 $checked = ' checked ';
2328 }
2329 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
2330 $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
2331 } else {
2332 $value = yn($value ? 1 : 0);
2333 }
2334 } elseif ($type == 'mail' || $type == 'email') {
2335 $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
2336 } elseif ($type == 'ip') {
2337 $value = dol_print_ip($value, 0);
2338 } elseif ($type == 'icon') {
2339 $value = '<span class="'.$value.'"></span>';
2340 } elseif ($type == 'url') {
2341 $value = dol_print_url($value, '_blank', 32, 1);
2342 } elseif ($type == 'phone') {
2343 $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
2344 } elseif ($type == 'price') {
2345 //$value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
2346 if ($value || $value == '0') {
2347 $value = price($value, 0, $outputlangs, 0, getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'), -1).' '.$outputlangs->getCurrencySymbol($conf->currency);
2348 }
2349 } elseif ($type == 'pricecy') {
2350 $currency = $conf->currency;
2351 if (!empty($value)) {
2352 // $value in memory is a php string like '0.01:EUR'
2353 $pricetmp = explode(':', $value);
2354 $currency = !empty($pricetmp[1]) ? $pricetmp[1] : $conf->currency;
2355 $value = $pricetmp[0];
2356 }
2357 if ($value || $value == '0') {
2358 $value = price($value, 0, $outputlangs, 0, getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'), -1, $currency);
2359 }
2360 } elseif ($type == 'select') {
2361 $valstr = (!empty($param['options'][$value]) ? $param['options'][$value] : '');
2362 if (($pos = strpos($valstr, "|")) !== false) {
2363 $valstr = substr($valstr, 0, $pos);
2364 }
2365 if ($langfile && $valstr) {
2366 $value = $outputlangs->trans($valstr);
2367 } else {
2368 $value = $valstr;
2369 }
2370 } elseif ($type == 'sellist') {
2371 $param_list = array_keys($param['options']);
2372 $InfoFieldList = explode(":", $param_list[0]);
2373
2374 $selectkey = "rowid";
2375 $keyList = 'rowid';
2376
2377 if (count($InfoFieldList) >= 3) {
2378 $selectkey = $InfoFieldList[2];
2379 $keyList = $InfoFieldList[2].' as rowid';
2380 }
2381
2382 $fields_label = explode('|', $InfoFieldList[1]);
2383 if (is_array($fields_label)) {
2384 $keyList .= ', ';
2385 $keyList .= implode(', ', $fields_label);
2386 }
2387
2388 $filter_categorie = false;
2389 if (count($InfoFieldList) > 5) {
2390 if ($InfoFieldList[0] == 'categorie') {
2391 $filter_categorie = true;
2392 }
2393 }
2394
2395 $sql = "SELECT ".$this->db->sanitize($keyList, 0, 0, 1);
2396 $sql .= ' FROM '.$this->db->prefix().$this->db->sanitize($InfoFieldList[0]);
2397 if (!empty($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra.') !== false) {
2398 $sql .= ' as main';
2399 }
2400 if ($selectkey == 'rowid' && empty($value)) {
2401 $sql .= " WHERE ".$this->db->sanitize($selectkey)." = 0";
2402 } elseif ($selectkey == 'rowid') {
2403 $sql .= " WHERE ".$this->db->sanitize($selectkey)." = ".((int) $value);
2404 } else {
2405 $sql .= " WHERE ".$this->db->sanitize($selectkey)." = '".$this->db->escape($value)."'";
2406 }
2407 //$sql.= ' AND entity = '.$conf->entity;
2408
2409 dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
2410
2411 $resql = $this->db->query($sql);
2412 if ($resql) {
2413 if (!$filter_categorie) {
2414 $value = ''; // value was used, so now we reset it to use it to build final output
2415
2416 $obj = $this->db->fetch_object($resql);
2417
2418 // Several field into label (eq table:code|label:rowid)
2419 $fields_label = explode('|', $InfoFieldList[1]);
2420
2421 if (is_array($fields_label) && count($fields_label) > 1) {
2422 foreach ($fields_label as $field_toshow) {
2423 $translabel = '';
2424 $field_toshow = preg_replace('/^.*\./', '', $field_toshow);
2425 if (!empty($obj->$field_toshow)) {
2426 $translabel = $outputlangs->trans($obj->$field_toshow);
2427
2428 if ($translabel != $obj->$field_toshow) {
2429 $value .= dol_trunc($translabel, 24) . ' ';
2430 } else {
2431 $value .= $obj->$field_toshow . ' ';
2432 }
2433 }
2434 }
2435 } else {
2436 $translabel = '';
2437 $tmppropname = $InfoFieldList[1];
2438 //$obj->$tmppropname = '';
2439 if (!empty(isset($obj->$tmppropname) ? $obj->$tmppropname : '')) {
2440 $translabel = $outputlangs->trans($obj->$tmppropname);
2441 }
2442 if ($translabel != (isset($obj->$tmppropname) ? $obj->$tmppropname : '')) {
2443 $value = dol_trunc($translabel, 18);
2444 } else {
2445 $value = isset($obj->$tmppropname) ? $obj->$tmppropname : '';
2446 }
2447 }
2448 } else {
2449 $toprint = array();
2450 $obj = $this->db->fetch_object($resql);
2451 if ($obj->rowid) {
2452 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
2453 $c = new Categorie($this->db);
2454 $result = $c->fetch($obj->rowid);
2455 if ($result > 0) {
2456 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
2457 foreach ($ways as $way) {
2458 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories'.($mode ? ' '.$mode : '').'"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #bbb"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
2459 }
2460 }
2461 }
2462 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
2463 }
2464 } else {
2465 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
2466 }
2467 } elseif ($type == 'radio') {
2468 if ($required && !isset($param['options'][$value])) {
2469 $outputlangs->load('errors');
2470 $value = '<span class="opacitymedium">'.$outputlangs->trans('ErrorNoValueForRadioType').'</span>';
2471 } else {
2472 if (isset($param['options'][$value])) {
2473 $value = $outputlangs->trans($param['options'][$value]);
2474 } else {
2475 $value = '';
2476 }
2477 }
2478 } elseif ($type == 'checkbox') {
2479 $value_arr = explode(',', $value);
2480 $value = '';
2481 $toprint = array();
2482 if (is_array($value_arr)) {
2483 foreach ($value_arr as $keyval => $valueval) {
2484 if (!empty($valueval)) {
2485 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories'.($mode ? ' '.$mode : '').'" style="background: #bbb">'.$param['options'][$valueval].'</li>';
2486 }
2487 }
2488 }
2489 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
2490 } elseif ($type == 'chkbxlst') { // Example with extrafield value = "societe:nom:rowid" or "categorie:label:rowid:::product"
2491 // Only if something to display (perf)
2492 if (!is_null($value) && $value != '') {
2493 $value_arr = explode(',', $value);
2494
2495 $param_list = array_keys($param['options']);
2496 $InfoFieldList = explode(":", $param_list[0]);
2497
2498 $selectkey = "rowid";
2499 $keyList = 'rowid';
2500
2501 if (count($InfoFieldList) >= 3) {
2502 $selectkey = $InfoFieldList[2];
2503 $keyList = $InfoFieldList[2].' as rowid';
2504 }
2505
2506 $fields_label = explode('|', $InfoFieldList[1]);
2507 if (is_array($fields_label)) {
2508 $keyList .= ', ';
2509 $keyList .= implode(', ', $fields_label);
2510 }
2511
2512 $filter_categorie = false;
2513 if (count($InfoFieldList) > 5 && ((string) $InfoFieldList[5] != '')) {
2514 if ($InfoFieldList[0] == 'categorie') {
2515 $filter_categorie = true;
2516 }
2517 }
2518
2519 $sql = "SELECT ".$this->db->sanitize($keyList, 0, 0, 1);
2520 $sql .= " FROM ".$this->db->prefix().$this->db->sanitize($InfoFieldList[0]);
2521 if (strpos($InfoFieldList[4], 'extra.') !== false) {
2522 $sql .= ' as main';
2523 }
2524 $sql .= " WHERE ".$this->db->sanitize($selectkey)." IN (".$this->db->sanitize(implode(',', $value_arr)).")";
2525
2526 dol_syslog(get_class($this).':showOutputField type=chkbxlst', LOG_DEBUG);
2527
2528 $resql = $this->db->query($sql);
2529
2530 if ($resql) {
2531 if (!$filter_categorie) {
2532 $value = ''; // value was used, so now we reset it to use it to build final output
2533 $toprint = array();
2534 $numOfElem = $this->db->num_rows($resql);
2535 while ($obj = $this->db->fetch_object($resql)) {
2536 // Several field into label (eq table:code|label:rowid)
2537 $fields_label = explode('|', $InfoFieldList[1]);
2538 $txtcolor = '#bbb';
2539 //var_dump($key, $type, $InfoFieldList, $fields_label, $object);
2540 if (count($fields_label) > 0) {
2541 if ($InfoFieldList[0] == 'societe' && $InfoFieldList[1] == 'nom' && $InfoFieldList[2] == 'rowid') {
2542 // Special cas for thirdparties
2543 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
2544 $tmpthirdpartystatic = new Societe($this->db);
2545 $tmpthirdpartystatic->id = $obj->rowid;
2546 $tmpthirdpartystatic->name = $obj->nom;
2547 if ($numOfElem >= 3) {
2548 $tmpthirdpartystatic->name = $obj->nom;
2549 $label = $tmpthirdpartystatic->getNomUrl(2);
2550 } elseif ($numOfElem >= 2) {
2551 $tmpthirdpartystatic->name = dol_trunc($obj->nom, 5);
2552 $label = $tmpthirdpartystatic->getNomUrl(1);
2553 } else {
2554 $tmpthirdpartystatic->name = $obj->nom;
2555 $label = $tmpthirdpartystatic->getNomUrl(1);
2556 }
2557 } else {
2558 $label = '<li class="select2-search-choice-dolibarr noborderoncategories'.($mode ? ' '.$mode : '').'" style="background: '.$txtcolor.'">';
2559 foreach ($fields_label as $field_toshow) {
2560 $translabel = '';
2561 if (!empty($obj->$field_toshow)) {
2562 $translabel = $outputlangs->trans($obj->$field_toshow);
2563 }
2564 if ($translabel != $field_toshow) {
2565 $label .= ' '.dol_trunc($translabel, 18);
2566 } else {
2567 $label .= ' '.$obj->$field_toshow;
2568 }
2569 }
2570 $label .= '</li>';
2571 }
2572 $toprint[] = $label;
2573 }
2574 }
2575 } else {
2576 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2577
2578 $toprint = array();
2579 $numElem = $this->db->num_rows($resql);
2580 while ($obj = $this->db->fetch_object($resql)) {
2581 $c = new Categorie($this->db);
2582 $c->fetch($obj->rowid);
2583 if ((string) $mode != 'list') {
2584 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
2585 foreach ($ways as $way) {
2586 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories'.($mode ? ' '.$mode : '').'"'.($c->color ? ' style="background: #'.$c->color.';"' : ' style="background: #bbb"').'>'.img_object('', 'category').' '.$way.'</li>';
2587 }
2588 } else {
2589 // Check contrast with background and correct text color
2590 $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.
2591 if ($c->color) {
2592 if (colorIsLight($c->color)) {
2593 $forced_color = 'categtextblack';
2594 }
2595 }
2596
2597 // $mode is 'list'
2598 $s = '<li class="select2-search-choice-dolibarr noborderoncategories '.$forced_color.' list"'.($c->color ? ' style="background: #'.$c->color.';"' : ' style="background: #bbb"').'>';
2599 if ($numElem >= 2) {
2600 $s .= img_object($c->label, 'category', 'class="small"');
2601 } else {
2602 $s .= img_object($c->label, 'category', 'class="small"').' '.$c->label;
2603 }
2604 $s .= '</li>';
2605 $toprint[] = $s;
2606 }
2607 }
2608 }
2609 if (!empty($toprint)) {
2610 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
2611 }
2612 } else {
2613 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
2614 }
2615 }
2616 } elseif ($type == 'link') {
2617 // Only if something to display (perf)
2618 if ($value) { // If we have -1 here, pb is into insert, not into output (fix insert instead of changing code here to compensate)
2619 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
2620
2621 $InfoFieldList = explode(":", $param_list[0]);
2622 $classname = $InfoFieldList[0];
2623 $classpath = $InfoFieldList[1];
2624 if (!empty($classpath)) {
2625 dol_include_once($InfoFieldList[1]);
2626 if ($classname && class_exists($classname)) {
2627 $tmpobject = new $classname($this->db);
2628 '@phan-var-force CommonObject $tmpobject';
2629 $tmpobject->fetch($value);
2630
2631 if (get_class($tmpobject) == 'Categorie') {
2632 // For category object, rendering must use the same method than the one defined into showCategories()
2633 $color = $tmpobject->color;
2634 $sfortag = '<span class="noborderoncategories"' . ($color ? ' style="background: #' . $color . ';"' : ' style="background: #bbb"') . '>';
2635 $sfortag .= $tmpobject->getNomUrl(3);
2636 $sfortag .= '</span>';
2637 $value = $sfortag;
2638 } else {
2639 $value = $tmpobject->getNomUrl(3);
2640 }
2641 }
2642 } else {
2643 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
2644 return 'Error bad setup of extrafield';
2645 }
2646 }
2647 } elseif ($type == 'point') {
2648 if (!empty($value)) {
2649 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
2650 $dolgeophp = new DolGeoPHP($this->db);
2651 $value = $dolgeophp->getXYString($value);
2652 } else {
2653 $value = '';
2654 }
2655 } elseif (in_array($type, ['multipts', 'linestrg', 'polygon'])) {
2656 if (!empty($value)) {
2657 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
2658 $dolgeophp = new DolGeoPHP($this->db);
2659 $value = $dolgeophp->getPointString($value);
2660 } else {
2661 $value = '';
2662 }
2663 } elseif ($type == 'text') {
2664 $value = '<div class="'.($cssview ? $cssview : 'shortmessagecut').'">'.dol_htmlentitiesbr($value).'</div>';
2665 } elseif ($type == 'html') {
2666 $value = dol_htmlentitiesbr($value);
2667 } elseif ($type == 'password') {
2668 $value = dol_trunc(preg_replace('/./i', '*', $value), 8, 'right', 'UTF-8', 1);
2669 } elseif ($type == 'stars') {
2670 $objectid = (int) $object->id;
2671 $nbstars = (int) $value;
2672 if ($showValueInsteadOfInputField == 1) {
2673 $value = '<span style="display:none;" id="'.$key.$object->id.'">'.dol_escape_htmltag($value).'</span>';
2674 } else {
2675 $value = '<input type="hidden" class="flat" name="'.$key.'" id="'.$key.$objectid.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').'>';
2676 }
2677
2678 $value .= '<div class="star-selection" id="'.$key.$objectid.'_selection">';
2679 $i = 1;
2680 while ($i <= $size) {
2681 $value .= '<span class="star'.($i <= $nbstars ? ' active' : '').'" data-value="'.$i.'">'.img_picto('', 'fontawesome_star_fas').'</span>';
2682 $i++;
2683 }
2684 $value .= '</div>';
2685 $value .= '<script>
2686 $(document).ready(function() { /* extrafields.class.php 2 */
2687 let container = $("#'.$key.$objectid.'_selection");
2688 let selectedStars = parseInt($("#'.$key.$objectid.'").val() || $("#'.$key.$objectid.'").text()) || 0;
2689 ';
2690 if ($showValueInsteadOfInputField == 0) {
2691 $value .= '
2692 container.find(".star").on("mouseover", function() {
2693 console.log("Mouse over a star");
2694 let selectedStar = $(this).data("value");
2695 container.find(".star").each(function() {
2696 $(this).toggleClass("active", $(this).data("value") <= selectedStar);
2697 });
2698 });
2699 container.on("mouseout", function() {
2700 console.log("Mouse out of star");
2701 container.find(".star").each(function() {
2702 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2703 });
2704 });
2705 container.find(".star").off("click").on("click", function() {
2706 console.log("We click on star, we call the ajax core/ajax/updateextrafield.php");
2707 selectedStars = $(this).data("value");
2708 if (selectedStars == 1 && $("#'.$key.$objectid.'").val() == 1) {
2709 selectedStars = 0;
2710 }
2711 container.find("#'.$key.$objectid.'").val(selectedStars);
2712 container.find(".star").each(function() {
2713 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2714 });
2715 $.ajax({
2716 url: "'.DOL_URL_ROOT.'/core/ajax/updateextrafield.php",
2717 method: "POST",
2718 data: {
2719 objectType: \''.dol_escape_js($extrafieldsobjectkey).'\',
2720 objectId: '.((int) $objectid).',
2721 field: \''.dol_escape_js($key).'\',
2722 value: selectedStars,
2723 token: \''.newToken().'\'
2724 },
2725 success: function(response) {
2726 var res = JSON.parse(response);
2727 console[res.status === "success" ? "log" : "error"](res.message);
2728 },
2729 error: function(xhr, status, error) {
2730 console.log("Ajax request failed while updating '.$key.':", error);
2731 }
2732 });
2733 });';
2734 }
2735 $value .= '
2736 });
2737 </script>';
2738 } else {
2739 $showsize = round((float) $size);
2740 if ($showsize > 48) {
2741 $showsize = 48;
2742 }
2743 }
2744
2745 //print $type.'-'.$size;
2746 $out = $value;
2747
2748 return $out;
2749 }
2750
2759 public function getAlignFlag($key, $extrafieldsobjectkey = '')
2760 {
2761 return $this->getCSSClass($key, $extrafieldsobjectkey);
2762 }
2763
2772 public function getCSSClass($key, $extrafieldsobjectkey = '', $mode = 'csslist')
2773 {
2774 $type = 'varchar';
2775 if (!empty($extrafieldsobjectkey)) {
2776 $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
2777 }
2778
2779 $cssstring = '';
2780
2781 if ($mode == 'csslist') {
2782 if (in_array($type, array('date', 'datetime', 'datetimegmt',))) {
2783 $cssstring = "center";
2784 } elseif (in_array($type, array('int', 'price', 'double', 'duration'))) {
2785 $cssstring = "right";
2786 } elseif (in_array($type, array('boolean', 'radio', 'checkbox', 'ip', 'icon'))) {
2787 $cssstring = "center";
2788 }
2789
2790 if (!empty($this->attributes[$extrafieldsobjectkey][$mode][$key])) {
2791 $cssstring .= ($cssstring ? ' ' : '').$this->attributes[$extrafieldsobjectkey][$mode][$key];
2792 } else {
2793 if (in_array($type, array('ip'))) {
2794 $cssstring .= ($cssstring ? ' ' : '').'tdoverflowmax150';
2795 }
2796 }
2797 }
2798
2799 if ($mode == 'css' || $mode == 'cssview') {
2800 if (!empty($this->attributes[$extrafieldsobjectkey][$mode][$key])) {
2801 $cssstring = ($cssstring ? ' ' : '').$this->attributes[$extrafieldsobjectkey][$mode][$key];
2802 }
2803 }
2804
2805 return $cssstring;
2806 }
2807
2818 public function showSeparator($key, $object, $colspan = 2, $display_type = 'card', $mode = '')
2819 {
2820 global $conf, $langs;
2821
2822 $tagtype = 'tr';
2823 $tagtype_dyn = 'td';
2824
2825 if ($display_type == 'line') {
2826 $tagtype = 'div';
2827 $tagtype_dyn = 'span';
2828 $colspan = 0;
2829 }
2830
2831 $extrafield_param = $this->attributes[$object->table_element]['param'][$key];
2832 $extrafield_param_list = array();
2833 if (!empty($extrafield_param) && is_array($extrafield_param)) {
2834 $extrafield_param_list = array_keys($extrafield_param['options']);
2835 }
2836
2837 // Set $extrafield_collapse_display_value (do we have to collapse/expand the group after the separator)
2838 $extrafield_collapse_display_value = -1;
2839 $expand_display = false;
2840 if (is_array($extrafield_param_list) && count($extrafield_param_list) > 0) {
2841 $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
2842 $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));
2843 }
2844 $disabledcookiewrite = 0;
2845 if ($mode == 'create') {
2846 // On create mode, force separator group to not be collapsible
2847 $extrafield_collapse_display_value = 1;
2848 $expand_display = true; // We force group to be shown expanded
2849 $disabledcookiewrite = 1; // We keep status of group unchanged into the cookie
2850 }
2851
2852 $out = '<'.$tagtype.' id="trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'" class="trextrafieldseparator trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'">';
2853 $out .= '<'.$tagtype_dyn.' '.(!empty($colspan) ? 'colspan="' . $colspan . '"' : '').'>';
2854 if ($mode == 'create' || $mode == 'edit') {
2855 $out .= '<br>';
2856 }
2857 // Some js code will be injected here to manage the collapsing of extrafields
2858 // Output the picto
2859 $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>';
2860 $out .= '&nbsp;';
2861 $out .= '<strong>';
2862 $out .= $langs->trans($this->attributes[$object->table_element]['label'][$key]);
2863 $out .= '</strong>';
2864 $out .= '</'.$tagtype_dyn.'>';
2865 $out .= '</'.$tagtype.'>';
2866
2867 $collapse_group = $key.(!empty($object->id) ? '_'.$object->id : '');
2868 //$extrafields_collapse_num = $this->attributes[$object->table_element]['pos'][$key].(!empty($object->id)?'_'.$object->id:'');
2869
2870 if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
2871 // Set the collapse_display status to cookie in priority or if ignorecollapsesetup is 1, if cookie and ignorecollapsesetup not defined, use the setup.
2872 $this->expand_display[$collapse_group] = $expand_display;
2873
2874 if (!empty($conf->use_javascript_ajax)) {
2875 $out .= '<!-- Add js script to manage the collapse/uncollapse of extrafields separators '.$key.' -->'."\n";
2876 $out .= '<script nonce="'.getNonce().'" type="text/javascript">'."\n";
2877 $out .= 'jQuery(document).ready(function(){'."\n";
2878 if (empty($disabledcookiewrite)) {
2879 if (!$expand_display) {
2880 $out .= ' console.log("Inject js for the collapsing of extrafield '.$key.' - hide");'."\n";
2881 $out .= ' jQuery(".trextrafields_collapse'.$collapse_group.'").hide();'."\n";
2882 } else {
2883 $out .= ' console.log("Inject js for collapsing of extrafield '.$key.' - keep visible and set cookie");'."\n";
2884 $out .= ' document.cookie = "DOLUSER_COLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=1; path='.$_SERVER["PHP_SELF"].'"'."\n";
2885 }
2886 }
2887 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'").click(function(){'."\n";
2888 $out .= ' console.log("We click on collapse/uncollapse to hide/show .trextrafields_collapse'.$collapse_group.'");'."\n";
2889 $out .= ' jQuery(".trextrafields_collapse'.$collapse_group.'").toggle(100, function(){'."\n";
2890 $out .= ' if (jQuery(".trextrafields_collapse'.$collapse_group.'").is(":hidden")) {'."\n";
2891 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').' '.$tagtype_dyn.' span").addClass("fa-plus-square").removeClass("fa-minus-square");'."\n";
2892 $out .= ' document.cookie = "DOLUSER_COLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=0; path='.$_SERVER["PHP_SELF"].'"'."\n";
2893 $out .= ' } else {'."\n";
2894 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').' '.$tagtype_dyn.' span").addClass("fa-minus-square").removeClass("fa-plus-square");'."\n";
2895 $out .= ' document.cookie = "DOLUSER_COLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=1; path='.$_SERVER["PHP_SELF"].'"'."\n";
2896 $out .= ' }'."\n";
2897 $out .= ' });'."\n";
2898 $out .= ' });'."\n";
2899 $out .= '});'."\n";
2900 $out .= '</script>'."\n";
2901 }
2902 } else {
2903 $this->expand_display[$collapse_group] = 1;
2904 }
2905
2906 return $out;
2907 }
2908
2920 public function setOptionalsFromPost($extralabels, $object, $onlykey = '', $todefaultifmissing = 0)
2921 {
2922 global $langs, $hookmanager;
2923
2924 $nofillrequired = 0; // For error when required field left blank
2925 $error_field_required = array();
2926
2927 if (isset($this->attributes[$object->table_element]['label']) && is_array($this->attributes[$object->table_element]['label'])) {
2928 $extralabels = $this->attributes[$object->table_element]['label'];
2929 }
2930
2931 if (is_array($extralabels)) {
2932 // Get extra fields
2933 foreach ($extralabels as $key => $value) {
2934 if (!empty($onlykey) && $onlykey != '@GETPOSTISSET' && $key != $onlykey) {
2935 continue;
2936 }
2937
2938 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')))) {
2939 //when unticking boolean field, it's not set in POST
2940 continue;
2941 }
2942
2943 $key_type = $this->attributes[$object->table_element]['type'][$key];
2944 if ($key_type == 'separate') {
2945 continue;
2946 }
2947
2948 $enabled = 1;
2949 if (isset($this->attributes[$object->table_element]['enabled'][$key])) { // 'enabled' is often a condition on module enabled or not
2950 $enabled = (int) dol_eval((string) $this->attributes[$object->table_element]['enabled'][$key], 1, 1, '2');
2951 }
2952
2953 $visibility = 1;
2954 if (isset($this->attributes[$object->table_element]['list'][$key])) { // 'list' is option for visibility
2955 $visibility = (int) dol_eval((string) $this->attributes[$object->table_element]['list'][$key], 1, 1, '2');
2956 }
2957
2958 $perms = 1;
2959 if (isset($this->attributes[$object->table_element]['perms'][$key])) {
2960 $perms = (int) dol_eval((string) $this->attributes[$object->table_element]['perms'][$key], 1, 1, '2');
2961 }
2962 if (empty($enabled)
2963 || (
2964 $onlykey === '@GETPOSTISSET'
2965 && in_array($this->attributes[$object->table_element]['type'][$key], array('boolean', 'checkbox', 'chkbxlst'))
2966 && in_array(abs($enabled), array(2, 5))
2967 && ! GETPOSTISSET('options_' . $key) // Update hidden checkboxes and multiselect only if they are provided
2968 )
2969 ) {
2970 continue;
2971 }
2972
2973 $visibility_abs = abs($visibility);
2974 // not modify if extra field is not in update form (0 : never, 2 or -2 : list only, 5 or - 5 : list and view only)
2975 if (empty($visibility_abs) || $visibility_abs == 2 || $visibility_abs == 5) {
2976 continue;
2977 }
2978 if (empty($perms)) {
2979 continue;
2980 }
2981
2982 if ($this->attributes[$object->table_element]['required'][$key]) { // Value is required
2983 // Check if functionally empty without using GETPOST (depending on the type of extrafield, a
2984 // technically non-empty value may be treated as empty functionally).
2985 // value can be alpha, int, array, etc...
2986 $v = $_POST["options_".$key] ?? null;
2987 $type = $this->attributes[$object->table_element]['type'][$key];
2988 if (self::isEmptyValue($v, $type)) {
2989 //print 'ccc'.$value.'-'.$this->attributes[$object->table_element]['required'][$key];
2990
2991 // 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.
2992
2993 $nofillrequired++;
2994 if (!empty($this->attributes[$object->table_element]['langfile'][$key])) {
2995 $langs->load($this->attributes[$object->table_element]['langfile'][$key]);
2996 }
2997 $error_field_required[$key] = $langs->transnoentitiesnoconv($value);
2998 }
2999 }
3000
3001 if (in_array($key_type, array('date'))) {
3002 // Clean parameters
3003 $value_key = dol_mktime(12, 0, 0, GETPOSTINT("options_".$key."month"), GETPOSTINT("options_".$key."day"), GETPOSTINT("options_".$key."year"));
3004 } elseif (in_array($key_type, array('datetime'))) {
3005 // Clean parameters
3006 $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');
3007 } elseif (in_array($key_type, array('datetimegmt'))) {
3008 // Clean parameters
3009 $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');
3010 } elseif (in_array($key_type, array('duration'))) {
3011 $value_hours = GETPOSTINT("options_" . $key . "hour");
3012 $value_minutes = GETPOSTINT("options_" . $key . "min");
3013 $value_key = $value_hours * 3600 + $value_minutes * 60;
3014 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
3015 $value_arr = GETPOST("options_".$key, 'array'); // check if an array
3016 if (!empty($value_arr)) {
3017 $value_key = implode(',', $value_arr);
3018 } else {
3019 $value_key = '';
3020 }
3021 } elseif (in_array($key_type, array('price', 'double'))) {
3022 $value_arr = GETPOST("options_".$key, 'alpha');
3023 $value_key = price2num($value_arr);
3024 } elseif (in_array($key_type, array('pricecy'))) {
3025 $value_key = price2num(GETPOST("options_".$key, 'alpha')).':'.GETPOST("options_".$key."currency_id", 'alpha');
3026 } elseif (in_array($key_type, array('html'))) {
3027 $value_key = GETPOST("options_".$key, 'restricthtml');
3028 } elseif (in_array($key_type, ['point', 'multipts', 'linestrg', 'polygon'])) {
3029 // construct point
3030 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
3031 $geojson = GETPOST("options_".$key, 'restricthtml');
3032 if ($geojson != '{}') {
3033 $dolgeophp = new DolGeoPHP($this->db);
3034 $value_key = $dolgeophp->getWkt($geojson);
3035 } else {
3036 $value_key = '';
3037 }
3038 } elseif (in_array($key_type, array('text'))) {
3039 $label_security_check = 'alphanohtml';
3040 // by default 'alphanohtml' (better security); hidden conf MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML allows basic html
3041 if (getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_REF_LABELS')) {
3042 $label_security_check = 'nohtml';
3043 } else {
3044 $label_security_check = !getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML') ? 'alphanohtml' : 'restricthtml';
3045 }
3046 $value_key = GETPOST("options_".$key, $label_security_check);
3047 } else {
3048 $value_key = GETPOST("options_".$key);
3049 if (in_array($key_type, array('link')) && $value_key == '-1') {
3050 $value_key = '';
3051 }
3052 }
3053
3054 if (!empty($error_field_required[$key]) && $todefaultifmissing) {
3055 // Value is required but we have a default value and we asked to set empty value to the default value
3056 if (!empty($this->attributes[$object->table_element]['default']) && !is_null($this->attributes[$object->table_element]['default'][$key])) {
3057 $value_key = $this->attributes[$object->table_element]['default'][$key];
3058 unset($error_field_required[$key]);
3059 $nofillrequired--;
3060 }
3061 }
3062
3063 // Hook to process custom extrafields from POST
3064 if (is_object($hookmanager)) {
3065 $parameters = array(
3066 'key' => $key,
3067 'key_type' => $key_type,
3068 'value_key' => &$value_key,
3069 );
3070 $reshook = $hookmanager->executeHooks('setOptionalsFromPostExtra', $parameters, $object);
3071 }
3072
3073 $object->array_options["options_".$key] = $value_key;
3074 }
3075
3076 if ($nofillrequired) {
3077 $langs->load('errors');
3078 $this->error = $langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required);
3079 setEventMessages($this->error, null, 'errors');
3080 return -1;
3081 } else {
3082 return 1;
3083 }
3084 } else {
3085 return 0;
3086 }
3087 }
3088
3097 public function getOptionalsFromPost($extrafieldsobjectkey, $keysuffix = '', $keyprefix = '')
3098 {
3099 global $_POST;
3100
3101 if (is_string($extrafieldsobjectkey) && !empty($this->attributes[$extrafieldsobjectkey]['label']) && is_array($this->attributes[$extrafieldsobjectkey]['label'])) {
3102 $extralabels = $this->attributes[$extrafieldsobjectkey]['label'];
3103 } else {
3104 $extralabels = $extrafieldsobjectkey;
3105 }
3106
3107 if (is_array($extralabels)) {
3108 $array_options = array();
3109
3110 // Get extra fields
3111 foreach ($extralabels as $key => $value) {
3112 $key_type = '';
3113 if (is_string($extrafieldsobjectkey)) {
3114 $key_type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
3115 }
3116
3117 if (in_array($key_type, array('date'))) {
3118 $dateparamname_start = $keyprefix . 'options_' . $key . $keysuffix . '_start';
3119 $dateparamname_end = $keyprefix . 'options_' . $key . $keysuffix . '_end';
3120
3121 if (GETPOST($dateparamname_start . 'year') || GETPOST($dateparamname_end . 'year')) {
3122 $value_key = array();
3123 // values provided as a component year, month, day, etc.
3124 if (GETPOST($dateparamname_start . 'year')) {
3125 $value_key['start'] = dol_mktime(0, 0, 0, GETPOSTINT($dateparamname_start . 'month'), GETPOSTINT($dateparamname_start . 'day'), GETPOSTINT($dateparamname_start . 'year'));
3126 }
3127 if (GETPOST($dateparamname_end . 'year')) {
3128 $value_key['end'] = dol_mktime(23, 59, 59, GETPOSTINT($dateparamname_end . 'month'), GETPOSTINT($dateparamname_end . 'day'), GETPOSTINT($dateparamname_end . 'year'));
3129 }
3130 } elseif (GETPOST($keyprefix."options_".$key.$keysuffix."year")) {
3131 // Clean parameters
3132 $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"));
3133 } else {
3134 continue; // Value was not provided, we should not set it.
3135 }
3136 } elseif (in_array($key_type, array('datetime', 'datetimegmt'))) {
3137 $dateparamname_start = $keyprefix . 'options_' . $key . $keysuffix . '_start';
3138 $dateparamname_end = $keyprefix . 'options_' . $key . $keysuffix . '_end';
3139
3140 if (GETPOST($dateparamname_start . 'year') || GETPOST($dateparamname_end . 'year')) {
3141 // values provided as a date pair (start date + end date), each date being broken down as year, month, day, etc.
3142 $dateparamname_start_hour = GETPOSTINT($dateparamname_start . 'hour') != '-1' ? GETPOSTINT($dateparamname_start . 'hour') : '00';
3143 $dateparamname_start_min = GETPOSTINT($dateparamname_start . 'min') != '-1' ? GETPOSTINT($dateparamname_start . 'min') : '00';
3144 $dateparamname_start_sec = GETPOSTINT($dateparamname_start . 'sec') != '-1' ? GETPOSTINT($dateparamname_start . 'sec') : '00';
3145 $dateparamname_end_hour = GETPOSTINT($dateparamname_end . 'hour') != '-1' ? GETPOSTINT($dateparamname_end . 'hour') : '23';
3146 $dateparamname_end_min = GETPOSTINT($dateparamname_end . 'min') != '-1' ? GETPOSTINT($dateparamname_end . 'min') : '59';
3147 $dateparamname_end_sec = GETPOSTINT($dateparamname_end . 'sec') != '-1' ? GETPOSTINT($dateparamname_end . 'sec') : '59';
3148 if ($key_type == 'datetimegmt') {
3149 $value_key = array(
3150 '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'),
3151 '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')
3152 );
3153 } else {
3154 $value_key = array(
3155 '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'),
3156 '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')
3157 );
3158 }
3159 } elseif (GETPOST($keyprefix."options_".$key.$keysuffix."year")) {
3160 // Clean parameters
3161 if ($key_type == 'datetimegmt') {
3162 $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');
3163 } else {
3164 $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');
3165 }
3166 } else {
3167 continue; // Value was not provided, we should not set it.
3168 }
3169 } elseif ($key_type == 'select') {
3170 // to detect if we are in search context
3171 if (GETPOSTISARRAY($keyprefix."options_".$key.$keysuffix)) {
3172 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix, 'array:aZ09');
3173 // Make sure we get an array even if there's only one selected
3174 $value_arr = (array) $value_arr;
3175 $value_key = implode(',', $value_arr);
3176 } else {
3177 $value_key = GETPOST($keyprefix."options_".$key.$keysuffix);
3178 }
3179 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
3180 // We test on a hidden field named "..._multiselect" that is always set to 1 if param is in form so
3181 // when nothing is provided we can make a difference between noparam in the form and param was set to nothing.
3182 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix.'_multiselect')) {
3183 continue; // Value was not provided, we should not set it.
3184 }
3185 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
3186 // Make sure we get an array even if there's only one checkbox
3187 $value_arr = (array) $value_arr;
3188 $value_key = implode(',', $value_arr);
3189 } elseif (in_array($key_type, array('pricecy'))) {
3190 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
3191 continue; // Value was not provided, we should not set it.
3192 }
3193 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
3194 if ($keyprefix != 'search_') { // If value is for a search, we must keep complex string like '>100 <=150'
3195 $value_key = price2num($value_arr).':'.GETPOST($keyprefix."options_".$key.$keysuffix."currency_id", 'alpha');
3196 } else {
3197 $value_key = $value_arr;
3198 }
3199 } elseif (in_array($key_type, array('price', 'double', 'int'))) {
3200 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
3201 continue; // Value was not provided, we should not set it.
3202 }
3203 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
3204 if ($keyprefix != 'search_') { // If value is for a search, we must keep complex string like '>100 <=150'
3205 $value_key = price2num($value_arr);
3206 } else {
3207 $value_key = $value_arr;
3208 }
3209 } elseif (in_array($key_type, array('boolean'))) {
3210 // We test on a hidden field named "..._boolean" that is always set to 1 if param is in form so
3211 // when nothing is provided we can make a difference between noparam in the form and param was set to nothing.
3212 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix."_boolean")) {
3213 $value_key = '';
3214 } else {
3215 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
3216 $value_key = $value_arr;
3217 }
3218 } elseif (in_array($key_type, array('html'))) {
3219 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
3220 continue; // Value was not provided, we should not set it.
3221 }
3222 $value_key = dol_htmlcleanlastbr(GETPOST($keyprefix."options_".$key.$keysuffix, 'restricthtml'));
3223 } else {
3224 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
3225 continue; // Value was not provided, we should not set it.
3226 }
3227
3228 $value_key = GETPOST($keyprefix."options_".$key.$keysuffix);
3229 if ($value_key === '') {
3230 $value_key = null;
3231 }
3232 }
3233
3234 $array_options[$keyprefix."options_".$key] = $value_key; // No keyprefix here. keyprefix is used only for read.
3235 }
3236
3237 return $array_options;
3238 }
3239
3240 return 0;
3241 }
3242
3248 public static function getListOfTypesLabels()
3249 {
3250 global $langs;
3251
3252 $arraytype2label = array('');
3253
3254 $tmptype2label = ExtraFields::$type2label;
3255 foreach ($tmptype2label as $key => $val) {
3256 $arraytype2label[$key] = $langs->transnoentitiesnoconv($val);
3257 }
3258
3259 if (!getDolGlobalString('MAIN_USE_EXTRAFIELDS_ICON')) {
3260 unset($arraytype2label['icon']);
3261 }
3262 if (!getDolGlobalString('MAIN_USE_GEOPHP')) {
3263 unset($arraytype2label['point']);
3264 unset($arraytype2label['multipts']);
3265 unset($arraytype2label['linestrg']);
3266 unset($arraytype2label['polygon']);
3267 }
3268
3269 return $arraytype2label;
3270 }
3271
3279 public static function isEmptyValue($v, string $type)
3280 {
3281 if ($v === null || $v === '') {
3282 return true;
3283 }
3284 if (is_array($v) || $type == 'select') {
3285 return empty($v);
3286 }
3287 if ($type == 'link') {
3288 return ($v == '-1');
3289 }
3290 if ($type == 'sellist') {
3291 return ($v == '0');
3292 }
3293 return empty($v); // Note empty('0') is also true, tested 7.0 up to 8.3.12
3294 }
3295}
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...
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.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into JavaScript code.
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
dol_print_email($email, $contactid=0, $socid=0, $addlink=0, $max=0, $showinvalid=1, $withpicto=0, $morecss='paddingrightonly')
Show EMail link formatted for HTML output.
yn($yesno, $format=1, $color=0)
Return yes or no in current language.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
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