dolibarr 21.0.3
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) > 3 && !empty($InfoFieldList[3])) {
1425 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
1426 $keyList .= ', '.$parentField;
1427 }
1428 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
1429 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1430 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
1431 } else {
1432 $keyList = $InfoFieldList[2].' as rowid';
1433 }
1434 }
1435
1436 $filter_categorie = false;
1437 if (count($InfoFieldList) > 5) {
1438 if ($InfoFieldList[0] == 'categorie') {
1439 $filter_categorie = true;
1440 }
1441 }
1442
1443 if (!$filter_categorie) {
1444 $fields_label = explode('|', $InfoFieldList[1]);
1445 if (is_array($fields_label)) {
1446 $keyList .= ', ';
1447 $keyList .= implode(', ', $fields_label);
1448 }
1449
1450 $sqlwhere = '';
1451 $sql = "SELECT ".$keyList;
1452 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
1453
1454 // Add filter from 4th field
1455 if (!empty($InfoFieldList[4])) {
1456 // can use current entity filter
1457 if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
1458 $InfoFieldList[4] = str_replace('$ENTITY$', (string) $conf->entity, $InfoFieldList[4]);
1459 }
1460 // can use SELECT request
1461 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
1462 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
1463 }
1464
1465 // current object id can be use into filter
1466 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
1467 $InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
1468 } else {
1469 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
1470 }
1471
1472 // We have to join on extrafield table
1473 $errstr = '';
1474 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1475 $sql .= ' as main, '.$this->db->sanitize($this->db->prefix().$InfoFieldList[0]).'_extrafields as extra';
1476 $sqlwhere .= " WHERE extra.fk_object = main.".$this->db->sanitize($InfoFieldList[2]);
1477 $sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1478 } else {
1479 //print $InfoFieldList[4];
1480 $sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1481 }
1482 } else {
1483 $sqlwhere .= ' WHERE 1=1';
1484 }
1485
1486 // Add Usf filter on second line
1487 /*
1488 if ($Usf) {
1489 $errorstr = '';
1490 $sqlusf .= forgeSQLFromUniversalSearchCriteria($Usf, $errorstr);
1491 if (!$errorstr) {
1492 $sqlwhere .= $sqlusf;
1493 } else {
1494 $sqlwhere .= " AND invalid_usf_filter_of_extrafield";
1495 }
1496 }
1497 */
1498
1499 // Some tables may have field, some other not. For the moment we disable it.
1500 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
1501 $sqlwhere .= ' AND entity = '.((int) $conf->entity);
1502 }
1503 $sql .= $sqlwhere;
1504
1505 $sql .= $this->db->order(implode(',', $fields_label));
1506 //print $sql;
1507
1508 dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
1509 $resql = $this->db->query($sql);
1510 if ($resql) {
1511 $out .= '<option value="0">&nbsp;</option>';
1512 $num = $this->db->num_rows($resql);
1513 $i = 0;
1514 while ($i < $num) {
1515 $labeltoshow = '';
1516 $obj = $this->db->fetch_object($resql);
1517
1518 // Several field into label (eq table:code|label:rowid)
1519 $notrans = false;
1520 $fields_label = explode('|', $InfoFieldList[1]);
1521 if (is_array($fields_label) && count($fields_label) > 1) {
1522 $notrans = true;
1523 foreach ($fields_label as $field_toshow) {
1524 $labeltoshow .= $obj->$field_toshow.' ';
1525 }
1526 } else {
1527 $labeltoshow = $obj->{$InfoFieldList[1]};
1528 }
1529
1530 if ($value == $obj->rowid) {
1531 if (!$notrans) {
1532 foreach ($fields_label as $field_toshow) {
1533 $translabel = $langs->trans($obj->$field_toshow);
1534 $labeltoshow = $translabel.' ';
1535 }
1536 }
1537 $out .= '<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
1538 } else {
1539 if (!$notrans) {
1540 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
1541 $labeltoshow = $translabel;
1542 }
1543 if (empty($labeltoshow)) {
1544 $labeltoshow = '(not defined)';
1545 }
1546
1547 if (!empty($InfoFieldList[3]) && $parentField) {
1548 $parent = $parentName.':'.$obj->{$parentField};
1549 }
1550
1551 $out .= '<option value="'.$obj->rowid.'"';
1552 $out .= ($value == $obj->rowid ? ' selected' : '');
1553 $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
1554 $out .= '>'.$labeltoshow.'</option>';
1555 }
1556
1557 $i++;
1558 }
1559 $this->db->free($resql);
1560 } else {
1561 print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
1562 }
1563 } else {
1564 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
1565 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
1566 $out .= '<option value="0">&nbsp;</option>';
1567 if (is_array($data)) {
1568 foreach ($data as $data_key => $data_value) {
1569 $out .= '<option value="'.$data_key.'"';
1570 $out .= ($value == $data_key ? ' selected' : '');
1571 $out .= '>'.$data_value.'</option>';
1572 }
1573 }
1574 }
1575 }
1576 $out .= '</select>';
1577 } elseif ($type == 'checkbox') {
1578 $value_arr = $value;
1579 if (!is_array($value)) {
1580 $value_arr = explode(',', $value);
1581 }
1582 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ? null : $param['options']), $value_arr, 0, 0, '', 0, '100%');
1583 } elseif ($type == 'radio') {
1584 $out = '';
1585 foreach ($param['options'] as $keyopt => $val) {
1586 $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
1587 $out .= ' value="'.$keyopt.'"';
1588 $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
1589 $out .= ($value == $keyopt ? 'checked' : '');
1590 $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$langs->trans($val).'</label><br>';
1591 }
1592 } elseif ($type == 'chkbxlst') { // List of values selected from a table (n choices)
1593 if (is_array($value)) {
1594 $value_arr = $value;
1595 } else {
1596 $value_arr = explode(',', $value);
1597 }
1598
1599 if (is_array($param['options'])) {
1600 $tmpparamoptions = array_keys($param['options']);
1601 $paramoptions = preg_split('/[\r\n]+/', $tmpparamoptions[0]);
1602
1603 $InfoFieldList = explode(":", $paramoptions[0], 5);
1604 // 0 : tableName
1605 // 1 : label field name
1606 // 2 : key fields name (if different of rowid)
1607 // optional parameters...
1608 // 3 : key field parent (for dependent lists). How this is used ?
1609 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value. Or use USF on the second line.
1610 // 5 : string category type. This replace the filter.
1611 // 6 : ids categories list separated by comma for category root. This replace the filter.
1612 // 7 : sort field (not used here but used into format for commobject)
1613
1614 // If there is a filter, we extract it by taking all content inside parenthesis.
1615 if (! empty($InfoFieldList[4])) {
1616 $pos = 0;
1617 $parenthesisopen = 0;
1618 while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
1619 if (substr($InfoFieldList[4], $pos, 1) == '(') {
1620 $parenthesisopen++;
1621 }
1622 if (substr($InfoFieldList[4], $pos, 1) == ')') {
1623 $parenthesisopen--;
1624 }
1625 $pos++;
1626 }
1627 $tmpbefore = substr($InfoFieldList[4], 0, $pos);
1628 $tmpafter = substr($InfoFieldList[4], $pos + 1);
1629 //var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
1630 $InfoFieldList[4] = $tmpbefore;
1631 if ($tmpafter !== '') {
1632 $InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
1633 }
1634
1635 // Fix better compatibility with some old extrafield syntax filter "(field=123)"
1636 $reg = array();
1637 if (preg_match('/^\‍(?([a-z0-9]+)([=<>]+)(\d+)\‍)?$/i', $InfoFieldList[4], $reg)) {
1638 $InfoFieldList[4] = '('.$reg[1].':'.$reg[2].':'.$reg[3].')';
1639 }
1640
1641 //var_dump($InfoFieldList);
1642 }
1643
1644 //$Usf = empty($paramoptions[1]) ? '' :$paramoptions[1];
1645
1646 $parentName = '';
1647 $parentField = '';
1648 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
1649
1650 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
1651 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
1652 $keyList .= ', '.$parentField;
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
1662 $filter_categorie = false;
1663 if (count($InfoFieldList) > 5) {
1664 if ($InfoFieldList[0] == 'categorie') {
1665 $filter_categorie = true;
1666 }
1667 }
1668
1669 if (!$filter_categorie) {
1670 $fields_label = explode('|', $InfoFieldList[1]);
1671 if (is_array($fields_label)) {
1672 $keyList .= ', ';
1673 $keyList .= implode(', ', $fields_label);
1674 }
1675
1676 $sqlwhere = '';
1677 $sql = "SELECT ".$keyList;
1678 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
1679
1680 // Add filter from 4th field
1681 if (!empty($InfoFieldList[4])) {
1682 // can use current entity filter
1683 if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
1684 $InfoFieldList[4] = str_replace('$ENTITY$', (string) $conf->entity, $InfoFieldList[4]);
1685 }
1686 // can use SELECT request
1687 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
1688 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
1689 }
1690
1691 // current object id can be use into filter
1692 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
1693 $InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
1694 } elseif (preg_match("#^.*list.php$#", $_SERVER["PHP_SELF"])) {
1695 // Pattern for word=$ID$
1696 $word = '\b[a-zA-Z0-9-\.-_]+\b=\$ID\$';
1697
1698 // Removing spaces around =, ( and )
1699 $InfoFieldList[4] = preg_replace('# *(=|\‍(|\‍)) *#', '$1', $InfoFieldList[4]);
1700
1701 $nbPreg = 1;
1702 // While we have parenthesis
1703 while ($nbPreg != 0) {
1704 // Initialise counters
1705 $nbPregRepl = $nbPregSel = 0;
1706 // Remove all parenthesis not preceded with '=' sign
1707 $InfoFieldList[4] = preg_replace('#([^=])(\‍([^)^(]*('.$word.')[^)^(]*\‍))#', '$1 $3 ', $InfoFieldList[4], -1, $nbPregRepl);
1708 // Remove all escape characters around '=' and parenthesis
1709 $InfoFieldList[4] = preg_replace('# *(=|\‍(|\‍)) *#', '$1', $InfoFieldList[4]);
1710 // Remove all parentheses preceded with '='
1711 $InfoFieldList[4] = preg_replace('#\b[a-zA-Z0-9-\.-_]+\b=\‍([^)^(]*('.$word.')[^)^(]*\‍)#', '$1 ', $InfoFieldList[4], -1, $nbPregSel);
1712 // On retire les escapes autour des = et parenthèses
1713 $InfoFieldList[4] = preg_replace('# *(=|\‍(|\‍)) *#', '$1', $InfoFieldList[4]);
1714
1715 // UPdate the totals counter for the loop
1716 $nbPreg = $nbPregRepl + $nbPregSel;
1717 }
1718
1719 // In case there is AND ou OR, before or after
1720 $matchCondition = array();
1721 preg_match('#(AND|OR|) *('.$word.') *(AND|OR|)#', $InfoFieldList[4], $matchCondition);
1722 while (!empty($matchCondition[0])) {
1723 // If the two sides differ but are not empty
1724 if (!empty($matchCondition[1]) && !empty($matchCondition[3]) && $matchCondition[1] != $matchCondition[3]) {
1725 // Nobody sain would do that without parentheses
1726 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
1727 } else {
1728 if (!empty($matchCondition[1])) {
1729 $boolCond = (($matchCondition[1] == "AND") ? ' AND TRUE ' : ' OR FALSE ');
1730 $InfoFieldList[4] = str_replace($matchCondition[0], $boolCond.$matchCondition[3], $InfoFieldList[4]);
1731 } elseif (!empty($matchCondition[3])) {
1732 $boolCond = (($matchCondition[3] == "AND") ? ' TRUE AND ' : ' FALSE OR');
1733 $InfoFieldList[4] = str_replace($matchCondition[0], $boolCond, $InfoFieldList[4]);
1734 } else {
1735 $InfoFieldList[4] = " TRUE ";
1736 }
1737 }
1738
1739 // In case there is AND ou OR, before or after
1740 preg_match('#(AND|OR|) *('.$word.') *(AND|OR|)#', $InfoFieldList[4], $matchCondition);
1741 }
1742 } else {
1743 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
1744 }
1745
1746 // We have to join on extrafield table
1747 $errstr = '';
1748 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1749 $sql .= ' as main, '.$this->db->sanitize($this->db->prefix().$InfoFieldList[0]).'_extrafields as extra';
1750 $sqlwhere .= " WHERE extra.fk_object = main.".$this->db->sanitize($InfoFieldList[2]);
1751 $sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1752 } else {
1753 $sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
1754 }
1755 } else {
1756 $sqlwhere .= ' WHERE 1=1';
1757 }
1758
1759 // Add Usf filter on second line
1760 /*
1761 if ($Usf) {
1762 $errorstr = '';
1763 $sqlusf .= forgeSQLFromUniversalSearchCriteria($Usf, $errorstr);
1764 if (!$errorstr) {
1765 $sqlwhere .= $sqlusf;
1766 } else {
1767 $sqlwhere .= " AND invalid_usf_filter_of_extrafield";
1768 }
1769 }
1770 */
1771
1772 // Some tables may have field, some other not. For the moment we disable it.
1773 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
1774 $sqlwhere .= " AND entity = ".((int) $conf->entity);
1775 }
1776 // $sql.=preg_replace('/^ AND /','',$sqlwhere);
1777 // print $sql;
1778
1779 $sql .= $sqlwhere;
1780 $sql .= ' ORDER BY '.implode(', ', $fields_label);
1781
1782 dol_syslog(get_class($this).'::showInputField type=chkbxlst', LOG_DEBUG);
1783 $resql = $this->db->query($sql);
1784 if ($resql) {
1785 $num = $this->db->num_rows($resql);
1786 $i = 0;
1787
1788 $data = array();
1789
1790 while ($i < $num) {
1791 $labeltoshow = '';
1792 $obj = $this->db->fetch_object($resql);
1793
1794 $notrans = false;
1795 // Several field into label (eq table:code|label:rowid)
1796 $fields_label = explode('|', $InfoFieldList[1]);
1797 if (is_array($fields_label)) {
1798 $notrans = true;
1799 foreach ($fields_label as $field_toshow) {
1800 $labeltoshow .= $obj->$field_toshow.' ';
1801 }
1802 } else {
1803 $labeltoshow = $obj->{$InfoFieldList[1]};
1804 }
1805 $labeltoshow = dol_trunc($labeltoshow, 45);
1806
1807 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
1808 $labeltoshow = '';
1809 foreach ($fields_label as $field_toshow) {
1810 $translabel = $langs->trans($obj->$field_toshow);
1811 if ($translabel != $obj->$field_toshow) {
1812 $labeltoshow .= ' '.dol_trunc($translabel, 18).' ';
1813 } else {
1814 $labeltoshow .= ' '.dol_trunc($obj->$field_toshow, 18).' ';
1815 }
1816 }
1817 $data[$obj->rowid] = $labeltoshow;
1818 } else {
1819 if (!$notrans) {
1820 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
1821 if ($translabel != $obj->{$InfoFieldList[1]}) {
1822 $labeltoshow = dol_trunc($translabel, 18);
1823 } else {
1824 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
1825 }
1826 }
1827 if (empty($labeltoshow)) {
1828 $labeltoshow = '(not defined)';
1829 }
1830
1831 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
1832 $data[$obj->rowid] = $labeltoshow;
1833 }
1834
1835 if (!empty($InfoFieldList[3]) && $parentField) {
1836 $parent = $parentName.':'.$obj->{$parentField};
1837 }
1838
1839 $data[$obj->rowid] = $labeltoshow;
1840 }
1841
1842 $i++;
1843 }
1844 $this->db->free($resql);
1845
1846 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, 0, 0, '', 0, '100%');
1847 } else {
1848 print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
1849 }
1850 } else {
1851 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
1852 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
1853 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, 0, 0, '', 0, '100%');
1854 }
1855 }
1856 } elseif ($type == 'link') {
1857 $param_list = array_keys($param['options']); // $param_list[0] = 'ObjectName:classPath' but can also be 'ObjectName:classPath:1:(status:=:1)'
1858 /* Removed.
1859 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
1860 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.
1861 if (strpos($param_list[0], '$ID$') !== false && !empty($objectid)) {
1862 $param_list[0] = str_replace('$ID$', $objectid, $param_list[0]);
1863 }*/
1864 $showempty = (($required && $default != '') ? 0 : 1);
1865
1866 $tmparray = explode(':', $param_list[0]);
1867
1868 $element = $extrafieldsobjectkey; // $extrafieldsobjectkey comes from $object->table_element but we need $object->element
1869 if ($element == 'socpeople') {
1870 $element = 'contact';
1871 } elseif ($element == 'projet') {
1872 $element = 'project';
1873 }
1874
1875 //$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
1876 $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.
1877 $objectfield = $element.':options_'.$key; // Example: 'actioncomm:options_fff' To be used in priority to know object linked with all its definition (including filters)
1878
1879 $out = $form->selectForForms($objectdesc, $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, '', 0, 0, '', $objectfield);
1880 } elseif (in_array($type, ['point', 'multipts', 'linestrg', 'polygon'])) {
1881 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
1882 $dolgeophp = new DolGeoPHP($this->db);
1883 $geojson = '{}';
1884 $centroidjson = getDolGlobalString('MAIN_INFO_SOCIETE_GEO_COORDINATES', '{}');
1885 if (!empty($value)) {
1886 $tmparray = $dolgeophp->parseGeoString($value);
1887 $geojson = $tmparray['geojson'];
1888 $centroidjson = $tmparray['centroidjson'];
1889 }
1890 if (!preg_match('/search_/', $keyprefix)) {
1891 require_once DOL_DOCUMENT_ROOT.'/core/class/geomapeditor.class.php';
1892 $geomapeditor = new GeoMapEditor();
1893 $out .= $geomapeditor->getHtml($keyprefix.$key.$keysuffix, $geojson, $centroidjson, $type);
1894 } else {
1895 // If keyprefix is search_ or search_options_, we must just use a simple text field
1896 $out = '';
1897 }
1898 } elseif ($type == 'password') {
1899 // If prefix is 'search_', field is used as a filter, we use a common text field.
1900 $out = '<input style="display:none" type="text" name="fakeusernameremembered">'; // Hidden field to reduce impact of evil Google Chrome autopopulate bug.
1901 $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 : '').'>';
1902 } elseif ($type == 'stars') {
1903 $out = '<input type="hidden" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').'>';
1904 $out .= '<div class="star-selection" id="'.$keyprefix.$key.$keysuffix.'_selection">';
1905 $i = 1;
1906 while ($i <= $size) {
1907 $out .= '<span class="star" data-value="'.$i.'">'.img_picto('', 'fontawesome_star_fas').'</span>';
1908 $i++;
1909 }
1910 $out .= '</div>';
1911 $out .= '<script>
1912 jQuery(function($) {
1913 let container = $("#'.$keyprefix.$key.$keysuffix.'_selection");
1914 let selectedStars = parseInt($("#'.$keyprefix.$key.$keysuffix.'").val()) || 0;
1915 container.find(".star").each(function() {
1916 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
1917 });
1918 container.find(".star").on("mouseover", function() {
1919 let selectedStar = $(this).data("value");
1920 container.find(".star").each(function() {
1921 $(this).toggleClass("active", $(this).data("value") <= selectedStar);
1922 });
1923 });
1924 container.on("mouseout", function() {
1925 container.find(".star").each(function() {
1926 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
1927 });
1928 });
1929 container.find(".star").off("click").on("click", function() {
1930 selectedStars = $(this).data("value");
1931 if (selectedStars === 1 && $("#'.$keyprefix.$key.$keysuffix.'").val() == 1) {
1932 selectedStars = 0;
1933 }
1934 $("#'.$keyprefix.$key.$keysuffix.'").val(selectedStars);
1935 container.find(".star").each(function() {
1936 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
1937 });
1938 });
1939 });
1940 </script>';
1941 }
1942 if (!empty($hidden)) {
1943 $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
1944 }
1945
1946 // If alwayseditable is false, and object is not in draft, then showOutputField
1947 // @phan-suppress-next-line PhanUndeclaredConstantOfClass
1948 if ($alwayseditable == 0 && is_object($object) && isset($object->status) && defined(get_class($object)."::STATUS_DRAFT") && $object->status != $object::STATUS_DRAFT) {
1949 $out = $this->showOutputField($key, $value, $moreparam, $extrafieldsobjectkey, null, $object);
1950 }
1951 /* Add comments
1952 if ($type == 'date') $out.=' (YYYY-MM-DD)';
1953 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
1954 */
1955 /*if (!empty($help) && $keyprefix != 'search_options_') {
1956 $out .= $form->textwithpicto('', $help, 1, 'help', '', 0, 3);
1957 }*/
1958 return $out;
1959 }
1960
1961
1973 public function showOutputField($key, $value, $moreparam = '', $extrafieldsobjectkey = '', $outputlangs = null, $object = null)
1974 {
1975 global $conf, $langs;
1976
1977 if (is_null($outputlangs) || !is_object($outputlangs)) {
1978 $outputlangs = $langs;
1979 }
1980
1981 if (empty($extrafieldsobjectkey)) {
1982 dol_syslog(get_class($this).'::showOutputField extrafieldsobjectkey required', LOG_ERR);
1983 return 'BadValueForParamExtraFieldsObjectKey';
1984 }
1985
1986 $label = $this->attributes[$extrafieldsobjectkey]['label'][$key];
1987 $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
1988 $size = $this->attributes[$extrafieldsobjectkey]['size'][$key]; // Can be '255', '24,8'...
1989 $default = $this->attributes[$extrafieldsobjectkey]['default'][$key];
1990 $computed = $this->attributes[$extrafieldsobjectkey]['computed'][$key];
1991 $unique = $this->attributes[$extrafieldsobjectkey]['unique'][$key];
1992 $required = $this->attributes[$extrafieldsobjectkey]['required'][$key];
1993 $param = $this->attributes[$extrafieldsobjectkey]['param'][$key];
1994 $perms = (int) dol_eval($this->attributes[$extrafieldsobjectkey]['perms'][$key], 1, 1, '2');
1995 $langfile = $this->attributes[$extrafieldsobjectkey]['langfile'][$key];
1996 $list = (string) dol_eval($this->attributes[$extrafieldsobjectkey]['list'][$key], 1, 1, '2');
1997 $help = $this->attributes[$extrafieldsobjectkey]['help'][$key];
1998 $cssview = $this->attributes[$extrafieldsobjectkey]['cssview'][$key];
1999 $alwayseditable = $this->attributes[$extrafieldsobjectkey]['alwayseditable'][$key];
2000
2001 // If alwayseditable is false, and object is not in draft, then we show value instead of input field
2002 $showValueInsteadOfInputField = 0; // Variable used to disable update of fields via ajax
2003 // @phan-suppress-next-line PhanUndeclaredConstantOfClass
2004 if ($alwayseditable == 0 && is_object($object) && isset($object->status) && defined(get_class($object)."::STATUS_DRAFT") && $object->status != $object::STATUS_DRAFT) {
2005 $showValueInsteadOfInputField = 1;
2006 }
2007
2008 $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)
2009
2010 if ($hidden) {
2011 return ''; // This is a protection. If field is hidden, we should just not call this method.
2012 }
2013
2014 //if ($computed) $value = // $value is already calculated into $value before calling this method
2015 $showsize = 0;
2016 if ($type == 'date') {
2017 $showsize = 10;
2018 if ($value !== '') {
2019 $value = dol_print_date($value, 'day'); // For date without hour, date is always GMT for storage and output
2020 }
2021 } elseif ($type == 'datetime') {
2022 $showsize = 19;
2023 if ($value !== '') {
2024 $value = dol_print_date($value, 'dayhour', 'tzuserrel');
2025 }
2026 } elseif ($type == 'duration') {
2027 $showsize = 10;
2028 if ($value !== '') {
2029 $value = intval($value);
2030 $value = convertSecondToTime($value);
2031 }
2032 } elseif ($type == 'datetimegmt') {
2033 $showsize = 19;
2034 if ($value !== '') {
2035 $value = dol_print_date($value, 'dayhour', 'gmt');
2036 }
2037 } elseif ($type == 'int') {
2038 $showsize = 10;
2039 } elseif ($type == 'double') {
2040 if (!empty($value)) {
2041 //$value=price($value);
2042 //$sizeparts = explode(",", $size);
2043 //$number_decimals = array_key_exists(1, $sizeparts) ? $sizeparts[1] : 0;
2044 $value = price($value, 0, $outputlangs, 0, 0, -2, '');
2045 }
2046 } elseif ($type == 'boolean') {
2047 $checked = '';
2048 if (!empty($value)) {
2049 $checked = ' checked ';
2050 }
2051 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
2052 $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
2053 } else {
2054 $value = yn($value ? 1 : 0);
2055 }
2056 } elseif ($type == 'mail') {
2057 $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
2058 } elseif ($type == 'ip') {
2059 $value = dol_print_ip($value, 0);
2060 } elseif ($type == 'icon') {
2061 $value = '<span class="'.$value.'"></span>';
2062 } elseif ($type == 'url') {
2063 $value = dol_print_url($value, '_blank', 32, 1);
2064 } elseif ($type == 'phone') {
2065 $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
2066 } elseif ($type == 'price') {
2067 //$value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
2068 if ($value || $value == '0') {
2069 $value = price($value, 0, $outputlangs, 0, getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'), -1).' '.$outputlangs->getCurrencySymbol($conf->currency);
2070 }
2071 } elseif ($type == 'pricecy') {
2072 $currency = $conf->currency;
2073 if (!empty($value)) {
2074 // $value in memory is a php string like '0.01:EUR'
2075 $pricetmp = explode(':', $value);
2076 $currency = !empty($pricetmp[1]) ? $pricetmp[1] : $conf->currency;
2077 $value = $pricetmp[0];
2078 }
2079 if ($value || $value == '0') {
2080 $value = price($value, 0, $outputlangs, 0, getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'), -1, $currency);
2081 }
2082 } elseif ($type == 'select') {
2083 $valstr = (!empty($param['options'][$value]) ? $param['options'][$value] : '');
2084 if (($pos = strpos($valstr, "|")) !== false) {
2085 $valstr = substr($valstr, 0, $pos);
2086 }
2087 if ($langfile && $valstr) {
2088 $value = $outputlangs->trans($valstr);
2089 } else {
2090 $value = $valstr;
2091 }
2092 } elseif ($type == 'sellist') {
2093 $param_list = array_keys($param['options']);
2094 $InfoFieldList = explode(":", $param_list[0]);
2095
2096 $selectkey = "rowid";
2097 $keyList = 'rowid';
2098
2099 if (count($InfoFieldList) >= 3) {
2100 $selectkey = $InfoFieldList[2];
2101 $keyList = $InfoFieldList[2].' as rowid';
2102 }
2103
2104 $fields_label = explode('|', $InfoFieldList[1]);
2105 if (is_array($fields_label)) {
2106 $keyList .= ', ';
2107 $keyList .= implode(', ', $fields_label);
2108 }
2109
2110 $filter_categorie = false;
2111 if (count($InfoFieldList) > 5) {
2112 if ($InfoFieldList[0] == 'categorie') {
2113 $filter_categorie = true;
2114 }
2115 }
2116
2117 $sql = "SELECT ".$keyList;
2118 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
2119 if (!empty($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra.') !== false) {
2120 $sql .= ' as main';
2121 }
2122 if ($selectkey == 'rowid' && empty($value)) {
2123 $sql .= " WHERE ".$selectkey." = 0";
2124 } elseif ($selectkey == 'rowid') {
2125 $sql .= " WHERE ".$selectkey." = ".((int) $value);
2126 } else {
2127 $sql .= " WHERE ".$selectkey." = '".$this->db->escape($value)."'";
2128 }
2129
2130 //$sql.= ' AND entity = '.$conf->entity;
2131
2132 dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
2133 $resql = $this->db->query($sql);
2134 if ($resql) {
2135 if (!$filter_categorie) {
2136 $value = ''; // value was used, so now we reset it to use it to build final output
2137
2138 $obj = $this->db->fetch_object($resql);
2139
2140 // Several field into label (eq table:code|label:rowid)
2141 $fields_label = explode('|', $InfoFieldList[1]);
2142
2143 if (is_array($fields_label) && count($fields_label) > 1) {
2144 foreach ($fields_label as $field_toshow) {
2145 $translabel = '';
2146 if (!empty($obj->$field_toshow)) {
2147 $translabel = $outputlangs->trans($obj->$field_toshow);
2148
2149 if ($translabel != $obj->$field_toshow) {
2150 $value .= dol_trunc($translabel, 24) . ' ';
2151 } else {
2152 $value .= $obj->$field_toshow . ' ';
2153 }
2154 }
2155 }
2156 } else {
2157 $translabel = '';
2158 $tmppropname = $InfoFieldList[1];
2159 //$obj->$tmppropname = '';
2160 if (!empty(isset($obj->$tmppropname) ? $obj->$tmppropname : '')) {
2161 $translabel = $outputlangs->trans($obj->$tmppropname);
2162 }
2163 if ($translabel != (isset($obj->$tmppropname) ? $obj->$tmppropname : '')) {
2164 $value = dol_trunc($translabel, 18);
2165 } else {
2166 $value = isset($obj->$tmppropname) ? $obj->$tmppropname : '';
2167 }
2168 }
2169 } else {
2170 $toprint = array();
2171 $obj = $this->db->fetch_object($resql);
2172 if ($obj->rowid) {
2173 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
2174 $c = new Categorie($this->db);
2175 $result = $c->fetch($obj->rowid);
2176 if ($result > 0) {
2177 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
2178 foreach ($ways as $way) {
2179 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #bbb"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
2180 }
2181 }
2182 }
2183 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
2184 }
2185 } else {
2186 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
2187 }
2188 } elseif ($type == 'radio') {
2189 if (!isset($param['options'][$value])) {
2190 $outputlangs->load('errors');
2191 $value = $outputlangs->trans('ErrorNoValueForRadioType');
2192 } else {
2193 $value = $outputlangs->trans($param['options'][$value]);
2194 }
2195 } elseif ($type == 'checkbox') {
2196 $value_arr = explode(',', $value);
2197 $value = '';
2198 $toprint = array();
2199 if (is_array($value_arr)) {
2200 foreach ($value_arr as $keyval => $valueval) {
2201 if (!empty($valueval)) {
2202 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$param['options'][$valueval].'</li>';
2203 }
2204 }
2205 }
2206 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
2207 } elseif ($type == 'chkbxlst') {
2208 $value_arr = explode(',', $value);
2209
2210 $param_list = array_keys($param['options']);
2211 $InfoFieldList = explode(":", $param_list[0]);
2212
2213 $selectkey = "rowid";
2214 $keyList = 'rowid';
2215
2216 if (count($InfoFieldList) >= 3) {
2217 $selectkey = $InfoFieldList[2];
2218 $keyList = $InfoFieldList[2].' as rowid';
2219 }
2220
2221 $fields_label = explode('|', $InfoFieldList[1]);
2222 if (is_array($fields_label)) {
2223 $keyList .= ', ';
2224 $keyList .= implode(', ', $fields_label);
2225 }
2226
2227 $filter_categorie = false;
2228 if (count($InfoFieldList) > 5) {
2229 if ($InfoFieldList[0] == 'categorie') {
2230 $filter_categorie = true;
2231 }
2232 }
2233
2234 $sql = "SELECT ".$keyList;
2235 $sql .= " FROM ".$this->db->prefix().$InfoFieldList[0];
2236 if (strpos($InfoFieldList[4], 'extra.') !== false) {
2237 $sql .= ' as main';
2238 }
2239 // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
2240 // $sql.= ' AND entity = '.$conf->entity;
2241
2242 dol_syslog(get_class($this).':showOutputField:$type=chkbxlst', LOG_DEBUG);
2243 $resql = $this->db->query($sql);
2244 if ($resql) {
2245 if (!$filter_categorie) {
2246 $value = ''; // value was used, so now we reset it to use it to build final output
2247 $toprint = array();
2248 while ($obj = $this->db->fetch_object($resql)) {
2249 // Several field into label (eq table:code|label:rowid)
2250 $fields_label = explode('|', $InfoFieldList[1]);
2251 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
2252 if (is_array($fields_label) && count($fields_label) > 1) {
2253 $label = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">';
2254 foreach ($fields_label as $field_toshow) {
2255 $translabel = '';
2256 if (!empty($obj->$field_toshow)) {
2257 $translabel = $outputlangs->trans($obj->$field_toshow);
2258 }
2259 if ($translabel != $field_toshow) {
2260 $label .= ' '.dol_trunc($translabel, 18);
2261 } else {
2262 $label .= ' '.$obj->$field_toshow;
2263 }
2264 }
2265 $label .= '</li>';
2266 $toprint[] = $label;
2267 } else {
2268 $translabel = '';
2269 if (!empty($obj->{$InfoFieldList[1]})) {
2270 $translabel = $outputlangs->trans($obj->{$InfoFieldList[1]});
2271 }
2272 if ($translabel != $obj->{$InfoFieldList[1]}) {
2273 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.dol_trunc($translabel, 18).'</li>';
2274 } else {
2275 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$obj->{$InfoFieldList[1]}.'</li>';
2276 }
2277 }
2278 }
2279 }
2280 } else {
2281 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2282
2283 $toprint = array();
2284 while ($obj = $this->db->fetch_object($resql)) {
2285 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
2286 $c = new Categorie($this->db);
2287 $c->fetch($obj->rowid);
2288 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
2289 foreach ($ways as $way) {
2290 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"'.($c->color ? ' style="background: #'.$c->color.';"' : ' style="background: #bbb"').'>'.img_object('', 'category').' '.$way.'</li>';
2291 }
2292 }
2293 }
2294 }
2295 if (!empty($toprint)) {
2296 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
2297 }
2298 } else {
2299 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
2300 }
2301 } elseif ($type == 'link') {
2302 $out = '';
2303
2304 // Only if something to display (perf)
2305 if ($value) { // If we have -1 here, pb is into insert, not into output (fix insert instead of changing code here to compensate)
2306 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
2307
2308 $InfoFieldList = explode(":", $param_list[0]);
2309 $classname = $InfoFieldList[0];
2310 $classpath = $InfoFieldList[1];
2311 if (!empty($classpath)) {
2312 dol_include_once($InfoFieldList[1]);
2313 if ($classname && class_exists($classname)) {
2314 $tmpobject = new $classname($this->db);
2315 '@phan-var-force CommonObject $tmpobject';
2316 $tmpobject->fetch($value);
2317
2318 if (get_class($tmpobject) == 'Categorie') {
2319 // For category object, rendering must use the same method than the one deinfed into showCategories()
2320 $color = $tmpobject->color;
2321 $sfortag = '<span class="noborderoncategories"' . ($color ? ' style="background: #' . $color . ';"' : ' style="background: #bbb"') . '>';
2322 $sfortag .= $tmpobject->getNomUrl(3);
2323 $sfortag .= '</span>';
2324 $value = $sfortag;
2325 } else {
2326 $value = $tmpobject->getNomUrl(3);
2327 }
2328 }
2329 } else {
2330 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
2331 return 'Error bad setup of extrafield';
2332 }
2333 }
2334 } elseif ($type == 'point') {
2335 if (!empty($value)) {
2336 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
2337 $dolgeophp = new DolGeoPHP($this->db);
2338 $value = $dolgeophp->getXYString($value);
2339 } else {
2340 $value = '';
2341 }
2342 } elseif (in_array($type, ['multipts', 'linestrg', 'polygon'])) {
2343 if (!empty($value)) {
2344 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
2345 $dolgeophp = new DolGeoPHP($this->db);
2346 $value = $dolgeophp->getPointString($value);
2347 } else {
2348 $value = '';
2349 }
2350 } elseif ($type == 'text') {
2351 $value = '<div class="'.($cssview ? $cssview : 'shortmessagecut').'">'.dol_htmlentitiesbr($value).'</div>';
2352 } elseif ($type == 'html') {
2353 $value = dol_htmlentitiesbr($value);
2354 } elseif ($type == 'password') {
2355 $value = dol_trunc(preg_replace('/./i', '*', $value), 8, 'right', 'UTF-8', 1);
2356 } elseif ($type == 'stars') {
2357 $objectid = (int) $object->id;
2358 if ($showValueInsteadOfInputField == 1) {
2359 $value = '<span style="display:none;" id="'.$key.$object->id.'">'.dol_escape_htmltag($value).'</span>';
2360 } else {
2361 $value = '<input type="hidden" class="flat" name="'.$key.'" id="'.$key.$objectid.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').'>';
2362 }
2363
2364 $value .= '<div class="star-selection" id="'.$key.$objectid.'_selection">';
2365 $i = 1;
2366 while ($i <= $size) {
2367 $value .= '<span class="star" data-value="'.$i.'">'.img_picto('', 'fontawesome_star_fas').'</span>';
2368 $i++;
2369 }
2370 $value .= '</div>';
2371 $value .= '<script>
2372 $(document).ready(function() {
2373 let container = $("#'.$key.$objectid.'_selection");
2374 let selectedStars = parseInt($("#'.$key.$objectid.'").val() || $("#'.$key.$objectid.'").text()) || 0;
2375 container.find(".star").each(function() {
2376 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2377 });';
2378 if ($showValueInsteadOfInputField == 0) {
2379 $value .= '
2380 container.find(".star").on("mouseover", function() {
2381 let selectedStar = $(this).data("value");
2382 container.find(".star").each(function() {
2383 $(this).toggleClass("active", $(this).data("value") <= selectedStar);
2384 });
2385 });
2386 container.on("mouseout", function() {
2387 container.find(".star").each(function() {
2388 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2389 });
2390 });
2391 container.find(".star").off("click").on("click", function() {
2392 selectedStars = $(this).data("value");
2393 if (selectedStars == 1 && $("#'.$key.$objectid.'").val() == 1) {
2394 selectedStars = 0;
2395 }
2396 container.find("#'.$key.$objectid.'").val(selectedStars);
2397 container.find(".star").each(function() {
2398 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
2399 });
2400 $.ajax({
2401 url: "'.DOL_URL_ROOT.'/core/ajax/editextrafield.php",
2402 method: "POST",
2403 data: {
2404 objectType: \''.dol_escape_js($extrafieldsobjectkey).'\',
2405 objectId: '.((int) $objectid).',
2406 field: \''.dol_escape_js($key).'\',
2407 value: selectedStars,
2408 token: "'.newToken().'"
2409 },
2410 success: function(response) {
2411 var res = JSON.parse(response);
2412 console[res.status === "success" ? "log" : "error"](res.message);
2413 },
2414 error: function(xhr, status, error) {
2415 console.log("Ajax request failed while updating '.$key.':", error);
2416 }
2417 });
2418 });';
2419 }
2420 $value .= '
2421 });
2422 </script>';
2423 } else {
2424 $showsize = round((float) $size);
2425 if ($showsize > 48) {
2426 $showsize = 48;
2427 }
2428 }
2429
2430 //print $type.'-'.$size;
2431 $out = $value;
2432
2433 return $out;
2434 }
2435
2443 public function getAlignFlag($key, $extrafieldsobjectkey = '')
2444 {
2445 $type = 'varchar';
2446 if (!empty($extrafieldsobjectkey)) {
2447 $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
2448 }
2449
2450 $cssstring = '';
2451
2452 if (in_array($type, array('date', 'datetime', 'datetimegmt',))) {
2453 $cssstring = "center";
2454 } elseif (in_array($type, array('int', 'price', 'double', 'duration'))) {
2455 $cssstring = "right";
2456 } elseif (in_array($type, array('boolean', 'radio', 'checkbox', 'ip', 'icon'))) {
2457 $cssstring = "center";
2458 }
2459
2460 if (!empty($this->attributes[$extrafieldsobjectkey]['csslist'][$key])) {
2461 $cssstring .= ($cssstring ? ' ' : '').$this->attributes[$extrafieldsobjectkey]['csslist'][$key];
2462 } else {
2463 if (in_array($type, array('ip'))) {
2464 $cssstring .= ($cssstring ? ' ' : '').'tdoverflowmax150';
2465 }
2466 }
2467
2468 return $cssstring;
2469 }
2470
2481 public function showSeparator($key, $object, $colspan = 2, $display_type = 'card', $mode = '')
2482 {
2483 global $conf, $langs;
2484
2485 $tagtype = 'tr';
2486 $tagtype_dyn = 'td';
2487
2488 if ($display_type == 'line') {
2489 $tagtype = 'div';
2490 $tagtype_dyn = 'span';
2491 $colspan = 0;
2492 }
2493
2494 $extrafield_param = $this->attributes[$object->table_element]['param'][$key];
2495 $extrafield_param_list = array();
2496 if (!empty($extrafield_param) && is_array($extrafield_param)) {
2497 $extrafield_param_list = array_keys($extrafield_param['options']);
2498 }
2499
2500 // Set $extrafield_collapse_display_value (do we have to collapse/expand the group after the separator)
2501 $extrafield_collapse_display_value = -1;
2502 $expand_display = false;
2503 if (is_array($extrafield_param_list) && count($extrafield_param_list) > 0) {
2504 $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
2505 $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));
2506 }
2507 $disabledcookiewrite = 0;
2508 if ($mode == 'create') {
2509 // On create mode, force separator group to not be collapsible
2510 $extrafield_collapse_display_value = 1;
2511 $expand_display = true; // We force group to be shown expanded
2512 $disabledcookiewrite = 1; // We keep status of group unchanged into the cookie
2513 }
2514
2515 $out = '<'.$tagtype.' id="trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'" class="trextrafieldseparator trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'">';
2516 $out .= '<'.$tagtype_dyn.' '.(!empty($colspan) ? 'colspan="' . $colspan . '"' : '').'>';
2517 // Some js code will be injected here to manage the collapsing of extrafields
2518 // Output the picto
2519 $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>';
2520 $out .= '&nbsp;';
2521 $out .= '<strong>';
2522 $out .= $langs->trans($this->attributes[$object->table_element]['label'][$key]);
2523 $out .= '</strong>';
2524 $out .= '</'.$tagtype_dyn.'>';
2525 $out .= '</'.$tagtype.'>';
2526
2527 $collapse_group = $key.(!empty($object->id) ? '_'.$object->id : '');
2528 //$extrafields_collapse_num = $this->attributes[$object->table_element]['pos'][$key].(!empty($object->id)?'_'.$object->id:'');
2529
2530 if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
2531 // Set the collapse_display status to cookie in priority or if ignorecollapsesetup is 1, if cookie and ignorecollapsesetup not defined, use the setup.
2532 $this->expand_display[$collapse_group] = $expand_display;
2533
2534 if (!empty($conf->use_javascript_ajax)) {
2535 $out .= '<!-- Add js script to manage the collapse/uncollapse of extrafields separators '.$key.' -->'."\n";
2536 $out .= '<script nonce="'.getNonce().'" type="text/javascript">'."\n";
2537 $out .= 'jQuery(document).ready(function(){'."\n";
2538 if (empty($disabledcookiewrite)) {
2539 if (!$expand_display) {
2540 $out .= ' console.log("Inject js for the collapsing of extrafield '.$key.' - hide");'."\n";
2541 $out .= ' jQuery(".trextrafields_collapse'.$collapse_group.'").hide();'."\n";
2542 } else {
2543 $out .= ' console.log("Inject js for collapsing of extrafield '.$key.' - keep visible and set cookie");'."\n";
2544 $out .= ' document.cookie = "DOLUSER_COLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=1; path='.$_SERVER["PHP_SELF"].'"'."\n";
2545 }
2546 }
2547 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'").click(function(){'."\n";
2548 $out .= ' console.log("We click on collapse/uncollapse to hide/show .trextrafields_collapse'.$collapse_group.'");'."\n";
2549 $out .= ' jQuery(".trextrafields_collapse'.$collapse_group.'").toggle(100, function(){'."\n";
2550 $out .= ' if (jQuery(".trextrafields_collapse'.$collapse_group.'").is(":hidden")) {'."\n";
2551 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').' '.$tagtype_dyn.' span").addClass("fa-plus-square").removeClass("fa-minus-square");'."\n";
2552 $out .= ' document.cookie = "DOLUSER_COLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=0; path='.$_SERVER["PHP_SELF"].'"'."\n";
2553 $out .= ' } else {'."\n";
2554 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').' '.$tagtype_dyn.' span").addClass("fa-minus-square").removeClass("fa-plus-square");'."\n";
2555 $out .= ' document.cookie = "DOLUSER_COLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=1; path='.$_SERVER["PHP_SELF"].'"'."\n";
2556 $out .= ' }'."\n";
2557 $out .= ' });'."\n";
2558 $out .= ' });'."\n";
2559 $out .= '});'."\n";
2560 $out .= '</script>'."\n";
2561 }
2562 } else {
2563 $this->expand_display[$collapse_group] = 1;
2564 }
2565
2566 return $out;
2567 }
2568
2580 public function setOptionalsFromPost($extralabels, &$object, $onlykey = '', $todefaultifmissing = 0)
2581 {
2582 global $langs;
2583
2584 $nofillrequired = 0; // For error when required field left blank
2585 $error_field_required = array();
2586
2587 if (isset($this->attributes[$object->table_element]['label']) && is_array($this->attributes[$object->table_element]['label'])) {
2588 $extralabels = $this->attributes[$object->table_element]['label'];
2589 }
2590
2591 if (is_array($extralabels)) {
2592 // Get extra fields
2593 foreach ($extralabels as $key => $value) {
2594 if (!empty($onlykey) && $onlykey != '@GETPOSTISSET' && $key != $onlykey) {
2595 continue;
2596 }
2597
2598 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')))) {
2599 //when unticking boolean field, it's not set in POST
2600 continue;
2601 }
2602
2603 $key_type = $this->attributes[$object->table_element]['type'][$key];
2604 if ($key_type == 'separate') {
2605 continue;
2606 }
2607
2608 $enabled = 1;
2609 if (isset($this->attributes[$object->table_element]['enabled'][$key])) { // 'enabled' is often a condition on module enabled or not
2610 $enabled = (int) dol_eval((string) $this->attributes[$object->table_element]['enabled'][$key], 1, 1, '2');
2611 }
2612
2613 $visibility = 1;
2614 if (isset($this->attributes[$object->table_element]['list'][$key])) { // 'list' is option for visibility
2615 $visibility = (int) dol_eval($this->attributes[$object->table_element]['list'][$key], 1, 1, '2');
2616 }
2617
2618 $perms = 1;
2619 if (isset($this->attributes[$object->table_element]['perms'][$key])) {
2620 $perms = (int) dol_eval($this->attributes[$object->table_element]['perms'][$key], 1, 1, '2');
2621 }
2622 if (empty($enabled)
2623 || (
2624 $onlykey === '@GETPOSTISSET'
2625 && in_array($this->attributes[$object->table_element]['type'][$key], array('boolean', 'checkbox', 'chkbxlst'))
2626 && in_array(abs($enabled), array(2, 5))
2627 && ! GETPOSTISSET('options_' . $key) // Update hidden checkboxes and multiselect only if they are provided
2628 )
2629 ) {
2630 continue;
2631 }
2632
2633 $visibility_abs = abs($visibility);
2634 // not modify if extra field is not in update form (0 : never, 2 or -2 : list only, 5 or - 5 : list and view only)
2635 if (empty($visibility_abs) || $visibility_abs == 2 || $visibility_abs == 5) {
2636 continue;
2637 }
2638 if (empty($perms)) {
2639 continue;
2640 }
2641
2642 if ($this->attributes[$object->table_element]['required'][$key]) { // Value is required
2643 // Check if functionally empty without using GETPOST (depending on the type of extrafield, a
2644 // technically non-empty value may be treated as empty functionally).
2645 // value can be alpha, int, array, etc...
2646 $v = $_POST["options_".$key] ?? null;
2647 $type = $this->attributes[$object->table_element]['type'][$key];
2648 if (self::isEmptyValue($v, $type)) {
2649 //print 'ccc'.$value.'-'.$this->attributes[$object->table_element]['required'][$key];
2650
2651 // 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.
2652
2653 $nofillrequired++;
2654 if (!empty($this->attributes[$object->table_element]['langfile'][$key])) {
2655 $langs->load($this->attributes[$object->table_element]['langfile'][$key]);
2656 }
2657 $error_field_required[$key] = $langs->transnoentitiesnoconv($value);
2658 }
2659 }
2660
2661 if (in_array($key_type, array('date'))) {
2662 // Clean parameters
2663 $value_key = dol_mktime(12, 0, 0, GETPOSTINT("options_".$key."month"), GETPOSTINT("options_".$key."day"), GETPOSTINT("options_".$key."year"));
2664 } elseif (in_array($key_type, array('datetime'))) {
2665 // Clean parameters
2666 $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');
2667 } elseif (in_array($key_type, array('datetimegmt'))) {
2668 // Clean parameters
2669 $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');
2670 } elseif (in_array($key_type, array('duration'))) {
2671 $value_hours = GETPOSTINT("options_" . $key . "hour");
2672 $value_minutes = GETPOSTINT("options_" . $key . "minute");
2673 $value_key = $value_hours * 3600 + $value_minutes * 60;
2674 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
2675 $value_arr = GETPOST("options_".$key, 'array'); // check if an array
2676 if (!empty($value_arr)) {
2677 $value_key = implode(',', $value_arr);
2678 } else {
2679 $value_key = '';
2680 }
2681 } elseif (in_array($key_type, array('price', 'double'))) {
2682 $value_arr = GETPOST("options_".$key, 'alpha');
2683 $value_key = price2num($value_arr);
2684 } elseif (in_array($key_type, array('pricecy', 'double'))) {
2685 $value_key = price2num(GETPOST("options_".$key, 'alpha')).':'.GETPOST("options_".$key."currency_id", 'alpha');
2686 } elseif (in_array($key_type, array('html'))) {
2687 $value_key = GETPOST("options_".$key, 'restricthtml');
2688 } elseif (in_array($key_type, ['point', 'multipts', 'linestrg', 'polygon'])) {
2689 // construct point
2690 require_once DOL_DOCUMENT_ROOT.'/core/class/dolgeophp.class.php';
2691 $geojson = GETPOST("options_".$key, 'restricthtml');
2692 if ($geojson != '{}') {
2693 $dolgeophp = new DolGeoPHP($this->db);
2694 $value_key = $dolgeophp->getWkt($geojson);
2695 } else {
2696 $value_key = '';
2697 }
2698 } elseif (in_array($key_type, array('text'))) {
2699 $label_security_check = 'alphanohtml';
2700 // by default 'alphanohtml' (better security); hidden conf MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML allows basic html
2701 if (getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_REF_LABELS')) {
2702 $label_security_check = 'nohtml';
2703 } else {
2704 $label_security_check = !getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML') ? 'alphanohtml' : 'restricthtml';
2705 }
2706 $value_key = GETPOST("options_".$key, $label_security_check);
2707 } else {
2708 $value_key = GETPOST("options_".$key);
2709 if (in_array($key_type, array('link')) && $value_key == '-1') {
2710 $value_key = '';
2711 }
2712 }
2713
2714 if (!empty($error_field_required[$key]) && $todefaultifmissing) {
2715 // Value is required but we have a default value and we asked to set empty value to the default value
2716 if (!empty($this->attributes[$object->table_element]['default']) && !is_null($this->attributes[$object->table_element]['default'][$key])) {
2717 $value_key = $this->attributes[$object->table_element]['default'][$key];
2718 unset($error_field_required[$key]);
2719 $nofillrequired--;
2720 }
2721 }
2722
2723 $object->array_options["options_".$key] = $value_key;
2724 }
2725
2726 if ($nofillrequired) {
2727 $langs->load('errors');
2728 $this->error = $langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required);
2729 setEventMessages($this->error, null, 'errors');
2730 return -1;
2731 } else {
2732 return 1;
2733 }
2734 } else {
2735 return 0;
2736 }
2737 }
2738
2747 public function getOptionalsFromPost($extrafieldsobjectkey, $keysuffix = '', $keyprefix = '')
2748 {
2749 global $_POST;
2750
2751 if (is_string($extrafieldsobjectkey) && !empty($this->attributes[$extrafieldsobjectkey]['label']) && is_array($this->attributes[$extrafieldsobjectkey]['label'])) {
2752 $extralabels = $this->attributes[$extrafieldsobjectkey]['label'];
2753 } else {
2754 $extralabels = $extrafieldsobjectkey;
2755 }
2756
2757 if (is_array($extralabels)) {
2758 $array_options = array();
2759
2760 // Get extra fields
2761 foreach ($extralabels as $key => $value) {
2762 $key_type = '';
2763 if (is_string($extrafieldsobjectkey)) {
2764 $key_type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
2765 }
2766
2767 if (in_array($key_type, array('date'))) {
2768 $dateparamname_start = $keyprefix . 'options_' . $key . $keysuffix . '_start';
2769 $dateparamname_end = $keyprefix . 'options_' . $key . $keysuffix . '_end';
2770
2771 if (GETPOST($dateparamname_start . 'year') || GETPOST($dateparamname_end . 'year')) {
2772 $value_key = array();
2773 // values provided as a component year, month, day, etc.
2774 if (GETPOST($dateparamname_start . 'year')) {
2775 $value_key['start'] = dol_mktime(0, 0, 0, GETPOSTINT($dateparamname_start . 'month'), GETPOSTINT($dateparamname_start . 'day'), GETPOSTINT($dateparamname_start . 'year'));
2776 }
2777 if (GETPOST($dateparamname_end . 'year')) {
2778 $value_key['end'] = dol_mktime(23, 59, 59, GETPOSTINT($dateparamname_end . 'month'), GETPOSTINT($dateparamname_end . 'day'), GETPOSTINT($dateparamname_end . 'year'));
2779 }
2780 } elseif (GETPOST($keyprefix."options_".$key.$keysuffix."year")) {
2781 // Clean parameters
2782 $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"));
2783 } else {
2784 continue; // Value was not provided, we should not set it.
2785 }
2786 } elseif (in_array($key_type, array('datetime', 'datetimegmt'))) {
2787 $dateparamname_start = $keyprefix . 'options_' . $key . $keysuffix . '_start';
2788 $dateparamname_end = $keyprefix . 'options_' . $key . $keysuffix . '_end';
2789
2790 if (GETPOST($dateparamname_start . 'year') || GETPOST($dateparamname_end . 'year')) {
2791 // values provided as a date pair (start date + end date), each date being broken down as year, month, day, etc.
2792 $dateparamname_start_hour = GETPOSTINT($dateparamname_start . 'hour') != '-1' ? GETPOSTINT($dateparamname_start . 'hour') : '00';
2793 $dateparamname_start_min = GETPOSTINT($dateparamname_start . 'min') != '-1' ? GETPOSTINT($dateparamname_start . 'min') : '00';
2794 $dateparamname_start_sec = GETPOSTINT($dateparamname_start . 'sec') != '-1' ? GETPOSTINT($dateparamname_start . 'sec') : '00';
2795 $dateparamname_end_hour = GETPOSTINT($dateparamname_end . 'hour') != '-1' ? GETPOSTINT($dateparamname_end . 'hour') : '23';
2796 $dateparamname_end_min = GETPOSTINT($dateparamname_end . 'min') != '-1' ? GETPOSTINT($dateparamname_end . 'min') : '59';
2797 $dateparamname_end_sec = GETPOSTINT($dateparamname_end . 'sec') != '-1' ? GETPOSTINT($dateparamname_end . 'sec') : '59';
2798 if ($key_type == 'datetimegmt') {
2799 $value_key = array(
2800 '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'),
2801 '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')
2802 );
2803 } else {
2804 $value_key = array(
2805 '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'),
2806 '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')
2807 );
2808 }
2809 } elseif (GETPOST($keyprefix."options_".$key.$keysuffix."year")) {
2810 // Clean parameters
2811 if ($key_type == 'datetimegmt') {
2812 $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');
2813 } else {
2814 $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');
2815 }
2816 } else {
2817 continue; // Value was not provided, we should not set it.
2818 }
2819 } elseif ($key_type == 'select') {
2820 // to detect if we are in search context
2821 if (GETPOSTISARRAY($keyprefix."options_".$key.$keysuffix)) {
2822 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix, 'array:aZ09');
2823 // Make sure we get an array even if there's only one selected
2824 $value_arr = (array) $value_arr;
2825 $value_key = implode(',', $value_arr);
2826 } else {
2827 $value_key = GETPOST($keyprefix."options_".$key.$keysuffix);
2828 }
2829 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
2830 // We test on a hidden field named "..._multiselect" that is always set to 1 if param is in form so
2831 // when nothing is provided we can make a difference between noparam in the form and param was set to nothing.
2832 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix.'_multiselect')) {
2833 continue; // Value was not provided, we should not set it.
2834 }
2835 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
2836 // Make sure we get an array even if there's only one checkbox
2837 $value_arr = (array) $value_arr;
2838 $value_key = implode(',', $value_arr);
2839 } elseif (in_array($key_type, array('price', 'double', 'int'))) {
2840 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
2841 continue; // Value was not provided, we should not set it.
2842 }
2843 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
2844 if ($keyprefix != 'search_') { // If value is for a search, we must keep complex string like '>100 <=150'
2845 $value_key = price2num($value_arr);
2846 } else {
2847 $value_key = $value_arr;
2848 }
2849 } elseif (in_array($key_type, array('boolean'))) {
2850 // We test on a hidden field named "..._boolean" that is always set to 1 if param is in form so
2851 // when nothing is provided we can make a difference between noparam in the form and param was set to nothing.
2852 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix."_boolean")) {
2853 $value_key = '';
2854 } else {
2855 $value_arr = GETPOST($keyprefix."options_".$key.$keysuffix);
2856 $value_key = $value_arr;
2857 }
2858 } elseif (in_array($key_type, array('html'))) {
2859 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
2860 continue; // Value was not provided, we should not set it.
2861 }
2862 $value_key = dol_htmlcleanlastbr(GETPOST($keyprefix."options_".$key.$keysuffix, 'restricthtml'));
2863 } else {
2864 if (!GETPOSTISSET($keyprefix."options_".$key.$keysuffix)) {
2865 continue; // Value was not provided, we should not set it.
2866 }
2867
2868 $value_key = GETPOST($keyprefix."options_".$key.$keysuffix);
2869 if ($value_key === '') {
2870 $value_key = null;
2871 }
2872 }
2873
2874 $array_options[$keyprefix."options_".$key] = $value_key; // No keyprefix here. keyprefix is used only for read.
2875 }
2876
2877 return $array_options;
2878 }
2879
2880 return 0;
2881 }
2882
2888 public static function getListOfTypesLabels()
2889 {
2890 global $langs;
2891
2892 $arraytype2label = array('');
2893
2894 $tmptype2label = ExtraFields::$type2label;
2895 foreach ($tmptype2label as $key => $val) {
2896 $arraytype2label[$key] = $langs->transnoentitiesnoconv($val);
2897 }
2898
2899 if (!getDolGlobalString('MAIN_USE_EXTRAFIELDS_ICON')) {
2900 unset($arraytype2label['icon']);
2901 }
2902 if (!getDolGlobalString('MAIN_USE_GEOPHP')) {
2903 unset($arraytype2label['point']);
2904 unset($arraytype2label['multipts']);
2905 unset($arraytype2label['linestrg']);
2906 unset($arraytype2label['polygon']);
2907 }
2908
2909 return $arraytype2label;
2910 }
2911
2919 public static function isEmptyValue($v, string $type)
2920 {
2921 if ($v === null || $v === '') {
2922 return true;
2923 }
2924 if (is_array($v) || $type == 'select') {
2925 return empty($v);
2926 }
2927 if ($type == 'link') {
2928 return ($v == '-1');
2929 }
2930 if ($type == 'sellist') {
2931 return ($v == '0');
2932 }
2933 return empty($v); // Note empty('0') is also true, tested 7.0 up to 8.3.12
2934 }
2935}
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:459
$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