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