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