dolibarr 22.0.5
categorie.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2005 Matthieu Valleton <mv@seeschloss.org>
3 * Copyright (C) 2005 Davoleau Brice <brice.davoleau@gmail.com>
4 * Copyright (C) 2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
5 * Copyright (C) 2006-2012 Regis Houssin <regis.houssin@inodbox.com>
6 * Copyright (C) 2006-2012 Laurent Destailleur <eldy@users.sourceforge.net>
7 * Copyright (C) 2007 Patrick Raguin <patrick.raguin@gmail.com>
8 * Copyright (C) 2013-2016 Juanjo Menent <jmenent@2byte.es>
9 * Copyright (C) 2013-2018 Philippe Grand <philippe.grand@atoo-net.com>
10 * Copyright (C) 2015 Marcos García <marcosgdf@gmail.com>
11 * Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
12 * Copyright (C) 2016-2024 Charlene Benke <charlene@patas-monkey.com>
13 * Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
14 * Copyright (C) 2022-2023 Solution Libre SAS <contact@solution-libre.fr>
15 * Copyright (C) 2023-2024 Benjamin Falière <benjamin.faliere@altairis.fr>
16 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
17 * Copyright (C) 2025 Alexandre Spangaro <alexandre@inovea-conseil.com>
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 3 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <https://www.gnu.org/licenses/>.
31 */
32
39require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
40require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
41require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';
42require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.class.php';
43require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
44require_once DOL_DOCUMENT_ROOT.'/knowledgemanagement/class/knowledgerecord.class.php';
45
46
51{
52 // Categories types (we use string because we want to accept any modules/types in a future)
53 const TYPE_PRODUCT = 'product';
54 const TYPE_SUPPLIER = 'supplier';
55 const TYPE_CUSTOMER = 'customer';
56 const TYPE_MEMBER = 'member';
57 const TYPE_CONTACT = 'contact';
58 const TYPE_USER = 'user';
59 const TYPE_PROJECT = 'project';
60 const TYPE_ACCOUNT = 'bank_account';
61 const TYPE_BANK_LINE = 'bank_line';
62 const TYPE_WAREHOUSE = 'warehouse';
63 const TYPE_ACTIONCOMM = 'actioncomm';
64 const TYPE_WEBSITE_PAGE = 'website_page';
65 const TYPE_TICKET = 'ticket';
66 const TYPE_KNOWLEDGEMANAGEMENT = 'knowledgemanagement';
67 const TYPE_FICHINTER = 'fichinter';
68 const TYPE_ORDER = 'order';
69 const TYPE_INVOICE = 'invoice';
70 const TYPE_SUPPLIER_ORDER = 'supplier_order';
71 const TYPE_SUPPLIER_INVOICE = 'supplier_invoice';
72
73
77 public $picto = 'category';
78
79
83 public $MAP_ID = array(
84 'product' => 0,
85 'service' => 0,
86 'supplier' => 1,
87 'customer' => 2,
88 'member' => 3,
89 'contact' => 4,
90 'bank_account' => 5,
91 'project' => 6,
92 'user' => 7,
93 'bank_line' => 8,
94 'warehouse' => 9,
95 'actioncomm' => 10,
96 'website_page' => 11,
97 'ticket' => 12,
98 'knowledgemanagement' => 13,
99 'fichinter' => 14,
100 'order' => 16,
101 'invoice' => 17,
102 'supplier_order' => 20,
103 'supplier_invoice' => 21
104 );
105
111 public static $MAP_ID_TO_CODE = array(
112 0 => 'product',
113 1 => 'supplier',
114 2 => 'customer',
115 3 => 'member',
116 4 => 'contact',
117 5 => 'bank_account',
118 6 => 'project',
119 7 => 'user',
120 8 => 'bank_line',
121 9 => 'warehouse',
122 10 => 'actioncomm',
123 11 => 'website_page',
124 12 => 'ticket',
125 13 => 'knowledgemanagement',
126 14 => 'fichinter',
127 16 => 'order',
128 17 => 'invoice',
129 20 => 'supplier_order',
130 21 => 'supplier_invoice'
131 );
132
138 public $MAP_CAT_FK = array(
139 'customer' => 'soc',
140 'supplier' => 'soc',
141 'contact' => 'socpeople',
142 'bank_account' => 'account',
143 );
144
150 public $MAP_CAT_TABLE = array(
151 'customer' => 'societe',
152 'supplier' => 'fournisseur',
153 'bank_account' => 'account',
154 );
155
161 public $MAP_OBJ_CLASS = array(
162 'product' => 'Product',
163 'service' => 'Product',
164 'customer' => 'Societe',
165 'supplier' => 'Fournisseur',
166 'member' => 'Adherent',
167 'contact' => 'Contact',
168 'user' => 'User',
169 'account' => 'Account', // old for bank account
170 'bank_account' => 'Account',
171 'project' => 'Project',
172 'warehouse' => 'Entrepot',
173 'actioncomm' => 'ActionComm',
174 'website_page' => 'WebsitePage',
175 'ticket' => 'Ticket',
176 'knowledgemanagement' => 'KnowledgeRecord',
177 'fichinter' => 'Fichinter',
178 'order' => 'Commande',
179 'invoice' => 'Facture',
180 'supplier_order' => 'CommandeFournisseur',
181 'supplier_invoice' => 'FactureFournisseur'
182 );
183
189 public static $MAP_TYPE_TITLE_AREA = array(
190 'product' => 'Products',
191 'service' => 'Services',
192 'customer' => 'ProspectsOrCustomers',
193 'supplier' => 'Suppliers',
194 'member' => 'Members',
195 'contact' => 'Contacts',
196 'user' => 'Users',
197 'account' => 'Accounts', // old for bank account
198 'bank_account' => 'BankAccounts',
199 'bank_line' => 'BankTransactions',
200 'project' => 'Projects',
201 'warehouse' => 'Warehouse',
202 'actioncomm' => 'AgendaEvents',
203 'website_page' => 'WebsitePages',
204 'ticket' => 'Tickets',
205 'knowledgemanagement' => 'KnowledgeRecords',
206 'fichinter' => 'Interventions',
207 'order' => 'Orders',
208 'invoice' => 'Invoices',
209 'supplier_order' => 'SuppliersOrders',
210 'supplier_invoice' => 'SuppliersInvoices'
211 );
212
217 public $MAP_OBJ_TABLE = array(
218 'customer' => 'societe',
219 'supplier' => 'societe',
220 'member' => 'adherent',
221 'contact' => 'socpeople',
222 'account' => 'bank_account', // old for bank account
223 'project' => 'projet',
224 'warehouse' => 'entrepot',
225 'knowledgemanagement' => 'knowledgemanagement_knowledgerecord',
226 'fichinter' => 'fichinter',
227 'order' => 'commande',
228 'invoice' => 'facture',
229 'supplier_order' => 'commande_fournisseur',
230 'supplier_invoice' => 'facture_fourn'
231 );
232
236 public $element = 'category';
237
241 public $table_element = 'categorie';
242
246 public $fk_parent;
247
251 public $label;
252
256 public $description;
257
261 public $color;
262
266 public $position;
267
271 public $visible;
272
276 public $socid;
277
300 public $type;
301
305 public $cats = array();
306
310 public $motherof = array();
311
315 public $childs = array();
316
320 public $multilangs = array();
321
325 public $imgWidth;
326
330 public $imgHeight;
331
356 public $fields = array(
357 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'position' => 10, 'notnull' => 1, 'visible' => 0,),
358 'entity' => array('type' => 'integer', 'label' => 'Entity', 'enabled' => 1, 'visible' => 0, 'default' => '1', 'notnull' => 1, 'index' => 1, 'position' => 5),
359 'fk_parent' => array('type' => 'integer', 'label' => 'ParentCategory', 'enabled' => 1, 'position' => 20, 'notnull' => 1, 'visible' => 0, 'css' => 'maxwidth500 widthcentpercentminusxx',),
360 'label' => array('type' => 'varchar(180)', 'label' => 'Ref', 'enabled' => 1, 'position' => 25, 'notnull' => 1, 'visible' => 1, 'alwayseditable' => 1, 'css' => 'minwidth300', 'cssview' => 'wordbreak', 'csslist' => 'tdoverflowmax150', 'showoncombobox' => 1),
361 'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'position' => 30, 'notnull' => 0, 'visible' => 0, 'alwayseditable' => 1,),
362 'type' => array('type' => 'integer', 'label' => 'Type', 'enabled' => 1, 'position' => 35, 'notnull' => 1, 'visible' => 0, 'alwayseditable' => 1,),
363 'description' => array('type' => 'text', 'label' => 'Description', 'enabled' => 1, 'position' => 40, 'notnull' => 0, 'visible' => 1, 'alwayseditable' => 1,),
364 'color' => array('type' => 'varchar(8)', 'label' => 'Color', 'enabled' => 1, 'position' => 45, 'notnull' => 0, 'visible' => 1, 'alwayseditable' => 1,),
365 'position' => array('type' => 'integer', 'label' => 'Position', 'enabled' => 1, 'position' => 50, 'notnull' => 0, 'visible' => -1, 'alwayseditable' => 1,),
366 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'picto' => 'company', 'enabled' => 1, 'position' => 55, 'notnull' => 0, 'visible' => 0, 'alwayseditable' => 1, 'css' => 'maxwidth500 widthcentpercentminusxx', 'csslist' => 'tdoverflowmax150',),
367 'visible' => array('type' => 'integer', 'label' => 'Visible', 'enabled' => 1, 'position' => 60, 'notnull' => 1, 'visible' => 0, 'alwayseditable' => 1,),
368 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'position' => 900, 'notnull' => 0, 'visible' => -2, 'alwayseditable' => 1,),
369 'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'position' => 70, 'notnull' => 0, 'visible' => -1, 'alwayseditable' => 1,),
370 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'position' => 75, 'notnull' => 1, 'visible' => -1, 'alwayseditable' => 1,),
371 'fk_user_creat' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'position' => 80, 'notnull' => 0, 'visible' => -2, 'alwayseditable' => 1, 'css' => 'maxwidth500 widthcentpercentminusxx', 'csslist' => 'tdoverflowmax150',),
372 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'position' => 85, 'notnull' => -1, 'visible' => -2, 'alwayseditable' => 1, 'css' => 'maxwidth500 widthcentpercentminusxx', 'csslist' => 'tdoverflowmax150',),
373 );
374
380 public function __construct($db)
381 {
382 global $hookmanager;
383
384 $this->db = $db;
385
386 $this->ismultientitymanaged = 1;
387 $this->isextrafieldmanaged = 1;
388
389 if (is_object($hookmanager)) {
390 $hookmanager->initHooks(array('category'));
391 $parameters = array();
392 $reshook = $hookmanager->executeHooks('constructCategory', $parameters, $this); // Note that $action and $object may have been modified by some hooks
393 if ($reshook >= 0 && !empty($hookmanager->resArray)) {
394 foreach ($hookmanager->resArray as $mapList) {
395 $mapId = $mapList['id'];
396 $mapCode = $mapList['code'];
397 self::$MAP_ID_TO_CODE[$mapId] = $mapCode;
398 $this->MAP_ID[$mapCode] = $mapId;
399 $this->MAP_CAT_FK[$mapCode] = isset($mapList['cat_fk']) ? $mapList['cat_fk'] : null;
400 $this->MAP_CAT_TABLE[$mapCode] = isset($mapList['cat_table']) ? $mapList['cat_table'] : null;
401 $this->MAP_OBJ_CLASS[$mapCode] = $mapList['obj_class'];
402 $this->MAP_OBJ_TABLE[$mapCode] = $mapList['obj_table'];
403 }
404 }
405 }
406 }
407
413 public function getMapList()
414 {
415 $mapList = array();
416
417 foreach ($this->MAP_ID as $mapCode => $mapId) {
418 $mapList[] = array(
419 'id' => $mapId,
420 'code' => $mapCode,
421 'cat_fk' => (empty($this->MAP_CAT_FK[$mapCode]) ? $mapCode : $this->MAP_CAT_FK[$mapCode]),
422 'cat_table' => (empty($this->MAP_CAT_TABLE[$mapCode]) ? $mapCode : $this->MAP_CAT_TABLE[$mapCode]),
423 'obj_class' => (empty($this->MAP_OBJ_CLASS[$mapCode]) ? $mapCode : $this->MAP_OBJ_CLASS[$mapCode]),
424 'obj_table' => (empty($this->MAP_OBJ_TABLE[$mapCode]) ? $mapCode : $this->MAP_OBJ_TABLE[$mapCode])
425 );
426 }
427
428 return $mapList;
429 }
430
436 public function getMapId()
437 {
438 return $this->MAP_ID;
439 }
440
450 public function fetch($id, $label = '', $type = null, $ref_ext = '')
451 {
452 // Check parameters
453 if (empty($id) && empty($label) && empty($ref_ext)) {
454 $this->error = "No category to search for";
455 return -1;
456 }
457 if (!is_null($type) && !is_numeric($type)) {
458 $type = $this->MAP_ID[$type];
459 }
460
461 $sql = "SELECT rowid, fk_parent, entity, label, description, color, position, fk_soc, visible, type, ref_ext";
462 $sql .= ", date_creation, tms, fk_user_creat, fk_user_modif";
463 $sql .= " FROM ".MAIN_DB_PREFIX."categorie";
464 if ($id) {
465 $sql .= " WHERE rowid = ".((int) $id);
466 } elseif (!empty($ref_ext)) {
467 $sql .= " WHERE ref_ext LIKE '".$this->db->escape($ref_ext)."'";
468 } else {
469 $sql .= " WHERE label = '".$this->db->escape($label)."' AND entity IN (".getEntity('category').")";
470 if (!is_null($type)) {
471 $sql .= " AND type = ".((int) $type);
472 }
473 }
474
475 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
476 $resql = $this->db->query($sql);
477 if ($resql) {
478 if ($this->db->num_rows($resql) > 0 && $res = $this->db->fetch_array($resql)) {
479 $this->id = $res['rowid'];
480 //$this->ref = $res['rowid'];
481 $this->fk_parent = (int) $res['fk_parent'];
482 $this->label = $res['label'];
483 $this->description = $res['description'];
484 $this->color = $res['color'];
485 $this->position = $res['position'];
486 $this->socid = (int) $res['fk_soc'];
487 $this->visible = (int) $res['visible'];
488 $this->type = $res['type'];
489 $this->ref_ext = $res['ref_ext'];
490 $this->entity = (int) $res['entity'];
491 $this->date_creation = $this->db->jdate($res['date_creation']);
492 $this->date_modification = $this->db->jdate($res['tms']);
493 $this->user_creation_id = (int) $res['fk_user_creat'];
494 $this->user_modification_id = (int) $res['fk_user_modif'];
495
496 // Retrieve all extrafield
497 // fetch optionals attributes and labels
498 $this->fetch_optionals();
499
500 $this->db->free($resql);
501
502 // multilangs
503 if (getDolGlobalInt('MAIN_MULTILANGS')) {
504 $this->getMultiLangs();
505 }
506
507 return 1;
508 } else {
509 $this->error = "No category found";
510 return 0;
511 }
512 } else {
513 dol_print_error($this->db);
514 $this->error = $this->db->lasterror;
515 $this->errors[] = $this->db->lasterror;
516 return -1;
517 }
518 }
519
530 public function create($user, $notrigger = 0)
531 {
532 global $conf, $langs;
533 $langs->load('categories');
534
535 $type = $this->type;
536
537 if (!is_numeric($type)) {
538 $type = $this->MAP_ID[$type];
539 }
540
541 $error = 0;
542
543 dol_syslog(get_class($this).'::create', LOG_DEBUG);
544
545 // Clean parameters
546 $this->label = trim($this->label);
547 $this->description = trim($this->description);
548 $this->color = trim($this->color);
549 $this->position = (int) $this->position;
550 if (isset($this->import_key)) {
551 $this->import_key = trim($this->import_key);
552 }
553 $this->ref_ext = trim($this->ref_ext);
554 if (empty($this->visible)) {
555 $this->visible = 0;
556 }
557 $this->fk_parent = ($this->fk_parent != "" ? intval($this->fk_parent) : 0);
558
559 if ($this->already_exists()) {
560 $this->error = $langs->trans("ImpossibleAddCat", $this->label);
561 $this->error .= " : ".$langs->trans("CategoryExistsAtSameLevel");
562 dol_syslog($this->error, LOG_WARNING);
563 return -4;
564 }
565
566 $this->db->begin();
567 $now = dol_now();
568 $sql = "INSERT INTO ".MAIN_DB_PREFIX."categorie (";
569 $sql .= "fk_parent,";
570 $sql .= " label,";
571 $sql .= " description,";
572 $sql .= " color,";
573 $sql .= " position,";
574 if (getDolGlobalString('CATEGORY_ASSIGNED_TO_A_CUSTOMER')) {
575 $sql .= "fk_soc,";
576 }
577 $sql .= " visible,";
578 $sql .= " type,";
579 $sql .= " import_key,";
580 $sql .= " ref_ext,";
581 $sql .= " entity,";
582 $sql .= " date_creation,";
583 $sql .= " fk_user_creat";
584 $sql .= ") VALUES (";
585 $sql .= (int) $this->fk_parent.",";
586 $sql .= "'".$this->db->escape($this->label)."', ";
587 $sql .= "'".$this->db->escape($this->description)."', ";
588 $sql .= "'".$this->db->escape($this->color)."', ";
589 $sql .= (int) $this->position.",";
590 if (getDolGlobalString('CATEGORY_ASSIGNED_TO_A_CUSTOMER')) {
591 $sql .= ($this->socid > 0 ? $this->socid : 'null').", ";
592 }
593 $sql .= "'".$this->db->escape((string) $this->visible)."', ";
594 $sql .= ((int) $type).", ";
595 $sql .= (!empty($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : 'null').", ";
596 $sql .= (!empty($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : 'null').", ";
597 $sql .= (int) $conf->entity.", ";
598 $sql .= "'".$this->db->idate($now)."', ";
599 $sql .= (int) $user->id;
600 $sql .= ")";
601
602 $res = $this->db->query($sql);
603 if ($res) {
604 $id = $this->db->last_insert_id(MAIN_DB_PREFIX."categorie");
605
606 if ($id > 0) {
607 $this->id = $id;
608
609 $action = 'create';
610
611 // Actions on extra fields
612 $result = $this->insertExtraFields();
613 if ($result < 0) {
614 $error++;
615 }
616
617 if (!$error && !$notrigger) {
618 // Call trigger
619 $result = $this->call_trigger('CATEGORY_CREATE', $user);
620 if ($result < 0) {
621 $error++;
622 }
623 // End call triggers
624 }
625
626 if (!$error) {
627 $this->db->commit();
628 return $id;
629 } else {
630 $this->db->rollback();
631 return -3;
632 }
633 } else {
634 $this->db->rollback();
635 return -2;
636 }
637 } else {
638 $this->error = $this->db->error();
639 $this->db->rollback();
640 return -1;
641 }
642 }
643
653 public function update(User $user, $notrigger = 0)
654 {
655 global $langs;
656
657 $error = 0;
658
659 // Clean parameters
660 $this->label = trim($this->label);
661 $this->description = trim($this->description);
662 $this->ref_ext = trim($this->ref_ext);
663 $this->fk_parent = ($this->fk_parent != "" ? intval($this->fk_parent) : 0);
664 $this->visible = ($this->visible != "" ? intval($this->visible) : 0);
665
666 if ($this->already_exists()) {
667 $this->error = $langs->trans("ImpossibleUpdateCat");
668 $this->error .= " : ".$langs->trans("CategoryExistsAtSameLevel");
669 return -1;
670 }
671
672 $this->db->begin();
673
674 $sql = "UPDATE ".MAIN_DB_PREFIX."categorie";
675 $sql .= " SET label = '".$this->db->escape($this->label)."',";
676 $sql .= " description = '".$this->db->escape($this->description)."',";
677 $sql .= " ref_ext = '".$this->db->escape($this->ref_ext)."',";
678 $sql .= " color = '".$this->db->escape($this->color)."'";
679 $sql .= ", position = ".(int) $this->position;
680 if (getDolGlobalString('CATEGORY_ASSIGNED_TO_A_CUSTOMER')) {
681 $sql .= ", fk_soc = ".($this->socid > 0 ? $this->socid : 'null');
682 }
683 $sql .= ", visible = ".(int) $this->visible;
684 $sql .= ", fk_parent = ".(int) $this->fk_parent;
685 $sql .= ", fk_user_modif = ".(int) $user->id;
686 $sql .= " WHERE rowid = ".((int) $this->id);
687
688 dol_syslog(get_class($this)."::update", LOG_DEBUG);
689 if ($this->db->query($sql)) {
690 $action = 'update';
691
692 // Actions on extra fields
693 $result = $this->insertExtraFields();
694 if ($result < 0) {
695 $error++;
696 }
697
698 if (!$error && !$notrigger) {
699 // Call trigger
700 $result = $this->call_trigger('CATEGORY_MODIFY', $user);
701 if ($result < 0) {
702 $error++;
703 }
704 // End call triggers
705 }
706
707 if (!$error) {
708 $this->db->commit();
709 return 1;
710 } else {
711 $this->db->rollback();
712 return -1;
713 }
714 } else {
715 $this->db->rollback();
716 dol_print_error($this->db);
717 return -1;
718 }
719 }
720
728 public function delete($user, $notrigger = 0)
729 {
730 $error = 0;
731
732 // Clean parameters
733 $this->fk_parent = ($this->fk_parent != "" ? intval($this->fk_parent) : 0);
734
735 dol_syslog(get_class($this)."::remove");
736
737 $this->db->begin();
738
739 if (/* !$error && */ !$notrigger) {
740 // Call trigger
741 $result = $this->call_trigger('CATEGORY_DELETE', $user);
742 if ($result < 0) {
743 $error++;
744 }
745 // End call triggers
746 }
747
748 /* FIX #1317 : Check for child category and move up 1 level*/
749 if (!$error) {
750 $sql = "UPDATE ".MAIN_DB_PREFIX."categorie";
751 $sql .= " SET fk_parent = ".((int) $this->fk_parent);
752 $sql .= " WHERE fk_parent = ".((int) $this->id);
753
754 if (!$this->db->query($sql)) {
755 $this->error = $this->db->lasterror();
756 $error++;
757 }
758 }
759
760 $arraydelete = array(
761 'categorie_account' => 'fk_categorie',
762 'categorie_actioncomm' => 'fk_categorie',
763 'categorie_contact' => 'fk_categorie',
764 'categorie_fournisseur' => 'fk_categorie',
765 'categorie_knowledgemanagement' => array('field' => 'fk_categorie', 'enabled' => isModEnabled('knowledgemanagement')),
766 'categorie_fichinter' => array('field' => 'fk_categorie', 'enabled' => false ), // not yet fully implemented (wait 22) isModEnabled('intervention')),
767 'categorie_member' => 'fk_categorie',
768 'categorie_user' => 'fk_categorie',
769 'categorie_product' => 'fk_categorie',
770 'categorie_project' => 'fk_categorie',
771 'categorie_societe' => 'fk_categorie',
772 'categorie_ticket' => array('field' => 'fk_categorie', 'enabled' => isModEnabled('ticket')),
773 'categorie_warehouse' => 'fk_categorie',
774 'categorie_website_page' => array('field' => 'fk_categorie', 'enabled' => isModEnabled('website')),
775 'category_bankline' => 'fk_categ',
776 'categorie_lang' => 'fk_category',
777 'categorie' => 'rowid',
778 );
779 foreach ($arraydelete as $key => $value) {
780 if (is_array($value)) {
781 if (empty($value['enabled'])) {
782 continue;
783 }
784 $value = $value['field'];
785 }
786 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$key;
787 $sql .= " WHERE ".$value." = ".((int) $this->id);
788 if (!$this->db->query($sql)) {
789 $this->errors[] = $this->db->lasterror();
790 dol_syslog("Error sql=".$sql." ".$this->error, LOG_ERR);
791 $error++;
792 }
793 }
794
795 // Removed extrafields
796 if (!$error) {
797 $result = $this->deleteExtraFields();
798 if ($result < 0) {
799 $error++;
800 dol_syslog(get_class($this)."::delete erreur ".$this->error, LOG_ERR);
801 }
802 }
803
804 if (!$error) {
805 $this->db->commit();
806 return 1;
807 } else {
808 $this->db->rollback();
809 return -1;
810 }
811 }
812
813 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
822 public function add_type($obj, $type = '')
823 {
824 // phpcs:enable
825 global $user;
826
827 $error = 0;
828
829 if ($this->id == -1) {
830 return -2;
831 }
832
833 if (empty($type)) {
834 $type = $obj->element;
835 }
836
837 dol_syslog(get_class($this).'::add_type', LOG_DEBUG);
838
839 $this->db->begin();
840
841 $sql = "INSERT INTO ".MAIN_DB_PREFIX."categorie_".(empty($this->MAP_CAT_TABLE[$type]) ? $type : $this->MAP_CAT_TABLE[$type]);
842 $sql .= " (fk_categorie, fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type]).")";
843 $sql .= " VALUES (".((int) $this->id).", ".((int) $obj->id).")";
844
845 if ($this->db->query($sql)) {
846 if (getDolGlobalString('CATEGORIE_RECURSIV_ADD')) {
847 $sql = 'SELECT fk_parent FROM '.MAIN_DB_PREFIX.'categorie';
848 $sql .= " WHERE rowid = ".((int) $this->id);
849
850 dol_syslog(get_class($this)."::add_type", LOG_DEBUG);
851 $resql = $this->db->query($sql);
852 if ($resql) {
853 if ($this->db->num_rows($resql) > 0) {
854 $objparent = $this->db->fetch_object($resql);
855
856 if (!empty($objparent->fk_parent)) {
857 $cat = new Categorie($this->db);
858 $cat->id = $objparent->fk_parent;
859 if (!$cat->containsObject($type, $obj->id)) {
860 $result = $cat->add_type($obj, $type);
861 if ($result < 0) {
862 $this->error = $cat->error;
863 $error++;
864 }
865 }
866 }
867 }
868 } else {
869 $error++;
870 $this->error = $this->db->lasterror();
871 }
872
873 if ($error) {
874 $this->db->rollback();
875 return -1;
876 }
877 }
878
879 // Call trigger
880 $this->context = array('linkto' => $obj); // Save object we want to link category to into category instance to provide information to trigger
881 $result = $this->call_trigger('CATEGORY_MODIFY', $user);
882 if ($result < 0) {
883 $error++;
884 }
885 // End call triggers
886
887 if (!$error) {
888 $this->db->commit();
889 return 1;
890 } else {
891 $this->db->rollback();
892 return -2;
893 }
894 } else {
895 $this->db->rollback();
896 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
897 $this->error = $this->db->lasterrno();
898 return -3;
899 } else {
900 $this->error = $this->db->lasterror();
901 }
902 return -1;
903 }
904 }
905
906 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
915 public function del_type($obj, $type)
916 {
917 // phpcs:enable
918 global $user;
919
920 $error = 0;
921
922 // For backward compatibility
923 if ($type == 'societe') {
924 $type = 'customer';
925 dol_syslog(get_class($this)."::del_type(): type 'societe' is deprecated, please use 'customer' instead", LOG_WARNING);
926 } elseif ($type == 'fournisseur') {
927 $type = 'supplier';
928 dol_syslog(get_class($this)."::del_type(): type 'fournisseur' is deprecated, please use 'supplier' instead", LOG_WARNING);
929 }
930
931 $this->db->begin();
932
933 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_".(empty($this->MAP_CAT_TABLE[$type]) ? $type : $this->MAP_CAT_TABLE[$type]);
934 $sql .= " WHERE fk_categorie = ".((int) $this->id);
935 $sql .= " AND fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])." = ".((int) $obj->id);
936
937 dol_syslog(get_class($this).'::del_type', LOG_DEBUG);
938 if ($this->db->query($sql)) {
939 // Call trigger
940 $this->context = array('unlinkoff' => $obj); // Save object we want to link category to into category instance to provide information to trigger
941 $result = $this->call_trigger('CATEGORY_MODIFY', $user);
942 if ($result < 0) {
943 $error++;
944 }
945 // End call triggers
946
947 if (!$error) {
948 $this->db->commit();
949 return 1;
950 } else {
951 $this->db->rollback();
952 return -2;
953 }
954 } else {
955 $this->db->rollback();
956 $this->error = $this->db->lasterror();
957 return -1;
958 }
959 }
960
979 public function getObjectsInCateg($type, $onlyids = 0, $limit = 0, $offset = 0, $sortfield = '', $sortorder = 'ASC', $filter = '', $filtermode = 'AND', $filterlang = '')
980 {
981 global $user;
982
983 if (empty($onlyids)) {
984 dol_syslog("getObjectsInCateg: This method used with parameter onlyids=0 is deprecated. Try by using instead getListForItem().", LOG_WARNING);
985 }
986
987 $objs = array();
988
989 $classnameforobj = $this->MAP_OBJ_CLASS[$type];
990 if (!empty($classnameforobj) && class_exists($classnameforobj)) {
991 $tmpobj = new $classnameforobj($this->db);
994 $sql = "SELECT c.fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])." as fk_object";
995 $sql .= " FROM ".MAIN_DB_PREFIX."categorie_".(empty($this->MAP_CAT_TABLE[$type]) ? $type : $this->MAP_CAT_TABLE[$type])." as c";
996 $sql .= ", ".MAIN_DB_PREFIX.(empty($this->MAP_OBJ_TABLE[$type]) ? $type : $this->MAP_OBJ_TABLE[$type])." as o";
997 if (!empty($filterlang)) {
998 $sql .= ", ".MAIN_DB_PREFIX.(empty($this->MAP_OBJ_TABLE[$type]) ? $type : $this->MAP_OBJ_TABLE[$type])."_lang as ol";
999 }
1000 $sql .= " WHERE o.entity IN (".getEntity($tmpobj->element).")";
1001 $sql .= " AND c.fk_categorie = ".((int) $this->id);
1002 // Compatibility with actioncomm table which has id instead of rowid
1003 if ((array_key_exists($type, $this->MAP_OBJ_TABLE) && $this->MAP_OBJ_TABLE[$type] == "actioncomm") || $type == "actioncomm") {
1004 $sql .= " AND c.fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])." = o.id";
1005 } else {
1006 $sql .= " AND c.fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])." = o.rowid";
1007 }
1008 if (!empty($filterlang)) {
1009 $sql .= " AND ol.fk_".(empty($this->MAP_OBJ_TABLE[$type]) ? $type : $this->MAP_OBJ_TABLE[$type])." = o.rowid";
1010 $sql .= " AND ol.lang = '".$this->db->escape($filterlang)."'";
1011 }
1012 // Protection for external users
1013 if (($type == 'customer' || $type == 'supplier') && $user->socid > 0) {
1014 $sql .= " AND o.rowid = ".((int) $user->socid);
1015 }
1016
1017 $errormessage = '';
1018 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
1019 if ($errormessage) {
1020 $this->errors[] = $errormessage;
1021 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1022 return -1;
1023 }
1024
1025 $sql .= $this->db->order($sortfield, $sortorder);
1026 if ($limit > 0 || $offset > 0) {
1027 $sql .= $this->db->plimit($limit + 1, $offset);
1028 }
1029
1030 dol_syslog(get_class($this)."::getObjectsInCateg", LOG_DEBUG);
1031
1032 $resql = $this->db->query($sql);
1033 if ($resql) {
1034 while ($rec = $this->db->fetch_array($resql)) {
1035 if ($onlyids) {
1036 $objs[] = $rec['fk_object'];
1037 } else {
1038 $tmpobj->id = 0;
1039 $tmpobj->fetch($rec['fk_object']); // The fetch will erase $tmpobj->id only if it succeed.
1040 // @phpstan-ignore-next-line
1041 if ($tmpobj->id > 0) { // Failing fetch may happen for example when a category supplier was set and third party was moved as customer only. The object supplier can't be loaded.
1042 $objs[] = clone $tmpobj;
1043 }
1044 }
1045 }
1046 } else {
1047 $this->error = $this->db->error().' sql='.$sql;
1048 return -1;
1049 }
1050 }
1051
1052 return $objs;
1053 }
1054
1063 public function containsObject($type, $object_id)
1064 {
1065 $sql = "SELECT COUNT(*) as nb FROM ".MAIN_DB_PREFIX."categorie_".(empty($this->MAP_CAT_TABLE[$type]) ? $type : $this->MAP_CAT_TABLE[$type]);
1066 $sql .= " WHERE fk_categorie = ".((int) $this->id)." AND fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])." = ".((int) $object_id);
1067
1068 dol_syslog(get_class($this)."::containsObject", LOG_DEBUG);
1069
1070 $resql = $this->db->query($sql);
1071 if ($resql) {
1072 return $this->db->fetch_object($resql)->nb;
1073 } else {
1074 $this->error = $this->db->error();
1075 return -1;
1076 }
1077 }
1078
1091 public function getListForItem($id, $type = 'customer', $sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0)
1092 {
1093 $categories = array();
1094
1095 $type = sanitizeVal($type, 'aZ09');
1096
1097 $sub_type = $type;
1098 $subcol_name = "fk_".$type;
1099 if ($type == "customer") {
1100 $sub_type = "societe";
1101 $subcol_name = "fk_soc";
1102 }
1103 if ($type == "supplier") {
1104 $sub_type = "fournisseur";
1105 $subcol_name = "fk_soc";
1106 }
1107 if ($type == "contact") {
1108 $subcol_name = "fk_socpeople";
1109 }
1110
1111 $idoftype = (int) (array_key_exists($type, $this->MAP_ID) ? $this->MAP_ID[$type] : -1);
1112
1113 $sql = "SELECT s.rowid";
1114 $sqlfields = $sql; // $sql fields to remove for count total
1115 $sql .= " FROM ".MAIN_DB_PREFIX."categorie as s, ".MAIN_DB_PREFIX."categorie_".$this->db->sanitize($sub_type)." as sub";
1116 $sql .= ' WHERE s.entity IN ('.getEntity('category').')';
1117 $sql .= ' AND s.type = '.((int) $idoftype);
1118 $sql .= ' AND s.rowid = sub.fk_categorie';
1119 $sql .= " AND sub.".$this->db->sanitize($subcol_name)." = ".((int) $id);
1120
1121 $offset = 0;
1122 $nbtotalofrecords = '';
1123 if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
1124 $sqlforcount = preg_replace('/^'.preg_quote($sqlfields, '/').'/', 'SELECT COUNT(*) as nbtotalofrecords', $sql);
1125 $sqlforcount = preg_replace('/GROUP BY .*$/', '', $sqlforcount);
1126
1127 $resql = $this->db->query($sqlforcount);
1128 if ($resql) {
1129 $objforcount = $this->db->fetch_object($resql);
1130 $nbtotalofrecords = $objforcount->nbtotalofrecords;
1131 } else {
1132 dol_print_error($this->db);
1133 }
1134
1135 if (($page * $limit) > $nbtotalofrecords) { // if total resultset is smaller then paging size (filtering), goto and load page 0
1136 $page = 0;
1137 $offset = 0;
1138 }
1139 $this->db->free($resql);
1140 }
1141
1142 $sql .= $this->db->order($sortfield, $sortorder);
1143 if ($limit) {
1144 if ($page < 0) {
1145 $page = 0;
1146 }
1147 $offset = $limit * $page;
1148
1149 $sql .= $this->db->plimit($limit + 1, $offset);
1150 }
1151
1152 $result = $this->db->query($sql);
1153 if ($result) {
1154 $i = 0;
1155 $num = $this->db->num_rows($result);
1156 $min = min($num, ($limit <= 0 ? $num : $limit));
1157 while ($i < $min) {
1158 $obj = $this->db->fetch_object($result);
1159 $category_static = new Categorie($this->db);
1160 if ($category_static->fetch($obj->rowid)) {
1161 $categories[$i]['id'] = $category_static->id;
1162 $categories[$i]['fk_parent'] = $category_static->fk_parent;
1163 $categories[$i]['label'] = $category_static->label;
1164 $categories[$i]['description'] = $category_static->description;
1165 $categories[$i]['color'] = $category_static->color;
1166 $categories[$i]['position'] = $category_static->position;
1167 $categories[$i]['socid'] = $category_static->socid;
1168 $categories[$i]['ref_ext'] = $category_static->ref_ext;
1169 $categories[$i]['visible'] = $category_static->visible;
1170 $categories[$i]['type'] = $category_static->type;
1171 $categories[$i]['entity'] = $category_static->entity;
1172 $categories[$i]['array_options'] = $category_static->array_options;
1173
1174 // multilangs
1175 if (getDolGlobalInt('MAIN_MULTILANGS') && isset($category_static->multilangs)) {
1176 $categories[$i]['multilangs'] = $category_static->multilangs;
1177 }
1178 }
1179 $i++;
1180 }
1181 } else {
1182 $this->error = $this->db->lasterror();
1183 return -1;
1184 }
1185 if (!count($categories)) {
1186 return [];
1187 }
1188
1189 return $categories;
1190 }
1191
1192 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1198 public function get_filles()
1199 {
1200 // phpcs:enable
1201 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."categorie";
1202 $sql .= " WHERE fk_parent = ".((int) $this->id);
1203 $sql .= " AND entity IN (".getEntity('category').")";
1204
1205 $res = $this->db->query($sql);
1206 if ($res) {
1207 $cats = array();
1208 while ($rec = $this->db->fetch_array($res)) {
1209 $cat = new Categorie($this->db);
1210 $cat->fetch($rec['rowid']);
1211 $cats[] = $cat;
1212 }
1213 return $cats;
1214 } else {
1215 dol_print_error($this->db);
1216 return -1;
1217 }
1218 }
1219
1220 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1227 protected function load_motherof()
1228 {
1229 // phpcs:enable
1230 $this->motherof = array();
1231
1232 // Load array[child]=parent
1233 $sql = "SELECT fk_parent as id_parent, rowid as id_son";
1234 $sql .= " FROM ".MAIN_DB_PREFIX."categorie";
1235 $sql .= " WHERE fk_parent <> 0";
1236 $sql .= " AND entity IN (".getEntity('category').")";
1237
1238 dol_syslog(get_class($this)."::load_motherof", LOG_DEBUG);
1239 $resql = $this->db->query($sql);
1240 if ($resql) {
1241 while ($obj = $this->db->fetch_object($resql)) {
1242 $this->motherof[$obj->id_son] = $obj->id_parent;
1243 }
1244 return 1;
1245 } else {
1246 dol_print_error($this->db);
1247 return -1;
1248 }
1249 }
1250
1251 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1272 public function get_full_arbo($type, $fromid = 0, $include = 0, $forcelangcode = '')
1273 {
1274 // phpcs:enable
1275 global $langs;
1276
1277 if (!is_numeric($type)) {
1278 $type = $this->MAP_ID[$type];
1279 }
1280 if (is_null($type)) {
1281 $this->error = 'BadValueForParameterType';
1282 return -1;
1283 }
1284
1285 if (is_string($fromid)) {
1286 $fromid = explode(',', $fromid);
1287 } elseif (is_numeric($fromid)) {
1288 if ($fromid > 0) {
1289 $fromid = array($fromid);
1290 } else {
1291 $fromid = array();
1292 }
1293 } elseif (!is_array($fromid)) {
1294 $fromid = array();
1295 }
1296
1297 $this->cats = array();
1298 $nbcateg = 0;
1299
1300 // Init this->motherof that is array(id_son=>id_parent, ...)
1301 $this->load_motherof();
1302
1303 if ($forcelangcode) {
1304 $current_lang = $forcelangcode;
1305 } else {
1306 $current_lang = $langs->getDefaultLang();
1307 }
1308
1309 // Init $this->cats array
1310 // Note: The DISTINCT reduces pb with old tables with duplicates but should not be used
1311 $sql = "SELECT DISTINCT c.rowid, c.label, c.ref_ext, c.description, c.color, c.position, c.fk_parent, c.visible";
1312 if (getDolGlobalInt('MAIN_MULTILANGS') && $current_lang !== 'none') {
1313 $sql .= ", t.label as label_trans, t.description as description_trans";
1314 }
1315 $sql .= " FROM ".MAIN_DB_PREFIX."categorie as c";
1316 if (getDolGlobalInt('MAIN_MULTILANGS') && $current_lang !== 'none') {
1317 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."categorie_lang as t ON t.fk_category = c.rowid AND t.lang = '".$this->db->escape($current_lang)."'";
1318 }
1319 $sql .= " WHERE c.entity IN (".getEntity('category').")";
1320 $sql .= " AND c.type = ".(int) $type;
1321
1322 dol_syslog(get_class($this)."::get_full_arbo get category list", LOG_DEBUG);
1323
1324 $resql = $this->db->query($sql);
1325 if ($resql) {
1326 $i = 0;
1327 $nbcateg = $this->db->num_rows($resql);
1328
1329 while ($obj = $this->db->fetch_object($resql)) {
1330 $this->cats[$obj->rowid]['rowid'] = $obj->rowid;
1331 $this->cats[$obj->rowid]['id'] = $obj->rowid;
1332 $this->cats[$obj->rowid]['fk_parent'] = $obj->fk_parent;
1333 $this->cats[$obj->rowid]['label'] = !empty($obj->label_trans) ? $obj->label_trans : $obj->label;
1334 $this->cats[$obj->rowid]['description'] = !empty($obj->description_trans) ? $obj->description_trans : $obj->description;
1335 $this->cats[$obj->rowid]['color'] = $obj->color;
1336 $this->cats[$obj->rowid]['position'] = $obj->position;
1337 $this->cats[$obj->rowid]['visible'] = $obj->visible;
1338 $this->cats[$obj->rowid]['ref_ext'] = $obj->ref_ext;
1339 $this->cats[$obj->rowid]['picto'] = 'category';
1340 // fields are filled with buildPathFromId later
1341 $this->cats[$obj->rowid]['fullpath'] = '';
1342 $this->cats[$obj->rowid]['fulllabel'] = '';
1343 $i++;
1344 }
1345 } else {
1346 dol_print_error($this->db);
1347 return -1;
1348 }
1349
1350 // We add the fullpath property to each elements of first level (no parent exists)
1351 dol_syslog(get_class($this)."::get_full_arbo call to buildPathFromId", LOG_DEBUG);
1352
1353 foreach ($this->cats as $key => $val) {
1354 //print 'key='.$key.'<br>'."\n";
1355 $this->buildPathFromId($key, $nbcateg); // Process a branch from the root category key (this category has no parent) and adds level to $this->cats items
1356 }
1357
1358 // Include or exclude leaf (including $fromid) from tree
1359 if (count($fromid) > 0) {
1360 $keyfiltercatid = '('.implode('|', $fromid).')';
1361
1362 //print "Look to discard category ".$fromid."\n";
1363 $keyfilter1 = '^'.$keyfiltercatid.'$';
1364 $keyfilter2 = '_'.$keyfiltercatid.'$';
1365 $keyfilter3 = '^'.$keyfiltercatid.'_';
1366 $keyfilter4 = '_'.$keyfiltercatid.'_';
1367 foreach (array_keys($this->cats) as $key) {
1368 $fullpath = (string) $this->cats[$key]['fullpath'];
1369 $test = (preg_match('/'.$keyfilter1.'/', $fullpath) || preg_match('/'.$keyfilter2.'/', $fullpath)
1370 || preg_match('/'.$keyfilter3.'/', $fullpath) || preg_match('/'.$keyfilter4.'/', $fullpath));
1371
1372 if (($test && !$include) || (!$test && $include)) {
1373 unset($this->cats[$key]); // @phpstan-ignore-line
1374 }
1375 }
1376 }
1377
1378 dol_syslog(get_class($this)."::get_full_arbo dol_sort_array", LOG_DEBUG);
1379
1380 $this->cats = dol_sort_array($this->cats, 'fulllabel', 'asc', 1, 0, 1); // Sort on full label like "Label 1 >> Sublabel a >> Subsublabel"
1381
1382 return $this->cats;
1383 }
1384
1395 private function buildPathFromId($id_categ, $protection = 1000)
1396 {
1397 //dol_syslog(get_class($this)."::buildPathFromId id_categ=".$id_categ." protection=".$protection, LOG_DEBUG);
1398
1399 if (!empty($this->cats[$id_categ]['fullpath'])) {
1400 // Already defined
1401 dol_syslog(get_class($this)."::buildPathFromId fullpath and fulllabel already defined", LOG_WARNING);
1402 return -1;
1403 }
1404
1405 // $this->cats[$id_categ] is supposed to be already an array. We just want to complete it with property fullpath and fulllabel
1406
1407 // Define fullpath and fulllabel
1408 $this->cats[$id_categ]['fullpath'] = '_'.$id_categ;
1409 $this->cats[$id_categ]['fulllabel'] = $this->cats[$id_categ]['label'];
1410 $i = 0;
1411 $cursor_categ = $id_categ;
1412 //print 'Work for id_categ='.$id_categ.'<br>'."\n";
1413 while ((empty($protection) || $i < $protection) && !empty($this->motherof[$cursor_categ])) {
1414 //print '&nbsp; cursor_categ='.$cursor_categ.' i='.$i.' '.$this->motherof[$cursor_categ].'<br>'."\n";
1415 $this->cats[$id_categ]['fullpath'] = '_'.$this->motherof[$cursor_categ].$this->cats[$id_categ]['fullpath'];
1416 $this->cats[$id_categ]['fulllabel'] = (empty($this->cats[$this->motherof[$cursor_categ]]) ? 'NotFound' : $this->cats[$this->motherof[$cursor_categ]]['label']).' >> '.$this->cats[$id_categ]['fulllabel'];
1417 //print '&nbsp; Result for id_categ='.$id_categ.' : '.$this->cats[$id_categ]['fullpath'].' '.$this->cats[$id_categ]['fulllabel'].'<br>'."\n";
1418 $i++;
1419 $cursor_categ = $this->motherof[$cursor_categ];
1420 }
1421 //print 'Result for id_categ='.$id_categ.' : '.$this->cats[$id_categ]['fullpath'].'<br>'."\n";
1422
1423 // We count number of _ to have level
1424 $nbunderscore = substr_count($this->cats[$id_categ]['fullpath'], '_');
1425 $this->cats[$id_categ]['level'] = ($nbunderscore ? $nbunderscore : null);
1426
1427 return 1;
1428 }
1429
1430
1431 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1439 public function get_all_categories($type = null, $parent = false)
1440 {
1441 // phpcs:enable
1442 if (!is_numeric($type) && !is_null($type)) {
1443 $type = $this->MAP_ID[$type];
1444 }
1445
1446 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."categorie";
1447 $sql .= " WHERE entity IN (".getEntity('category').")";
1448 if (!is_null($type)) {
1449 $sql .= " AND type = ".(int) $type;
1450 }
1451 if ($parent) {
1452 $sql .= " AND fk_parent = 0";
1453 }
1454
1455 $res = $this->db->query($sql);
1456 if ($res) {
1457 $cats = array();
1458 while ($rec = $this->db->fetch_array($res)) {
1459 $cat = new Categorie($this->db);
1460 $cat->fetch($rec['rowid']);
1461 $cats[$rec['rowid']] = $cat;
1462 }
1463 return $cats;
1464 } else {
1465 dol_print_error($this->db);
1466 return -1;
1467 }
1468 }
1469
1470 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1477 public function get_main_categories($type = null)
1478 {
1479 // phpcs:enable
1480 return $this->get_all_categories($type, true);
1481 }
1482
1483 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1489 public function already_exists()
1490 {
1491 // phpcs:enable
1492 $type = $this->type;
1493
1494 if (!is_numeric($type)) {
1495 $type = $this->MAP_ID[$type];
1496 }
1497
1498 /* We have to select any rowid from llx_categorie which category's mother and label
1499 * are equals to those of the calling category
1500 */
1501 $sql = "SELECT c.rowid";
1502 $sql .= " FROM ".MAIN_DB_PREFIX."categorie as c ";
1503 $sql .= " WHERE c.entity IN (".getEntity('category').")";
1504 $sql .= " AND c.type = ".((int) $type);
1505 $sql .= " AND c.fk_parent = ".((int) $this->fk_parent);
1506 $sql .= " AND c.label = '".$this->db->escape($this->label)."'";
1507
1508 dol_syslog(get_class($this)."::already_exists", LOG_DEBUG);
1509
1510 $resql = $this->db->query($sql);
1511 if ($resql) {
1512 if ($this->db->num_rows($resql) > 0) { // Checking for empty resql
1513 $obj = $this->db->fetch_object($resql);
1514 /* If object called create, obj cannot have is id.
1515 * If object called update, he mustn't have the same label as an other category for this mother.
1516 * So if the result has the same id, update is not for label, and if result has an other one, update may be for label.
1517 */
1518 if (!empty($obj) && $obj->rowid > 0 && $obj->rowid != $this->id) {
1519 dol_syslog(get_class($this)."::already_exists category with name=".$this->label." and parent ".$this->fk_parent." exists: rowid=".$obj->rowid." current_id=".$this->id, LOG_DEBUG);
1520 return 1;
1521 }
1522 }
1523 dol_syslog(get_class($this)."::already_exists no category with same name=".$this->label." and same parent ".$this->fk_parent." than category id=".$this->id, LOG_DEBUG);
1524 return 0;
1525 } else {
1526 $this->error = $this->db->error();
1527 return -1;
1528 }
1529 }
1530
1531
1532 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1543 public function print_all_ways($sep = '&gt;&gt;', $url = '', $nocolor = 0, $addpicto = 0)
1544 {
1545 // phpcs:enable
1546 $ways = array();
1547
1548 $all_ways = $this->get_all_ways(); // Load array of categories to reach this->id
1549
1550 foreach ($all_ways as $way) { // It seems we always have 1 entry in this array.
1551 $w = array();
1552 $i = 0;
1553 $forced_color = '';
1554 foreach ($way as $cat) { // Loop on each successive categories to reach the target of current category
1555 $i++;
1556
1557 if (empty($nocolor)) {
1558 $forced_color = 'colortoreplace';
1559 if ($i == count($way)) { // Last category in hierarchy
1560 // Check contrast with background and correct text color
1561 $forced_color = 'categtextwhite'; // We want color white because the getNomUrl of a tag is always called inside a dark background like '<span color="bbb"></span>' to show it as a tag. TODO Add this in param to force when called outside of span.
1562 if ($cat->color) {
1563 if (colorIsLight($cat->color)) {
1564 $forced_color = 'categtextblack';
1565 }
1566 }
1567 }
1568 }
1569
1570 if ($url == '') {
1571 $link = '<a href="'.DOL_URL_ROOT.'/categories/viewcat.php?id='.((int) $cat->id).'&type='.urlencode($cat->type).'" class="'.($i < count($way) ? 'small ' : '').$forced_color.'">';
1572 $linkend = '</a>';
1573 $w[] = $link.(($addpicto && $i == 1) ? img_object('', 'category', 'class="paddingright"') : '').$cat->label.$linkend;
1574 } elseif ($url == 'none') {
1575 $link = '<span class="valignmiddle '.($i < count($way) ? 'small ' : '').$forced_color.'">';
1576 $linkend = '</span>';
1577 $w[] = $link.(($addpicto && $i == 1) ? img_object('', 'category', 'class="paddingright"') : '').$cat->label.$linkend;
1578 } else {
1579 $w[] = '<a class="valignmiddle '.($i < count($way) ? 'small ' : '').$forced_color.'" href="'.DOL_URL_ROOT.'/'.$url.'?catid='.((int) $cat->id).'">'.($addpicto ? img_object('', 'category') : '').$cat->label.'</a>';
1580 }
1581 }
1582 $newcategwithpath = preg_replace('/colortoreplace/', $forced_color, implode('<span class="inline-block valignmiddle paddingleft paddingright '.$forced_color.'">'.$sep.'</span>', $w));
1583
1584 $ways[] = $newcategwithpath;
1585 }
1586
1587 return $ways;
1588 }
1589
1590
1591 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1598 public function get_meres()
1599 {
1600 // phpcs:enable
1601 $parents = array();
1602
1603 $sql = "SELECT fk_parent FROM ".MAIN_DB_PREFIX."categorie";
1604 $sql .= " WHERE rowid = ".((int) $this->id);
1605
1606 $res = $this->db->query($sql);
1607
1608 if ($res) {
1609 while ($rec = $this->db->fetch_array($res)) {
1610 if ($rec['fk_parent'] > 0) {
1611 $cat = new Categorie($this->db);
1612 $cat->fetch($rec['fk_parent']);
1613 $parents[] = $cat;
1614 }
1615 }
1616 return $parents;
1617 } else {
1618 dol_print_error($this->db);
1619 return -1;
1620 }
1621 }
1622
1623 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1630 public function get_all_ways()
1631 {
1632 // phpcs:enable
1633 $ways = array();
1634
1635 $parents = $this->get_meres();
1636 if (is_array($parents)) {
1637 foreach ($parents as $parent) {
1638 $all_ways = $parent->get_all_ways(); // recursivity. TODO Add a protection for infinite loop
1639 foreach ($all_ways as $way) {
1640 $w = $way;
1641 $w[] = $this;
1642 $ways[] = $w;
1643 }
1644 }
1645 }
1646
1647 if (count($ways) == 0) {
1648 $ways[0][0] = $this;
1649 }
1650
1651 return $ways;
1652 }
1653
1664 public function containing($id, $type, $mode = 'object')
1665 {
1666 $cats = array();
1667
1668 if (is_numeric($type)) {
1669 $type = array_search($type, $this->MAP_ID);
1670 }
1671
1672 if ($type === Categorie::TYPE_BANK_LINE) { // TODO Remove this after migration of llx_category_bankline into llx_categorie_bankline
1673 // Load bank categories
1674 $sql = "SELECT c.label, c.rowid";
1675 $sql .= " FROM ".MAIN_DB_PREFIX."category_bankline as a, ".MAIN_DB_PREFIX."categorie as c";
1676 $sql .= " WHERE a.lineid=".((int) $id)." AND a.fk_categ = c.rowid";
1677 $sql .= " AND c.entity IN (".getEntity('category').")";
1678 $sql .= " ORDER BY c.label";
1679
1680 $res = $this->db->query($sql);
1681 if ($res) {
1682 while ($obj = $this->db->fetch_object($res)) {
1683 if ($mode == 'id') {
1684 $cats[] = $obj->rowid;
1685 } elseif ($mode == 'label') {
1686 $cats[] = $obj->label;
1687 } else {
1688 $cat = new Categorie($this->db);
1689 $cat->id = $obj->rowid;
1690 $cat->label = $obj->label;
1691 $cats[] = $cat;
1692 }
1693 }
1694 } else {
1695 dol_print_error($this->db);
1696 return -1;
1697 }
1698 } else {
1699 $sql = "SELECT ct.fk_categorie, c.label, c.rowid";
1700 $sql .= " FROM ".MAIN_DB_PREFIX."categorie_".(empty($this->MAP_CAT_TABLE[$type]) ? $type : $this->MAP_CAT_TABLE[$type])." as ct, ".MAIN_DB_PREFIX."categorie as c";
1701 $sql .= " WHERE ct.fk_categorie = c.rowid AND ct.fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])." = ".(int) $id;
1702 // This seems useless because the table already contains id of category of 1 unique type. So commented.
1703 // So now it works also with external added categories.
1704 //$sql .= " AND c.type = ".((int) $this->MAP_ID[$type]);
1705 $sql .= " AND c.entity IN (".getEntity('category').")";
1706
1707 $res = $this->db->query($sql);
1708 if ($res) {
1709 while ($obj = $this->db->fetch_object($res)) {
1710 if ($mode == 'id') {
1711 $cats[] = $obj->rowid;
1712 } elseif ($mode == 'label') {
1713 $cats[] = $obj->label;
1714 } else {
1715 $cat = new Categorie($this->db);
1716 $cat->fetch($obj->fk_categorie);
1717 $cats[] = $cat;
1718 }
1719 }
1720 } else {
1721 dol_print_error($this->db);
1722 return -1;
1723 }
1724 }
1725
1726 return $cats;
1727 }
1728
1740 public function rechercher($id, $nom, $type, $exact = false, $case = false)
1741 {
1742 // Deprecation warning
1743 if (is_numeric($type)) {
1744 dol_syslog(__METHOD__.': using numeric types is deprecated.', LOG_WARNING);
1745 }
1746
1747 $cats = array();
1748
1749 // For backward compatibility
1750 if (is_numeric($type)) {
1751 // We want to reverse lookup
1752 $map_type = array_flip($this->MAP_ID);
1753 $type = $map_type[$type];
1754 dol_syslog(get_class($this)."::rechercher(): numeric types are deprecated, please use string instead", LOG_WARNING);
1755 }
1756
1757 // Generation requete recherche
1758 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."categorie";
1759 $sql .= " WHERE type = ".((int) $this->MAP_ID[$type]);
1760 $sql .= " AND entity IN (".getEntity('category').")";
1761 if ($nom) {
1762 if (!$exact) {
1763 $nom = '%'.$this->db->escape(str_replace('*', '%', $nom)).'%';
1764 }
1765 if (!$case) {
1766 $sql .= " AND label LIKE '".$this->db->escape($nom)."'";
1767 } else {
1768 $sql .= " AND label LIKE BINARY '".$this->db->escape($nom)."'";
1769 }
1770 }
1771 if ($id) {
1772 $sql .= " AND rowid = ".((int) $id);
1773 }
1774
1775 $res = $this->db->query($sql);
1776 if ($res) {
1777 while ($rec = $this->db->fetch_array($res)) {
1778 $cat = new Categorie($this->db);
1779 $cat->fetch($rec['rowid']);
1780 $cats[] = $cat;
1781 }
1782
1783 return $cats;
1784 } else {
1785 $this->error = $this->db->error().' sql='.$sql;
1786 return -1;
1787 }
1788 }
1789
1796 public function isAnyPhotoAvailable($sdir)
1797 {
1798 include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
1799 include_once DOL_DOCUMENT_ROOT . '/core/lib/images.lib.php';
1800
1801 $sdir .= '/' . get_exdir($this->id, 2, 0, 0, $this, 'category') . $this->id . "/photos/";
1802
1803 $dir_osencoded = dol_osencode($sdir);
1804 if (file_exists($dir_osencoded)) {
1805 $handle = opendir($dir_osencoded);
1806 if (is_resource($handle)) {
1807 while (($file = readdir($handle)) !== false) {
1808 if (!utf8_check($file)) {
1809 $file = mb_convert_encoding($file, 'UTF-8', 'ISO-8859-1'); // To be sure data is stored in UTF8 in memory
1810 }
1811 if (dol_is_file($sdir . $file) && image_format_supported($file) >= 0) {
1812 return true;
1813 }
1814 }
1815 }
1816 }
1817 return false;
1818 }
1819
1826 public function getTooltipContentArray($params)
1827 {
1828 global $langs;
1829
1830 $langs->load('categories');
1831
1832 $datas = [];
1833
1834 $datas['label'] = $langs->trans("ShowCategory").': '.($this->ref ? $this->ref : $this->label);
1835
1836 return $datas;
1837 }
1838
1852 public function getNomUrl($withpicto = 0, $option = '', $maxlength = 0, $moreparam = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = 0)
1853 {
1854 global $conf, $langs, $hookmanager;
1855
1856 if (!empty($conf->dol_no_mouse_hover)) {
1857 $notooltip = 1; // Force disable tooltips
1858 }
1859
1860 $result = '';
1861 $params = [
1862 'id' => $this->id,
1863 'objecttype' => $this->element,
1864 'option' => $option,
1865 ];
1866 $classfortooltip = 'classfortooltip';
1867 $dataparams = '';
1868 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1869 $classfortooltip = 'classforajaxtooltip';
1870 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1871 $label = '';
1872 } else {
1873 $label = implode($this->getTooltipContentArray($params));
1874 }
1875
1876 $url = DOL_URL_ROOT.'/categories/viewcat.php?id='.$this->id.'&type='.$this->type.$moreparam.'&backtopage='.urlencode($_SERVER['PHP_SELF'].($moreparam ? '?'.$moreparam : ''));
1877
1878 if ($option !== 'nolink') {
1879 // Add param to save lastsearch_values or not
1880 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1881 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1882 $add_save_lastsearch_values = 1;
1883 }
1884 if (/* $url && */ $add_save_lastsearch_values) {
1885 $url .= '&save_lastsearch_values=1';
1886 }
1887 }
1888
1889 // Check contrast with background and correct text color
1890 $forced_color = 'categtextwhite'; // We want color white because the getNomUrl of a tag is always called inside a dark background like '<span color="bbb"></span>' to show it as a tag. TODO Add this in param to force when called outside of span.
1891 if ($this->color) {
1892 if (colorIsLight($this->color)) { // If color is light, we force color to dark
1893 $forced_color = 'categtextblack';
1894 }
1895 }
1896
1897 $linkclose = '';
1898 if (empty($notooltip)) {
1899 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1900 $label = $langs->trans("ShowMyObject");
1901 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
1902 }
1903 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
1904 $linkclose .= $dataparams.' class="'.$classfortooltip.' '.$forced_color.($morecss ? ' '.$morecss : '').'"';
1905 } else {
1906 $linkclose = ' class="'.$forced_color.($morecss ? ' '.$morecss : '').'"';
1907 }
1908
1909 if ($option == 'nolink' /* || empty($url) */) {
1910 $linkstart = '<span';
1911 } else {
1912 $linkstart = '<a href="'.$url.'"';
1913 }
1914 $linkstart .= $linkclose.'>';
1915 if ($option == 'nolink' /* || empty($url) */) {
1916 $linkend = '</span>';
1917 } else {
1918 $linkend = '</a>';
1919 }
1920
1921 $result .= $linkstart;
1922
1923 if ($withpicto) {
1924 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'"'), 0, 0, $notooltip ? 0 : 1);
1925 }
1926
1927 if ($withpicto != 2) {
1928 $result .= dol_trunc(($this->ref ? $this->ref : $this->label), $maxlength);
1929 }
1930
1931 $result .= $linkend;
1932
1933 global $action;
1934 $hookmanager->initHooks(array($this->element . 'dao'));
1935 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1936 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1937 if ($reshook > 0) {
1938 $result = $hookmanager->resPrint;
1939 } else {
1940 $result .= $hookmanager->resPrint;
1941 }
1942 return $result;
1943 }
1944
1945
1946 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1954 public function add_photo($sdir, $file)
1955 {
1956 // phpcs:enable
1957 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1958
1959 $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, 'category').$this->id."/";
1960 $dir .= "photos/";
1961
1962 if (!file_exists($dir)) {
1963 dol_mkdir($dir);
1964 }
1965
1966 if (file_exists($dir)) {
1967 if (is_array($file['name'])) {
1968 $nbfile = count($file['name']);
1969 for ($i = 0; $i < $nbfile; $i++) {
1970 $originImage = $dir.$file['name'][$i];
1971
1972 // Cree fichier en taille origine
1973 dol_move_uploaded_file($file['tmp_name'][$i], $originImage, 1, 0, 0);
1974
1975 if (file_exists($originImage)) {
1976 // Create thumbs
1977 $this->addThumbs($originImage);
1978 }
1979 }
1980 } else {
1981 $originImage = $dir.$file['name'];
1982
1983 // Cree fichier en taille origine
1984 dol_move_uploaded_file($file['tmp_name'], $originImage, 1, 0, 0);
1985
1986 if (file_exists($originImage)) {
1987 // Create thumbs
1988 $this->addThumbs($originImage);
1989 }
1990 }
1991 }
1992 }
1993
1994 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2002 public function liste_photos($dir, $nbmax = 0)
2003 {
2004 // phpcs:enable
2005 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2006
2007 $nbphoto = 0;
2008 $tabobj = array();
2009
2010 $dirthumb = $dir.'thumbs/';
2011
2012 if (file_exists($dir)) {
2013 $handle = opendir($dir);
2014 if (is_resource($handle)) {
2015 while (($file = readdir($handle)) !== false) {
2016 if (dol_is_file($dir.$file) && preg_match('/(\.jpeg|\.jpg|\.bmp|\.gif|\.png|\.tiff)$/i', $dir.$file)) {
2017 $nbphoto++;
2018 $photo = $file;
2019
2020 // On determine nom du fichier vignette
2021 $photo_vignette = '';
2022 $regs = array();
2023 if (preg_match('/(\.jpeg|\.jpg|\.bmp|\.gif|\.png|\.tiff)$/i', $photo, $regs)) {
2024 $photo_vignette = preg_replace('/'.$regs[0].'/i', '', $photo).'_small'.$regs[0];
2025 }
2026
2027 // Object
2028 $obj = array();
2029 $obj['photo'] = $photo;
2030 if ($photo_vignette && is_file($dirthumb.$photo_vignette)) {
2031 $obj['photo_vignette'] = 'thumbs/'.$photo_vignette;
2032 } else {
2033 $obj['photo_vignette'] = "";
2034 }
2035
2036 $tabobj[$nbphoto - 1] = $obj;
2037
2038 // On continue ou on arrete de boucler
2039 if ($nbmax && $nbphoto >= $nbmax) {
2040 break;
2041 }
2042 }
2043 }
2044
2045 closedir($handle);
2046 }
2047 }
2048
2049 return $tabobj;
2050 }
2051
2052 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2059 public function delete_photo($file)
2060 {
2061 // phpcs:enable
2062 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2063
2064 $dir = dirname($file).'/'; // Chemin du dossier contenant l'image d'origine
2065 $dirthumb = $dir.'/thumbs/'; // Chemin du dossier contenant la vignette
2066 $filename = preg_replace('/'.preg_quote($dir, '/').'/i', '', $file); // Nom du fichier
2067
2068 // On efface l'image d'origine
2069 dol_delete_file($file, 0); // do not use disableglob, ecmfiles will not be deleted
2070
2071 // Si elle existe, on efface la vignette
2072 $regs = array();
2073 if (preg_match('/(\.jpeg|\.jpg|\.bmp|\.gif|\.png|\.tiff)$/i', $filename, $regs)) {
2074 $photo_vignette = preg_replace('/'.$regs[0].'/i', '', $filename).'_small'.$regs[0];
2075 if (file_exists($dirthumb.$photo_vignette)) {
2076 dol_delete_file($dirthumb.$photo_vignette, 1);
2077 }
2078 }
2079 }
2080
2081 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2088 public function get_image_size($file)
2089 {
2090 // phpcs:enable
2091 $infoImg = getimagesize($file); // Recuperation des infos de l'image
2092 $this->imgWidth = $infoImg[0]; // Largeur de l'image
2093 $this->imgHeight = $infoImg[1]; // Hauteur de l'image
2094 }
2095
2104 public function setMultiLangs(User $user, $notrigger = 0)
2105 {
2106 global $langs;
2107
2108 $langs_available = $langs->get_available_languages();
2109 $current_lang = $langs->getDefaultLang();
2110
2111 foreach ($langs_available as $key => $value) {
2112 $sql = "SELECT rowid";
2113 $sql .= " FROM ".MAIN_DB_PREFIX."categorie_lang";
2114 $sql .= " WHERE fk_category=".((int) $this->id);
2115 $sql .= " AND lang = '".$this->db->escape($key)."'";
2116
2117 $result = $this->db->query($sql);
2118
2119 if ($key == $current_lang) {
2120 $sql2 = '';
2121 if ($this->db->num_rows($result)) { // if no line in database
2122 $sql2 = "UPDATE ".MAIN_DB_PREFIX."categorie_lang";
2123 $sql2 .= " SET label = '".$this->db->escape($this->label)."',";
2124 $sql2 .= " description = '".$this->db->escape($this->description)."'";
2125 $sql2 .= " WHERE fk_category = ".((int) $this->id)." AND lang = '".$this->db->escape($key)."'";
2126 } elseif (isset($this->multilangs[$key])) {
2127 $sql2 = "INSERT INTO ".MAIN_DB_PREFIX."categorie_lang (fk_category, lang, label, description)";
2128 $sql2 .= " VALUES(".((int) $this->id).", '".$this->db->escape($key)."', '".$this->db->escape($this->label)."'";
2129 $sql2 .= ", '".$this->db->escape($this->multilangs[$key]["description"])."')";
2130 }
2131 dol_syslog(get_class($this).'::setMultiLangs', LOG_DEBUG);
2132 if ($sql2 && !$this->db->query($sql2)) {
2133 $this->error = $this->db->lasterror();
2134 return -1;
2135 }
2136 } elseif (isset($this->multilangs[$key])) {
2137 if ($this->db->num_rows($result)) { // if no line in database
2138 $sql2 = "UPDATE ".MAIN_DB_PREFIX."categorie_lang";
2139 $sql2 .= " SET label='".$this->db->escape($this->multilangs[$key]["label"])."',";
2140 $sql2 .= " description='".$this->db->escape($this->multilangs[$key]["description"])."'";
2141 $sql2 .= " WHERE fk_category=".((int) $this->id)." AND lang='".$this->db->escape($key)."'";
2142 } else {
2143 $sql2 = "INSERT INTO ".MAIN_DB_PREFIX."categorie_lang (fk_category, lang, label, description)";
2144 $sql2 .= " VALUES(".((int) $this->id).", '".$this->db->escape($key)."', '".$this->db->escape($this->multilangs[$key]["label"])."'";
2145 $sql2 .= ",'".$this->db->escape($this->multilangs[$key]["description"])."')";
2146 }
2147
2148 // on ne sauvegarde pas des champs vides
2149 if ($this->multilangs[$key]["label"] || $this->multilangs[$key]["description"] || $this->multilangs[$key]["note"]) {
2150 dol_syslog(get_class($this).'::setMultiLangs', LOG_DEBUG);
2151 }
2152 if (!$this->db->query($sql2)) {
2153 $this->error = $this->db->lasterror();
2154 return -1;
2155 }
2156 }
2157 }
2158
2159 // Call trigger
2160 if (!$notrigger) {
2161 $result = $this->call_trigger('CATEGORY_SET_MULTILANGS', $user);
2162 if ($result < 0) {
2163 $this->error = $this->db->lasterror();
2164 return -1;
2165 }
2166 }
2167 // End call triggers
2168
2169 return 1;
2170 }
2171
2180 public function delMultiLangs($langtodelete, $user)
2181 {
2182 $sql = "DELETE FROM ".$this->db->prefix()."categorie_lang";
2183 $sql .= " WHERE fk_category = ".((int) $this->id)." AND lang = '".$this->db->escape($langtodelete)."'";
2184
2185 dol_syslog(get_class($this).'::delMultiLangs', LOG_DEBUG);
2186 $result = $this->db->query($sql);
2187 if ($result) {
2188 // Call trigger
2189 $result = $this->call_trigger('CATEGORY_DEL_MULTILANGS', $user);
2190 if ($result < 0) {
2191 $this->error = $this->db->lasterror();
2192 dol_syslog(get_class($this).'::delMultiLangs error='.$this->error, LOG_ERR);
2193 return -1;
2194 }
2195 // End call triggers
2196 return 1;
2197 } else {
2198 $this->error = $this->db->lasterror();
2199 dol_syslog(get_class($this).'::delMultiLangs error='.$this->error, LOG_ERR);
2200 return -1;
2201 }
2202 }
2203
2209 public function getMultiLangs()
2210 {
2211 global $langs;
2212
2213 $current_lang = $langs->getDefaultLang();
2214
2215 $sql = "SELECT lang, label, description";
2216 $sql .= " FROM ".MAIN_DB_PREFIX."categorie_lang";
2217 $sql .= " WHERE fk_category=".((int) $this->id);
2218
2219 $result = $this->db->query($sql);
2220 if ($result) {
2221 while ($obj = $this->db->fetch_object($result)) {
2222 //print 'lang='.$obj->lang.' current='.$current_lang.'<br>';
2223 if ($obj->lang == $current_lang) { // si on a les traduct. dans la langue courante on les charge en infos principales.
2224 $this->label = $obj->label;
2225 $this->description = $obj->description;
2226 }
2227 $this->multilangs[$obj->lang]["label"] = $obj->label;
2228 $this->multilangs[$obj->lang]["description"] = $obj->description;
2229 }
2230 return 1;
2231 } else {
2232 $this->error = $langs->trans("Error")." : ".$this->db->error()." - ".$sql;
2233 return -1;
2234 }
2235 }
2236
2243 public function getLibStatut($mode)
2244 {
2245 return '';
2246 }
2247
2248
2256 public function initAsSpecimen()
2257 {
2258 dol_syslog(get_class($this)."::initAsSpecimen");
2259
2260 // Initialise parameters
2261 $this->id = 0;
2262 $this->fk_parent = 0;
2263 $this->label = 'SPECIMEN';
2264 $this->specimen = 1;
2265 $this->description = 'This is a description';
2266 $this->socid = 1;
2267 $this->type = self::TYPE_PRODUCT;
2268
2269 return 1;
2270 }
2271
2280 public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
2281 {
2282 $tables = array(
2283 'categorie_societe'
2284 );
2285
2286 return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables, 1);
2287 }
2288
2297 public static function getFilterJoinQuery($type, $rowIdName)
2298 {
2299 if ($type == 'bank_account') {
2300 $type = 'account';
2301 }
2302
2303 return " LEFT JOIN ".MAIN_DB_PREFIX."categorie_".$type." as cp ON ".$rowIdName." = cp.fk_".$type;
2304 }
2305
2315 public static function getFilterSelectQuery($type, $rowIdName, $searchList)
2316 {
2317 if ($type == 'bank_account') {
2318 $type = 'account';
2319 }
2320 if ($type == 'customer') {
2321 $type = 'societe';
2322 }
2323 if ($type == 'supplier') {
2324 $type = 'fournisseur';
2325 }
2326
2327 if (empty($searchList) && !is_array($searchList)) {
2328 return "";
2329 }
2330
2331 $searchCategorySqlList = array();
2332 foreach ($searchList as $searchCategory) {
2333 if (intval($searchCategory) == -2) {
2334 $searchCategorySqlList[] = " cp.fk_categorie IS NULL";
2335 } elseif (intval($searchCategory) > 0) {
2336 $searchCategorySqlList[] = " ".$rowIdName." IN (SELECT fk_".$type." FROM ".MAIN_DB_PREFIX."categorie_".$type." WHERE fk_categorie = ".((int) $searchCategory).")";
2337 }
2338 }
2339
2340 if (!empty($searchCategorySqlList)) {
2341 return " AND (".implode(' AND ', $searchCategorySqlList).")";
2342 } else {
2343 return "";
2344 }
2345 }
2346
2352 public function countNbOfCategories()
2353 {
2354 dol_syslog(get_class($this)."::count_all_categories", LOG_DEBUG);
2355 $sql = "SELECT COUNT(rowid) FROM ".MAIN_DB_PREFIX."categorie";
2356 $sql .= " WHERE entity IN (".getEntity('category').")";
2357
2358 $res = $this->db->query($sql);
2359 if ($res) {
2360 $obj = $this->db->fetch_object($res);
2361 return $obj->count;
2362 } else {
2363 dol_print_error($this->db);
2364 return -1;
2365 }
2366 }
2367}
print $object position
Definition edit.php:206
$object ref
Definition info.php:90
Class to manage categories.
getListForItem($id, $type='customer', $sortfield="s.rowid", $sortorder='ASC', $limit=0, $page=0)
Return the list of the categories of a given element (a product, a customer, ...).
get_image_size($file)
Load size of image file.
load_motherof()
Load the array this->motherof that is array(id_son=>id_parent, ...), so array of all child categories...
getLibStatut($mode)
Return label of contact status.
delete_photo($file)
Efface la photo de la categorie et sa vignette.
containing($id, $type, $mode='object')
Return list of categories (object instances or labels) linked to a given object having id $id and typ...
getTooltipContentArray($params)
getTooltipContentArray
countNbOfCategories()
Count all categories.
del_type($obj, $type)
Delete object from category.
buildPathFromId($id_categ, $protection=1000)
For category id_categ and its children available in this->cats, define property fullpath and fulllabe...
static getFilterSelectQuery($type, $rowIdName, $searchList)
Return the additional SQL SELECT query for filtering a list by a category.
delMultiLangs($langtodelete, $user)
Delete a language for this category.
create($user, $notrigger=0)
Add category into database.
rechercher($id, $nom, $type, $exact=false, $case=false)
Returns categories whose id or name matches.
getMultiLangs()
Load array this->multilangs.
isAnyPhotoAvailable($sdir)
Return if at least one photo is available.
already_exists()
Check if a category with same label already exists for this cat's parent or root and for this cat's t...
get_filles()
Return direct children ids of a category into an array.
initAsSpecimen()
Initialise an instance with random values.
get_all_categories($type=null, $parent=false)
Returns all categories.
static getFilterJoinQuery($type, $rowIdName)
Return the additional SQL JOIN query for filtering a list by a category.
containsObject($type, $object_id)
Check for the presence of a given object in the current category.
get_main_categories($type=null)
Returns the first level categories (which are not child)
__construct($db)
Constructor.
getMapId()
Get MAP_ID.
static replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
setMultiLangs(User $user, $notrigger=0)
Create or Update translations of categories labels.
getMapList()
Get map list.
$fields
'type' if the field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter...
get_meres()
Returns an array containing the list of parent categories Note: A category can only have one parent b...
get_full_arbo($type, $fromid=0, $include=0, $forcelangcode='')
Rebuilding the category tree as an array Return an array of table('id','id_mere',....
add_type($obj, $type='')
Link an object to the category.
update(User $user, $notrigger=0)
Update category.
liste_photos($dir, $nbmax=0)
Return an array with all photos inside the directory.
get_all_ways()
Returns in a array all possible paths to go to the category starting with the major categories repres...
getNomUrl($withpicto=0, $option='', $maxlength=0, $moreparam='', $notooltip=0, $morecss='', $save_lastsearch_value=0)
Return name and link of category (with picto) Use ->id, ->ref, ->label, ->color.
print_all_ways($sep='&gt;&gt;', $url='', $nocolor=0, $addpicto=0)
Returns the path of the category, with the names of the categories separated by $sep (" >> " by defau...
add_photo($sdir, $file)
Add the image uploaded as $file to the directory $sdir/<category>-<id>/photos/.
fetch($id, $label='', $type=null, $ref_ext='')
Load category into memory from database.
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
deleteExtraFields()
Delete all extra fields values for the current object.
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
call_trigger($triggerName, $user)
Call trigger based on this instance.
addThumbs($file, $quality=50)
Build thumb.
Class to manage Dolibarr database access.
Class to manage Dolibarr users.
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
dol_is_file($pathoffile)
Return if path is a file.
dol_move_uploaded_file($src_file, $dest_file, $allowoverwrite, $disablevirusscan=0, $uploaderrorcode=0, $nohook=0, $keyforsourcefile='addedfile', $upload_dir='', $mode=0)
Check validity of a file upload from an GUI page, and move it to its final destination.
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
colorIsLight($stringcolor)
Return true if the color is light.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
sanitizeVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
utf8_check($str)
Check if a string is in UTF8.
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
image_format_supported($file, $acceptsvg=0)
Return if a filename is file name of a supported image format.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:158