dolibarr 19.0.4
extrafields.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2002-2003 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2002-2003 Jean-Louis Bergamo <jlb@j1b.org>
4 * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
5 * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
6 * Copyright (C) 2009-2012 Laurent Destailleur <eldy@users.sourceforge.net>
7 * Copyright (C) 2009-2012 Regis Houssin <regis.houssin@inodbox.com>
8 * Copyright (C) 2013 Florian Henry <forian.henry@open-concept.pro>
9 * Copyright (C) 2015-2023 Charlene BENKE <charlene@patas-monkey.com>
10 * Copyright (C) 2016 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
11 * Copyright (C) 2017 Nicolas ZABOURI <info@inovea-conseil.com>
12 * Copyright (C) 2018-2022 Frédéric France <frederic.france@netlogic.fr>
13 * Copyright (C) 2022 Antonin MARCHAL <antonin@letempledujeu.fr>
14 * Copyright (C) 2024 Joachim Kueter <git-jk@bloxera.com>
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 3 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program. If not, see <https://www.gnu.org/licenses/>.
28 */
29
41{
45 public $db;
46
50 public $attributes;
51
55 public $expand_display;
56
60 public $error = '';
61
65 public $errors = array();
66
70 public $errno;
71
75 public static $type2label = array(
76 'varchar'=>'String1Line',
77 'text'=>'TextLongNLines',
78 'html'=>'HtmlText',
79 'int'=>'Int',
80 'double'=>'Float',
81 'date'=>'Date',
82 'datetime'=>'DateAndTime',
83 //'datetimegmt'=>'DateAndTimeUTC',
84 'boolean'=>'Boolean',
85 'price'=>'ExtrafieldPrice',
86 'pricecy'=>'ExtrafieldPriceWithCurrency',
87 'phone'=>'ExtrafieldPhone',
88 'mail'=>'ExtrafieldMail',
89 'url'=>'ExtrafieldUrl',
90 'ip'=>'ExtrafieldIP',
91 'icon'=>'Icon',
92 'password' => 'ExtrafieldPassword',
93 'select' => 'ExtrafieldSelect',
94 'sellist' => 'ExtrafieldSelectList',
95 'radio' => 'ExtrafieldRadio',
96 'checkbox' => 'ExtrafieldCheckBox',
97 'chkbxlst' => 'ExtrafieldCheckBoxFromList',
98 'link' => 'ExtrafieldLink',
99 'separate' => 'ExtrafieldSeparator',
100 );
101
107 public function __construct($db)
108 {
109 $this->db = $db;
110 $this->error = '';
111 $this->errors = array();
112 $this->attributes = array();
113 }
114
141 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())
142 {
143 if (empty($attrname)) {
144 return -1;
145 }
146 if (empty($label)) {
147 return -1;
148 }
149
150 $result = 0;
151
152 // Clean properties
153 if ($type == 'separate') {
154 $unique = 0;
155 $required = 0;
156 } // Force unique and not required if this is a separator field to avoid troubles.
157 if ($elementtype == 'thirdparty') {
158 $elementtype = 'societe';
159 }
160 if ($elementtype == 'contact') {
161 $elementtype = 'socpeople';
162 }
163 // If property has a computed formula, it must not be a required or unique field
164 if (!empty($computed)) {
165 $required = 0;
166 $unique = 0;
167 }
168
169 // Create field into database except for separator type which is not stored in database
170 if ($type != 'separate') {
171 $result = $this->create($attrname, $type, $size, $elementtype, $unique, $required, $default_value, $param, $perms, $list, $computed, $help, $moreparams);
172 }
173 $err1 = $this->errno;
174 if ($result > 0 || $err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' || $type == 'separate') {
175 // Add declaration of field into table
176 $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);
177 $err2 = $this->errno;
178 if ($result2 > 0 || ($err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' && $err2 == 'DB_ERROR_RECORD_ALREADY_EXISTS')) {
179 $this->error = '';
180 $this->errno = 0;
181 return 1;
182 } else {
183 return -2;
184 }
185 } else {
186 return -1;
187 }
188 }
189
209 private function create($attrname, $type = 'varchar', $length = '255', $elementtype = 'member', $unique = 0, $required = 0, $default_value = '', $param = array(), $perms = '', $list = '0', $computed = '', $help = '', $moreparams = array())
210 {
211 if ($elementtype == 'thirdparty') {
212 $elementtype = 'societe';
213 }
214 if ($elementtype == 'contact') {
215 $elementtype = 'socpeople';
216 }
217
218 $table = $elementtype.'_extrafields';
219 if ($elementtype == 'categorie') {
220 $table = 'categories_extrafields';
221 }
222
223 if (!empty($attrname) && preg_match("/^\w[a-zA-Z0-9_]*$/", $attrname) && !is_numeric($attrname)) {
224 if ($type == 'boolean') {
225 $typedb = 'int';
226 $lengthdb = '1';
227 } elseif ($type == 'price') {
228 $typedb = 'double';
229 $lengthdb = '24,8';
230 } elseif ($type == 'pricecy') {
231 $typedb = 'varchar';
232 $lengthdb = '64';
233 } elseif ($type == 'phone') {
234 $typedb = 'varchar';
235 $lengthdb = '20';
236 } elseif ($type == 'mail' || $type == 'ip' || $type == 'icon') {
237 $typedb = 'varchar';
238 $lengthdb = '128';
239 } elseif ($type == 'url') {
240 $typedb = 'varchar';
241 $lengthdb = '255';
242 } elseif (($type == 'select') || ($type == 'sellist') || ($type == 'radio') || ($type == 'checkbox') || ($type == 'chkbxlst')) {
243 $typedb = 'varchar';
244 $lengthdb = '255';
245 } elseif ($type == 'link') {
246 $typedb = 'int';
247 $lengthdb = '11';
248 } elseif ($type == 'html') {
249 $typedb = 'text';
250 $lengthdb = $length;
251 } elseif ($type == 'password') {
252 $typedb = 'varchar';
253 $lengthdb = '128';
254 } else {
255 $typedb = $type;
256 $lengthdb = $length;
257 if ($type == 'varchar' && empty($lengthdb)) {
258 $lengthdb = '255';
259 }
260 }
261 $field_desc = array(
262 'type'=>$typedb,
263 'value'=>$lengthdb,
264 'null'=>($required ? 'NOT NULL' : 'NULL'),
265 'default' => $default_value
266 );
267
268 $result = $this->db->DDLAddField($this->db->prefix().$table, $attrname, $field_desc);
269 if ($result > 0) {
270 if ($unique) {
271 $sql = "ALTER TABLE ".$this->db->prefix().$table." ADD UNIQUE INDEX uk_".$table."_".$attrname." (".$attrname.")";
272 $resql = $this->db->query($sql, 1, 'dml');
273 }
274 return 1;
275 } else {
276 $this->error = $this->db->lasterror();
277 $this->errno = $this->db->lasterrno();
278 return -1;
279 }
280 } else {
281 return 0;
282 }
283 }
284
285 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
313 private function create_label($attrname, $label = '', $type = '', $pos = 0, $size = '', $elementtype = 'member', $unique = 0, $required = 0, $param = '', $alwayseditable = 0, $perms = '', $list = '-1', $help = '', $default = '', $computed = '', $entity = '', $langfile = '', $enabled = '1', $totalizable = 0, $printable = 0, $moreparams = array())
314 {
315 // phpcs:enable
316 global $conf, $user;
317
318 if ($elementtype == 'thirdparty') {
319 $elementtype = 'societe';
320 }
321 if ($elementtype == 'contact') {
322 $elementtype = 'socpeople';
323 }
324
325 // Clean parameters
326 if (empty($pos)) {
327 $pos = 0;
328 }
329 if (empty($list)) {
330 $list = '0';
331 }
332 if (empty($required)) {
333 $required = 0;
334 }
335 if (empty($unique)) {
336 $unique = 0;
337 }
338 if (empty($printable)) {
339 $printable = 0;
340 }
341 if (empty($alwayseditable)) {
342 $alwayseditable = 0;
343 }
344 if (empty($totalizable)) {
345 $totalizable = 0;
346 }
347
348 $css = '';
349 if (!empty($moreparams) && !empty($moreparams['css'])) {
350 $css = $moreparams['css'];
351 }
352 $csslist = '';
353 if (!empty($moreparams) && !empty($moreparams['csslist'])) {
354 $csslist = $moreparams['csslist'];
355 }
356 $cssview = '';
357 if (!empty($moreparams) && !empty($moreparams['cssview'])) {
358 $cssview = $moreparams['cssview'];
359 }
360
361 if (!empty($attrname) && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname) && !is_numeric($attrname)) {
362 if (is_array($param) && count($param) > 0) {
363 $params = serialize($param);
364 } elseif (strlen($param) > 0) {
365 $params = trim($param);
366 } else {
367 $params = '';
368 }
369
370 $sql = "INSERT INTO ".$this->db->prefix()."extrafields(";
371 $sql .= " name,";
372 $sql .= " label,";
373 $sql .= " type,";
374 $sql .= " pos,";
375 $sql .= " size,";
376 $sql .= " entity,";
377 $sql .= " elementtype,";
378 $sql .= " fieldunique,";
379 $sql .= " fieldrequired,";
380 $sql .= " param,";
381 $sql .= " alwayseditable,";
382 $sql .= " perms,";
383 $sql .= " langs,";
384 $sql .= " list,";
385 $sql .= " printable,";
386 $sql .= " fielddefault,";
387 $sql .= " fieldcomputed,";
388 $sql .= " fk_user_author,";
389 $sql .= " fk_user_modif,";
390 $sql .= " datec,";
391 $sql .= " enabled,";
392 $sql .= " help,";
393 $sql .= " totalizable,";
394 $sql .= " css,";
395 $sql .= " csslist,";
396 $sql .= " cssview";
397 $sql .= " )";
398 $sql .= " VALUES('".$this->db->escape($attrname)."',";
399 $sql .= " '".$this->db->escape($label)."',";
400 $sql .= " '".$this->db->escape($type)."',";
401 $sql .= " ".((int) $pos).",";
402 $sql .= " '".$this->db->escape($size)."',";
403 $sql .= " ".($entity === '' ? $conf->entity : $entity).",";
404 $sql .= " '".$this->db->escape($elementtype)."',";
405 $sql .= " ".((int) $unique).",";
406 $sql .= " ".((int) $required).",";
407 $sql .= " '".$this->db->escape($params)."',";
408 $sql .= " ".((int) $alwayseditable).",";
409 $sql .= " ".($perms ? "'".$this->db->escape($perms)."'" : "null").",";
410 $sql .= " ".($langfile ? "'".$this->db->escape($langfile)."'" : "null").",";
411 $sql .= " '".$this->db->escape($list)."',";
412 $sql .= " '".$this->db->escape($printable)."',";
413 $sql .= " ".($default ? "'".$this->db->escape($default)."'" : "null").",";
414 $sql .= " ".($computed ? "'".$this->db->escape($computed)."'" : "null").",";
415 $sql .= " ".(is_object($user) ? $user->id : 0).",";
416 $sql .= " ".(is_object($user) ? $user->id : 0).",";
417 $sql .= "'".$this->db->idate(dol_now())."',";
418 $sql .= " ".($enabled ? "'".$this->db->escape($enabled)."'" : "1").",";
419 $sql .= " ".($help ? "'".$this->db->escape($help)."'" : "null").",";
420 $sql .= " ".($totalizable ? 'TRUE' : 'FALSE').",";
421 $sql .= " ".($css ? "'".$this->db->escape($css)."'" : "null").",";
422 $sql .= " ".($csslist ? "'".$this->db->escape($csslist)."'" : "null").",";
423 $sql .= " ".($cssview ? "'".$this->db->escape($cssview)."'" : "null");
424 $sql .= ')';
425
426 dol_syslog(get_class($this)."::create_label", LOG_DEBUG);
427 if ($this->db->query($sql)) {
428 return 1;
429 } else {
430 $this->error = $this->db->lasterror();
431 $this->errno = $this->db->lasterrno();
432 return -1;
433 }
434 }
435 return -1;
436 }
437
445 public function delete($attrname, $elementtype = 'member')
446 {
447 if ($elementtype == 'thirdparty') {
448 $elementtype = 'societe';
449 }
450 if ($elementtype == 'contact') {
451 $elementtype = 'socpeople';
452 }
453
454 $table = $elementtype.'_extrafields';
455 if ($elementtype == 'categorie') {
456 $table = 'categories_extrafields';
457 }
458
459 $error = 0;
460
461 if (!empty($attrname) && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
462 $result = $this->delete_label($attrname, $elementtype);
463 if ($result < 0) {
464 $this->error = $this->db->lasterror();
465 $this->errors[] = $this->db->lasterror();
466 $error++;
467 }
468
469 if (!$error) {
470 $sql = "SELECT COUNT(rowid) as nb";
471 $sql .= " FROM ".$this->db->prefix()."extrafields";
472 $sql .= " WHERE elementtype = '".$this->db->escape($elementtype)."'";
473 $sql .= " AND name = '".$this->db->escape($attrname)."'";
474 //$sql.= " AND entity IN (0,".$conf->entity.")"; Do not test on entity here. We want to see if there is still on field remaning in other entities before deleting field in table
475 $resql = $this->db->query($sql);
476 if ($resql) {
477 $obj = $this->db->fetch_object($resql);
478 if ($obj->nb <= 0) {
479 $result = $this->db->DDLDropField($this->db->prefix().$table, $attrname); // This also drop the unique key
480 if ($result < 0) {
481 $this->error = $this->db->lasterror();
482 $this->errors[] = $this->db->lasterror();
483 $error++;
484 }
485 }
486 }
487 }
488
489 return $result;
490 } else {
491 return 0;
492 }
493 }
494
495 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
503 private function delete_label($attrname, $elementtype = 'member')
504 {
505 // phpcs:enable
506 global $conf;
507
508 if ($elementtype == 'thirdparty') {
509 $elementtype = 'societe';
510 }
511 if ($elementtype == 'contact') {
512 $elementtype = 'socpeople';
513 }
514
515 if (isset($attrname) && $attrname != '' && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
516 $sql = "DELETE FROM ".$this->db->prefix()."extrafields";
517 $sql .= " WHERE name = '".$this->db->escape($attrname)."'";
518 $sql .= " AND entity IN (0,".$conf->entity.')';
519 $sql .= " AND elementtype = '".$this->db->escape($elementtype)."'";
520
521 dol_syslog(get_class($this)."::delete_label", LOG_DEBUG);
522 $resql = $this->db->query($sql);
523 if ($resql) {
524 return 1;
525 } else {
526 dol_print_error($this->db);
527 return -1;
528 }
529 } else {
530 return 0;
531 }
532 }
533
561 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())
562 {
563 global $action, $hookmanager;
564
565 if ($elementtype == 'thirdparty') {
566 $elementtype = 'societe';
567 }
568 if ($elementtype == 'contact') {
569 $elementtype = 'socpeople';
570 }
571
572 $table = $elementtype.'_extrafields';
573 if ($elementtype == 'categorie') {
574 $table = 'categories_extrafields';
575 }
576
577 if (isset($attrname) && $attrname != '' && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
578 // Clean parameters
579 if ($type == 'boolean') {
580 $typedb = 'int';
581 $lengthdb = '1';
582 } elseif ($type == 'price') {
583 $typedb = 'double';
584 $lengthdb = '24,8';
585 } elseif ($type == 'pricecy') {
586 $typedb = 'varchar';
587 $lengthdb = '64';
588 } elseif ($type == 'phone') {
589 $typedb = 'varchar';
590 $lengthdb = '20';
591 } elseif ($type == 'mail' || $type == 'ip' || $type == 'icon') {
592 $typedb = 'varchar';
593 $lengthdb = '128';
594 } elseif ($type == 'url') {
595 $typedb = 'varchar';
596 $lengthdb = '255';
597 } elseif (($type == 'select') || ($type == 'sellist') || ($type == 'radio') || ($type == 'checkbox') || ($type == 'chkbxlst')) {
598 $typedb = 'varchar';
599 $lengthdb = '255';
600 } elseif ($type == 'html') {
601 $typedb = 'text';
602 $lengthdb = $length;
603 } elseif ($type == 'link') {
604 $typedb = 'int';
605 $lengthdb = '11';
606 } elseif ($type == 'password') {
607 $typedb = 'varchar';
608 $lengthdb = '128';
609 } else {
610 $typedb = $type;
611 $lengthdb = $length;
612 }
613 $field_desc = array('type'=>$typedb, 'value'=>$lengthdb, 'null'=>($required ? 'NOT NULL' : 'NULL'), 'default'=>$default);
614
615 // If property has a computed formula, it must not be a required or unique field
616 if (!empty($computed)) {
617 $required = 0;
618 $unique = 0;
619 }
620
621 if (is_object($hookmanager)) {
622 $hookmanager->initHooks(array('extrafieldsdao'));
623 $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);
624 $reshook = $hookmanager->executeHooks('updateExtrafields', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
625
626 if ($reshook < 0) {
627 $this->error = $this->db->lasterror();
628 return -1;
629 }
630 }
631
632 if ($type != 'separate') { // No table update when separate type
633 $result = $this->db->DDLUpdateField($this->db->prefix().$table, $attrname, $field_desc);
634 }
635 if ($result > 0 || $type == 'separate') {
636 if ($label) {
637 $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);
638 }
639 if ($result > 0) {
640 $sql = '';
641 if ($unique) {
642 $sql = "ALTER TABLE ".$this->db->prefix().$table." ADD UNIQUE INDEX uk_".$table."_".$attrname." (".$attrname.")";
643 } else {
644 $sql = "ALTER TABLE ".$this->db->prefix().$table." DROP INDEX IF EXISTS uk_".$table."_".$attrname;
645 }
646 dol_syslog(get_class($this).'::update', LOG_DEBUG);
647 $resql = $this->db->query($sql, 1, 'dml');
648 /*if ($resql < 0) {
649 $this->error = $this->db->lasterror();
650 return -1;
651 }*/
652 return 1;
653 } else {
654 $this->error = $this->db->lasterror();
655 return -1;
656 }
657 } else {
658 $this->error = $this->db->lasterror();
659 return -1;
660 }
661 } else {
662 return 0;
663 }
664 }
665
666 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
694 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())
695 {
696 // phpcs:enable
697 global $conf, $user;
698 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);
699
700 // Clean parameters
701 if ($elementtype == 'thirdparty') {
702 $elementtype = 'societe';
703 }
704 if ($elementtype == 'contact') {
705 $elementtype = 'socpeople';
706 }
707
708 if (empty($pos)) {
709 $pos = 0;
710 }
711 if (empty($list)) {
712 $list = '0';
713 }
714 if (empty($totalizable)) {
715 $totalizable = 0;
716 }
717 if (empty($required)) {
718 $required = 0;
719 }
720 if (empty($unique)) {
721 $unique = 0;
722 }
723 if (empty($alwayseditable)) {
724 $alwayseditable = 0;
725 }
726
727 $css = '';
728 if (!empty($moreparams) && !empty($moreparams['css'])) {
729 $css = $moreparams['css'];
730 }
731 $csslist = '';
732 if (!empty($moreparams) && !empty($moreparams['csslist'])) {
733 $csslist = $moreparams['csslist'];
734 }
735 $cssview = '';
736 if (!empty($moreparams) && !empty($moreparams['cssview'])) {
737 $cssview = $moreparams['cssview'];
738 }
739
740 if (isset($attrname) && $attrname != '' && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
741 $this->db->begin();
742
743 if (is_array($param) && count($param) > 0) {
744 $params = serialize($param);
745 } elseif (is_array($param)) {
746 $params = '';
747 } elseif (strlen($param) > 0) {
748 $params = trim($param);
749 } else {
750 $params = '';
751 }
752
753 if ($entity === '' || $entity != '0') {
754 // We dont want on all entities, we delete all and current
755 $sql_del = "DELETE FROM ".$this->db->prefix()."extrafields";
756 $sql_del .= " WHERE name = '".$this->db->escape($attrname)."'";
757 $sql_del .= " AND entity IN (0, ".($entity === '' ? $conf->entity : $entity).")";
758 $sql_del .= " AND elementtype = '".$this->db->escape($elementtype)."'";
759 } else {
760 // We want on all entities ($entities = '0'), we delete on all only (we keep setup specific to each entity)
761 $sql_del = "DELETE FROM ".$this->db->prefix()."extrafields";
762 $sql_del .= " WHERE name = '".$this->db->escape($attrname)."'";
763 $sql_del .= " AND entity = 0";
764 $sql_del .= " AND elementtype = '".$this->db->escape($elementtype)."'";
765 }
766 $resql1 = $this->db->query($sql_del);
767
768 $sql = "INSERT INTO ".$this->db->prefix()."extrafields(";
769 $sql .= " name,"; // This is code
770 $sql .= " entity,";
771 $sql .= " label,";
772 $sql .= " type,";
773 $sql .= " size,";
774 $sql .= " elementtype,";
775 $sql .= " fieldunique,";
776 $sql .= " fieldrequired,";
777 $sql .= " perms,";
778 $sql .= " langs,";
779 $sql .= " pos,";
780 $sql .= " alwayseditable,";
781 $sql .= " param,";
782 $sql .= " list,";
783 $sql .= " printable,";
784 $sql .= " totalizable,";
785 $sql .= " fielddefault,";
786 $sql .= " fieldcomputed,";
787 $sql .= " fk_user_author,";
788 $sql .= " fk_user_modif,";
789 $sql .= " datec,";
790 $sql .= " enabled,";
791 $sql .= " help,";
792 $sql .= " css,";
793 $sql .= " csslist,";
794 $sql .= " cssview";
795 $sql .= ") VALUES (";
796 $sql .= "'".$this->db->escape($attrname)."',";
797 $sql .= " ".($entity === '' ? $conf->entity : $entity).",";
798 $sql .= " '".$this->db->escape($label)."',";
799 $sql .= " '".$this->db->escape($type)."',";
800 $sql .= " '".$this->db->escape($size)."',";
801 $sql .= " '".$this->db->escape($elementtype)."',";
802 $sql .= " ".$unique.",";
803 $sql .= " ".$required.",";
804 $sql .= " ".($perms ? "'".$this->db->escape($perms)."'" : "null").",";
805 $sql .= " ".($langfile ? "'".$this->db->escape($langfile)."'" : "null").",";
806 $sql .= " ".$pos.",";
807 $sql .= " '".$this->db->escape($alwayseditable)."',";
808 $sql .= " '".$this->db->escape($params)."',";
809 $sql .= " '".$this->db->escape($list)."', ";
810 $sql .= " '".$this->db->escape($printable)."', ";
811 $sql .= " ".($totalizable ? 'TRUE' : 'FALSE').",";
812 $sql .= " ".(($default != '') ? "'".$this->db->escape($default)."'" : "null").",";
813 $sql .= " ".($computed ? "'".$this->db->escape($computed)."'" : "null").",";
814 $sql .= " ".$user->id.",";
815 $sql .= " ".$user->id.",";
816 $sql .= "'".$this->db->idate(dol_now())."',";
817 $sql .= "'".$this->db->escape($enabled)."',";
818 $sql .= " ".($help ? "'".$this->db->escape($help)."'" : "null").",";
819 $sql .= " ".($css ? "'".$this->db->escape($css)."'" : "null").",";
820 $sql .= " ".($csslist ? "'".$this->db->escape($csslist)."'" : "null").",";
821 $sql .= " ".($cssview ? "'".$this->db->escape($cssview)."'" : "null");
822 $sql .= ")";
823
824 $resql2 = $this->db->query($sql);
825
826 if ($resql1 && $resql2) {
827 $this->db->commit();
828 return 1;
829 } else {
830 $this->db->rollback();
831 dol_print_error($this->db);
832 return -1;
833 }
834 } else {
835 return 0;
836 }
837 }
838
839
840 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
848 public function fetch_name_optionals_label($elementtype, $forceload = false)
849 {
850 // phpcs:enable
851 global $conf;
852
853 if (empty($elementtype)) {
854 return array();
855 }
856
857 if ($elementtype == 'thirdparty') {
858 $elementtype = 'societe';
859 }
860 if ($elementtype == 'contact') {
861 $elementtype = 'socpeople';
862 }
863 if ($elementtype == 'order_supplier') {
864 $elementtype = 'commande_fournisseur';
865 }
866
867 // Test cache $this->attributes[$elementtype]['loaded'] to see if we must do something
868 // TODO
869
870 $array_name_label = array();
871
872 // 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
873 $sql = "SELECT rowid, name, label, type, size, elementtype, fieldunique, fieldrequired, param, pos, alwayseditable, perms, langs, list, printable, totalizable, fielddefault, fieldcomputed, entity, enabled, help,";
874 $sql .= " css, cssview, csslist";
875 $sql .= " FROM ".$this->db->prefix()."extrafields";
876 //$sql.= " WHERE entity IN (0,".$conf->entity.")"; // Filter is done later
877 if ($elementtype && $elementtype != 'all') {
878 $sql .= " WHERE elementtype = '".$this->db->escape($elementtype)."'"; // Filed with object->table_element
879 }
880 $sql .= " ORDER BY pos";
881
882 $resql = $this->db->query($sql);
883 if ($resql) {
884 $count = 0;
885 if ($this->db->num_rows($resql)) {
886 while ($tab = $this->db->fetch_object($resql)) {
887 if ($tab->entity != 0 && $tab->entity != $conf->entity) {
888 // 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
889 if ($tab->fieldrequired && is_null($tab->fielddefault)) {
890 $this->attributes[$tab->elementtype]['mandatoryfieldsofotherentities'][$tab->name] = $tab->type;
891 }
892 continue;
893 }
894
895 // We can add this attribute to object. TODO Remove this and return $this->attributes[$elementtype]['label']
896 if ($tab->type != 'separate') {
897 $array_name_label[$tab->name] = $tab->label;
898 }
899
900 $this->attributes[$tab->elementtype]['type'][$tab->name] = $tab->type;
901 $this->attributes[$tab->elementtype]['label'][$tab->name] = $tab->label;
902 $this->attributes[$tab->elementtype]['size'][$tab->name] = $tab->size;
903 $this->attributes[$tab->elementtype]['elementtype'][$tab->name] = $tab->elementtype;
904 $this->attributes[$tab->elementtype]['default'][$tab->name] = $tab->fielddefault;
905 $this->attributes[$tab->elementtype]['computed'][$tab->name] = $tab->fieldcomputed;
906 $this->attributes[$tab->elementtype]['unique'][$tab->name] = $tab->fieldunique;
907 $this->attributes[$tab->elementtype]['required'][$tab->name] = $tab->fieldrequired;
908 $this->attributes[$tab->elementtype]['param'][$tab->name] = ($tab->param ? jsonOrUnserialize($tab->param) : '');
909 $this->attributes[$tab->elementtype]['pos'][$tab->name] = $tab->pos;
910 $this->attributes[$tab->elementtype]['alwayseditable'][$tab->name] = $tab->alwayseditable;
911 $this->attributes[$tab->elementtype]['perms'][$tab->name] = ((is_null($tab->perms) || strlen($tab->perms) == 0) ? 1 : $tab->perms);
912 $this->attributes[$tab->elementtype]['langfile'][$tab->name] = $tab->langs;
913 $this->attributes[$tab->elementtype]['list'][$tab->name] = $tab->list;
914 $this->attributes[$tab->elementtype]['printable'][$tab->name] = $tab->printable;
915 $this->attributes[$tab->elementtype]['totalizable'][$tab->name] = ($tab->totalizable ? 1 : 0);
916 $this->attributes[$tab->elementtype]['entityid'][$tab->name] = $tab->entity;
917 $this->attributes[$tab->elementtype]['enabled'][$tab->name] = $tab->enabled;
918 $this->attributes[$tab->elementtype]['help'][$tab->name] = $tab->help;
919 $this->attributes[$tab->elementtype]['css'][$tab->name] = $tab->css;
920 $this->attributes[$tab->elementtype]['cssview'][$tab->name] = $tab->cssview;
921 $this->attributes[$tab->elementtype]['csslist'][$tab->name] = $tab->csslist;
922
923 $this->attributes[$tab->elementtype]['loaded'] = 1;
924 $count++;
925 }
926 }
927 if ($elementtype) {
928 $this->attributes[$elementtype]['loaded'] = 1; // Note: If nothing is found, we also set the key 'loaded' to 1.
929 $this->attributes[$elementtype]['count'] = $count;
930 }
931 } else {
932 $this->error = $this->db->lasterror();
933 dol_syslog(get_class($this)."::fetch_name_optionals_label ".$this->error, LOG_ERR);
934 }
935
936 return $array_name_label;
937 }
938
939
955 public function showInputField($key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '', $objectid = 0, $extrafieldsobjectkey = '', $mode = 0)
956 {
957 global $conf, $langs, $form;
958
959 if (!is_object($form)) {
960 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
961 $form = new Form($this->db);
962 }
963
964 $out = '';
965
966 if (!preg_match('/options_$/', $keyprefix)) { // Because we work on extrafields, we add 'options_' to prefix if not already added
967 $keyprefix = $keyprefix.'options_';
968 }
969
970 if (empty($extrafieldsobjectkey)) {
971 dol_syslog(get_class($this).'::showInputField extrafieldsobjectkey required', LOG_ERR);
972 return 'BadValueForParamExtraFieldsObjectKey';
973 }
974
975 $label = $this->attributes[$extrafieldsobjectkey]['label'][$key];
976 $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
977 $size = $this->attributes[$extrafieldsobjectkey]['size'][$key];
978 $default = $this->attributes[$extrafieldsobjectkey]['default'][$key];
979 $computed = $this->attributes[$extrafieldsobjectkey]['computed'][$key];
980 $unique = $this->attributes[$extrafieldsobjectkey]['unique'][$key];
981 $required = $this->attributes[$extrafieldsobjectkey]['required'][$key];
982 $param = $this->attributes[$extrafieldsobjectkey]['param'][$key];
983 $perms = dol_eval($this->attributes[$extrafieldsobjectkey]['perms'][$key], 1, 1, '2');
984 $langfile = $this->attributes[$extrafieldsobjectkey]['langfile'][$key];
985 $list = dol_eval($this->attributes[$extrafieldsobjectkey]['list'][$key], 1, 1, '2');
986 $totalizable = $this->attributes[$extrafieldsobjectkey]['totalizable'][$key];
987 $help = $this->attributes[$extrafieldsobjectkey]['help'][$key];
988 $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)
989
990 //var_dump('key='.$key.' '.$value.' '.$moreparam.' '.$keysuffix.' '.$keyprefix.' '.$objectid.' '.$extrafieldsobjectkey.' '.$mode);
991 //var_dump('label='.$label.' type='.$type.' param='.var_export($param, 1));
992
993 if ($computed) {
994 if (!preg_match('/^search_/', $keyprefix)) {
995 return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
996 } else {
997 return '';
998 }
999 }
1000
1001 //
1002 // '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'
1003 if (empty($morecss)) {
1004 // Add automatic css
1005 if ($type == 'date') {
1006 $morecss = 'minwidth100imp';
1007 } elseif ($type == 'datetime' || $type == 'datetimegmt' || $type == 'link') {
1008 $morecss = 'minwidth200imp';
1009 } elseif (in_array($type, array('int', 'integer', 'double', 'price'))) {
1010 $morecss = 'maxwidth75';
1011 } elseif ($type == 'password') {
1012 $morecss = 'maxwidth100';
1013 } elseif ($type == 'url') {
1014 $morecss = 'minwidth400';
1015 } elseif ($type == 'boolean') {
1016 $morecss = '';
1017 } elseif ($type == 'radio') {
1018 $morecss = 'width25';
1019 } else {
1020 if (empty($size) || round((float) $size) < 12) {
1021 $morecss = 'minwidth100';
1022 } elseif (round((float) $size) <= 48) {
1023 $morecss = 'minwidth200';
1024 } else {
1025 $morecss = 'minwidth400';
1026 }
1027 }
1028 // If css forced in attribute, we use this one
1029 if (!empty($this->attributes[$extrafieldsobjectkey]['css'][$key])) {
1030 $morecss = $this->attributes[$extrafieldsobjectkey]['css'][$key];
1031 }
1032 }
1033
1034 if (in_array($type, array('date'))) {
1035 $tmp = explode(',', $size);
1036 $newsize = $tmp[0];
1037 $showtime = 0;
1038
1039 // Do not show current date when field not required (see selectDate() method)
1040 if (!$required && $value == '') {
1041 $value = '-1';
1042 }
1043
1044 if ($mode == 1) {
1045 // search filter on a date extrafield shows two inputs to select a date range
1046 $prefill = array(
1047 'start' => isset($value['start']) ? $value['start'] : '',
1048 'end' => isset($value['end']) ? $value['end'] : ''
1049 );
1050 $out = '<div ' . ($moreparam ? $moreparam : '') . '><div class="nowrap">';
1051 $out .= $form->selectDate($prefill['start'], $keyprefix.$key.$keysuffix.'_start', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("From"));
1052 $out .= '</div><div class="nowrap">';
1053 $out .= $form->selectDate($prefill['end'], $keyprefix.$key.$keysuffix.'_end', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"));
1054 $out .= '</div></div>';
1055 } else {
1056 // TODO Must also support $moreparam
1057 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
1058 }
1059 } elseif (in_array($type, array('datetime', 'datetimegmt'))) {
1060 $tmp = explode(',', $size);
1061 $newsize = $tmp[0];
1062 $showtime = 1;
1063
1064 // Do not show current date when field not required (see selectDate() method)
1065 if (!$required && $value == '') {
1066 $value = '-1';
1067 }
1068
1069 if ($mode == 1) {
1070 // search filter on a date extrafield shows two inputs to select a date range
1071 $prefill = array(
1072 'start' => isset($value['start']) ? $value['start'] : '',
1073 'end' => isset($value['end']) ? $value['end'] : ''
1074 );
1075 $out = '<div ' . ($moreparam ? $moreparam : '') . '><div class="nowrap">';
1076 $out .= $form->selectDate($prefill['start'], $keyprefix.$key.$keysuffix.'_start', 1, 1, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("From"), 'tzuserrel');
1077 $out .= '</div><div class="nowrap">';
1078 $out .= $form->selectDate($prefill['end'], $keyprefix.$key.$keysuffix.'_end', 1, 1, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
1079 $out .= '</div></div>';
1080 } else {
1081 // TODO Must also support $moreparam
1082 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
1083 }
1084 } elseif (in_array($type, array('int', 'integer'))) {
1085 $tmp = explode(',', $size);
1086 $newsize = $tmp[0];
1087 $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 : '').'>';
1088 } elseif (preg_match('/varchar/', $type)) {
1089 $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 : '').'>';
1090 } elseif (in_array($type, array('mail', 'ip', 'phone', 'url'))) {
1091 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
1092 } elseif ($type == 'icon') {
1093 /* External lib inclusion are not allowed in backoffice. Also lib is included several time if there is several icon file.
1094 Some code must be added into main when MAIN_ADD_ICONPICKER_JS is set to add of lib in html header
1095 $out ='<link rel="stylesheet" href="'.dol_buildpath('/myfield/css/fontawesome-iconpicker.min.css', 1).'">';
1096 $out.='<script src="'.dol_buildpath('/myfield/js/fontawesome-iconpicker.min.js', 1).'"></script>';
1097 */
1098 $out.= '<input type="text" class="form-control icp icp-auto iconpicker-element iconpicker-input flat '.$morecss.' maxwidthonsmartphone"';
1099 $out.= ' name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
1100 if (getDolGlobalInt('MAIN_ADD_ICONPICKER_JS')) {
1101 $out.='<script>';
1102 $options="{ title: '<b>".$langs->trans("IconFieldSelector")."</b>', placement: 'right', showFooter: false, templates: {";
1103 $options.="iconpicker: '<div class=\"iconpicker\"><div style=\"background-color:#EFEFEF;\" class=\"iconpicker-items\"></div></div>',";
1104 $options.="iconpickerItem: '<a role=\"button\" href=\"#\" class=\"iconpicker-item\" style=\"background-color:#DDDDDD;\"><i></i></a>',";
1105 // $options.="buttons: '<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-cancel btn btn-default btn-sm\">".$langs->trans("Cancel")."</button>";
1106 // $options.="<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-accept btn btn-primary btn-sm\">".$langs->trans("Save")."</button>',";
1107 $options.="footer: '<div class=\"popover-footer\" style=\"background-color:#EFEFEF;\"></div>',";
1108 $options.="search: '<input type=\"search\" class\"form-control iconpicker-search\" placeholder=\"".$langs->trans("TypeToFilter")."\" />',";
1109 $options.="popover: '<div class=\"iconpicker-popover popover\">";
1110 $options.=" <div class=\"arrow\" ></div>";
1111 $options.=" <div class=\"popover-title\" style=\"text-align:center;background-color:#EFEFEF;\"></div>";
1112 $options.=" <div class=\"popover-content \" ></div>";
1113 $options.="</div>'}}";
1114 $out.="$('#".$keyprefix.$key.$keysuffix."').iconpicker(".$options.");";
1115 $out.='</script>';
1116 }
1117 } elseif ($type == 'text') {
1118 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
1119 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
1120 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
1121 $out = $doleditor->Create(1);
1122 } else {
1123 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
1124 }
1125 } elseif ($type == 'html') {
1126 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
1127 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
1128 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_5, '90%');
1129 $out = $doleditor->Create(1);
1130 } else {
1131 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
1132 }
1133 } elseif ($type == 'boolean') {
1134 if (empty($mode)) {
1135 $checked = '';
1136 if (!empty($value)) {
1137 $checked = ' checked value="1" ';
1138 } else {
1139 $checked = ' value="1" ';
1140 }
1141
1142 $out = '<input type="checkbox" class="flat valignmiddle'.($morecss ? ' '.$morecss : '').' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>';
1143 } else {
1144 $out = $form->selectyesno($keyprefix.$key.$keysuffix, $value, 1, false, 1);
1145 }
1146 $out .= '<input type="hidden" name="'.$keyprefix.$key.$keysuffix.'_boolean" value="1">'; // A hidden field ending with "_boolean" that is always set to 1.
1147 } elseif ($type == 'price') {
1148 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
1149 $value = price($value);
1150 }
1151 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> '.$langs->getCurrencySymbol($conf->currency);
1152 } elseif ($type == 'pricecy') {
1153 $currency = $conf->currency;
1154 if (!empty($value)) {
1155 // $value in memory is a php string like '10.01:USD'
1156 $pricetmp = explode(':', $value);
1157 $currency = !empty($pricetmp[1]) ? $pricetmp[1] : $conf->currency;
1158 $value = price($pricetmp[0]);
1159 }
1160 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
1161 $out .= $form->selectCurrency($currency, $keyprefix.$key.$keysuffix.'currency_id');
1162 } elseif ($type == 'double') {
1163 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
1164 $value = price($value);
1165 }
1166 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
1167 } elseif ($type == 'select') {
1168 $out = '';
1169 if ($mode) {
1170 $options = array();
1171 foreach ($param['options'] as $okey => $val) {
1172 if ((string) $okey == '') {
1173 continue;
1174 }
1175
1176 $valarray = explode('|', $val);
1177 $val = $valarray[0];
1178
1179 if ($langfile && $val) {
1180 $options[$okey] = $langs->trans($val);
1181 } else {
1182 $options[$okey] = $val;
1183 }
1184 }
1185 $selected = array();
1186 if (!is_array($value)) {
1187 $selected = explode(',', $value);
1188 }
1189
1190 $out .= $form->multiselectarray($keyprefix.$key.$keysuffix, $options, $selected, 0, 0, $morecss, 0, 0, '', '', '', !empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2'));
1191 } else {
1192 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
1193 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1194 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
1195 }
1196
1197 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
1198 $out .= '<option value="0">&nbsp;</option>';
1199 foreach ($param['options'] as $key2 => $val2) {
1200 if ((string) $key2 == '') {
1201 continue;
1202 }
1203 $valarray = explode('|', $val2);
1204 $val2 = $valarray[0];
1205 $parent = '';
1206 if (!empty($valarray[1])) {
1207 $parent = $valarray[1];
1208 }
1209 $out .= '<option value="'.$key2.'"';
1210 $out .= (((string) $value == (string) $key2) ? ' selected' : '');
1211 $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
1212 $out .= '>';
1213 if ($langfile && $val2) {
1214 $out .= $langs->trans($val2);
1215 } else {
1216 $out .= $val2;
1217 }
1218 $out .= '</option>';
1219 }
1220 $out .= '</select>';
1221 }
1222 } elseif ($type == 'sellist') {
1223 $out = '';
1224 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
1225 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1226 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
1227 }
1228
1229 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
1230 if (is_array($param['options'])) {
1231 $param_list = array_keys($param['options']);
1232 $InfoFieldList = explode(":", $param_list[0]);
1233 $parentName = '';
1234 $parentField = '';
1235 // 0 : tableName
1236 // 1 : label field name
1237 // 2 : key fields name (if differ of rowid)
1238 // 3 : key field parent (for dependent lists)
1239 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
1240 // 5 : id category type
1241 // 6 : ids categories list separated by comma for category root
1242 // 7 : sort by (to be close to common object)
1243 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
1244
1245
1246 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
1247 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1248 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
1249 } else {
1250 $keyList = $InfoFieldList[2].' as rowid';
1251 }
1252 }
1253 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
1254 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
1255 $keyList .= ', '.$parentField;
1256 }
1257
1258 $filter_categorie = false;
1259 if (count($InfoFieldList) > 5) {
1260 if ($InfoFieldList[0] == 'categorie') {
1261 $filter_categorie = true;
1262 }
1263 }
1264
1265 if ($filter_categorie === false) {
1266 $fields_label = explode('|', $InfoFieldList[1]);
1267 if (is_array($fields_label)) {
1268 $keyList .= ', ';
1269 $keyList .= implode(', ', $fields_label);
1270 }
1271
1272 $sqlwhere = '';
1273 $sql = "SELECT ".$keyList;
1274 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
1275 if (!empty($InfoFieldList[4])) {
1276 // can use current entity filter
1277 if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
1278 $InfoFieldList[4] = str_replace('$ENTITY$', $conf->entity, $InfoFieldList[4]);
1279 }
1280 // can use SELECT request
1281 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
1282 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
1283 }
1284
1285 // current object id can be use into filter
1286 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
1287 $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
1288 } else {
1289 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
1290 }
1291 //We have to join on extrafield table
1292 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1293 $sql .= ' as main, '.$this->db->prefix().$InfoFieldList[0].'_extrafields as extra';
1294 $sqlwhere .= " WHERE extra.fk_object=main.".$InfoFieldList[2]." AND ".$InfoFieldList[4];
1295 } else {
1296 $sqlwhere .= " WHERE ".$InfoFieldList[4];
1297 }
1298 } else {
1299 $sqlwhere .= ' WHERE 1=1';
1300 }
1301 // Some tables may have field, some other not. For the moment we disable it.
1302 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
1303 $sqlwhere .= ' AND entity = '.((int) $conf->entity);
1304 }
1305 $sql .= $sqlwhere;
1306 //print $sql;
1307
1308 $sql .= ' ORDER BY '.implode(', ', $fields_label);
1309
1310 dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
1311 $resql = $this->db->query($sql);
1312 if ($resql) {
1313 $out .= '<option value="0">&nbsp;</option>';
1314 $num = $this->db->num_rows($resql);
1315 $i = 0;
1316 while ($i < $num) {
1317 $labeltoshow = '';
1318 $obj = $this->db->fetch_object($resql);
1319
1320 // Several field into label (eq table:code|label:rowid)
1321 $notrans = false;
1322 $fields_label = explode('|', $InfoFieldList[1]);
1323 if (is_array($fields_label) && count($fields_label) > 1) {
1324 $notrans = true;
1325 foreach ($fields_label as $field_toshow) {
1326 $labeltoshow .= $obj->$field_toshow.' ';
1327 }
1328 } else {
1329 $labeltoshow = $obj->{$InfoFieldList[1]};
1330 }
1331 $labeltoshow = $labeltoshow;
1332
1333 if ($value == $obj->rowid) {
1334 if (!$notrans) {
1335 foreach ($fields_label as $field_toshow) {
1336 $translabel = $langs->trans($obj->$field_toshow);
1337 $labeltoshow = $translabel.' ';
1338 }
1339 }
1340 $out .= '<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
1341 } else {
1342 if (!$notrans) {
1343 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
1344 $labeltoshow = $translabel;
1345 }
1346 if (empty($labeltoshow)) {
1347 $labeltoshow = '(not defined)';
1348 }
1349
1350 if (!empty($InfoFieldList[3]) && $parentField) {
1351 $parent = $parentName.':'.$obj->{$parentField};
1352 }
1353
1354 $out .= '<option value="'.$obj->rowid.'"';
1355 $out .= ($value == $obj->rowid ? ' selected' : '');
1356 $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
1357 $out .= '>'.$labeltoshow.'</option>';
1358 }
1359
1360 $i++;
1361 }
1362 $this->db->free($resql);
1363 } else {
1364 print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
1365 }
1366 } else {
1367 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
1368 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
1369 $out .= '<option value="0">&nbsp;</option>';
1370 if (is_array($data)) {
1371 foreach ($data as $data_key => $data_value) {
1372 $out .= '<option value="'.$data_key.'"';
1373 $out .= ($value == $data_key ? ' selected' : '');
1374 $out .= '>'.$data_value.'</option>';
1375 }
1376 }
1377 }
1378 }
1379 $out .= '</select>';
1380 } elseif ($type == 'checkbox') {
1381 $value_arr = $value;
1382 if (!is_array($value)) {
1383 $value_arr = explode(',', $value);
1384 }
1385 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ? null : $param['options']), $value_arr, '', 0, '', 0, '100%');
1386 } elseif ($type == 'radio') {
1387 $out = '';
1388 foreach ($param['options'] as $keyopt => $val) {
1389 $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
1390 $out .= ' value="'.$keyopt.'"';
1391 $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
1392 $out .= ($value == $keyopt ? 'checked' : '');
1393 $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$langs->trans($val).'</label><br>';
1394 }
1395 } elseif ($type == 'chkbxlst') {
1396 if (is_array($value)) {
1397 $value_arr = $value;
1398 } else {
1399 $value_arr = explode(',', $value);
1400 }
1401
1402 if (is_array($param['options'])) {
1403 $param_list = array_keys($param['options']);
1404 $InfoFieldList = explode(":", $param_list[0]);
1405 $parentName = '';
1406 $parentField = '';
1407 // 0 : tableName
1408 // 1 : label field name
1409 // 2 : key fields name (if differ of rowid)
1410 // 3 : key field parent (for dependent lists)
1411 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
1412 // 5 : id category type
1413 // 6 : ids categories list separated by comma for category root
1414 // 7 : sort by (to be close to common object)
1415 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
1416
1417 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
1418 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
1419 $keyList .= ', '.$parentField;
1420 }
1421 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
1422 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1423 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
1424 } else {
1425 $keyList = $InfoFieldList[2].' as rowid';
1426 }
1427 }
1428
1429 $filter_categorie = false;
1430 if (count($InfoFieldList) > 5) {
1431 if ($InfoFieldList[0] == 'categorie') {
1432 $filter_categorie = true;
1433 }
1434 }
1435
1436 if ($filter_categorie === false) {
1437 $fields_label = explode('|', $InfoFieldList[1]);
1438 if (is_array($fields_label)) {
1439 $keyList .= ', ';
1440 $keyList .= implode(', ', $fields_label);
1441 }
1442
1443 $sqlwhere = '';
1444 $sql = "SELECT ".$keyList;
1445 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
1446 if (!empty($InfoFieldList[4])) {
1447 // can use current entity filter
1448 if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
1449 $InfoFieldList[4] = str_replace('$ENTITY$', $conf->entity, $InfoFieldList[4]);
1450 }
1451 // can use SELECT request
1452 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
1453 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
1454 }
1455
1456 // current object id can be use into filter
1457 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
1458 $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
1459 } elseif (preg_match("#^.*list.php$#", $_SERVER["PHP_SELF"])) {
1460 // Pattern for word=$ID$
1461 $word = '\b[a-zA-Z0-9-\.-_]+\b=\$ID\$';
1462
1463 // Removing space arount =, ( and )
1464 $InfoFieldList[4] = preg_replace('# *(=|\‍(|\‍)) *#', '$1', $InfoFieldList[4]);
1465
1466 $nbPreg = 1;
1467 // While we have parenthesis
1468 while ($nbPreg != 0) {
1469 // Init des compteurs
1470 $nbPregRepl = $nbPregSel = 0;
1471 // On retire toutes les parenthèses sans = avant
1472 $InfoFieldList[4] = preg_replace('#([^=])(\‍([^)^(]*('.$word.')[^)^(]*\‍))#', '$1 $3 ', $InfoFieldList[4], -1, $nbPregRepl);
1473 // On retire les espaces autour des = et parenthèses
1474 $InfoFieldList[4] = preg_replace('# *(=|\‍(|\‍)) *#', '$1', $InfoFieldList[4]);
1475 // On retire toutes les parenthèses avec = avant
1476 $InfoFieldList[4] = preg_replace('#\b[a-zA-Z0-9-\.-_]+\b=\‍([^)^(]*('.$word.')[^)^(]*\‍)#', '$1 ', $InfoFieldList[4], -1, $nbPregSel);
1477 // On retire les espaces autour des = et parenthèses
1478 $InfoFieldList[4] = preg_replace('# *(=|\‍(|\‍)) *#', '$1', $InfoFieldList[4]);
1479
1480 // Calcul du compteur général pour la boucle
1481 $nbPreg = $nbPregRepl + $nbPregSel;
1482 }
1483
1484 // Si l'on a un AND ou un OR, avant ou après
1485 $matchCondition = array();
1486 preg_match('#(AND|OR|) *('.$word.') *(AND|OR|)#', $InfoFieldList[4], $matchCondition);
1487 while (!empty($matchCondition[0])) {
1488 // If the two sides differ but are not empty
1489 if (!empty($matchCondition[1]) && !empty($matchCondition[3]) && $matchCondition[1] != $matchCondition[3]) {
1490 // Nobody sain would do that without parentheses
1491 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
1492 } else {
1493 if (!empty($matchCondition[1])) {
1494 $boolCond = (($matchCondition[1] == "AND") ? ' AND TRUE ' : ' OR FALSE ');
1495 $InfoFieldList[4] = str_replace($matchCondition[0], $boolCond.$matchCondition[3], $InfoFieldList[4]);
1496 } elseif (!empty($matchCondition[3])) {
1497 $boolCond = (($matchCondition[3] == "AND") ? ' TRUE AND ' : ' FALSE OR');
1498 $InfoFieldList[4] = str_replace($matchCondition[0], $boolCond, $InfoFieldList[4]);
1499 } else {
1500 $InfoFieldList[4] = " TRUE ";
1501 }
1502 }
1503
1504 // Si l'on a un AND ou un OR, avant ou après
1505 preg_match('#(AND|OR|) *('.$word.') *(AND|OR|)#', $InfoFieldList[4], $matchCondition);
1506 }
1507 } else {
1508 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
1509 }
1510
1511 // We have to join on extrafield table
1512 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1513 $sql .= ' as main, '.$this->db->prefix().$InfoFieldList[0].'_extrafields as extra';
1514 $sqlwhere .= " WHERE extra.fk_object=main.".$InfoFieldList[2]." AND ".$InfoFieldList[4];
1515 } else {
1516 $sqlwhere .= " WHERE ".$InfoFieldList[4];
1517 }
1518 } else {
1519 $sqlwhere .= ' WHERE 1=1';
1520 }
1521 // Some tables may have field, some other not. For the moment we disable it.
1522 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
1523 $sqlwhere .= " AND entity = ".((int) $conf->entity);
1524 }
1525 // $sql.=preg_replace('/^ AND /','',$sqlwhere);
1526 // print $sql;
1527
1528 $sql .= $sqlwhere;
1529 $sql .= ' ORDER BY '.implode(', ', $fields_label);
1530
1531 dol_syslog(get_class($this).'::showInputField type=chkbxlst', LOG_DEBUG);
1532 $resql = $this->db->query($sql);
1533 if ($resql) {
1534 $num = $this->db->num_rows($resql);
1535 $i = 0;
1536
1537 $data = array();
1538
1539 while ($i < $num) {
1540 $labeltoshow = '';
1541 $obj = $this->db->fetch_object($resql);
1542
1543 $notrans = false;
1544 // Several field into label (eq table:code|label:rowid)
1545 $fields_label = explode('|', $InfoFieldList[1]);
1546 if (is_array($fields_label)) {
1547 $notrans = true;
1548 foreach ($fields_label as $field_toshow) {
1549 $labeltoshow .= $obj->$field_toshow.' ';
1550 }
1551 } else {
1552 $labeltoshow = $obj->{$InfoFieldList[1]};
1553 }
1554 $labeltoshow = dol_trunc($labeltoshow, 45);
1555
1556 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
1557 $labeltoshow = '';
1558 foreach ($fields_label as $field_toshow) {
1559 $translabel = $langs->trans($obj->$field_toshow);
1560 if ($translabel != $obj->$field_toshow) {
1561 $labeltoshow .= ' '.dol_trunc($translabel, 18).' ';
1562 } else {
1563 $labeltoshow .= ' '.dol_trunc($obj->$field_toshow, 18).' ';
1564 }
1565 }
1566 $data[$obj->rowid] = $labeltoshow;
1567 } else {
1568 if (!$notrans) {
1569 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
1570 if ($translabel != $obj->{$InfoFieldList[1]}) {
1571 $labeltoshow = dol_trunc($translabel, 18);
1572 } else {
1573 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
1574 }
1575 }
1576 if (empty($labeltoshow)) {
1577 $labeltoshow = '(not defined)';
1578 }
1579
1580 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
1581 $data[$obj->rowid] = $labeltoshow;
1582 }
1583
1584 if (!empty($InfoFieldList[3]) && $parentField) {
1585 $parent = $parentName.':'.$obj->{$parentField};
1586 }
1587
1588 $data[$obj->rowid] = $labeltoshow;
1589 }
1590
1591 $i++;
1592 }
1593 $this->db->free($resql);
1594
1595 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, '', 0, '', 0, '100%');
1596 } else {
1597 print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
1598 }
1599 } else {
1600 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
1601 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
1602 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, '', 0, '', 0, '100%');
1603 }
1604 }
1605 } elseif ($type == 'link') {
1606 $param_list = array_keys($param['options']); // $param_list[0] = 'ObjectName:classPath' but can also be 'ObjectName:classPath:1:(status:=:1)'
1607 /* Removed.
1608 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
1609 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.
1610 if (strpos($param_list[0], '$ID$') !== false && !empty($objectid)) {
1611 $param_list[0] = str_replace('$ID$', $objectid, $param_list[0]);
1612 }*/
1613 $showempty = (($required && $default != '') ? 0 : 1);
1614
1615 $tmparray = explode(':', $param_list[0]);
1616
1617 $element = $extrafieldsobjectkey; // $extrafieldsobjectkey comes from $object->table_element but we need $object->element
1618 if ($element == 'socpeople') {
1619 $element = 'contact';
1620 } elseif ( $element == 'projet' ) {
1621 $element = 'project';
1622 }
1623
1624 //$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
1625 $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.
1626 $objectfield = $element.':options_'.$key; // Example: 'actioncomm:options_fff' To be used in priority to know object linked with all its definition (including filters)
1627
1628 $out = $form->selectForForms($objectdesc, $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, '', 0, 0, '', $objectfield);
1629 } elseif ($type == 'password') {
1630 // If prefix is 'search_', field is used as a filter, we use a common text field.
1631 $out = '<input style="display:none" type="text" name="fakeusernameremembered">'; // Hidden field to reduce impact of evil Google Chrome autopopulate bug.
1632 $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 : '').'>';
1633 }
1634 if (!empty($hidden)) {
1635 $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
1636 }
1637 /* Add comments
1638 if ($type == 'date') $out.=' (YYYY-MM-DD)';
1639 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
1640 */
1641 /*if (!empty($help) && $keyprefix != 'search_options_') {
1642 $out .= $form->textwithpicto('', $help, 1, 'help', '', 0, 3);
1643 }*/
1644 return $out;
1645 }
1646
1647
1658 public function showOutputField($key, $value, $moreparam = '', $extrafieldsobjectkey = '', $outputlangs = null)
1659 {
1660 global $conf, $langs;
1661
1662 if (is_null($outputlangs) || !is_object($outputlangs)) {
1663 $outputlangs = $langs;
1664 }
1665
1666 if (empty($extrafieldsobjectkey)) {
1667 dol_syslog(get_class($this).'::showOutputField extrafieldsobjectkey required', LOG_ERR);
1668 return 'BadValueForParamExtraFieldsObjectKey';
1669 }
1670
1671 $label = $this->attributes[$extrafieldsobjectkey]['label'][$key];
1672 $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
1673 $size = $this->attributes[$extrafieldsobjectkey]['size'][$key]; // Can be '255', '24,8'...
1674 $default = $this->attributes[$extrafieldsobjectkey]['default'][$key];
1675 $computed = $this->attributes[$extrafieldsobjectkey]['computed'][$key];
1676 $unique = $this->attributes[$extrafieldsobjectkey]['unique'][$key];
1677 $required = $this->attributes[$extrafieldsobjectkey]['required'][$key];
1678 $param = $this->attributes[$extrafieldsobjectkey]['param'][$key];
1679 $perms = dol_eval($this->attributes[$extrafieldsobjectkey]['perms'][$key], 1, 1, '2');
1680 $langfile = $this->attributes[$extrafieldsobjectkey]['langfile'][$key];
1681 $list = dol_eval($this->attributes[$extrafieldsobjectkey]['list'][$key], 1, 1, '2');
1682 $help = $this->attributes[$extrafieldsobjectkey]['help'][$key];
1683 $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)
1684
1685 if ($hidden) {
1686 return ''; // This is a protection. If field is hidden, we should just not call this method.
1687 }
1688
1689 //if ($computed) $value = // $value is already calculated into $value before calling this method
1690
1691 $showsize = 0;
1692 if ($type == 'date') {
1693 $showsize = 10;
1694 if ($value !== '') {
1695 $value = dol_print_date($value, 'day'); // For date without hour, date is always GMT for storage and output
1696 }
1697 } elseif ($type == 'datetime') {
1698 $showsize = 19;
1699 if ($value !== '') {
1700 $value = dol_print_date($value, 'dayhour', 'tzuserrel');
1701 }
1702 } elseif ($type == 'datetimegmt') {
1703 $showsize = 19;
1704 if ($value !== '') {
1705 $value = dol_print_date($value, 'dayhour', 'gmt');
1706 }
1707 } elseif ($type == 'int') {
1708 $showsize = 10;
1709 } elseif ($type == 'double') {
1710 if (!empty($value)) {
1711 //$value=price($value);
1712 $sizeparts = explode(",", $size);
1713 $number_decimals = array_key_exists(1, $sizeparts) ? $sizeparts[1] : 0;
1714 $value = price($value, 0, $outputlangs, 0, 0, $number_decimals, '');
1715 }
1716 } elseif ($type == 'boolean') {
1717 $checked = '';
1718 if (!empty($value)) {
1719 $checked = ' checked ';
1720 }
1721 $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
1722 } elseif ($type == 'mail') {
1723 $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
1724 } elseif ($type == 'ip') {
1725 $value = dol_print_ip($value, 0);
1726 } elseif ($type == 'icon') {
1727 $value = '<span class="'.$value.'"></span>';
1728 } elseif ($type == 'url') {
1729 $value = dol_print_url($value, '_blank', 32, 1);
1730 } elseif ($type == 'phone') {
1731 $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
1732 } elseif ($type == 'price') {
1733 //$value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
1734 if ($value || $value == '0') {
1735 $value = price($value, 0, $outputlangs, 0, $conf->global->MAIN_MAX_DECIMALS_TOT, -1).' '.$outputlangs->getCurrencySymbol($conf->currency);
1736 }
1737 } elseif ($type == 'pricecy') {
1738 $currency = $conf->currency;
1739 if (!empty($value)) {
1740 // $value in memory is a php string like '0.01:EUR'
1741 $pricetmp = explode(':', $value);
1742 $currency = !empty($pricetmp[1]) ? $pricetmp[1] : $conf->currency;
1743 $value = $pricetmp[0];
1744 }
1745 if ($value || $value == '0') {
1746 $value = price($value, 0, $outputlangs, 0, $conf->global->MAIN_MAX_DECIMALS_TOT, -1, $currency);
1747 }
1748 } elseif ($type == 'select') {
1749 $valstr = (!empty($param['options'][$value]) ? $param['options'][$value] : '');
1750 if (($pos = strpos($valstr, "|")) !== false) {
1751 $valstr = substr($valstr, 0, $pos);
1752 }
1753 if ($langfile && $valstr) {
1754 $value = $outputlangs->trans($valstr);
1755 } else {
1756 $value = $valstr;
1757 }
1758 } elseif ($type == 'sellist') {
1759 $param_list = array_keys($param['options']);
1760 $InfoFieldList = explode(":", $param_list[0]);
1761
1762 $selectkey = "rowid";
1763 $keyList = 'rowid';
1764
1765 if (count($InfoFieldList) >= 3) {
1766 $selectkey = $InfoFieldList[2];
1767 $keyList = $InfoFieldList[2].' as rowid';
1768 }
1769
1770 $fields_label = explode('|', $InfoFieldList[1]);
1771 if (is_array($fields_label)) {
1772 $keyList .= ', ';
1773 $keyList .= implode(', ', $fields_label);
1774 }
1775
1776 $filter_categorie = false;
1777 if (count($InfoFieldList) > 5) {
1778 if ($InfoFieldList[0] == 'categorie') {
1779 $filter_categorie = true;
1780 }
1781 }
1782
1783 $sql = "SELECT ".$keyList;
1784 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
1785 if (!empty($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra.') !== false) {
1786 $sql .= ' as main';
1787 }
1788 if ($selectkey == 'rowid' && empty($value)) {
1789 $sql .= " WHERE ".$selectkey." = 0";
1790 } elseif ($selectkey == 'rowid') {
1791 $sql .= " WHERE ".$selectkey." = ".((int) $value);
1792 } else {
1793 $sql .= " WHERE ".$selectkey." = '".$this->db->escape($value)."'";
1794 }
1795
1796 //$sql.= ' AND entity = '.$conf->entity;
1797
1798 dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
1799 $resql = $this->db->query($sql);
1800 if ($resql) {
1801 if ($filter_categorie === false) {
1802 $value = ''; // value was used, so now we reste it to use it to build final output
1803
1804 $obj = $this->db->fetch_object($resql);
1805
1806 // Several field into label (eq table:code|label:rowid)
1807 $fields_label = explode('|', $InfoFieldList[1]);
1808
1809 if (is_array($fields_label) && count($fields_label) > 1) {
1810 foreach ($fields_label as $field_toshow) {
1811 $translabel = '';
1812 if (!empty($obj->$field_toshow)) {
1813 $translabel = $outputlangs->trans($obj->$field_toshow);
1814
1815 if ($translabel != $obj->$field_toshow) {
1816 $value .= dol_trunc($translabel, 24) . ' ';
1817 } else {
1818 $value .= $obj->$field_toshow . ' ';
1819 }
1820 }
1821 }
1822 } else {
1823 $translabel = '';
1824 $tmppropname = $InfoFieldList[1];
1825 //$obj->$tmppropname = '';
1826 if (!empty(isset($obj->$tmppropname) ? $obj->$tmppropname : '')) {
1827 $translabel = $outputlangs->trans($obj->$tmppropname);
1828 }
1829 if ($translabel != (isset($obj->$tmppropname) ? $obj->$tmppropname : '')) {
1830 $value = dol_trunc($translabel, 18);
1831 } else {
1832 $value = isset($obj->$tmppropname) ? $obj->$tmppropname : '';
1833 }
1834 }
1835 } else {
1836 $toprint = array();
1837 $obj = $this->db->fetch_object($resql);
1838 if ($obj->rowid) {
1839 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
1840 $c = new Categorie($this->db);
1841 $result = $c->fetch($obj->rowid);
1842 if ($result > 0) {
1843 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
1844 foreach ($ways as $way) {
1845 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #bbb"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
1846 }
1847 }
1848 }
1849 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
1850 }
1851 } else {
1852 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
1853 }
1854 } elseif ($type == 'radio') {
1855 if (!isset($param['options'][$value])) {
1856 $outputlangs->load('errors');
1857 $value = $outputlangs->trans('ErrorNoValueForRadioType');
1858 } else {
1859 $value = $outputlangs->trans($param['options'][$value]);
1860 }
1861 } elseif ($type == 'checkbox') {
1862 $value_arr = explode(',', $value);
1863 $value = '';
1864 $toprint = array();
1865 if (is_array($value_arr)) {
1866 foreach ($value_arr as $keyval => $valueval) {
1867 if (!empty($valueval)) {
1868 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$param['options'][$valueval].'</li>';
1869 }
1870 }
1871 }
1872 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
1873 } elseif ($type == 'chkbxlst') {
1874 $value_arr = explode(',', $value);
1875
1876 $param_list = array_keys($param['options']);
1877 $InfoFieldList = explode(":", $param_list[0]);
1878
1879 $selectkey = "rowid";
1880 $keyList = 'rowid';
1881
1882 if (count($InfoFieldList) >= 3) {
1883 $selectkey = $InfoFieldList[2];
1884 $keyList = $InfoFieldList[2].' as rowid';
1885 }
1886
1887 $fields_label = explode('|', $InfoFieldList[1]);
1888 if (is_array($fields_label)) {
1889 $keyList .= ', ';
1890 $keyList .= implode(', ', $fields_label);
1891 }
1892
1893 $filter_categorie = false;
1894 if (count($InfoFieldList) > 5) {
1895 if ($InfoFieldList[0] == 'categorie') {
1896 $filter_categorie = true;
1897 }
1898 }
1899
1900 $sql = "SELECT ".$keyList;
1901 $sql .= " FROM ".$this->db->prefix().$InfoFieldList[0];
1902 if (strpos($InfoFieldList[4], 'extra.') !== false) {
1903 $sql .= ' as main';
1904 }
1905 // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
1906 // $sql.= ' AND entity = '.$conf->entity;
1907
1908 dol_syslog(get_class($this).':showOutputField:$type=chkbxlst', LOG_DEBUG);
1909 $resql = $this->db->query($sql);
1910 if ($resql) {
1911 if ($filter_categorie === false) {
1912 $value = ''; // value was used, so now we reste it to use it to build final output
1913 $toprint = array();
1914 while ($obj = $this->db->fetch_object($resql)) {
1915 // Several field into label (eq table:code|label:rowid)
1916 $fields_label = explode('|', $InfoFieldList[1]);
1917 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
1918 if (is_array($fields_label) && count($fields_label) > 1) {
1919 $label = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">';
1920 foreach ($fields_label as $field_toshow) {
1921 $translabel = '';
1922 if (!empty($obj->$field_toshow)) {
1923 $translabel = $outputlangs->trans($obj->$field_toshow);
1924 }
1925 if ($translabel != $field_toshow) {
1926 $label .= ' '.dol_trunc($translabel, 18);
1927 } else {
1928 $label .= ' '.$obj->$field_toshow;
1929 }
1930 }
1931 $label .= '</li>';
1932 $toprint[] = $label;
1933 } else {
1934 $translabel = '';
1935 if (!empty($obj->{$InfoFieldList[1]})) {
1936 $translabel = $outputlangs->trans($obj->{$InfoFieldList[1]});
1937 }
1938 if ($translabel != $obj->{$InfoFieldList[1]}) {
1939 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.dol_trunc($translabel, 18).'</li>';
1940 } else {
1941 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$obj->{$InfoFieldList[1]}.'</li>';
1942 }
1943 }
1944 }
1945 }
1946 } else {
1947 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
1948
1949 $toprint = array();
1950 while ($obj = $this->db->fetch_object($resql)) {
1951 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
1952 $c = new Categorie($this->db);
1953 $c->fetch($obj->rowid);
1954 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
1955 foreach ($ways as $way) {
1956 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"'.($c->color ? ' style="background: #'.$c->color.';"' : ' style="background: #bbb"').'>'.img_object('', 'category').' '.$way.'</li>';
1957 }
1958 }
1959 }
1960 }
1961 if (!empty($toprint)) {
1962 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
1963 }
1964 } else {
1965 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
1966 }
1967 } elseif ($type == 'link') {
1968 $out = '';
1969
1970 // Only if something to display (perf)
1971 if ($value) { // If we have -1 here, pb is into insert, not into ouptut (fix insert instead of changing code here to compensate)
1972 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
1973
1974 $InfoFieldList = explode(":", $param_list[0]);
1975 $classname = $InfoFieldList[0];
1976 $classpath = $InfoFieldList[1];
1977 if (!empty($classpath)) {
1978 dol_include_once($InfoFieldList[1]);
1979 if ($classname && class_exists($classname)) {
1980 $object = new $classname($this->db);
1981 $object->fetch($value);
1982 $value = $object->getNomUrl(3);
1983 }
1984 } else {
1985 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
1986 return 'Error bad setup of extrafield';
1987 }
1988 }
1989 } elseif ($type == 'text') {
1990 $value = dol_htmlentitiesbr($value);
1991 } elseif ($type == 'html') {
1992 $value = dol_htmlentitiesbr($value);
1993 } elseif ($type == 'password') {
1994 $value = dol_trunc(preg_replace('/./i', '*', $value), 8, 'right', 'UTF-8', 1);
1995 } else {
1996 $showsize = round((float) $size);
1997 if ($showsize > 48) {
1998 $showsize = 48;
1999 }
2000 }
2001
2002 //print $type.'-'.$size;
2003 $out = $value;
2004
2005 return $out;
2006 }
2007
2015 public function getAlignFlag($key, $extrafieldsobjectkey = '')
2016 {
2017 global $conf, $langs;
2018
2019 $type = 'varchar';
2020 if (!empty($extrafieldsobjectkey)) {
2021 $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
2022 }
2023
2024 $cssstring = '';
2025
2026 if (in_array($type, array('date', 'datetime', 'datetimegmt',))) {
2027 $cssstring = "center";
2028 } elseif (in_array($type, array('int', 'price', 'double'))) {
2029 $cssstring = "right";
2030 } elseif (in_array($type, array('boolean', 'radio', 'checkbox', 'ip', 'icon'))) {
2031 $cssstring = "center";
2032 }
2033
2034 if (!empty($this->attributes[$extrafieldsobjectkey]['csslist'][$key])) {
2035 $cssstring .= ($cssstring ? ' ' : '').$this->attributes[$extrafieldsobjectkey]['csslist'][$key];
2036 } else {
2037 if (in_array($type, array('ip'))) {
2038 $cssstring .= ($cssstring ? ' ' : '').'tdoverflowmax150';
2039 }
2040 }
2041
2042 return $cssstring;
2043 }
2044
2055 public function showSeparator($key, $object, $colspan = 2, $display_type = 'card', $mode = '')
2056 {
2057 global $conf, $langs;
2058
2059 $tagtype='tr';
2060 $tagtype_dyn='td';
2061
2062 if ($display_type=='line') {
2063 $tagtype='div';
2064 $tagtype_dyn='span';
2065 $colspan=0;
2066 }
2067
2068 $extrafield_param = $this->attributes[$object->table_element]['param'][$key];
2069 $extrafield_param_list = array();
2070 if (!empty($extrafield_param) && is_array($extrafield_param)) {
2071 $extrafield_param_list = array_keys($extrafield_param['options']);
2072 }
2073
2074 // Set $extrafield_collapse_display_value (do we have to collapse/expand the group after the separator)
2075 $extrafield_collapse_display_value = -1;
2076 $expand_display = false;
2077 if (is_array($extrafield_param_list) && count($extrafield_param_list) > 0) {
2078 $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
2079 $expand_display = ((isset($_COOKIE['DOLCOLLAPSE_'.$object->table_element.'_extrafields_'.$key]) || GETPOST('ignorecollapsesetup', 'int')) ? (empty($_COOKIE['DOLCOLLAPSE_'.$object->table_element.'_extrafields_'.$key]) ? false : true) : ($extrafield_collapse_display_value == 2 ? false : true));
2080 }
2081 $disabledcookiewrite = 0;
2082 if ($mode == 'create') {
2083 // On create mode, force separator group to not be collapsable
2084 $extrafield_collapse_display_value = 1;
2085 $expand_display = true; // We force group to be shown expanded
2086 $disabledcookiewrite = 1; // We keep status of group unchanged into the cookie
2087 }
2088
2089 $out = '<'.$tagtype.' id="trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'" class="trextrafieldseparator trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'">';
2090 $out .= '<'.$tagtype_dyn.' '.(!empty($colspan) ? 'colspan="' . $colspan . '"' : '').'>';
2091 // Some js code will be injected here to manage the collapsing of extrafields
2092 // Output the picto
2093 $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>';
2094 $out .= '&nbsp;';
2095 $out .= '<strong>';
2096 $out .= $langs->trans($this->attributes[$object->table_element]['label'][$key]);
2097 $out .= '</strong>';
2098 $out .= '</'.$tagtype_dyn.'>';
2099 $out .= '</'.$tagtype.'>';
2100
2101 $collapse_group = $key.(!empty($object->id) ? '_'.$object->id : '');
2102 //$extrafields_collapse_num = $this->attributes[$object->table_element]['pos'][$key].(!empty($object->id)?'_'.$object->id:'');
2103
2104 if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
2105 // Set the collapse_display status to cookie in priority or if ignorecollapsesetup is 1, if cookie and ignorecollapsesetup not defined, use the setup.
2106 $this->expand_display[$collapse_group] = $expand_display;
2107
2108 if (!empty($conf->use_javascript_ajax)) {
2109 $out .= '<!-- Add js script to manage the collapse/uncollapse of extrafields separators '.$key.' -->'."\n";
2110 $out .= '<script nonce="'.getNonce().'" type="text/javascript">'."\n";
2111 $out .= 'jQuery(document).ready(function(){'."\n";
2112 if (empty($disabledcookiewrite)) {
2113 if ($expand_display === false) {
2114 $out .= ' console.log("Inject js for the collapsing of extrafield '.$key.' - hide");'."\n";
2115 $out .= ' jQuery(".trextrafields_collapse'.$collapse_group.'").hide();'."\n";
2116 } else {
2117 $out .= ' console.log("Inject js for collapsing of extrafield '.$key.' - keep visible and set cookie");'."\n";
2118 $out .= ' document.cookie = "DOLCOLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=1; path='.$_SERVER["PHP_SELF"].'"'."\n";
2119 }
2120 }
2121 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'").click(function(){'."\n";
2122 $out .= ' console.log("We click on collapse/uncollapse to hide/show .trextrafields_collapse'.$collapse_group.'");'."\n";
2123 $out .= ' jQuery(".trextrafields_collapse'.$collapse_group.'").toggle(100, function(){'."\n";
2124 $out .= ' if (jQuery(".trextrafields_collapse'.$collapse_group.'").is(":hidden")) {'."\n";
2125 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').' '.$tagtype_dyn.' span").addClass("fa-plus-square").removeClass("fa-minus-square");'."\n";
2126 $out .= ' document.cookie = "DOLCOLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=0; path='.$_SERVER["PHP_SELF"].'"'."\n";
2127 $out .= ' } else {'."\n";
2128 $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').' '.$tagtype_dyn.' span").addClass("fa-minus-square").removeClass("fa-plus-square");'."\n";
2129 $out .= ' document.cookie = "DOLCOLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=1; path='.$_SERVER["PHP_SELF"].'"'."\n";
2130 $out .= ' }'."\n";
2131 $out .= ' });'."\n";
2132 $out .= ' });'."\n";
2133 $out .= '});'."\n";
2134 $out .= '</script>'."\n";
2135 }
2136 } else {
2137 $this->expand_display[$collapse_group] = 1;
2138 }
2139
2140 return $out;
2141 }
2142
2154 public function setOptionalsFromPost($extralabels, &$object, $onlykey = '', $todefaultifmissing = 0)
2155 {
2156 global $conf, $_POST, $langs;
2157
2158 $nofillrequired = 0; // For error when required field left blank
2159 $error_field_required = array();
2160
2161 if (isset($this->attributes[$object->table_element]['label']) && is_array($this->attributes[$object->table_element]['label'])) {
2162 $extralabels = $this->attributes[$object->table_element]['label'];
2163 }
2164
2165 if (is_array($extralabels)) {
2166 // Get extra fields
2167 foreach ($extralabels as $key => $value) {
2168 if (!empty($onlykey) && $onlykey != '@GETPOSTISSET' && $key != $onlykey) {
2169 continue;
2170 }
2171
2172 if (!empty($onlykey) && $onlykey == '@GETPOSTISSET' && !GETPOSTISSET('options_'.$key) && (! in_array($this->attributes[$object->table_element]['type'][$key], array('boolean', 'checkbox', 'chkbxlst')))) {
2173 //when unticking boolean field, it's not set in POST
2174 continue;
2175 }
2176
2177 $key_type = $this->attributes[$object->table_element]['type'][$key];
2178 if ($key_type == 'separate') {
2179 continue;
2180 }
2181
2182 $enabled = 1;
2183 if (isset($this->attributes[$object->table_element]['enabled'][$key])) { // 'enabled' is often a condition on module enabled or not
2184 $enabled = dol_eval($this->attributes[$object->table_element]['enabled'][$key], 1, 1, '2');
2185 }
2186
2187 $visibility = 1;
2188 if (isset($this->attributes[$object->table_element]['list'][$key])) { // 'list' is option for visibility
2189 $visibility = intval(dol_eval($this->attributes[$object->table_element]['list'][$key], 1, 1, '2'));
2190 }
2191
2192 $perms = 1;
2193 if (isset($this->attributes[$object->table_element]['perms'][$key])) {
2194 $perms = dol_eval($this->attributes[$object->table_element]['perms'][$key], 1, 1, '2');
2195 }
2196 if (empty($enabled)
2197 || (
2198 $onlykey === '@GETPOSTISSET'
2199 && in_array($this->attributes[$object->table_element]['type'][$key], array('boolean', 'checkbox', 'chkbxlst'))
2200 && in_array(abs($enabled), array(2, 5))
2201 && ! GETPOSTISSET('options_' . $key) // Update hidden checkboxes and multiselect only if they are provided
2202 )
2203 ) {
2204 continue;
2205 }
2206
2207 $visibility_abs = abs($visibility);
2208 // not modify if extra field is not in update form (0 : never, 2 or -2 : list only, 5 or - 5 : list and view only)
2209 if (empty($visibility_abs) || $visibility_abs == 2 || $visibility_abs == 5) {
2210 continue;
2211 }
2212 if (empty($perms)) {
2213 continue;
2214 }
2215
2216 if ($this->attributes[$object->table_element]['required'][$key]) { // Value is required
2217 // Check if functionally empty without using GETPOST (depending on the type of extrafield, a
2218 // technically non-empty value may be treated as empty functionally).
2219 // value can be alpha, int, array, etc...
2220 $v = $_POST["options_".$key] ?? null;
2221 $type = $this->attributes[$object->table_element]['type'][$key];
2222 if (self::isEmptyValue($v, $type)) {
2223 //print 'ccc'.$value.'-'.$this->attributes[$object->table_element]['required'][$key];
2224
2225 // 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.
2226
2227 $nofillrequired++;
2228 if (!empty($this->attributes[$object->table_element]['langfile'][$key])) {
2229 $langs->load($this->attributes[$object->table_element]['langfile'][$key]);
2230 }
2231 $error_field_required[$key] = $langs->transnoentitiesnoconv($value);
2232 }
2233 }
2234
2235 if (in_array($key_type, array('date'))) {
2236 // Clean parameters
2237 $value_key = dol_mktime(12, 0, 0, GETPOST("options_".$key."month", 'int'), GETPOST("options_".$key."day", 'int'), GETPOST("options_".$key."year", 'int'));
2238 } elseif (in_array($key_type, array('datetime'))) {
2239 // Clean parameters
2240 $value_key = dol_mktime(GETPOST("options_".$key."hour", 'int'), GETPOST("options_".$key."min", 'int'), GETPOST("options_".$key."sec", 'int'), GETPOST("options_".$key."month", 'int'), GETPOST("options_".$key."day", 'int'), GETPOST("options_".$key."year", 'int'), 'tzuserrel');
2241 } elseif (in_array($key_type, array('datetimegmt'))) {
2242 // Clean parameters
2243 $value_key = dol_mktime(GETPOST("options_".$key."hour", 'int'), GETPOST("options_".$key."min", 'int'), GETPOST("options_".$key."sec", 'int'), GETPOST("options_".$key."month", 'int'), GETPOST("options_".$key."day", 'int'), GETPOST("options_".$key."year", 'int'), 'gmt');
2244 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
2245 $value_arr = GETPOST("options_".$key, 'array'); // check if an array
2246 if (!empty($value_arr)) {
2247 $value_key = implode(',', $value_arr);
2248 } else {
2249 $value_key = '';
2250 }
2251 } elseif (in_array($key_type, array('price', 'double'))) {
2252 $value_arr = GETPOST("options_".$key, 'alpha');
2253 $value_key = price2num($value_arr);
2254 } elseif (in_array($key_type, array('pricecy', 'double'))) {
2255 $value_key = price2num(GETPOST("options_".$key, 'alpha')).':'.GETPOST("options_".$key."currency_id", 'alpha');
2256 } elseif (in_array($key_type, array('html'))) {
2257 $value_key = GETPOST("options_".$key, 'restricthtml');
2258 } elseif (in_array($key_type, array('text'))) {
2259 $label_security_check = 'alphanohtml';
2260 // by default 'alphanohtml' (better security); hidden conf MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML allows basic html
2261 if (getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_REF_LABELS')) {
2262 $label_security_check = 'nohtml';
2263 } else {
2264 $label_security_check = !getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML') ? 'alphanohtml' : 'restricthtml';
2265 }
2266 $value_key = GETPOST("options_".$key, $label_security_check);
2267 } else {
2268 $value_key = GETPOST("options_".$key);
2269 if (in_array($key_type, array('link')) && $value_key == '-1') {
2270 $value_key = '';
2271 }
2272 }
2273
2274 if (!empty($error_field_required[$key]) && $todefaultifmissing) {
2275 // Value is required but we have a default value and we asked to set empty value to the default value
2276 if (!empty($this->attributes[$object->table_element]['default']) && !is_null($this->attributes[$object->table_element]['default'][$key])) {
2277 $value_key = $this->attributes[$object->table_element]['default'][$key];
2278 unset($error_field_required[$key]);
2279 $nofillrequired--;
2280 }
2281 }
2282
2283 $object->array_options["options_".$key] = $value_key;
2284 }
2285
2286 if ($nofillrequired) {
2287 $langs->load('errors');
2288 $this->error = $langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required);
2289 setEventMessages($this->error, null, 'errors');
2290 return -1;
2291 } else {
2292 return 1;
2293 }
2294 } else {
2295 return 0;
2296 }
2297 }
2298
2307 public function getOptionalsFromPost($extrafieldsobjectkey, $keyprefix = '', $keysuffix = '')
2308 {
2309 global $_POST;
2310
2311 if (is_string($extrafieldsobjectkey) && !empty($this->attributes[$extrafieldsobjectkey]['label']) && is_array($this->attributes[$extrafieldsobjectkey]['label'])) {
2312 $extralabels = $this->attributes[$extrafieldsobjectkey]['label'];
2313 } else {
2314 $extralabels = $extrafieldsobjectkey;
2315 }
2316
2317 if (is_array($extralabels)) {
2318 $array_options = array();
2319
2320 // Get extra fields
2321 foreach ($extralabels as $key => $value) {
2322 $key_type = '';
2323 if (is_string($extrafieldsobjectkey)) {
2324 $key_type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
2325 }
2326
2327 if (in_array($key_type, array('date'))) {
2328 $dateparamname_start = $keysuffix . 'options_' . $key . $keyprefix . '_start';
2329 $dateparamname_end = $keysuffix . 'options_' . $key . $keyprefix . '_end';
2330 if (GETPOST($dateparamname_start . 'year') || GETPOST($dateparamname_end . 'year')) {
2331 $value_key = array();
2332 // values provided as a component year, month, day, etc.
2333 if (GETPOST($dateparamname_start . 'year')) {
2334 $value_key['start'] = dol_mktime(0, 0, 0, GETPOST($dateparamname_start . 'month', 'int'), GETPOST($dateparamname_start . 'day', 'int'), GETPOST($dateparamname_start . 'year', 'int'));
2335 }
2336 if (GETPOST($dateparamname_start . 'year')) {
2337 $value_key['end'] = dol_mktime(23, 59, 59, GETPOST($dateparamname_end . 'month', 'int'), GETPOST($dateparamname_end . 'day', 'int'), GETPOST($dateparamname_end . 'year', 'int'));
2338 }
2339 } elseif (GETPOST($keysuffix."options_".$key.$keyprefix."year")) {
2340 // Clean parameters
2341 $value_key = dol_mktime(12, 0, 0, GETPOST($keysuffix."options_".$key.$keyprefix."month", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."day", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."year", 'int'));
2342 } else {
2343 continue; // Value was not provided, we should not set it.
2344 }
2345 } elseif (in_array($key_type, array('datetime', 'datetimegmt'))) {
2346 $dateparamname_start = $keysuffix . 'options_' . $key . $keyprefix . '_start';
2347 $dateparamname_end = $keysuffix . 'options_' . $key . $keyprefix . '_end';
2348 if (GETPOST($dateparamname_start . 'year') && GETPOST($dateparamname_end . 'year')) {
2349 // values provided as a date pair (start date + end date), each date being broken down as year, month, day, etc.
2350 $dateparamname_end_hour = GETPOST($dateparamname_end . 'hour', 'int') != '-1' ? GETPOST($dateparamname_end . 'hour', 'int') : '23';
2351 $dateparamname_end_min = GETPOST($dateparamname_end . 'min', 'int') != '-1' ? GETPOST($dateparamname_end . 'min', 'int') : '59';
2352 $dateparamname_end_sec = GETPOST($dateparamname_end . 'sec', 'int') != '-1' ? GETPOST($dateparamname_end . 'sec', 'int') : '59';
2353 if ($key_type == 'datetimegmt') {
2354 $value_key = array(
2355 'start' => dol_mktime(GETPOST($dateparamname_start . 'hour', 'int'), GETPOST($dateparamname_start . 'min', 'int'), GETPOST($dateparamname_start . 'sec', 'int'), GETPOST($dateparamname_start . 'month', 'int'), GETPOST($dateparamname_start . 'day', 'int'), GETPOST($dateparamname_start . 'year', 'int'), 'gmt'),
2356 'end' => dol_mktime($dateparamname_end_hour, $dateparamname_end_min, $dateparamname_end_sec, GETPOST($dateparamname_end . 'month', 'int'), GETPOST($dateparamname_end . 'day', 'int'), GETPOST($dateparamname_end . 'year', 'int'), 'gmt')
2357 );
2358 } else {
2359 $value_key = array(
2360 'start' => dol_mktime(GETPOST($dateparamname_start . 'hour', 'int'), GETPOST($dateparamname_start . 'min', 'int'), GETPOST($dateparamname_start . 'sec', 'int'), GETPOST($dateparamname_start . 'month', 'int'), GETPOST($dateparamname_start . 'day', 'int'), GETPOST($dateparamname_start . 'year', 'int'), 'tzuserrel'),
2361 'end' => dol_mktime($dateparamname_end_hour, $dateparamname_end_min, $dateparamname_end_sec, GETPOST($dateparamname_end . 'month', 'int'), GETPOST($dateparamname_end . 'day', 'int'), GETPOST($dateparamname_end . 'year', 'int'), 'tzuserrel')
2362 );
2363 }
2364 } elseif (GETPOST($keysuffix."options_".$key.$keyprefix."year")) {
2365 // Clean parameters
2366 if ($key_type == 'datetimegmt') {
2367 $value_key = dol_mktime(GETPOST($keysuffix."options_".$key.$keyprefix."hour", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."min", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."sec", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."month", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."day", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."year", 'int'), 'gmt');
2368 } else {
2369 $value_key = dol_mktime(GETPOST($keysuffix."options_".$key.$keyprefix."hour", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."min", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."sec", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."month", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."day", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."year", 'int'), 'tzuserrel');
2370 }
2371 } else {
2372 continue; // Value was not provided, we should not set it.
2373 }
2374 } elseif ($key_type == 'select') {
2375 // to detect if we are in search context
2376 if (GETPOSTISARRAY($keysuffix."options_".$key.$keyprefix)) {
2377 $value_arr = GETPOST($keysuffix."options_".$key.$keyprefix, 'array:aZ09');
2378 // Make sure we get an array even if there's only one selected
2379 $value_arr = (array) $value_arr;
2380 $value_key = implode(',', $value_arr);
2381 } else {
2382 $value_key = GETPOST($keysuffix."options_".$key.$keyprefix);
2383 }
2384 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
2385 // We test on a hidden field named "..._multiselect" that is always set to 1 if param is in form so
2386 // when nothing is provided we can make a difference between noparam in the form and param was set to nothing.
2387 if (!GETPOSTISSET($keysuffix."options_".$key.$keyprefix.'_multiselect')) {
2388 continue; // Value was not provided, we should not set it.
2389 }
2390 $value_arr = GETPOST($keysuffix."options_".$key.$keyprefix);
2391 // Make sure we get an array even if there's only one checkbox
2392 $value_arr = (array) $value_arr;
2393 $value_key = implode(',', $value_arr);
2394 } elseif (in_array($key_type, array('price', 'double', 'int'))) {
2395 if (!GETPOSTISSET($keysuffix."options_".$key.$keyprefix)) {
2396 continue; // Value was not provided, we should not set it.
2397 }
2398 $value_arr = GETPOST($keysuffix."options_".$key.$keyprefix);
2399 if ($keysuffix != 'search_') { // If value is for a search, we must keep complex string like '>100 <=150'
2400 $value_key = price2num($value_arr);
2401 } else {
2402 $value_key = $value_arr;
2403 }
2404 } elseif (in_array($key_type, array('boolean'))) {
2405 // We test on a hidden field named "..._boolean" that is always set to 1 if param is in form so
2406 // when nothing is provided we can make a difference between noparam in the form and param was set to nothing.
2407 if (!GETPOSTISSET($keysuffix."options_".$key.$keyprefix."_boolean")) {
2408 $value_key = '';
2409 } else {
2410 $value_arr = GETPOST($keysuffix."options_".$key.$keyprefix);
2411 $value_key = $value_arr;
2412 }
2413 } elseif (in_array($key_type, array('html'))) {
2414 if (!GETPOSTISSET($keysuffix."options_".$key.$keyprefix)) {
2415 continue; // Value was not provided, we should not set it.
2416 }
2417 $value_key = dol_htmlcleanlastbr(GETPOST($keysuffix."options_".$key.$keyprefix, 'restricthtml'));
2418 } else {
2419 if (!GETPOSTISSET($keysuffix."options_".$key.$keyprefix)) {
2420 continue; // Value was not provided, we should not set it.
2421 }
2422 $value_key = GETPOST($keysuffix."options_".$key.$keyprefix);
2423 if ($value_key === '') {
2424 $value_key = null;
2425 }
2426 }
2427
2428 $array_options[$keysuffix."options_".$key] = $value_key; // No keyprefix here. keyprefix is used only for read.
2429 }
2430
2431 return $array_options;
2432 }
2433
2434 return 0;
2435 }
2436
2444 public static function isEmptyValue($v, string $type)
2445 {
2446 if ($v === null || $v === '') {
2447 return true;
2448 }
2449 if (is_array($v) || $type == 'select') {
2450 return empty($v);
2451 }
2452 if ($type == 'link') {
2453 return ($v == '-1');
2454 }
2455 if ($type == 'sellist') {
2456 return ($v == '0');
2457 }
2458 return (empty($v) && $v != '0');
2459 }
2460}
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:455
Class to manage categories.
Class to manage a WYSIWYG editor.
Class to manage standard extra fields.
create_label($attrname, $label='', $type='', $pos=0, $size='', $elementtype='member', $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.
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.
getOptionalsFromPost($extrafieldsobjectkey, $keyprefix='', $keysuffix='')
return array_options array of data of extrafields value of object sent by a search form
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.
getAlignFlag($key, $extrafieldsobjectkey='')
Return the CSS to use for this extrafield into list.
showInputField($key, $value, $moreparam='', $keysuffix='', $keyprefix='', $morecss='', $objectid=0, $extrafieldsobjectkey='', $mode=0)
Return HTML string to put an input field into a page Code very similar with showInputField of common ...
showSeparator($key, $object, $colspan=2, $display_type='card', $mode='')
Return HTML string to print separator extrafield.
create($attrname, $type='varchar', $length='255', $elementtype='member', $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.
showOutputField($key, $value, $moreparam='', $extrafieldsobjectkey='', $outputlangs=null)
Return HTML string to put an output field into a page.
delete_label($attrname, $elementtype='member')
Delete description of an optional attribute.
fetch_name_optionals_label($elementtype, $forceload=false)
Load the array of extrafields defintion $this->attributes.
__construct($db)
Constructor.
setOptionalsFromPost($extralabels, &$object, $onlykey='', $todefaultifmissing=0)
Fill array_options property of object by extrafields value (using for data sent by forms)
static isEmptyValue($v, string $type)
Return if a value is "empty" for a mandatory vision.
Class to manage generation of HTML components Only common components must be here.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed informations (by default a local PHP server timestamp) Re...
dol_print_ip($ip, $mode=0)
Return an IP formated to be shown on screen.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
dol_print_url($url, $target='_blank', $max=32, $withpicto=0, $morecss='')
Show Url link.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_print_email($email, $cid=0, $socid=0, $addlink=0, $max=64, $showinvalid=1, $withpicto=0)
Show EMail link formatted for HTML output.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
dol_print_phone($phone, $countrycode='', $cid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0)
Format phone numbers according to country.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_eval($s, $returnvalue=0, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
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 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...