dolibarr 21.0.4
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-2023 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-2024 Frédéric France <frederic.france@free.fr>
13 * Copyright (C) 2022 Antonin MARCHAL <antonin@letempledujeu.fr>
14 * Copyright (C) 2024 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 'mail' => '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
115 public function __construct($db)
116 {
117 $this->db = $db;
118 }
119
146 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())
147 {
148 if (empty($attrname)) {
149 return -1;
150 }
151 if (empty($label)) {
152 return -1;
153 }
154
155 $result = 0;
156
157 // Clean properties
158 if ($type == 'separator' || $type == 'separate') {
159 $type = 'separate';
160 $unique = 0;
161 $required = 0;
162 } // Force unique and not required if this is a separator field to avoid troubles.
163 if ($elementtype == 'thirdparty') {
164 $elementtype = 'societe';
165 }
166 if ($elementtype == 'contact') {
167 $elementtype = 'socpeople';
168 }
169 // If property has a computed formula, it must not be a required or unique field
170 if (!empty($computed)) {
171 $required = 0;
172 $unique = 0;
173 }
174
175 // Create field into database except for separator type which is not stored in database
176 if ($type != 'separate') {
177 $result = $this->create($attrname, $type, $size, $elementtype, $unique, $required, $default_value, $param, $perms, $list, $computed, $help, $moreparams);
178 }
179 $err1 = $this->errno;
180 if ($result > 0 || $err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' || $type == 'separate') {
181 // Add declaration of field into table
182 $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);
183 $err2 = $this->errno;
184 if ($result2 > 0
185 || ($err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' && $err2 == 'DB_ERROR_RECORD_ALREADY_EXISTS')
186 || ($type == 'separate' && $err2 == 'DB_ERROR_RECORD_ALREADY_EXISTS')) {
187 $this->error = '';
188 $this->errno = '0';
189 return 1;
190 } else {
191 return -2;
192 }
193 } else {
194 return -1;
195 }
196 }
197
224 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())
225 {
226 if (empty($attrname)) {
227 return -1;
228 }
229 if (empty($label)) {
230 return -1;
231 }
232
233 $result = 0;
234
235 if ($type == 'separator' || $type == 'separate') {
236 $type = 'separate';
237 $unique = 0;
238 $required = 0;
239 } // Force unique and not required if this is a separator field to avoid troubles.
240 if ($elementtype == 'thirdparty') {
241 $elementtype = 'societe';
242 }
243 if ($elementtype == 'contact') {
244 $elementtype = 'socpeople';
245 }
246
247 // Create field into database except for separator type which is not stored in database
248 if ($type != 'separate') {
249 dol_syslog(get_class($this).'::thisupdate', LOG_DEBUG);
250 $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);
251 }
252 $err1 = $this->errno;
253 if ($result > 0 || $err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' || $type == 'separate') {
254 // Add declaration of field into table
255 dol_syslog(get_class($this).'::thislabel', LOG_DEBUG);
256 $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);
257 $err2 = $this->errno;
258 if ($result2 > 0 || ($err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' && $err2 == 'DB_ERROR_RECORD_ALREADY_EXISTS')) {
259 $this->error = '';
260 $this->errno = '0';
261 return 1;
262 } else {
263 return -2;
264 }
265 } else {
266 return -1;
267 }
268 }
269
289 private function create($attrname, $type = 'varchar', $length = '255', $elementtype = '', $unique = 0, $required = 0, $default_value = '', $param = array(), $perms = '', $list = '0', $computed = '', $help = '', $moreparams = array())
290 {
291 if ($elementtype == 'thirdparty') {
292 $elementtype = 'societe';
293 }
294 if ($elementtype == 'contact') {
295 $elementtype = 'socpeople';
296 }
297
298 $table = $elementtype.'_extrafields';
299 if ($elementtype == 'categorie') {
300 $table = 'categories_extrafields';
301 }
302
303 if (!empty($attrname) && preg_match("/^\w[a-zA-Z0-9_]*$/", $attrname) && !is_numeric($attrname)) {
304 if ($type == 'boolean') {
305 $typedb = 'int';
306 $lengthdb = '1';
307 } elseif ($type == 'price') {
308 $typedb = 'double';
309 $lengthdb = '24,8';
310 } elseif ($type == 'pricecy') {
311 $typedb = 'varchar';
312 $lengthdb = '64';
313 } elseif ($type == 'phone') {
314 $typedb = 'varchar';
315 $lengthdb = '20';
316 } elseif ($type == 'mail' || $type == 'ip' || $type == 'icon') {
317 $typedb = 'varchar';
318 $lengthdb = '128';
319 } elseif ($type == 'url') {
320 $typedb = 'varchar';
321 $lengthdb = '255';
322 } elseif (($type == 'select') || ($type == 'sellist') || ($type == 'radio') || ($type == 'checkbox') || ($type == 'chkbxlst')) {
323 $typedb = 'varchar';
324 $lengthdb = '255';
325 } elseif ($type == 'link') {
326 $typedb = 'int';
327 $lengthdb = '11';
328 } elseif ($type == 'duration') {
329 $typedb = 'int';
330 $lengthdb = '11';
331 } elseif ($type == 'point') {
332 $typedb = 'point';
333 $lengthdb = '';
334 } elseif ($type == 'multipts') {
335 $typedb = 'multipoint';
336 $lengthdb = '';
337 } elseif ($type == 'linestrg') {
338 $typedb = 'linestring';
339 $lengthdb = '';
340 } elseif ($type == 'polygon') {
341 $typedb = 'polygon';
342 $lengthdb = '';
343 } elseif ($type == 'html') {
344 $typedb = 'text';
345 $lengthdb = $length;
346 } elseif ($type == 'password') {
347 $typedb = 'varchar';
348 $lengthdb = '128';
349 } elseif ($type == 'stars') {
350 $typedb = 'int';
351 $lengthdb = $length;
352 } else {
353 $typedb = $type;
354 $lengthdb = $length;
355 if ($type == 'varchar' && empty($lengthdb)) {
356 $lengthdb = '255';
357 }
358 }
359 $field_desc = array(
360 'type' => $typedb,
361 'value' => $lengthdb,
362 'null' => ($required ? 'NOT NULL' : 'NULL'),
363 'default' => $default_value
364 );
365
366 $result = $this->db->DDLAddField($this->db->prefix().$this->db->sanitize($table), $attrname, $field_desc);
367 if ($result > 0) {
368 if ($unique) {
369 $sql = "ALTER TABLE ".$this->db->prefix().$this->db->sanitize($table)." ADD UNIQUE INDEX uk_".$this->db->sanitize($table)."_".$attrname." (".$attrname.")";
370 $resql = $this->db->query($sql, 1, 'dml');
371 }
372 return 1;
373 } else {
374 $this->error = $this->db->lasterror();
375 $this->errno = $this->db->lasterrno();
376 return -1;
377 }
378 } else {
379 return 0;
380 }
381 }
382
383 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
411 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())
412 {
413 // phpcs:enable
414 global $conf, $user;
415
416 if ($elementtype == 'thirdparty') {
417 $elementtype = 'societe';
418 }
419 if ($elementtype == 'contact') {
420 $elementtype = 'socpeople';
421 }
422
423 // Clean parameters
424 if (empty($pos)) {
425 $pos = 0;
426 }
427 if (empty($list)) {
428 $list = '0';
429 }
430 if (empty($required)) {
431 $required = 0;
432 }
433 if (empty($unique)) {
434 $unique = 0;
435 }
436 if (empty($printable)) {
437 $printable = 0;
438 }
439 if (empty($alwayseditable)) {
440 $alwayseditable = 0;
441 }
442 if (empty($totalizable)) {
443 $totalizable = 0;
444 }
445
446 $css = '';
447 if (!empty($moreparams) && !empty($moreparams['css'])) {
448 $css = $moreparams['css'];
449 }
450 $csslist = '';
451 if (!empty($moreparams) && !empty($moreparams['csslist'])) {
452 $csslist = $moreparams['csslist'];
453 }
454 $cssview = '';
455 if (!empty($moreparams) && !empty($moreparams['cssview'])) {
456 $cssview = $moreparams['cssview'];
457 }
458
459 if (!empty($attrname) && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname) && !is_numeric($attrname)) {
460 if (is_array($param) && count($param) > 0) {
461 $params = serialize($param);
462 } elseif (strlen($param) > 0) {
463 $params = trim($param);
464 } else {
465 $params = '';
466 }
467
468 $sql = "INSERT INTO ".$this->db->prefix()."extrafields(";
469 $sql .= " name,";
470 $sql .= " label,";
471 $sql .= " type,";
472 $sql .= " pos,";
473 $sql .= " size,";
474 $sql .= " entity,";
475 $sql .= " elementtype,";
476 $sql .= " fieldunique,";
477 $sql .= " fieldrequired,";
478 $sql .= " param,";
479 $sql .= " alwayseditable,";
480 $sql .= " perms,";
481 $sql .= " langs,";
482 $sql .= " list,";
483 $sql .= " printable,";
484 $sql .= " fielddefault,";
485 $sql .= " fieldcomputed,";
486 $sql .= " fk_user_author,";
487 $sql .= " fk_user_modif,";
488 $sql .= " datec,";
489 $sql .= " enabled,";
490 $sql .= " help,";
491 $sql .= " totalizable,";
492 $sql .= " css,";
493 $sql .= " csslist,";
494 $sql .= " cssview";
495 $sql .= " )";
496 $sql .= " VALUES('".$this->db->escape($attrname)."',";
497 $sql .= " '".$this->db->escape($label)."',";
498 $sql .= " '".$this->db->escape($type)."',";
499 $sql .= " ".((int) $pos).",";
500 $sql .= " '".$this->db->escape($size)."',";
501 $sql .= " ".($entity === '' ? $conf->entity : $entity).",";
502 $sql .= " '".$this->db->escape($elementtype)."',";
503 $sql .= " ".((int) $unique).",";
504 $sql .= " ".((int) $required).",";
505 $sql .= " '".$this->db->escape($params)."',";
506 $sql .= " ".((int) $alwayseditable).",";
507 $sql .= " ".($perms ? "'".$this->db->escape($perms)."'" : "null").",";
508 $sql .= " ".($langfile ? "'".$this->db->escape($langfile)."'" : "null").",";
509 $sql .= " '".$this->db->escape($list)."',";
510 $sql .= " '".$this->db->escape($printable)."',";
511 $sql .= " ".($default ? "'".$this->db->escape($default)."'" : "null").",";
512 $sql .= " ".($computed ? "'".$this->db->escape($computed)."'" : "null").",";
513 $sql .= " ".(is_object($user) ? $user->id : 0).",";
514 $sql .= " ".(is_object($user) ? $user->id : 0).",";
515 $sql .= "'".$this->db->idate(dol_now())."',";
516 $sql .= " ".($enabled ? "'".$this->db->escape($enabled)."'" : "1").",";
517 $sql .= " ".($help ? "'".$this->db->escape($help)."'" : "null").",";
518 $sql .= " ".($totalizable ? 'TRUE' : 'FALSE').",";
519 $sql .= " ".($css ? "'".$this->db->escape($css)."'" : "null").",";
520 $sql .= " ".($csslist ? "'".$this->db->escape($csslist)."'" : "null").",";
521 $sql .= " ".($cssview ? "'".$this->db->escape($cssview)."'" : "null");
522 $sql .= ')';
523
524 if ($this->db->query($sql)) {
525 dol_syslog(get_class($this)."::create_label_success", LOG_DEBUG);
526 return 1;
527 } else {
528 dol_syslog(get_class($this)."::create_label_error", LOG_DEBUG);
529 $this->error = $this->db->lasterror();
530 $this->errno = $this->db->lasterrno();
531 return -1;
532 }
533 }
534 return -1;
535 }
536
544 public function delete($attrname, $elementtype = '')
545 {
546 if ($elementtype == 'thirdparty') {
547 $elementtype = 'societe';
548 }
549 if ($elementtype == 'contact') {
550 $elementtype = 'socpeople';
551 }
552
553 $table = $elementtype.'_extrafields';
554 if ($elementtype == 'categorie') {
555 $table = 'categories_extrafields';
556 }
557
558 $error = 0;
559
560 if (!empty($attrname) && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
561 $result = $this->delete_label($attrname, $elementtype);
562 if ($result < 0) {
563 $this->error = $this->db->lasterror();
564 $this->errors[] = $this->db->lasterror();
565 $error++;
566 }
567
568 if (!$error) {
569 $sql = "SELECT COUNT(rowid) as nb";
570 $sql .= " FROM ".$this->db->prefix()."extrafields";
571 $sql .= " WHERE elementtype = '".$this->db->escape($elementtype)."'";
572 $sql .= " AND name = '".$this->db->escape($attrname)."'";
573 //$sql.= " AND entity IN (0,".$conf->entity.")"; Do not test on entity here. We want to see if there is still on field remaining in other entities before deleting field in table
574 $resql = $this->db->query($sql);
575 if ($resql) {
576 $obj = $this->db->fetch_object($resql);
577 if ($obj->nb <= 0) {
578 $result = $this->db->DDLDropField($this->db->prefix().$table, $attrname); // This also drop the unique key
579 if ($result < 0) {
580 $this->error = $this->db->lasterror();
581 $this->errors[] = $this->db->lasterror();
582 $error++;
583 }
584 }
585 } else {
586 $this->error = $this->db->lasterror();
587 $this->errors[] = $this->db->lasterror();
588 $error++;
589 }
590 }
591 if (empty($error)) {
592 return $result;
593 } else {
594 return $error * -1;
595 }
596 } else {
597 return 0;
598 }
599 }
600
601 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
609 private function delete_label($attrname, $elementtype = '')
610 {
611 // phpcs:enable
612 global $conf;
613
614 if ($elementtype == 'thirdparty') {
615 $elementtype = 'societe';
616 }
617 if ($elementtype == 'contact') {
618 $elementtype = 'socpeople';
619 }
620
621 if (isset($attrname) && $attrname != '' && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
622 $sql = "DELETE FROM ".$this->db->prefix()."extrafields";
623 $sql .= " WHERE name = '".$this->db->escape($attrname)."'";
624 $sql .= " AND entity IN (0,".$conf->entity.')';
625 if (!empty($elementtype)) {
626 $sql .= " AND elementtype = '".$this->db->escape($elementtype)."'";
627 }
628
629 dol_syslog(get_class($this)."::delete_label", LOG_DEBUG);
630 $resql = $this->db->query($sql);
631 if ($resql) {
632 return 1;
633 } else {
634 dol_print_error($this->db);
635 return -1;
636 }
637 } else {
638 return 0;
639 }
640 }
641
669 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())
670 {
671 global $action, $hookmanager;
672
673 $result = 0;
674
675 if ($elementtype == 'thirdparty') {
676 $elementtype = 'societe';
677 }
678 if ($elementtype == 'contact') {
679 $elementtype = 'socpeople';
680 }
681
682 $table = $elementtype.'_extrafields';
683 if ($elementtype == 'categorie') {
684 $table = 'categories_extrafields';
685 }
686
687 if (isset($attrname) && $attrname != '' && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
688 // Clean parameters
689 if ($type == 'boolean') {
690 $typedb = 'int';
691 $lengthdb = '1';
692 } elseif ($type == 'price') {
693 $typedb = 'double';
694 $lengthdb = '24,8';
695 } elseif ($type == 'pricecy') {
696 $typedb = 'varchar';
697 $lengthdb = '64';
698 } elseif ($type == 'phone') {
699 $typedb = 'varchar';
700 $lengthdb = '20';
701 } elseif ($type == 'mail' || $type == 'ip' || $type == 'icon') {
702 $typedb = 'varchar';
703 $lengthdb = '128';
704 } elseif ($type == 'url') {
705 $typedb = 'varchar';
706 $lengthdb = '255';
707 } elseif (($type == 'select') || ($type == 'sellist') || ($type == 'radio') || ($type == 'checkbox') || ($type == 'chkbxlst')) {
708 $typedb = 'varchar';
709 $lengthdb = '255';
710 } elseif ($type == 'html') {
711 $typedb = 'text';
712 $lengthdb = $length;
713 } elseif ($type == 'link') {
714 $typedb = 'int';
715 $lengthdb = '11';
716 } elseif ($type == 'duration') {
717 $typedb = 'int';
718 $lengthdb = '11';
719 } elseif ($type == 'point') {
720 $typedb = 'point';
721 $lengthdb = '';
722 } elseif ($type == 'multipts') {
723 $typedb = 'multipoint';
724 $lengthdb = '';
725 } elseif ($type == 'linestrg') {
726 $typedb = 'linestring';
727 $lengthdb = '';
728 } elseif ($type == 'polygon') {
729 $typedb = 'polygon';
730 $lengthdb = '';
731 } elseif ($type == 'password') {
732 $typedb = 'varchar';
733 $lengthdb = '128';
734 } elseif ($type == 'stars') {
735 $typedb = 'int';
736 $lengthdb = $length;
737 } else {
738 $typedb = $type;
739 $lengthdb = $length;
740 }
741 $field_desc = array('type' => $typedb, 'value' => $lengthdb, 'null' => ($required ? 'NOT NULL' : 'NULL'), 'default' => $default);
742
743 // If property has a computed formula, it must not be a required or unique field
744 if (!empty($computed)) {
745 $required = 0;
746 $unique = 0;
747 }
748
749 if (is_object($hookmanager)) {
750 $hookmanager->initHooks(array('extrafieldsdao'));
751 $parameters = array('field_desc' => &$field_desc, 'table' => $table, 'attr_name' => $attrname, 'label' => $label, 'type' => $type, 'length' => $length, 'unique' => $unique, 'required' => $required, 'pos' => $pos, 'param' => $param, 'alwayseditable' => $alwayseditable, 'perms' => $perms, 'list' => $list, 'help' => $help, 'default' => $default, 'computed' => $computed, 'entity' => $entity, 'langfile' => $langfile, 'enabled' => $enabled, 'totalizable' => $totalizable, 'printable' => $printable);
752 $reshook = $hookmanager->executeHooks('updateExtrafields', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
753
754 if ($reshook < 0) {
755 $this->error = $this->db->lasterror();
756 return -1;
757 }
758 }
759
760 dol_syslog(get_class($this).'::DDLUpdateField', LOG_DEBUG);
761 if ($type != 'separate') { // No table update when separate type
762 $result = $this->db->DDLUpdateField($this->db->prefix().$table, $attrname, $field_desc);
763 }
764 if ($result > 0 || $type == 'separate') {
765 if ($label) {
766 dol_syslog(get_class($this).'::update_label', LOG_DEBUG);
767 $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);
768 }
769 if ($result > 0) {
770 $sql = '';
771 if ($unique) {
772 dol_syslog(get_class($this).'::update_unique', LOG_DEBUG);
773 $sql = "ALTER TABLE ".$this->db->prefix().$table." ADD UNIQUE INDEX uk_".$table."_".$this->db->sanitize($attrname)." (".$this->db->sanitize($attrname).")";
774 } else {
775 dol_syslog(get_class($this).'::update_common', LOG_DEBUG);
776 $sql = "ALTER TABLE ".$this->db->prefix().$table." DROP INDEX uk_".$table."_".$this->db->sanitize($attrname);
777 }
778 dol_syslog(get_class($this).'::update', LOG_DEBUG);
779 $resql = $this->db->query($sql, 1, 'dml');
780 /*if ($resql < 0) {
781 $this->error = $this->db->lasterror();
782 return -1;
783 }*/
784 return 1;
785 } else {
786 $this->error = $this->db->lasterror();
787 return -1;
788 }
789 } else {
790 $this->error = $this->db->lasterror();
791 return -1;
792 }
793 } else {
794 return 0;
795 }
796 }
797
798 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
827 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())
828 {
829 // phpcs:enable
830 global $conf, $user;
831 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);
832
833 // Clean parameters
834 if ($elementtype == 'thirdparty') {
835 $elementtype = 'societe';
836 }
837 if ($elementtype == 'contact') {
838 $elementtype = 'socpeople';
839 }
840
841 if (empty($pos)) {
842 $pos = 0;
843 }
844 if (empty($list)) {
845 $list = '0';
846 }
847 if (empty($totalizable)) {
848 $totalizable = 0;
849 }
850 if (empty($required)) {
851 $required = 0;
852 }
853 if (empty($unique)) {
854 $unique = 0;
855 }
856 if (empty($alwayseditable)) {
857 $alwayseditable = 0;
858 }
859
860 $css = '';
861 if (!empty($moreparams) && !empty($moreparams['css'])) {
862 $css = $moreparams['css'];
863 }
864 $csslist = '';
865 if (!empty($moreparams) && !empty($moreparams['csslist'])) {
866 $csslist = $moreparams['csslist'];
867 }
868 $cssview = '';
869 if (!empty($moreparams) && !empty($moreparams['cssview'])) {
870 $cssview = $moreparams['cssview'];
871 }
872
873 if (isset($attrname) && $attrname != '' && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
874 $this->db->begin();
875
876 if (is_array($param) && count($param) > 0) {
877 $params = serialize($param);
878 } elseif (is_array($param)) {
879 $params = '';
880 } elseif (strlen($param) > 0) {
881 $params = trim($param);
882 } else {
883 $params = '';
884 }
885
886 if ($entity === '' || $entity != '0') {
887 // We don't want on all entities, we delete all and current
888 $sql_del = "DELETE FROM ".$this->db->prefix()."extrafields";
889 $sql_del .= " WHERE name = '".$this->db->escape($attrname)."'";
890 $sql_del .= " AND entity IN (0, ".($entity === '' ? $conf->entity : $entity).")";
891 $sql_del .= " AND elementtype = '".$this->db->escape($elementtype)."'";
892 } else {
893 // We want on all entities ($entities = '0'), we delete on all only (we keep setup specific to each entity)
894 $sql_del = "DELETE FROM ".$this->db->prefix()."extrafields";
895 $sql_del .= " WHERE name = '".$this->db->escape($attrname)."'";
896 $sql_del .= " AND entity = 0";
897 $sql_del .= " AND elementtype = '".$this->db->escape($elementtype)."'";
898 }
899 $resql1 = $this->db->query($sql_del);
900
901 $sql = "INSERT INTO ".$this->db->prefix()."extrafields(";
902 $sql .= " name,"; // This is code
903 $sql .= " entity,";
904 $sql .= " label,";
905 $sql .= " type,";
906 $sql .= " size,";
907 $sql .= " elementtype,";
908 $sql .= " fieldunique,";
909 $sql .= " fieldrequired,";
910 $sql .= " perms,";
911 $sql .= " langs,";
912 $sql .= " pos,";
913 $sql .= " alwayseditable,";
914 $sql .= " param,";
915 $sql .= " list,";
916 $sql .= " printable,";
917 $sql .= " totalizable,";
918 $sql .= " fielddefault,";
919 $sql .= " fieldcomputed,";
920 $sql .= " fk_user_author,";
921 $sql .= " fk_user_modif,";
922 $sql .= " datec,";
923 $sql .= " enabled,";
924 $sql .= " help,";
925 $sql .= " css,";
926 $sql .= " csslist,";
927 $sql .= " cssview";
928 $sql .= ") VALUES (";
929 $sql .= "'".$this->db->escape($attrname)."',";
930 $sql .= " ".($entity === '' ? $conf->entity : $entity).",";
931 $sql .= " '".$this->db->escape($label)."',";
932 $sql .= " '".$this->db->escape($type)."',";
933 $sql .= " '".$this->db->escape($size)."',";
934 $sql .= " '".$this->db->escape($elementtype)."',";
935 $sql .= " ".((int) $unique).",";
936 $sql .= " ".((int) $required).",";
937 $sql .= " ".($perms ? "'".$this->db->escape($perms)."'" : "null").",";
938 $sql .= " ".($langfile ? "'".$this->db->escape($langfile)."'" : "null").",";
939 $sql .= " ".((int) $pos).",";
940 $sql .= " '".$this->db->escape($alwayseditable)."',";
941 $sql .= " '".$this->db->escape($params)."',";
942 $sql .= " '".$this->db->escape($list)."',";
943 $sql .= " ".((int) $printable).",";
944 $sql .= " ".($totalizable ? 'TRUE' : 'FALSE').",";
945 $sql .= " ".(($default != '') ? "'".$this->db->escape($default)."'" : "null").",";
946 $sql .= " ".($computed ? "'".$this->db->escape($computed)."'" : "null").",";
947 $sql .= " ".((int) $user->id).",";
948 $sql .= " ".((int) $user->id).",";
949 $sql .= "'".$this->db->idate(dol_now())."',";
950 $sql .= "'".$this->db->escape($enabled)."',";
951 $sql .= " ".($help ? "'".$this->db->escape($help)."'" : "null").",";
952 $sql .= " ".($css ? "'".$this->db->escape($css)."'" : "null").",";
953 $sql .= " ".($csslist ? "'".$this->db->escape($csslist)."'" : "null").",";
954 $sql .= " ".($cssview ? "'".$this->db->escape($cssview)."'" : "null");
955 $sql .= ")";
956
957 $resql2 = $this->db->query($sql);
958
959 if ($resql1 && $resql2) {
960 $this->db->commit();
961 return 1;
962 } else {
963 $this->db->rollback();
964 dol_print_error($this->db);
965 return -1;
966 }
967 } else {
968 return 0;
969 }
970 }
971
972 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
982 public function fetch_name_optionals_label($elementtype, $forceload = false, $attrname = '')
983 {
984 // phpcs:enable
985 global $conf;
986
987 if (empty($elementtype)) {
988 return array();
989 }
990
991 if ($elementtype == 'thirdparty') {
992 $elementtype = 'societe';
993 }
994 if ($elementtype == 'contact') {
995 $elementtype = 'socpeople';
996 }
997 if ($elementtype == 'order_supplier') {
998 $elementtype = 'commande_fournisseur';
999 }
1000
1001 // Test cache $this->attributes[$elementtype]['loaded'] to see if we must do something
1002 // TODO
1003
1004 $array_name_label = array();
1005
1006 // 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
1007 $sql = "SELECT rowid, name, label, type, size, elementtype, fieldunique, fieldrequired, param, pos, alwayseditable, perms, langs, list, printable, totalizable, fielddefault, fieldcomputed, entity, enabled, help,";
1008 $sql .= " css, cssview, csslist";
1009 $sql .= " FROM ".$this->db->prefix()."extrafields";
1010 //$sql.= " WHERE entity IN (0,".$conf->entity.")"; // Filter is done later
1011 if ($elementtype && $elementtype != 'all') {
1012 $sql .= " WHERE elementtype = '".$this->db->escape($elementtype)."'"; // Filed with object->table_element
1013 }
1014 if ($attrname && $elementtype && $elementtype != 'all') {
1015 $sql .= " AND name = '".$this->db->escape($attrname)."'";
1016 }
1017 $sql .= " ORDER BY pos";
1018
1019 $resql = $this->db->query($sql);
1020 if ($resql) {
1021 $count = 0;
1022 if ($this->db->num_rows($resql)) {
1023 while ($tab = $this->db->fetch_object($resql)) {
1024 if ($tab->entity != 0 && $tab->entity != $conf->entity) {
1025 // 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
1026 if ($tab->fieldrequired && is_null($tab->fielddefault)) {
1027 $this->attributes[$tab->elementtype]['mandatoryfieldsofotherentities'][$tab->name] = $tab->type;
1028 }
1029 continue;
1030 }
1031
1032 // We can add this attribute to object. TODO Remove this and return $this->attributes[$elementtype]['label']
1033 if ($tab->type != 'separate') {
1034 $array_name_label[$tab->name] = $tab->label;
1035 }
1036
1037
1038 $this->attributes[$tab->elementtype]['type'][$tab->name] = $tab->type;
1039 $this->attributes[$tab->elementtype]['label'][$tab->name] = $tab->label;
1040 $this->attributes[$tab->elementtype]['size'][$tab->name] = $tab->size;
1041 $this->attributes[$tab->elementtype]['elementtype'][$tab->name] = $tab->elementtype;
1042 $this->attributes[$tab->elementtype]['default'][$tab->name] = $tab->fielddefault;
1043 $this->attributes[$tab->elementtype]['computed'][$tab->name] = $tab->fieldcomputed;
1044 $this->attributes[$tab->elementtype]['unique'][$tab->name] = $tab->fieldunique;
1045 $this->attributes[$tab->elementtype]['required'][$tab->name] = $tab->fieldrequired;
1046 $this->attributes[$tab->elementtype]['param'][$tab->name] = ($tab->param ? jsonOrUnserialize($tab->param) : '');
1047 $this->attributes[$tab->elementtype]['pos'][$tab->name] = $tab->pos;
1048 $this->attributes[$tab->elementtype]['alwayseditable'][$tab->name] = $tab->alwayseditable;
1049 $this->attributes[$tab->elementtype]['perms'][$tab->name] = ((is_null($tab->perms) || strlen($tab->perms) == 0) ? 1 : $tab->perms);
1050 $this->attributes[$tab->elementtype]['langfile'][$tab->name] = $tab->langs;
1051 $this->attributes[$tab->elementtype]['list'][$tab->name] = $tab->list;
1052 $this->attributes[$tab->elementtype]['printable'][$tab->name] = $tab->printable;
1053 $this->attributes[$tab->elementtype]['totalizable'][$tab->name] = ($tab->totalizable ? 1 : 0);
1054 $this->attributes[$tab->elementtype]['entityid'][$tab->name] = $tab->entity;
1055 $this->attributes[$tab->elementtype]['enabled'][$tab->name] = $tab->enabled;
1056 $this->attributes[$tab->elementtype]['help'][$tab->name] = $tab->help;
1057 $this->attributes[$tab->elementtype]['css'][$tab->name] = $tab->css;
1058 $this->attributes[$tab->elementtype]['cssview'][$tab->name] = $tab->cssview;
1059 $this->attributes[$tab->elementtype]['csslist'][$tab->name] = $tab->csslist;
1060
1061 $this->attributes[$tab->elementtype]['loaded'] = 1;
1062 $count++;
1063 }
1064 }
1065 if ($elementtype) {
1066 $this->attributes[$elementtype]['loaded'] = 1; // Note: If nothing is found, we also set the key 'loaded' to 1.
1067 $this->attributes[$elementtype]['count'] = $count;
1068 }
1069 } else {
1070 $this->error = $this->db->lasterror();
1071 dol_syslog(get_class($this)."::fetch_name_optionals_label ".$this->error, LOG_ERR);
1072 }
1073
1074 return $array_name_label;
1075 }
1076
1077
1093 public function showInputField($key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '', $object = 0, $extrafieldsobjectkey = '', $mode = 0)
1094 {
1095 global $conf, $langs, $form;
1096
1097 if (!is_object($form)) {
1098 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
1099 $form = new Form($this->db);
1100 }
1101
1102 $objectid = (is_numeric($object) ? $object : $object->id);
1103
1104 $out = '';
1105
1106 if (!preg_match('/options_$/', $keyprefix)) { // Because we work on extrafields, we add 'options_' to prefix if not already added
1107 $keyprefix .= 'options_';
1108 }
1109
1110 if (empty($extrafieldsobjectkey)) {
1111 dol_syslog(get_class($this).'::showInputField extrafieldsobjectkey required', LOG_ERR);
1112 return 'BadValueForParamExtraFieldsObjectKey';
1113 }
1114
1115 $label = $this->attributes[$extrafieldsobjectkey]['label'][$key];
1116 $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
1117 $size = $this->attributes[$extrafieldsobjectkey]['size'][$key];
1118 $default = $this->attributes[$extrafieldsobjectkey]['default'][$key];
1119 $computed = $this->attributes[$extrafieldsobjectkey]['computed'][$key];
1120 $unique = $this->attributes[$extrafieldsobjectkey]['unique'][$key];
1121 $required = $this->attributes[$extrafieldsobjectkey]['required'][$key];
1122 $param = $this->attributes[$extrafieldsobjectkey]['param'][$key];
1123 $perms = (int) dol_eval($this->attributes[$extrafieldsobjectkey]['perms'][$key], 1, 1, '2');
1124 $langfile = $this->attributes[$extrafieldsobjectkey]['langfile'][$key];
1125 $list = (string) dol_eval($this->attributes[$extrafieldsobjectkey]['list'][$key], 1, 1, '2');
1126 $totalizable = $this->attributes[$extrafieldsobjectkey]['totalizable'][$key];
1127 $help = $this->attributes[$extrafieldsobjectkey]['help'][$key];
1128 $alwayseditable = $this->attributes[$extrafieldsobjectkey]['alwayseditable'][$key];
1129 $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)
1130
1131 //var_dump('key='.$key.' '.$value.' '.$moreparam.' '.$keysuffix.' '.$keyprefix.' '.$objectid.' '.$extrafieldsobjectkey.' '.$mode);
1132 //var_dump('label='.$label.' type='.$type.' param='.var_export($param, 1));
1133
1134 if ($computed) {
1135 if (!preg_match('/^search_/', $keyprefix)) {
1136 return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
1137 } else {
1138 return '';
1139 }
1140 }
1141
1142 //
1143 // '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'
1144 if (empty($morecss)) {
1145 // Add automatic css
1146 if ($type == 'date') {
1147 $morecss = 'minwidth100imp';
1148 } elseif ($type == 'datetime' || $type == 'datetimegmt' || $type == 'link') {
1149 $morecss = 'minwidth200imp';
1150 } elseif (in_array($type, array('int', 'integer', 'double', 'price'))) {
1151 $morecss = 'maxwidth75';
1152 } elseif ($type == 'password') {
1153 $morecss = 'maxwidth100';
1154 } elseif ($type == 'url') {
1155 $morecss = 'minwidth400';
1156 } elseif ($type == 'boolean') {
1157 $morecss = '';
1158 } elseif ($type == 'radio') {
1159 $morecss = 'width25';
1160 } else {
1161 if (empty($size) || round((float) $size) < 12) {
1162 $morecss = 'minwidth100';
1163 } elseif (round((float) $size) <= 48) {
1164 $morecss = 'minwidth200';
1165 } else {
1166 $morecss = 'minwidth400';
1167 }
1168 }
1169 // If css forced in attribute, we use this one
1170 if (!empty($this->attributes[$extrafieldsobjectkey]['css'][$key])) {
1171 $morecss = $this->attributes[$extrafieldsobjectkey]['css'][$key];
1172 }
1173 }
1174
1175 if (in_array($type, array('date'))) {
1176 $tmp = explode(',', $size);
1177 $newsize = $tmp[0];
1178 $showtime = 0;
1179
1180 // Do not show current date when field not required (see selectDate() method)
1181 if (!$required && $value == '') {
1182 $value = '-1';
1183 }
1184
1185 if ($mode == 1) {
1186 // search filter on a date extrafield shows two inputs to select a date range
1187 $prefill = array(
1188 'start' => isset($value['start']) ? $value['start'] : '',
1189 'end' => isset($value['end']) ? $value['end'] : ''
1190 );
1191 $out = '<div ' . ($moreparam ? $moreparam : '') . '><div class="nowrap">';
1192 $out .= $form->selectDate($prefill['start'], $keyprefix.$key.$keysuffix.'_start', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("From"));
1193 $out .= '</div><div class="nowrap">';
1194 $out .= $form->selectDate($prefill['end'], $keyprefix.$key.$keysuffix.'_end', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"));
1195 $out .= '</div></div>';
1196 } else {
1197 // TODO Must also support $moreparam
1198 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
1199 }
1200 } elseif (in_array($type, array('datetime', 'datetimegmt'))) {
1201 $tmp = explode(',', $size);
1202 $newsize = $tmp[0];
1203 $showtime = 1;
1204
1205 // Do not show current date when field not required (see selectDate() method)
1206 if (!$required && $value == '') {
1207 $value = '-1';
1208 }
1209
1210 if ($mode == 1) {
1211 // search filter on a date extrafield shows two inputs to select a date range
1212 $prefill = array(
1213 'start' => isset($value['start']) ? $value['start'] : '',
1214 'end' => isset($value['end']) ? $value['end'] : ''
1215 );
1216 $out = '<div ' . ($moreparam ? $moreparam : '') . '><div class="nowrap">';
1217 $out .= $form->selectDate($prefill['start'], $keyprefix.$key.$keysuffix.'_start', 1, 1, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("From"), 'tzuserrel');
1218 $out .= '</div><div class="nowrap">';
1219 $out .= $form->selectDate($prefill['end'], $keyprefix.$key.$keysuffix.'_end', 1, 1, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
1220 $out .= '</div></div>';
1221 } else {
1222 // TODO Must also support $moreparam
1223 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
1224 }
1225 } elseif (in_array($type, array('int', 'integer'))) {
1226 $tmp = explode(',', $size);
1227 $newsize = $tmp[0];
1228 $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 : '').'>';
1229 } elseif (preg_match('/varchar/', $type)) {
1230 $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 : '').'>';
1231 } elseif (in_array($type, array('mail', 'ip', 'phone', 'url'))) {
1232 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
1233 } elseif ($type == 'icon') {
1234 /* External lib inclusion are not allowed in backoffice. Also lib is included several time if there is several icon file.
1235 Some code must be added into main when MAIN_ADD_ICONPICKER_JS is set to add of lib in html header
1236 $out ='<link rel="stylesheet" href="'.dol_buildpath('/myfield/css/fontawesome-iconpicker.min.css', 1).'">';
1237 $out.='<script src="'.dol_buildpath('/myfield/js/fontawesome-iconpicker.min.js', 1).'"></script>';
1238 */
1239 $out .= '<input type="text" class="form-control icp icp-auto iconpicker-element iconpicker-input flat '.$morecss.' maxwidthonsmartphone"';
1240 $out .= ' name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
1241 if (getDolGlobalInt('MAIN_ADD_ICONPICKER_JS')) {
1242 $out .= '<script>';
1243 $options = "{ title: '<b>".$langs->trans("IconFieldSelector")."</b>', placement: 'right', showFooter: false, templates: {";
1244 $options .= "iconpicker: '<div class=\"iconpicker\"><div style=\"background-color:#EFEFEF;\" class=\"iconpicker-items\"></div></div>',";
1245 $options .= "iconpickerItem: '<a role=\"button\" href=\"#\" class=\"iconpicker-item\" style=\"background-color:#DDDDDD;\"><i></i></a>',";
1246 // $options.="buttons: '<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-cancel btn btn-default btn-sm\">".$langs->trans("Cancel")."</button>";
1247 // $options.="<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-accept btn btn-primary btn-sm\">".$langs->trans("Save")."</button>',";
1248 $options .= "footer: '<div class=\"popover-footer\" style=\"background-color:#EFEFEF;\"></div>',";
1249 $options .= "search: '<input type=\"search\" class\"form-control iconpicker-search\" placeholder=\"".$langs->trans("TypeToFilter")."\" />',";
1250 $options .= "popover: '<div class=\"iconpicker-popover popover\">";
1251 $options .= " <div class=\"arrow\" ></div>";
1252 $options .= " <div class=\"popover-title\" style=\"text-align:center;background-color:#EFEFEF;\"></div>";
1253 $options .= " <div class=\"popover-content \" ></div>";
1254 $options .= "</div>'}}";
1255 $out .= "$('#".$keyprefix.$key.$keysuffix."').iconpicker(".$options.");";
1256 $out .= '</script>';
1257 }
1258 } elseif ($type == 'text') {
1259 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
1260 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
1261 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
1262 $out = (string) $doleditor->Create(1);
1263 } else {
1264 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
1265 }
1266 } elseif ($type == 'html') {
1267 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
1268 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
1269 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && getDolGlobalInt('FCKEDITOR_ENABLE_SOCIETE'), ROWS_5, '90%');
1270 $out = (string) $doleditor->Create(1);
1271 } else {
1272 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
1273 }
1274 } elseif ($type == 'boolean') {
1275 if (empty($mode)) {
1276 $checked = '';
1277 if (!empty($value)) {
1278 $checked = ' checked value="1" ';
1279 } else {
1280 $checked = ' value="1" ';
1281 }
1282 $out = '<input type="checkbox" class="flat valignmiddle'.($morecss ? ' '.$morecss : '').' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>';
1283 } else {
1284 $out = $form->selectyesno($keyprefix.$key.$keysuffix, $value, 1, false, 1, 1, 'width75 yesno');
1285 }
1286 $out .= '<input type="hidden" name="'.$keyprefix.$key.$keysuffix.'_boolean" value="1">'; // A hidden field ending with "_boolean" that is always set to 1.
1287 } elseif ($type == 'price') {
1288 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
1289 $value = price($value);
1290 }
1291 $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).'">';
1292 } elseif ($type == 'pricecy') {
1293 $currency = $conf->currency;
1294 if (!empty($value)) {
1295 // $value in memory is a php string like '10.01:USD'
1296 $pricetmp = explode(':', $value);
1297 $currency = !empty($pricetmp[1]) ? $pricetmp[1] : $conf->currency;
1298 $value = price($pricetmp[0]);
1299 }
1300 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
1301 $out .= $form->selectCurrency($currency, $keyprefix.$key.$keysuffix.'currency_id');
1302 } elseif ($type == 'duration') {
1303 $value = intval($value);
1304 $out = $form->select_duration($keyprefix . $key, $value, 0, 'text', 0, 1);
1305 } elseif ($type == 'double') {
1306 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
1307 $value = price($value);
1308 }
1309 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
1310 } elseif ($type == 'select') {
1311 $out = '';
1312 if ($mode) {
1313 $options = array();
1314 foreach ($param['options'] as $okey => $val) {
1315 if ((string) $okey == '') {
1316 continue;
1317 }
1318
1319 $valarray = explode('|', $val);
1320 $val = $valarray[0];
1321
1322 if ($langfile && $val) {
1323 $options[$okey] = $langs->trans($val);
1324 } else {
1325 $options[$okey] = $val;
1326 }
1327 }
1328 $selected = array();
1329 if (!is_array($value)) {
1330 $selected = explode(',', $value);
1331 }
1332
1333 $out .= $form->multiselectarray($keyprefix.$key.$keysuffix, $options, $selected, 0, 0, $morecss, 0, 0, '', '', '', !empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2'));
1334 } else {
1335 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
1336 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1337 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
1338 }
1339
1340 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
1341 $out .= '<option value="0">&nbsp;</option>';
1342 foreach ($param['options'] as $key2 => $val2) {
1343 if ((string) $key2 == '') {
1344 continue;
1345 }
1346 $valarray = explode('|', $val2);
1347 $val2 = $valarray[0];
1348 $parent = '';
1349 if (!empty($valarray[1])) {
1350 $parent = $valarray[1];
1351 }
1352 $out .= '<option value="'.$key2.'"';
1353 $out .= (((string) $value == (string) $key2) ? ' selected' : '');
1354 $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
1355 $out .= '>';
1356 if ($langfile && $val2) {
1357 $out .= $langs->trans($val2);
1358 } else {
1359 $out .= $val2;
1360 }
1361 $out .= '</option>';
1362 }
1363 $out .= '</select>';
1364 }
1365 } elseif ($type == 'sellist') { // List of values selected from a table (1 choice)
1366 $out = '';
1367 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
1368 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1369 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
1370 }
1371
1372 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
1373 if (is_array($param['options'])) {
1374 $tmpparamoptions = array_keys($param['options']);
1375 $paramoptions = preg_split('/[\r\n]+/', $tmpparamoptions[0]);
1376
1377 $InfoFieldList = explode(":", $paramoptions[0], 5);
1378 // 0 : tableName
1379 // 1 : label field name
1380 // 2 : key fields name (if different of rowid)
1381 // optional parameters...
1382 // 3 : key field parent (for dependent lists). How this is used ?
1383 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value. Or use USF on the second line.
1384 // 5 : string category type. This replace the filter.
1385 // 6 : ids categories list separated by comma for category root. This replace the filter.
1386 // 7 : sort field (not used here but used into format for commobject)
1387
1388 // If there is a filter, we extract it by taking all content inside parenthesis.
1389 if (! empty($InfoFieldList[4])) {
1390 $pos = 0; // $pos will be position of ending filter
1391 $parenthesisopen = 0;
1392 while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
1393 if (substr($InfoFieldList[4], $pos, 1) == '(') {
1394 $parenthesisopen++;
1395 }
1396 if (substr($InfoFieldList[4], $pos, 1) == ')') {
1397 $parenthesisopen--;
1398 }
1399 $pos++;
1400 }
1401 $tmpbefore = substr($InfoFieldList[4], 0, $pos);
1402 $tmpafter = substr($InfoFieldList[4], $pos + 1);
1403 //var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
1404 $InfoFieldList[4] = $tmpbefore;
1405 if ($tmpafter !== '') {
1406 $InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
1407 }
1408
1409 // Fix better compatibility with some old extrafield syntax filter "(field=123)"
1410 $reg = array();
1411 if (preg_match('/^\‍(?([a-z0-9]+)([=<>]+)(\d+)\‍)?$/i', $InfoFieldList[4], $reg)) {
1412 $InfoFieldList[4] = '('.$reg[1].':'.$reg[2].':'.$reg[3].')';
1413 }
1414
1415 //var_dump($InfoFieldList);
1416 }
1417
1418 //$Usf = empty($paramoptions[1]) ? '' :$paramoptions[1];
1419
1420 $parentName = '';
1421 $parentField = '';
1422 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
1423
1424 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
1425 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1426 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
1427 } else {
1428 $keyList = $InfoFieldList[2].' as rowid';
1429 }
1430 }
1431 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
1432 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
1433 if (!empty($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra.') !== false) {
1434 $keyList .= ', main.'.$parentField;
1435 } else {
1436 $keyList .= ', '.$parentField;
1437 }
1438 }
1439
1440 $filter_categorie = false;
1441 if (count($InfoFieldList) > 5) {
1442 if ($InfoFieldList[0] == 'categorie') {
1443 $filter_categorie = true;
1444 }
1445 }
1446
1447 if (!$filter_categorie) {
1448 $fields_label = explode('|', $InfoFieldList[1]);
1449 if (is_array($fields_label)) {
1450 $keyList .= ', ';
1451 $keyList .= implode(', ', $fields_label);
1452 }
1453
1454 $sqlwhere = '';
1455 $sql = "SELECT ".$keyList;
1456 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
1457
1458 // Add filter from 4th field
1459 if (!empty($InfoFieldList[4])) {
1460 // can use current entity filter
1461 if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
1462 $InfoFieldList[4] = str_replace('$ENTITY$', (string) $conf->entity, $InfoFieldList[4]);
1463 }
1464 // can use SELECT request
1465 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
1466 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
1467 }
1468
1469 // current object id can be use into filter
1470 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
1471 $InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
1472 } else {
1473 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
1474 }
1475
1476 // We have to join on extrafield table
1477 $errstr = '';
1478 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1479 $sql .= ' as main, '.$this->db->sanitize($this->db->prefix().$InfoFieldList[0]).'_extrafields as extra';
1480 $sqlwhere .= " WHERE extra.fk_object = main.".$this->db->sanitize($InfoFieldList[2]);
1481 $sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1482 } else {
1483 //print $InfoFieldList[4];
1484 $sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1485 }
1486 } else {
1487 $sqlwhere .= ' WHERE 1=1';
1488 }
1489
1490 // Add Usf filter on second line
1491 /*
1492 if ($Usf) {
1493 $errorstr = '';
1494 $sqlusf .= forgeSQLFromUniversalSearchCriteria($Usf, $errorstr);
1495 if (!$errorstr) {
1496 $sqlwhere .= $sqlusf;
1497 } else {
1498 $sqlwhere .= " AND invalid_usf_filter_of_extrafield";
1499 }
1500 }
1501 */
1502
1503 // Some tables may have field, some other not. For the moment we disable it.
1504 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
1505 $sqlwhere .= ' AND entity = '.((int) $conf->entity);
1506 }
1507 $sql .= $sqlwhere;
1508
1509 $sql .= $this->db->order(implode(',', $fields_label));
1510 //print $sql;
1511
1512 dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
1513 $resql = $this->db->query($sql);
1514 if ($resql) {
1515 $out .= '<option value="0">&nbsp;</option>';
1516 $num = $this->db->num_rows($resql);
1517 $i = 0;
1518 while ($i < $num) {
1519 $labeltoshow = '';
1520 $obj = $this->db->fetch_object($resql);
1521
1522 // Several field into label (eq table:code|label:rowid)
1523 $notrans = false;
1524 $fields_label = explode('|', $InfoFieldList[1]);
1525 if (is_array($fields_label) && count($fields_label) > 1) {
1526 $notrans = true;
1527 foreach ($fields_label as $field_toshow) {
1528 $labeltoshow .= $obj->$field_toshow.' ';
1529 }
1530 } else {
1531 $labeltoshow = $obj->{$InfoFieldList[1]};
1532 }
1533
1534 if ($value == $obj->rowid) {
1535 if (!$notrans) {
1536 foreach ($fields_label as $field_toshow) {
1537 $translabel = $langs->trans($obj->$field_toshow);
1538 $labeltoshow = $translabel.' ';
1539 }
1540 }
1541 $out .= '<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
1542 } else {
1543 if (!$notrans) {
1544 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
1545 $labeltoshow = $translabel;
1546 }
1547 if (empty($labeltoshow)) {
1548 $labeltoshow = '(not defined)';
1549 }
1550
1551 if (!empty($InfoFieldList[3]) && $parentField) {
1552 $parent = $parentName.':'.$obj->{$parentField};
1553 }
1554
1555 $out .= '<option value="'.$obj->rowid.'"';
1556 $out .= ($value == $obj->rowid ? ' selected' : '');
1557 $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
1558 $out .= '>'.$labeltoshow.'</option>';
1559 }
1560
1561 $i++;
1562 }
1563 $this->db->free($resql);
1564 } else {
1565 print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
1566 }
1567 } else {
1568 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
1569 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
1570 $out .= '<option value="0">&nbsp;</option>';
1571 if (is_array($data)) {
1572 foreach ($data as $data_key => $data_value) {
1573 $out .= '<option value="'.$data_key.'"';
1574 $out .= ($value == $data_key ? ' selected' : '');
1575 $out .= '>'.$data_value.'</option>';
1576 }
1577 }
1578 }
1579 }
1580 $out .= '</select>';
1581 } elseif ($type == 'checkbox') {
1582 $value_arr = $value;
1583 if (!is_array($value)) {
1584 $value_arr = explode(',', $value);
1585 }
1586 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ? null : $param['options']), $value_arr, 0, 0, '', 0, '100%');
1587 } elseif ($type == 'radio') {
1588 $out = '';
1589 foreach ($param['options'] as $keyopt => $val) {
1590 $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
1591 $out .= ' value="'.$keyopt.'"';
1592 $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
1593 $out .= ($value == $keyopt ? 'checked' : '');
1594 $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$langs->trans($val).'</label><br>';
1595 }
1596 } elseif ($type == 'chkbxlst') { // List of values selected from a table (n choices)
1597 if (is_array($value)) {
1598 $value_arr = $value;
1599 } else {
1600 $value_arr = explode(',', $value);
1601 }
1602
1603 if (is_array($param['options'])) {
1604 $tmpparamoptions = array_keys($param['options']);
1605 $paramoptions = preg_split('/[\r\n]+/', $tmpparamoptions[0]);
1606
1607 $InfoFieldList = explode(":", $paramoptions[0], 5);
1608 // 0 : tableName
1609 // 1 : label field name
1610 // 2 : key fields name (if different of rowid)
1611 // optional parameters...
1612 // 3 : key field parent (for dependent lists). How this is used ?
1613 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value. Or use USF on the second line.
1614 // 5 : string category type. This replace the filter.
1615 // 6 : ids categories list separated by comma for category root. This replace the filter.
1616 // 7 : sort field (not used here but used into format for commobject)
1617
1618 // If there is a filter, we extract it by taking all content inside parenthesis.
1619 if (! empty($InfoFieldList[4])) {
1620 $pos = 0;
1621 $parenthesisopen = 0;
1622 while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
1623 if (substr($InfoFieldList[4], $pos, 1) == '(') {
1624 $parenthesisopen++;
1625 }
1626 if (substr($InfoFieldList[4], $pos, 1) == ')') {
1627 $parenthesisopen--;
1628 }
1629 $pos++;
1630 }
1631 $tmpbefore = substr($InfoFieldList[4], 0, $pos);
1632 $tmpafter = substr($InfoFieldList[4], $pos + 1);
1633 //var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
1634 $InfoFieldList[4] = $tmpbefore;
1635 if ($tmpafter !== '') {
1636 $InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
1637 }
1638
1639 // Fix better compatibility with some old extrafield syntax filter "(field=123)"
1640 $reg = array();
1641 if (preg_match('/^\‍(?([a-z0-9]+)([=<>]+)(\d+)\‍)?$/i', $InfoFieldList[4], $reg)) {
1642 $InfoFieldList[4] = '('.$reg[1].':'.$reg[2].':'.$reg[3].')';
1643 }
1644
1645 //var_dump($InfoFieldList);
1646 }
1647
1648 //$Usf = empty($paramoptions[1]) ? '' :$paramoptions[1];
1649
1650 $parentName = '';
1651 $parentField = '';
1652 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
1653
1654 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
1655 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1656 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
1657 } else {
1658 $keyList = $InfoFieldList[2].' as rowid';
1659 }
1660 }
1661 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
1662 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
1663 if (!empty($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra.') !== false) {
1664 $keyList .= ', main.'.$parentField;
1665 } else {
1666 $keyList .= ', '.$parentField;
1667 }
1668 }
1669
1670 $filter_categorie = false;
1671 if (count($InfoFieldList) > 5) {
1672 if ($InfoFieldList[0] == 'categorie') {
1673 $filter_categorie = true;
1674 }
1675 }
1676
1677 if (!$filter_categorie) {
1678 $fields_label = explode('|', $InfoFieldList[1]);
1679 if (is_array($fields_label)) {
1680 $keyList .= ', ';
1681 $keyList .= implode(', ', $fields_label);
1682 }
1683
1684 $sqlwhere = '';
1685 $sql = "SELECT ".$keyList;
1686 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
1687
1688 // Add filter from 4th field
1689 if (!empty($InfoFieldList[4])) {
1690 // can use current entity filter
1691 if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
1692 $InfoFieldList[4] = str_replace('$ENTITY$', (string) $conf->entity, $InfoFieldList[4]);
1693 }
1694 // can use SELECT request
1695 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
1696 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
1697 }
1698
1699 // current object id can be use into filter
1700 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
1701 $InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
1702 } elseif (preg_match("#^.*list.php$#", $_SERVER["PHP_SELF"])) {
1703 // Pattern for word=$ID$
1704 $word = '\b[a-zA-Z0-9-\.-_]+\b=\$ID\$';
1705
1706 // Removing spaces around =, ( and )
1707 $InfoFieldList[4] = preg_replace('# *(=|\‍(|\‍)) *#', '$1', $InfoFieldList[4]);
1708
1709 $nbPreg = 1;
1710 // While we have parenthesis
1711 while ($nbPreg != 0) {
1712 // Initialise counters
1713 $nbPregRepl = $nbPregSel = 0;
1714 // Remove all parenthesis not preceded with '=' sign
1715 $InfoFieldList[4] = preg_replace('#([^=])(\‍([^)^(]*('.$word.')[^)^(]*\‍))#', '$1 $3 ', $InfoFieldList[4], -1, $nbPregRepl);
1716 // Remove all escape characters around '=' and parenthesis
1717 $InfoFieldList[4] = preg_replace('# *(=|\‍(|\‍)) *#', '$1', $InfoFieldList[4]);
1718 // Remove all parentheses preceded with '='
1719 $InfoFieldList[4] = preg_replace('#\b[a-zA-Z0-9-\.-_]+\b=\‍([^)^(]*('.$word.')[^)^(]*\‍)#', '$1 ', $InfoFieldList[4], -1, $nbPregSel);
1720 // On retire les escapes autour des = et parenthèses
1721 $InfoFieldList[4] = preg_replace('# *(=|\‍(|\‍)) *#', '$1', $InfoFieldList[4]);
1722
1723 // UPdate the totals counter for the loop
1724 $nbPreg = $nbPregRepl + $nbPregSel;
1725 }
1726
1727 // In case there is AND ou OR, before or after
1728 $matchCondition = array();
1729 preg_match('#(AND|OR|) *('.$word.') *(AND|OR|)#', $InfoFieldList[4], $matchCondition);
1730 while (!empty($matchCondition[0])) {
1731 // If the two sides differ but are not empty
1732 if (!empty($matchCondition[1]) && !empty($matchCondition[3]) && $matchCondition[1] != $matchCondition[3]) {
1733 // Nobody sain would do that without parentheses
1734 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
1735 } else {
1736 if (!empty($matchCondition[1])) {
1737 $boolCond = (($matchCondition[1] == "AND") ? ' AND TRUE ' : ' OR FALSE ');
1738 $InfoFieldList[4] = str_replace($matchCondition[0], $boolCond.$matchCondition[3], $InfoFieldList[4]);
1739 } elseif (!empty($matchCondition[3])) {
1740 $boolCond = (($matchCondition[3] == "AND") ? ' TRUE AND ' : ' FALSE OR');
1741 $InfoFieldList[4] = str_replace($matchCondition[0], $boolCond, $InfoFieldList[4]);
1742 } else {
1743 $InfoFieldList[4] = " TRUE ";
1744 }
1745 }
1746
1747 // In case there is AND ou OR, before or after
1748 preg_match('#(AND|OR|) *('.$word.') *(AND|OR|)#', $InfoFieldList[4], $matchCondition);
1749 }
1750 } else {
1751 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
1752 }
1753
1754 // We have to join on extrafield table
1755 $errstr = '';
1756 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1757 $sql .= ' as main, '.$this->db->sanitize($this->db->prefix().$InfoFieldList[0]).'_extrafields as extra';
1758 $sqlwhere .= " WHERE extra.fk_object = main.".$this->db->sanitize($InfoFieldList[2]);
1759 $sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1760 } else {
1761 $sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1762 }
1763 } else {
1764 $sqlwhere .= ' WHERE 1=1';
1765 }
1766
1767 // Add Usf filter on second line
1768 /*
1769 if ($Usf) {
1770 $errorstr = '';
1771 $sqlusf .= forgeSQLFromUniversalSearchCriteria($Usf, $errorstr);
1772 if (!$errorstr) {
1773 $sqlwhere .= $sqlusf;
1774 } else {
1775 $sqlwhere .= " AND invalid_usf_filter_of_extrafield";
1776 }
1777 }
1778 */
1779
1780 // Some tables may have field, some other not. For the moment we disable it.
1781 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
1782 $sqlwhere .= " AND entity = ".((int) $conf->entity);
1783 }
1784 // $sql.=preg_replace('/^ AND /','',$sqlwhere);
1785 // print $sql;
1786
1787 $sql .= $sqlwhere;
1788 $sql .= ' ORDER BY '.implode(', ', $fields_label);
1789
1790 dol_syslog(get_class($this).'::showInputField type=chkbxlst', LOG_DEBUG);
1791 $resql = $this->db->query($sql);
1792 if ($resql) {
1793 $num = $this->db->num_rows($resql);
1794 $i = 0;
1795
1796 $data = array();
1797
1798 while ($i < $num) {
1799 $labeltoshow = '';
1800 $obj = $this->db->fetch_object($resql);
1801
1802 $notrans = false;
1803 // Several field into label (eq table:code|label:rowid)
1804 $fields_label = explode('|', $InfoFieldList[1]);
1805 if (is_array($fields_label)) {
1806 $notrans = true;
1807 foreach ($fields_label as $field_toshow) {
1808 $labeltoshow .= $obj->$field_toshow.' ';
1809 }
1810 } else {
1811 $labeltoshow = $obj->{$InfoFieldList[1]};
1812 }
1813 $labeltoshow = dol_trunc($labeltoshow, 45);
1814
1815 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
1816 $labeltoshow = '';
1817 foreach ($fields_label as $field_toshow) {
1818 $translabel = $langs->trans($obj->$field_toshow);
1819 if ($translabel != $obj->$field_toshow) {
1820 $labeltoshow .= ' '.dol_trunc($translabel, 18).' ';
1821 } else {
1822 $labeltoshow .= ' '.dol_trunc($obj->$field_toshow, 18).' ';
1823 }
1824 }
1825 $data[$obj->rowid] = $labeltoshow;
1826 } else {
1827 if (!$notrans) {
1828 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
1829 if ($translabel != $obj->{$InfoFieldList[1]}) {
1830 $labeltoshow = dol_trunc($translabel, 18);
1831 } else {
1832 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
1833 }
1834 }
1835 if (empty($labeltoshow)) {
1836 $labeltoshow = '(not defined)';
1837 }
1838
1839 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
1840 $data[$obj->rowid] = $labeltoshow;
1841 }
1842
1843 if (!empty($InfoFieldList[3]) && $parentField) {
1844 $parent = $parentName.':'.$obj->{$parentField};
1845 }
1846
1847 $data[$obj->rowid] = $labeltoshow;
1848 }
1849
1850 $i++;
1851 }
1852 $this->db->free($resql);
1853
1854 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, 0, 0, '', 0, '100%');
1855 } else {
1856 print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
1857 }
1858 } else {
1859 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
1860 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
1861 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, 0, 0, '', 0, '100%');
1862 }
1863 }
1864 } elseif ($type == 'link') {
1865 $param_list = array_keys($param['options']); // $param_list[0] = 'ObjectName:classPath' but can also be 'ObjectName:classPath:1:(status:=:1)'
1866 /* Removed.
1867 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
1868 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.
1869 if (strpos($param_list[0], '$ID$') !== false && !empty($objectid)) {
1870 $param_list[0] = str_replace('$ID$', $objectid, $param_list[0]);
1871 }*/
1872 $showempty = (($required && $default != '') ? 0 : 1);
1873
1874 $tmparray = explode(':', $param_list[0]);
1875
1876 $element = $extrafieldsobjectkey; // $extrafieldsobjectkey comes from $object->table_element but we need $object->element
1877 if ($element == 'socpeople') {
1878 $element = 'contact';
1879 } elseif ($element == 'projet') {
1880 $element = 'project';
1881 }
1882
1883 //$objectdesc = $param_list[0]; // Example: 'ObjectName:classPath:1:(status:=:1)' Replaced by next line: this was propagated also a filter by ajax call that was blocked by some WAF
1884 $objectdesc = $tmparray[0]; // 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 one into the definition in the ->fields of $elem if found.
1885 $objectfield = $element.':options_'.$key; // Example: 'actioncomm:options_fff' To be used in priority to know object linked with all its definition (including filters)
1886
1887 $out = $form->selectForForms($objectdesc, $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, '', 0, 0, '', $objectfield);
1888 } elseif (in_array($type, ['point', 'multipts', 'linestrg', 'polygon'])) {
1889 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
1890 $dolgeophp = new DolGeoPHP($this->db);
1891 $geojson = '{}';
1892 $centroidjson = getDolGlobalString('MAIN_INFO_SOCIETE_GEO_COORDINATES', '{}');
1893 if (!empty($value)) {
1894 $tmparray = $dolgeophp->parseGeoString($value);
1895 $geojson = $tmparray['geojson'];
1896 $centroidjson = $tmparray['centroidjson'];
1897 }
1898 if (!preg_match('/search_/', $keyprefix)) {
1899 require_once DOL_DOCUMENT_ROOT.'/core/class/geomapeditor.class.php';
1900 $geomapeditor = new GeoMapEditor();
1901 $out .= $geomapeditor->getHtml($keyprefix.$key.$keysuffix, $geojson, $centroidjson, $type);
1902 } else {
1903 // If keyprefix is search_ or search_options_, we must just use a simple text field
1904 $out = '';
1905 }
1906 } elseif ($type == 'password') {
1907 // If prefix is 'search_', field is used as a filter, we use a common text field.
1908 $out = '<input style="display:none" type="text" name="fakeusernameremembered">'; // Hidden field to reduce impact of evil Google Chrome autopopulate bug.
1909 $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 : '').'>';
1910 } elseif ($type == 'stars') {
1911 $out = '<input type="hidden" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').'>';
1912 $out .= '<div class="star-selection" id="'.$keyprefix.$key.$keysuffix.'_selection">';
1913 $i = 1;
1914 while ($i <= $size) {
1915 $out .= '<span class="star" data-value="'.$i.'">'.img_picto('', 'fontawesome_star_fas').'</span>';
1916 $i++;
1917 }
1918 $out .= '</div>';
1919 $out .= '<script>
1920 jQuery(function($) {
1921 let container = $("#'.$keyprefix.$key.$keysuffix.'_selection");
1922 let selectedStars = parseInt($("#'.$keyprefix.$key.$keysuffix.'").val()) || 0;
1923 container.find(".star").each(function() {
1924 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
1925 });
1926 container.find(".star").on("mouseover", function() {
1927 let selectedStar = $(this).data("value");
1928 container.find(".star").each(function() {
1929 $(this).toggleClass("active", $(this).data("value") <= selectedStar);
1930 });
1931 });
1932 container.on("mouseout", function() {
1933 container.find(".star").each(function() {
1934 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
1935 });
1936 });
1937 container.find(".star").off("click").on("click", function() {
1938 selectedStars = $(this).data("value");
1939 if (selectedStars === 1 && $("#'.$keyprefix.$key.$keysuffix.'").val() == 1) {
1940 selectedStars = 0;
1941 }
1942 $("#'.$keyprefix.$key.$keysuffix.'").val(selectedStars);
1943 container.find(".star").each(function() {
1944 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
1945 });
1946 });
1947 });
1948 </script>';
1949 }
1950 if (!empty($hidden)) {
1951 $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
1952 }
1953
1954 // If alwayseditable is false, and object is not in draft, then showOutputField
1955 // @phan-suppress-next-line PhanUndeclaredConstantOfClass
1956 if ($alwayseditable == 0 && is_object($object) && isset($object->status) && defined(get_class($object)."::STATUS_DRAFT") && $object->status != $object::STATUS_DRAFT) {
1957 $out = $this->showOutputField($key, $value, $moreparam, $extrafieldsobjectkey, null, $object);
1958 }
1959 /* Add comments
1960 if ($type == 'date') $out.=' (YYYY-MM-DD)';
1961 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
1962 */
1963 /*if (!empty($help) && $keyprefix != 'search_options_') {
1964 $out .= $form->textwithpicto('', $help, 1, 'help', '', 0, 3);
1965 }*/
1966 return $out;
1967 }
1968
1969
1981 public function showOutputField($key, $value, $moreparam = '', $extrafieldsobjectkey = '', $outputlangs = null, $object = null)
1982 {
1983 global $conf, $langs;
1984
1985 if (is_null($outputlangs) || !is_object($outputlangs)) {
1986 $outputlangs = $langs;
1987 }
1988
1989 if (empty($extrafieldsobjectkey)) {
1990 dol_syslog(get_class($this).'::showOutputField extrafieldsobjectkey required', LOG_ERR);
1991 return 'BadValueForParamExtraFieldsObjectKey';
1992 }
1993
1994 $label = $this->attributes[$extrafieldsobjectkey]['label'][$key];
1995 $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
1996 $size = $this->attributes[$extrafieldsobjectkey]['size'][$key]; // Can be '255', '24,8'...
1997 $default = $this->attributes[$extrafieldsobjectkey]['default'][$key];
1998 $computed = $this->attributes[$extrafieldsobjectkey]['computed'][$key];
1999 $unique = $this->attributes[$extrafieldsobjectkey]['unique'][$key];
2000 $required = $this->attributes[$extrafieldsobjectkey]['required'][$key];
2001 $param = $this->attributes[$extrafieldsobjectkey]['param'][$key];
2002 $perms = (int) dol_eval($this->attributes[$extrafieldsobjectkey]['perms'][$key], 1, 1, '2');
2003 $langfile = $this->attributes[$extrafieldsobjectkey]['langfile'][$key];
2004 $list = (string) dol_eval($this->attributes[$extrafieldsobjectkey]['list'][$key], 1, 1, '2');
2005 $help = $this->attributes[$extrafieldsobjectkey]['help'][$key];
2006 $cssview = $this->attributes[$extrafieldsobjectkey]['cssview'][$key];
2007 $alwayseditable = $this->attributes[$extrafieldsobjectkey]['alwayseditable'][$key];
2008
2009 // If alwayseditable is false, and object is not in draft, then we show value instead of input field
2010 $showValueInsteadOfInputField = 0; // Variable used to disable update of fields via ajax
2011 // @phan-suppress-next-line PhanUndeclaredConstantOfClass
2012 if ($alwayseditable == 0 && is_object($object) && isset($object->status) && defined(get_class($object)."::STATUS_DRAFT") && $object->status != $object::STATUS_DRAFT) {
2013 $showValueInsteadOfInputField = 1;
2014 }
2015
2016 $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)
2017
2018 if ($hidden) {
2019 return ''; // This is a protection. If field is hidden, we should just not call this method.
2020 }
2021
2022 //if ($computed) $value = // $value is already calculated into $value before calling this method
2023 $showsize = 0;
2024 if ($type == 'date') {
2025 $showsize = 10;
2026 if ($value !== '') {
2027 $value = dol_print_date($value, 'day'); // For date without hour, date is always GMT for storage and output
2028 }
2029 } elseif ($type == 'datetime') {
2030 $showsize = 19;
2031 if ($value !== '') {
2032 $value = dol_print_date($value, 'dayhour', 'tzuserrel');
2033 }
2034 } elseif ($type == 'duration') {
2035 $showsize = 10;
2036 if ($value !== '') {
2037 $value = intval($value);
2038 $value = convertSecondToTime($value);
2039 }
2040 } elseif ($type == 'datetimegmt') {
2041 $showsize = 19;
2042 if ($value !== '') {
2043 $value = dol_print_date($value, 'dayhour', 'gmt');
2044 }
2045 } elseif ($type == 'int') {
2046 $showsize = 10;
2047 } elseif ($type == 'double') {
2048 if (!empty($value)) {
2049 //$value=price($value);
2050 //$sizeparts = explode(",", $size);
2051 //$number_decimals = array_key_exists(1, $sizeparts) ? $sizeparts[1] : 0;
2052 $value = price($value, 0, $outputlangs, 0, 0, -2, '');
2053 }
2054 } elseif ($type == 'boolean') {
2055 $checked = '';
2056 if (!empty($value)) {
2057 $checked = ' checked ';
2058 }
2059 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
2060 $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
2061 } else {
2062 $value = yn($value ? 1 : 0);
2063 }
2064 } elseif ($type == 'mail') {
2065 $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
2066 } elseif ($type == 'ip') {
2067 $value = dol_print_ip($value, 0);
2068 } elseif ($type == 'icon') {
2069 $value = '<span class="'.$value.'"></span>';
2070 } elseif ($type == 'url') {
2071 $value = dol_print_url($value, '_blank', 32, 1);
2072 } elseif ($type == 'phone') {
2073 $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
2074 } elseif ($type == 'price') {
2075 //$value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
2076 if ($value || $value == '0') {
2077 $value = price($value, 0, $outputlangs, 0, getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'), -1).' '.$outputlangs->getCurrencySymbol($conf->currency);
2078 }
2079 } elseif ($type == 'pricecy') {
2080 $currency = $conf->currency;
2081 if (!empty($value)) {
2082 // $value in memory is a php string like '0.01:EUR'
2083 $pricetmp = explode(':', $value);
2084 $currency = !empty($pricetmp[1]) ? $pricetmp[1] : $conf->currency;
2085 $value = $pricetmp[0];
2086 }
2087 if ($value || $value == '0') {
2088 $value = price($value, 0, $outputlangs, 0, getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'), -1, $currency);
2089 }
2090 } elseif ($type == 'select') {
2091 $valstr = (!empty($param['options'][$value]) ? $param['options'][$value] : '');
2092 if (($pos = strpos($valstr, "|")) !== false) {
2093 $valstr = substr($valstr, 0, $pos);
2094 }
2095 if ($langfile && $valstr) {
2096 $value = $outputlangs->trans($valstr);
2097 } else {
2098 $value = $valstr;
2099 }
2100 } elseif ($type == 'sellist') {
2101 $param_list = array_keys($param['options']);
2102 $InfoFieldList = explode(":", $param_list[0]);
2103
2104 $selectkey = "rowid";
2105 $keyList = 'rowid';
2106
2107 if (count($InfoFieldList) >= 3) {
2108 $selectkey = $InfoFieldList[2];
2109 $keyList = $InfoFieldList[2].' as rowid';
2110 }
2111
2112 $fields_label = explode('|', $InfoFieldList[1]);
2113 if (is_array($fields_label)) {
2114 $keyList .= ', ';
2115 $keyList .= implode(', ', $fields_label);
2116 }
2117
2118 $filter_categorie = false;
2119 if (count($InfoFieldList) > 5) {
2120 if ($InfoFieldList[0] == 'categorie') {
2121 $filter_categorie = true;
2122 }
2123 }
2124
2125 $sql = "SELECT ".$keyList;
2126 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
2127 if (!empty($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra.') !== false) {
2128 $sql .= ' as main';
2129 }
2130 if ($selectkey == 'rowid' && empty($value)) {
2131 $sql .= " WHERE ".$selectkey." = 0";
2132 } elseif ($selectkey == 'rowid') {
2133 $sql .= " WHERE ".$selectkey." = ".((int) $value);
2134 } else {
2135 $sql .= " WHERE ".$selectkey." = '".$this->db->escape($value)."'";
2136 }
2137
2138 //$sql.= ' AND entity = '.$conf->entity;
2139
2140 dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
2141 $resql = $this->db->query($sql);
2142 if ($resql) {
2143 if (!$filter_categorie) {
2144 $value = ''; // value was used, so now we reset it to use it to build final output
2145
2146 $obj = $this->db->fetch_object($resql);
2147
2148 // Several field into label (eq table:code|label:rowid)
2149 $fields_label = explode('|', $InfoFieldList[1]);
2150
2151 if (is_array($fields_label) && count($fields_label) > 1) {
2152 foreach ($fields_label as $field_toshow) {
2153 $translabel = '';
2154 if (!empty($obj->$field_toshow)) {
2155 $translabel = $outputlangs->trans($obj->$field_toshow);
2156
2157 if ($translabel != $obj->$field_toshow) {
2158 $value .= dol_trunc($translabel, 24) . ' ';
2159 } else {
2160 $value .= $obj->$field_toshow . ' ';
2161 }
2162 }
2163 }
2164 } else {
2165 $translabel = '';
2166 $tmppropname = $InfoFieldList[1];
2167 //$obj->$tmppropname = '';
2168 if (!empty(isset($obj->$tmppropname) ? $obj->$tmppropname : '')) {
2169 $translabel = $outputlangs->trans($obj->$tmppropname);
2170 }
2171 if ($translabel != (isset($obj->$tmppropname) ? $obj->$tmppropname : '')) {
2172 $value = dol_trunc($translabel, 18);
2173 } else {
2174 $value = isset($obj->$tmppropname) ? $obj->$tmppropname : '';
2175 }
2176 }
2177 } else {
2178 $toprint = array();
2179 $obj = $this->db->fetch_object($resql);
2180 if ($obj->rowid) {
2181 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
2182 $c = new Categorie($this->db);
2183 $result = $c->fetch($obj->rowid);
2184 if ($result > 0) {
2185 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
2186 foreach ($ways as $way) {
2187 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #bbb"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
2188 }
2189 }
2190 }
2191 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
2192 }
2193 } else {
2194 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
2195 }
2196 } elseif ($type == 'radio') {
2197 if (!isset($param['options'][$value])) {
2198 $outputlangs->load('errors');
2199 $value = $outputlangs->trans('ErrorNoValueForRadioType');
2200 } else {
2201 $value = $outputlangs->trans($param['options'][$value]);
2202 }
2203 } elseif ($type == 'checkbox') {
2204 $value_arr = explode(',', $value);
2205 $value = '';
2206 $toprint = array();
2207 if (is_array($value_arr)) {
2208 foreach ($value_arr as $keyval => $valueval) {
2209 if (!empty($valueval)) {
2210 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$param['options'][$valueval].'</li>';
2211 }
2212 }
2213 }
2214 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
2215 } elseif ($type == 'chkbxlst') {
2216 $value_arr = explode(',', $value);
2217
2218 $param_list = array_keys($param['options']);
2219 $InfoFieldList = explode(":", $param_list[0]);
2220
2221 $selectkey = "rowid";
2222 $keyList = 'rowid';
2223
2224 if (count($InfoFieldList) >= 3) {
2225 $selectkey = $InfoFieldList[2];
2226 $keyList = $InfoFieldList[2].' as rowid';
2227 }
2228
2229 $fields_label = explode('|', $InfoFieldList[1]);
2230 if (is_array($fields_label)) {
2231 $keyList .= ', ';
2232 $keyList .= implode(', ', $fields_label);
2233 }
2234
2235 $filter_categorie = false;
2236 if (count($InfoFieldList) > 5) {
2237 if ($InfoFieldList[0] == 'categorie') {
2238 $filter_categorie = true;
2239 }
2240 }
2241
2242 $sql = "SELECT ".$keyList;
2243 $sql .= " FROM ".$this->db->prefix().$InfoFieldList[0];
2244 if (strpos($InfoFieldList[4], 'extra.') !== false) {
2245 $sql .= ' as main';
2246 }
2247 // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
2248 // $sql.= ' AND entity = '.$conf->entity;
2249
2250 dol_syslog(get_class($this).':showOutputField:$type=chkbxlst', LOG_DEBUG);
2251 $resql = $this->db->query($sql);
2252 if ($resql) {
2253 if (!$filter_categorie) {
2254 $value = ''; // value was used, so now we reset it to use it to build final output
2255 $toprint = array();
2256 while ($obj = $this->db->fetch_object($resql)) {
2257 // Several field into label (eq table:code|label:rowid)
2258 $fields_label = explode('|', $InfoFieldList[1]);
2259 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
2260 if (is_array($fields_label) && count($fields_label) > 1) {
2261 $label = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">';
2262 foreach ($fields_label as $field_toshow) {
2263 $translabel = '';
2264 if (!empty($obj->$field_toshow)) {
2265 $translabel = $outputlangs->trans($obj->$field_toshow);
2266 }
2267 if ($translabel != $field_toshow) {
2268 $label .= ' '.dol_trunc($translabel, 18);
2269 } else {
2270 $label .= ' '.$obj->$field_toshow;
2271 }
2272 }
2273 $label .= '</li>';
2274 $toprint[] = $label;
2275 } else {
2276 $translabel = '';
2277 if (!empty($obj->{$InfoFieldList[1]})) {
2278 $translabel = $outputlangs->trans($obj->{$InfoFieldList[1]});
2279 }
2280 if ($translabel != $obj->{$InfoFieldList[1]}) {
2281 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.dol_trunc($translabel, 18).'</li>';
2282 } else {
2283 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$obj->{$InfoFieldList[1]}.'</li>';
2284 }
2285 }
2286 }
2287 }
2288 } else {
2289 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2290
2291 $toprint = array();
2292 while ($obj = $this->db->fetch_object($resql)) {
2293 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
2294 $c = new Categorie($this->db);
2295 $c->fetch($obj->rowid);
2296 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
2297 foreach ($ways as $way) {
2298 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"'.($c->color ? ' style="background: #'.$c->color.';"' : ' style="background: #bbb"').'>'.img_object('', 'category').' '.$way.'</li>';
2299 }
2300 }
2301 }
2302 }
2303 if (!empty($toprint)) {
2304 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
2305 }
2306 } else {
2307 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
2308 }
2309 } elseif ($type == 'link') {
2310 $out = '';
2311
2312 // Only if something to display (perf)
2313 if ($value) { // If we have -1 here, pb is into insert, not into output (fix insert instead of changing code here to compensate)
2314 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
2315
2316 $InfoFieldList = explode(":", $param_list[0]);
2317 $classname = $InfoFieldList[0];
2318 $classpath = $InfoFieldList[1];
2319 if (!empty($classpath)) {
2320 dol_include_once($InfoFieldList[1]);
2321 if ($classname && class_exists($classname)) {
2322 $tmpobject = new $classname($this->db);
2323 '@phan-var-force CommonObject $tmpobject';
2324 $tmpobject->fetch($value);
2325
2326 if (get_class($tmpobject) == 'Categorie') {
2327 // For category object, rendering must use the same method than the one deinfed into showCategories()
2328 $color = $tmpobject->color;
2329 $sfortag = '<span class="noborderoncategories"' . ($color ? ' style="background: #' . $color . ';"' : ' style="background: #bbb"') . '>';
2330 $sfortag .= $tmpobject->getNomUrl(3);
2331 $sfortag .= '</span>';
2332 $value = $sfortag;
2333 } else {
2334 $value = $tmpobject->getNomUrl(3);
2335 }
2336 }
2337 } else {
2338 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
2339 return 'Error bad setup of extrafield';
2340 }
2341 }
2342 } elseif ($type == 'point') {
2343 if (!empty($value)) {
2344 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
2345 $dolgeophp = new DolGeoPHP($this->db);
2346 $value = $dolgeophp->getXYString($value);
2347 } else {
2348 $value = '';
2349 }
2350 } elseif (in_array($type, ['multipts', 'linestrg', 'polygon'])) {
2351 if (!empty($value)) {
2352 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
2353 $dolgeophp = new DolGeoPHP($this->db);
2354 $value = $dolgeophp->getPointString($value);
2355 } else {
2356 $value = '';
2357 }
2358 } elseif ($type == 'text') {
2359 $value = '<div class="'.($cssview ? $cssview : 'shortmessagecut').'">'.dol_htmlentitiesbr($value).'</div>';
2360 } elseif ($type == 'html') {
2361 $value = dol_htmlentitiesbr($value);
2362 } elseif ($type == 'password') {
2363 $value = dol_trunc(preg_replace('/./i', '*', $value), 8, 'right', 'UTF-8', 1);
2364 } elseif ($type == 'stars') {
2365 $objectid = (int) $object->id;
2366 if ($showValueInsteadOfInputField == 1) {
2367 $value = '<span style="display:none;" id="'.$key.$object->id.'">'.dol_escape_htmltag($value).'</span>';
2368 } else {
2369 $value = '<input type="hidden" class="flat" name="'.$key.'" id="'.$key.$objectid.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').'>';
2370 }
2371
2372 $value .= '<div class="star-selection" id="'.$key.$objectid.'_selection">';
2373 $i = 1;
2374 while ($i <= $size) {
2375 $value .= '<span class="star" data-value="'.$i.'">'.img_picto('', 'fontawesome_star_fas').'</span>';
2376 $i++;
2377 }
2378 $value .= '</div>';
2379 $value .= '<script>
2380 $(document).ready(function() {
2381 let container = $("#'.$key.$objectid.'_selection");
2382 let selectedStars = parseInt($("#'.$key.$objectid.'").val() || $("#'.$key.$objectid.'").text()) || 0;
2383 container.find(".star").each(function() {
2384 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2385 });';
2386 if ($showValueInsteadOfInputField == 0) {
2387 $value .= '
2388 container.find(".star").on("mouseover", function() {
2389 let selectedStar = $(this).data("value");
2390 container.find(".star").each(function() {
2391 $(this).toggleClass("active", $(this).data("value") <= selectedStar);
2392 });
2393 });
2394 container.on("mouseout", function() {
2395 container.find(".star").each(function() {
2396 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2397 });
2398 });
2399 container.find(".star").off("click").on("click", function() {
2400 selectedStars = $(this).data("value");
2401 if (selectedStars == 1 && $("#'.$key.$objectid.'").val() == 1) {
2402 selectedStars = 0;
2403 }
2404 container.find("#'.$key.$objectid.'").val(selectedStars);
2405 container.find(".star").each(function() {
2406 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2407 });
2408 $.ajax({
2409 url: "'.DOL_URL_ROOT.'/core/ajax/editextrafield.php",
2410 method: "POST",
2411 data: {
2412 objectType: \''.dol_escape_js($extrafieldsobjectkey).'\',
2413 objectId: '.((int) $objectid).',
2414 field: \''.dol_escape_js($key).'\',
2415 value: selectedStars,
2416 token: "'.newToken().'"
2417 },
2418 success: function(response) {
2419 var res = JSON.parse(response);
2420 console[res.status === "success" ? "log" : "error"](res.message);
2421 },
2422 error: function(xhr, status, error) {
2423 console.log("Ajax request failed while updating '.$key.':", error);
2424 }
2425 });
2426 });';
2427 }
2428 $value .= '
2429 });
2430 </script>';
2431 } else {
2432 $showsize = round((float) $size);
2433 if ($showsize > 48) {
2434 $showsize = 48;
2435 }
2436 }
2437
2438 //print $type.'-'.$size;
2439 $out = $value;
2440
2441 return $out;
2442 }
2443
2451 public function getAlignFlag($key, $extrafieldsobjectkey = '')
2452 {
2453 $type = 'varchar';
2454 if (!empty($extrafieldsobjectkey)) {
2455 $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
2456 }
2457
2458 $cssstring = '';
2459
2460 if (in_array($type, array('date', 'datetime', 'datetimegmt',))) {
2461 $cssstring = "center";
2462 } elseif (in_array($type, array('int', 'price', 'double', 'duration'))) {
2463 $cssstring = "right";
2464 } elseif (in_array($type, array('boolean', 'radio', 'checkbox', 'ip', 'icon'))) {
2465 $cssstring = "center";
2466 }
2467
2468 if (!empty($this->attributes[$extrafieldsobjectkey]['csslist'][$key])) {
2469 $cssstring .= ($cssstring ? ' ' : '').$this->attributes[$extrafieldsobjectkey]['csslist'][$key];
2470 } else {
2471 if (in_array($type, array('ip'))) {
2472 $cssstring .= ($cssstring ? ' ' : '').'tdoverflowmax150';
2473 }
2474 }
2475
2476 return $cssstring;
2477 }
2478
2489 public function showSeparator($key, $object, $colspan = 2, $display_type = 'card', $mode = '')
2490 {
2491 global $conf, $langs;
2492
2493 $tagtype = 'tr';
2494 $tagtype_dyn = 'td';
2495
2496 if ($display_type == 'line') {
2497 $tagtype = 'div';
2498 $tagtype_dyn = 'span';
2499 $colspan = 0;
2500 }
2501
2502 $extrafield_param = $this->attributes[$object->table_element]['param'][$key];
2503 $extrafield_param_list = array();
2504 if (!empty($extrafield_param) && is_array($extrafield_param)) {
2505 $extrafield_param_list = array_keys($extrafield_param['options']);
2506 }
2507
2508 // Set $extrafield_collapse_display_value (do we have to collapse/expand the group after the separator)
2509 $extrafield_collapse_display_value = -1;
2510 $expand_display = false;
2511 if (is_array($extrafield_param_list) && count($extrafield_param_list) > 0) {
2512 $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
2513 $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));
2514 }
2515 $disabledcookiewrite = 0;
2516 if ($mode == 'create') {
2517 // On create mode, force separator group to not be collapsible
2518 $extrafield_collapse_display_value = 1;
2519 $expand_display = true; // We force group to be shown expanded
2520 $disabledcookiewrite = 1; // We keep status of group unchanged into the cookie
2521 }
2522
2523 $out = '<'.$tagtype.' id="trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'" class="trextrafieldseparator trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'">';
2524 $out .= '<'.$tagtype_dyn.' '.(!empty($colspan) ? 'colspan="' . $colspan . '"' : '').'>';
2525 // Some js code will be injected here to manage the collapsing of extrafields
2526 // Output the picto
2527 $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>';
2528 $out .= '&nbsp;';
2529 $out .= '<strong>';
2530 $out .= $langs->trans($this->attributes[$object->table_element]['label'][$key]);
2531 $out .= '</strong>';
2532 $out .= '</'.$tagtype_dyn.'>';
2533 $out .= '</'.$tagtype.'>';
2534
2535 $collapse_group = $key.(!empty($object->id) ? '_'.$object->id : '');
2536 //$extrafields_collapse_num = $this->attributes[$object->table_element]['pos'][$key].(!empty($object->id)?'_'.$object->id:'');
2537
2538 if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
2539 // Set the collapse_display status to cookie in priority or if ignorecollapsesetup is 1, if cookie and ignorecollapsesetup not defined, use the setup.
2540 $this->expand_display[$collapse_group] = $expand_display;
2541
2542 if (!empty($conf->use_javascript_ajax)) {
2543 $out .= '<!-- Add js script to manage the collapse/uncollapse of extrafields separators '.$key.' -->'."\n";
2544 $out .= '<script nonce="'.getNonce().'" type="text/javascript">'."\n";
2545 $out .= 'jQuery(document).ready(function(){'."\n";
2546 if (empty($disabledcookiewrite)) {
2547 if (!$expand_display) {
2548 $out .= ' console.log("Inject js for the collapsing of extrafield '.$key.' - hide");'."\n";
2549 $out .= ' jQuery(".trextrafields_collapse'.$collapse_group.'").hide();'."\n";
2550 } else {
2551 $out .= ' console.log("Inject js for collapsing of extrafield '.$key.' - keep visible and set cookie");'."\n";
2552 $out .= ' document.cookie = "DOLUSER_COLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=1; path='.$_SERVER["PHP_SELF"].'"'."\n";
2553 }
2554 }
2555 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'").click(function(){'."\n";
2556 $out .= ' console.log("We click on collapse/uncollapse to hide/show .trextrafields_collapse'.$collapse_group.'");'."\n";
2557 $out .= ' jQuery(".trextrafields_collapse'.$collapse_group.'").toggle(100, function(){'."\n";
2558 $out .= ' if (jQuery(".trextrafields_collapse'.$collapse_group.'").is(":hidden")) {'."\n";
2559 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').' '.$tagtype_dyn.' span").addClass("fa-plus-square").removeClass("fa-minus-square");'."\n";
2560 $out .= ' document.cookie = "DOLUSER_COLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=0; path='.$_SERVER["PHP_SELF"].'"'."\n";
2561 $out .= ' } else {'."\n";
2562 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').' '.$tagtype_dyn.' span").addClass("fa-minus-square").removeClass("fa-plus-square");'."\n";
2563 $out .= ' document.cookie = "DOLUSER_COLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=1; path='.$_SERVER["PHP_SELF"].'"'."\n";
2564 $out .= ' }'."\n";
2565 $out .= ' });'."\n";
2566 $out .= ' });'."\n";
2567 $out .= '});'."\n";
2568 $out .= '</script>'."\n";
2569 }
2570 } else {
2571 $this->expand_display[$collapse_group] = 1;
2572 }
2573
2574 return $out;
2575 }
2576
2588 public function setOptionalsFromPost($extralabels, &$object, $onlykey = '', $todefaultifmissing = 0)
2589 {
2590 global $langs;
2591
2592 $nofillrequired = 0; // For error when required field left blank
2593 $error_field_required = array();
2594
2595 if (isset($this->attributes[$object->table_element]['label']) && is_array($this->attributes[$object->table_element]['label'])) {
2596 $extralabels = $this->attributes[$object->table_element]['label'];
2597 }
2598
2599 if (is_array($extralabels)) {
2600 // Get extra fields
2601 foreach ($extralabels as $key => $value) {
2602 if (!empty($onlykey) && $onlykey != '@GETPOSTISSET' && $key != $onlykey) {
2603 continue;
2604 }
2605
2606 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')))) {
2607 //when unticking boolean field, it's not set in POST
2608 continue;
2609 }
2610
2611 $key_type = $this->attributes[$object->table_element]['type'][$key];
2612 if ($key_type == 'separate') {
2613 continue;
2614 }
2615
2616 $enabled = 1;
2617 if (isset($this->attributes[$object->table_element]['enabled'][$key])) { // 'enabled' is often a condition on module enabled or not
2618 $enabled = (int) dol_eval((string) $this->attributes[$object->table_element]['enabled'][$key], 1, 1, '2');
2619 }
2620
2621 $visibility = 1;
2622 if (isset($this->attributes[$object->table_element]['list'][$key])) { // 'list' is option for visibility
2623 $visibility = (int) dol_eval($this->attributes[$object->table_element]['list'][$key], 1, 1, '2');
2624 }
2625
2626 $perms = 1;
2627 if (isset($this->attributes[$object->table_element]['perms'][$key])) {
2628 $perms = (int) dol_eval($this->attributes[$object->table_element]['perms'][$key], 1, 1, '2');
2629 }
2630 if (empty($enabled)
2631 || (
2632 $onlykey === '@GETPOSTISSET'
2633 && in_array($this->attributes[$object->table_element]['type'][$key], array('boolean', 'checkbox', 'chkbxlst'))
2634 && in_array(abs($enabled), array(2, 5))
2635 && ! GETPOSTISSET('options_' . $key) // Update hidden checkboxes and multiselect only if they are provided
2636 )
2637 ) {
2638 continue;
2639 }
2640
2641 $visibility_abs = abs($visibility);
2642 // not modify if extra field is not in update form (0 : never, 2 or -2 : list only, 5 or - 5 : list and view only)
2643 if (empty($visibility_abs) || $visibility_abs == 2 || $visibility_abs == 5) {
2644 continue;
2645 }
2646 if (empty($perms)) {
2647 continue;
2648 }
2649
2650 if ($this->attributes[$object->table_element]['required'][$key]) { // Value is required
2651 // Check if functionally empty without using GETPOST (depending on the type of extrafield, a
2652 // technically non-empty value may be treated as empty functionally).
2653 // value can be alpha, int, array, etc...
2654 $v = $_POST["options_".$key] ?? null;
2655 $type = $this->attributes[$object->table_element]['type'][$key];
2656 if (self::isEmptyValue($v, $type)) {
2657 //print 'ccc'.$value.'-'.$this->attributes[$object->table_element]['required'][$key];
2658
2659 // 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.
2660
2661 $nofillrequired++;
2662 if (!empty($this->attributes[$object->table_element]['langfile'][$key])) {
2663 $langs->load($this->attributes[$object->table_element]['langfile'][$key]);
2664 }
2665 $error_field_required[$key] = $langs->transnoentitiesnoconv($value);
2666 }
2667 }
2668
2669 if (in_array($key_type, array('date'))) {
2670 // Clean parameters
2671 $value_key = dol_mktime(12, 0, 0, GETPOSTINT("options_".$key."month"), GETPOSTINT("options_".$key."day"), GETPOSTINT("options_".$key."year"));
2672 } elseif (in_array($key_type, array('datetime'))) {
2673 // Clean parameters
2674 $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');
2675 } elseif (in_array($key_type, array('datetimegmt'))) {
2676 // Clean parameters
2677 $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');
2678 } elseif (in_array($key_type, array('duration'))) {
2679 $value_hours = GETPOSTINT("options_" . $key . "hour");
2680 $value_minutes = GETPOSTINT("options_" . $key . "minute");
2681 $value_key = $value_hours * 3600 + $value_minutes * 60;
2682 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
2683 $value_arr = GETPOST("options_".$key, 'array'); // check if an array
2684 if (!empty($value_arr)) {
2685 $value_key = implode(',', $value_arr);
2686 } else {
2687 $value_key = '';
2688 }
2689 } elseif (in_array($key_type, array('price', 'double'))) {
2690 $value_arr = GETPOST("options_".$key, 'alpha');
2691 $value_key = price2num($value_arr);
2692 } elseif (in_array($key_type, array('pricecy', 'double'))) {
2693 $value_key = price2num(GETPOST("options_".$key, 'alpha')).':'.GETPOST("options_".$key."currency_id", 'alpha');
2694 } elseif (in_array($key_type, array('html'))) {
2695 $value_key = GETPOST("options_".$key, 'restricthtml');
2696 } elseif (in_array($key_type, ['point', 'multipts', 'linestrg', 'polygon'])) {
2697 // construct point
2698 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
2699 $geojson = GETPOST("options_".$key, 'restricthtml');
2700 if ($geojson != '{}') {
2701 $dolgeophp = new DolGeoPHP($this->db);
2702 $value_key = $dolgeophp->getWkt($geojson);
2703 } else {
2704 $value_key = '';
2705 }
2706 } elseif (in_array($key_type, array('text'))) {
2707 $label_security_check = 'alphanohtml';
2708 // by default 'alphanohtml' (better security); hidden conf MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML allows basic html
2709 if (getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_REF_LABELS')) {
2710 $label_security_check = 'nohtml';
2711 } else {
2712 $label_security_check = !getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML') ? 'alphanohtml' : 'restricthtml';
2713 }
2714 $value_key = GETPOST("options_".$key, $label_security_check);
2715 } else {
2716 $value_key = GETPOST("options_".$key);
2717 if (in_array($key_type, array('link')) && $value_key == '-1') {
2718 $value_key = '';
2719 }
2720 }
2721
2722 if (!empty($error_field_required[$key]) && $todefaultifmissing) {
2723 // Value is required but we have a default value and we asked to set empty value to the default value
2724 if (!empty($this->attributes[$object->table_element]['default']) && !is_null($this->attributes[$object->table_element]['default'][$key])) {
2725 $value_key = $this->attributes[$object->table_element]['default'][$key];
2726 unset($error_field_required[$key]);
2727 $nofillrequired--;
2728 }
2729 }
2730
2731 $object->array_options["options_".$key] = $value_key;
2732 }
2733
2734 if ($nofillrequired) {
2735 $langs->load('errors');
2736 $this->error = $langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required);
2737 setEventMessages($this->error, null, 'errors');
2738 return -1;
2739 } else {
2740 return 1;
2741 }
2742 } else {
2743 return 0;
2744 }
2745 }
2746
2755 public function getOptionalsFromPost($extrafieldsobjectkey, $keysuffix = '', $keyprefix = '')
2756 {
2757 global $_POST;
2758
2759 if (is_string($extrafieldsobjectkey) && !empty($this->attributes[$extrafieldsobjectkey]['label']) && is_array($this->attributes[$extrafieldsobjectkey]['label'])) {
2760 $extralabels = $this->attributes[$extrafieldsobjectkey]['label'];
2761 } else {
2762 $extralabels = $extrafieldsobjectkey;
2763 }
2764
2765 if (is_array($extralabels)) {
2766 $array_options = array();
2767
2768 // Get extra fields
2769 foreach ($extralabels as $key => $value) {
2770 $key_type = '';
2771 if (is_string($extrafieldsobjectkey)) {
2772 $key_type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
2773 }
2774
2775 if (in_array($key_type, array('date'))) {
2776 $dateparamname_start = $keyprefix . 'options_' . $key . $keysuffix . '_start';
2777 $dateparamname_end = $keyprefix . 'options_' . $key . $keysuffix . '_end';
2778
2779 if (GETPOST($dateparamname_start . 'year') || GETPOST($dateparamname_end . 'year')) {
2780 $value_key = array();
2781 // values provided as a component year, month, day, etc.
2782 if (GETPOST($dateparamname_start . 'year')) {
2783 $value_key['start'] = dol_mktime(0, 0, 0, GETPOSTINT($dateparamname_start . 'month'), GETPOSTINT($dateparamname_start . 'day'), GETPOSTINT($dateparamname_start . 'year'));
2784 }
2785 if (GETPOST($dateparamname_end . 'year')) {
2786 $value_key['end'] = dol_mktime(23, 59, 59, GETPOSTINT($dateparamname_end . 'month'), GETPOSTINT($dateparamname_end . 'day'), GETPOSTINT($dateparamname_end . 'year'));
2787 }
2788 } elseif (GETPOST($keyprefix."options_".$key.$keysuffix."year")) {
2789 // Clean parameters
2790 $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"));
2791 } else {
2792 continue; // Value was not provided, we should not set it.
2793 }
2794 } elseif (in_array($key_type, array('datetime', 'datetimegmt'))) {
2795 $dateparamname_start = $keyprefix . 'options_' . $key . $keysuffix . '_start';
2796 $dateparamname_end = $keyprefix . 'options_' . $key . $keysuffix . '_end';
2797
2798 if (GETPOST($dateparamname_start . 'year') || GETPOST($dateparamname_end . 'year')) {
2799 // values provided as a date pair (start date + end date), each date being broken down as year, month, day, etc.
2800 $dateparamname_start_hour = GETPOSTINT($dateparamname_start . 'hour') != '-1' ? GETPOSTINT($dateparamname_start . 'hour') : '00';
2801 $dateparamname_start_min = GETPOSTINT($dateparamname_start . 'min') != '-1' ? GETPOSTINT($dateparamname_start . 'min') : '00';
2802 $dateparamname_start_sec = GETPOSTINT($dateparamname_start . 'sec') != '-1' ? GETPOSTINT($dateparamname_start . 'sec') : '00';
2803 $dateparamname_end_hour = GETPOSTINT($dateparamname_end . 'hour') != '-1' ? GETPOSTINT($dateparamname_end . 'hour') : '23';
2804 $dateparamname_end_min = GETPOSTINT($dateparamname_end . 'min') != '-1' ? GETPOSTINT($dateparamname_end . 'min') : '59';
2805 $dateparamname_end_sec = GETPOSTINT($dateparamname_end . 'sec') != '-1' ? GETPOSTINT($dateparamname_end . 'sec') : '59';
2806 if ($key_type == 'datetimegmt') {
2807 $value_key = array(
2808 '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'),
2809 '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')
2810 );
2811 } else {
2812 $value_key = array(
2813 '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'),
2814 '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')
2815 );
2816 }
2817 } elseif (GETPOST($keyprefix."options_".$key.$keysuffix."year")) {
2818 // Clean parameters
2819 if ($key_type == 'datetimegmt') {
2820 $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');
2821 } else {
2822 $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');
2823 }
2824 } else {
2825 continue; // Value was not provided, we should not set it.
2826 }
2827 } elseif ($key_type == 'select') {
2828 // to detect if we are in search context
2829 if (GETPOSTISARRAY($keyprefix."options_".$key.$keysuffix)) {
2830 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix, 'array:aZ09');
2831 // Make sure we get an array even if there's only one selected
2832 $value_arr = (array) $value_arr;
2833 $value_key = implode(',', $value_arr);
2834 } else {
2835 $value_key = GETPOST($keyprefix."options_".$key.$keysuffix);
2836 }
2837 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
2838 // We test on a hidden field named "..._multiselect" that is always set to 1 if param is in form so
2839 // when nothing is provided we can make a difference between noparam in the form and param was set to nothing.
2840 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix.'_multiselect')) {
2841 continue; // Value was not provided, we should not set it.
2842 }
2843 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
2844 // Make sure we get an array even if there's only one checkbox
2845 $value_arr = (array) $value_arr;
2846 $value_key = implode(',', $value_arr);
2847 } elseif (in_array($key_type, array('price', 'double', 'int'))) {
2848 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
2849 continue; // Value was not provided, we should not set it.
2850 }
2851 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
2852 if ($keyprefix != 'search_') { // If value is for a search, we must keep complex string like '>100 <=150'
2853 $value_key = price2num($value_arr);
2854 } else {
2855 $value_key = $value_arr;
2856 }
2857 } elseif (in_array($key_type, array('boolean'))) {
2858 // We test on a hidden field named "..._boolean" that is always set to 1 if param is in form so
2859 // when nothing is provided we can make a difference between noparam in the form and param was set to nothing.
2860 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix."_boolean")) {
2861 $value_key = '';
2862 } else {
2863 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
2864 $value_key = $value_arr;
2865 }
2866 } elseif (in_array($key_type, array('html'))) {
2867 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
2868 continue; // Value was not provided, we should not set it.
2869 }
2870 $value_key = dol_htmlcleanlastbr(GETPOST($keyprefix."options_".$key.$keysuffix, 'restricthtml'));
2871 } else {
2872 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
2873 continue; // Value was not provided, we should not set it.
2874 }
2875
2876 $value_key = GETPOST($keyprefix."options_".$key.$keysuffix);
2877 if ($value_key === '') {
2878 $value_key = null;
2879 }
2880 }
2881
2882 $array_options[$keyprefix."options_".$key] = $value_key; // No keyprefix here. keyprefix is used only for read.
2883 }
2884
2885 return $array_options;
2886 }
2887
2888 return 0;
2889 }
2890
2896 public static function getListOfTypesLabels()
2897 {
2898 global $langs;
2899
2900 $arraytype2label = array('');
2901
2902 $tmptype2label = ExtraFields::$type2label;
2903 foreach ($tmptype2label as $key => $val) {
2904 $arraytype2label[$key] = $langs->transnoentitiesnoconv($val);
2905 }
2906
2907 if (!getDolGlobalString('MAIN_USE_EXTRAFIELDS_ICON')) {
2908 unset($arraytype2label['icon']);
2909 }
2910 if (!getDolGlobalString('MAIN_USE_GEOPHP')) {
2911 unset($arraytype2label['point']);
2912 unset($arraytype2label['multipts']);
2913 unset($arraytype2label['linestrg']);
2914 unset($arraytype2label['polygon']);
2915 }
2916
2917 return $arraytype2label;
2918 }
2919
2927 public static function isEmptyValue($v, string $type)
2928 {
2929 if ($v === null || $v === '') {
2930 return true;
2931 }
2932 if (is_array($v) || $type == 'select') {
2933 return empty($v);
2934 }
2935 if ($type == 'link') {
2936 return ($v == '-1');
2937 }
2938 if ($type == 'sellist') {
2939 return ($v == '0');
2940 }
2941 return empty($v); // Note empty('0') is also true, tested 7.0 up to 8.3.12
2942 }
2943}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:66
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition ajax.lib.php:463
$c
Definition line.php:327
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.
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())
Modify description of personalized attribute This is a private method.
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())
Add a new extra field parameter.
getOptionalsFromPost($extrafieldsobjectkey, $keysuffix='', $keyprefix='')
return array_options array of data of extrafields value of object sent by a search form
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())
Add description of a new optional attribute.
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.
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())
Modify type of a personalized attribute.
static getListOfTypesLabels()
Return array with all possible types and labels of extrafields.
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())
Update an existing extra field parameter.
showInputField($key, $value, $moreparam='', $keysuffix='', $keyprefix='', $morecss='', $object=0, $extrafieldsobjectkey='', $mode=0)
Return HTML string to put an input field into a page Code very similar with showInputField of common ...
__construct($db)
Constructor.
delete_label($attrname, $elementtype='')
Delete description of an optional attribute.
static isEmptyValue($v, string $type)
Return if a value is "empty" for a mandatory vision.
showOutputField($key, $value, $moreparam='', $extrafieldsobjectkey='', $outputlangs=null, $object=null)
Return HTML string to put an output field into a page.
Class to manage generation of HTML components Only common components must be here.
Class to manage a Leaflet map width geometrics objects.
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:171
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:244
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
dol_print_ip($ip, $mode=0)
Return an IP formatted to be shown on screen.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_print_phone($phone, $countrycode='', $cid=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.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_print_url($url, $target='_blank', $max=32, $withpicto=0, $morecss='')
Show Url link.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
dol_now($mode='auto')
Return date for now.
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.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
dol_print_email($email, $cid=0, $socid=0, $addlink=0, $max=64, $showinvalid=1, $withpicto=0, $morecss='paddingrightonly')
Show EMail link formatted for HTML output.
yn($yesno, $format=1, $color=0)
Return yes or no in current language.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
This function is called to encode a string into a HTML string but differs from htmlentities because a...
jsonOrUnserialize($stringtodecode)
Decode an encode string.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:150