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