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