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