Passed
Branch develop (4336b1)
by
unknown
85:39
created

Categorie::debug_cats()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 0
dl 0
loc 12
rs 10
c 0
b 0
f 0
1
<?php
2
/* Copyright (C) 2005       Matthieu Valleton       <[email protected]>
3
 * Copyright (C) 2005       Davoleau Brice          <[email protected]>
4
 * Copyright (C) 2005       Rodolphe Quiedeville    <[email protected]>
5
 * Copyright (C) 2006-2012  Regis Houssin           <[email protected]>
6
 * Copyright (C) 2006-2012  Laurent Destailleur     <[email protected]>
7
 * Copyright (C) 2007       Patrick Raguin          <[email protected]>
8
 * Copyright (C) 2013-2016  Juanjo Menent           <[email protected]>
9
 * Copyright (C) 2013-2018  Philippe Grand          <[email protected]>
10
 * Copyright (C) 2015       Marcos García           <[email protected]>
11
 * Copyright (C) 2015       Raphaël Doursenaud      <[email protected]>
12
 * Copyright (C) 2016       Charlie Benke           <[email protected]>
13
 * Copyright (C) 2018-2022  Frédéric France         <[email protected]>
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
29
/**
30
 *	\file       htdocs/categories/class/categorie.class.php
31
 *	\ingroup    categorie
32
 *	\brief      File of class to manage categories
33
 */
34
35
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
36
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
37
require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';
38
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.class.php';
39
require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
40
require_once DOL_DOCUMENT_ROOT.'/knowledgemanagement/class/knowledgerecord.class.php';
41
42
43
/**
44
 *	Class to manage categories
45
 */
46
class Categorie extends CommonObject
47
{
48
	// Categories types (we use string because we want to accept any modules/types in a future)
49
	const TYPE_PRODUCT   = 'product';
50
	const TYPE_SUPPLIER  = 'supplier';
51
	const TYPE_CUSTOMER  = 'customer';
52
	const TYPE_MEMBER    = 'member';
53
	const TYPE_CONTACT   = 'contact';
54
	const TYPE_USER      = 'user';
55
	const TYPE_PROJECT   = 'project';
56
	const TYPE_ACCOUNT   = 'bank_account';
57
	const TYPE_BANK_LINE = 'bank_line';
58
	const TYPE_WAREHOUSE = 'warehouse';
59
	const TYPE_ACTIONCOMM = 'actioncomm';
60
	const TYPE_WEBSITE_PAGE = 'website_page';
61
	const TYPE_TICKET = 'ticket';
62
	const TYPE_KNOWLEDGEMANAGEMENT = 'knowledgemanagement';
63
64
	/**
65
	 * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
66
	 */
67
	public $picto = 'category';
68
69
70
	/**
71
	 * @var array Table of mapping between type string and ID used for field 'type' in table llx_categories
72
	 */
73
	protected $MAP_ID = array(
74
		'product'      => 0,
75
		'supplier'     => 1,
76
		'customer'     => 2,
77
		'member'       => 3,
78
		'contact'      => 4,
79
		'bank_account' => 5,
80
		'project'      => 6,
81
		'user'         => 7,
82
		'bank_line'    => 8,
83
		'warehouse'    => 9,
84
		'actioncomm'   => 10,
85
		'website_page' => 11,
86
		'ticket'       => 12,
87
		'knowledgemanagement' => 13
88
	);
89
90
	/**
91
	 * @var array Code mapping from ID
92
	 *
93
	 * @note This array should be removed in future, once previous constants are moved to the string value. Deprecated
94
	 */
95
	public static $MAP_ID_TO_CODE = array(
96
		0 => 'product',
97
		1 => 'supplier',
98
		2 => 'customer',
99
		3 => 'member',
100
		4 => 'contact',
101
		5 => 'bank_account',
102
		6 => 'project',
103
		7 => 'user',
104
		8 => 'bank_line',
105
		9 => 'warehouse',
106
		10 => 'actioncomm',
107
		11 => 'website_page',
108
		12 => 'ticket',
109
		13 => 'knowledgemanagement'
110
	);
111
112
	/**
113
	 * @var array Foreign keys mapping from type string when value does not match
114
	 *
115
	 * @todo Move to const array when PHP 5.6 will be our minimum target
116
	 */
117
	public $MAP_CAT_FK = array(
118
		'customer' => 'soc',
119
		'supplier' => 'soc',
120
		'contact'  => 'socpeople',
121
		'bank_account' => 'account',
122
	);
123
124
	/**
125
	 * @var array Category tables mapping from type string (llx_categorie_...) when value does not match
126
	 *
127
	 * @note Move to const array when PHP 5.6 will be our minimum target
128
	 */
129
	public $MAP_CAT_TABLE = array(
130
		'customer' => 'societe',
131
		'supplier' => 'fournisseur',
132
		'bank_account'=> 'account',
133
	);
134
135
	/**
136
	 * @var array Object class mapping from type string
137
	 *
138
	 * @note Move to const array when PHP 5.6 will be our minimum target
139
	 */
140
	public $MAP_OBJ_CLASS = array(
141
		'product'  => 'Product',
142
		'customer' => 'Societe',
143
		'supplier' => 'Fournisseur',
144
		'member'   => 'Adherent',
145
		'contact'  => 'Contact',
146
		'user'     => 'User',
147
		'account'  => 'Account', // old for bank account
148
		'bank_account'  => 'Account',
149
		'project'  => 'Project',
150
		'warehouse'=> 'Entrepot',
151
		'actioncomm' => 'ActionComm',
152
		'website_page' => 'WebsitePage',
153
		'ticket' => 'Ticket',
154
		'knowledgemanagement' => 'KnowledgeRecord'
155
	);
156
157
	/**
158
	 * @var array Title Area mapping from type string
159
	 *
160
	 * @note Move to const array when PHP 5.6 will be our minimum target
161
	 */
162
	public static $MAP_TYPE_TITLE_AREA = array(
163
		'product' => 'ProductsCategoriesArea',
164
		'customer' => 'CustomersCategoriesArea',
165
		'supplier' => 'SuppliersCategoriesArea',
166
		'member' => 'MembersCategoriesArea',
167
		'contact' => 'ContactsCategoriesArea',
168
		'user' => 'UsersCategoriesArea',
169
		'account' => 'AccountsCategoriesArea', // old for bank account
170
		'bank_account' => 'AccountsCategoriesArea',
171
		'project' => 'ProjectsCategoriesArea',
172
		'warehouse'=> 'StocksCategoriesArea',
173
		'actioncomm' => 'ActioncommCategoriesArea',
174
		'website_page' => 'WebsitePageCategoriesArea'
175
	);
176
177
	/**
178
	 * @var array 	Object table mapping from type string (table llx_...) when value of key does not match table name.
179
	 * 				This array may be completed by external modules with hook "constructCategory"
180
	 */
181
	public $MAP_OBJ_TABLE = array(
182
		'customer' => 'societe',
183
		'supplier' => 'societe',
184
		'member'   => 'adherent',
185
		'contact'  => 'socpeople',
186
		'account'  => 'bank_account', // old for bank account
187
		'project'  => 'projet',
188
		'warehouse'=> 'entrepot',
189
		'knowledgemanagement' => 'knowledgemanagement_knowledgerecord'
190
	);
191
192
	/**
193
	 * @var string ID to identify managed object
194
	 */
195
	public $element = 'category';
196
197
	/**
198
	 * @var string Name of table without prefix where object is stored
199
	 */
200
	public $table_element = 'categorie';
201
202
	/**
203
	 * @var int ID
204
	 */
205
	public $fk_parent;
206
207
	/**
208
	 * @var string Category label
209
	 */
210
	public $label;
211
212
	/**
213
	 * @var string description
214
	 */
215
	public $description;
216
217
	/**
218
	 * @var string     Color
219
	 */
220
	public $color;
221
222
	/**
223
	 * @var int Visible
224
	 */
225
	public $visible;
226
227
	/**
228
	 * @var int		  Id of thirdparty when CATEGORY_ASSIGNED_TO_A_CUSTOMER is set
229
	 */
230
	public $socid;
231
232
	/**
233
	 * @var string	Category type
234
	 *
235
	 * @see Categorie::TYPE_PRODUCT
236
	 * @see Categorie::TYPE_SUPPLIER
237
	 * @see Categorie::TYPE_CUSTOMER
238
	 * @see Categorie::TYPE_MEMBER
239
	 * @see Categorie::TYPE_CONTACT
240
	 * @see Categorie::TYPE_USER
241
	 * @see Categorie::TYPE_PROJECT
242
	 * @see Categorie::TYPE_ACCOUNT
243
	 * @see Categorie::TYPE_BANK_LINE
244
	 * @see Categorie::TYPE_WAREHOUSE
245
	 * @see Categorie::TYPE_ACTIONCOMM
246
	 * @see Categorie::TYPE_WEBSITE_PAGE
247
	 * @see Categorie::TYPE_TICKET
248
	 */
249
	public $type;
250
251
	/**
252
	 * @var array Categories table in memory
253
	 */
254
	public $cats = array();
255
256
	/**
257
	 * @var array Mother of table
258
	 */
259
	public $motherof = array();
260
261
	/**
262
	 * @var array Childs
263
	 */
264
	public $childs = array();
265
266
267
	/**
268
	 *	Constructor
269
	 *
270
	 *  @param		DoliDB		$db     Database handler
271
	 */
272
	public function __construct($db)
273
	{
274
		global $hookmanager;
275
276
		$this->db = $db;
277
278
		if (is_object($hookmanager)) {
279
			$hookmanager->initHooks(array('category'));
280
			$parameters = array();
281
			$reshook = $hookmanager->executeHooks('constructCategory', $parameters, $this); // Note that $action and $object may have been modified by some hooks
282
			if ($reshook >= 0 && !empty($hookmanager->resArray)) {
283
				foreach ($hookmanager->resArray as $mapList) {
284
					$mapId = $mapList['id'];
285
					$mapCode = $mapList['code'];
286
					self::$MAP_ID_TO_CODE[$mapId] = $mapCode;
287
					$this->MAP_ID[$mapCode] = $mapId;
288
					$this->MAP_CAT_FK[$mapCode] = $mapList['cat_fk'];
289
					$this->MAP_CAT_TABLE[$mapCode] = $mapList['cat_table'];
290
					$this->MAP_OBJ_CLASS[$mapCode] = $mapList['obj_class'];
291
					$this->MAP_OBJ_TABLE[$mapCode] = $mapList['obj_table'];
292
				}
293
			}
294
		}
295
	}
296
297
	/**
298
	 * Get map list
299
	 *
300
	 * @return	array
301
	 */
302
	public function getMapList()
303
	{
304
		$mapList = array();
305
306
		foreach ($this->MAP_ID as $mapCode => $mapId) {
307
			$mapList[] = array(
308
				'id'        => $mapId,
309
				'code'      => $mapCode,
310
				'cat_fk'    => (empty($this->MAP_CAT_FK[$mapCode]) ? $mapCode : $this->MAP_CAT_FK[$mapCode]),
311
				'cat_table' => (empty($this->MAP_CAT_TABLE[$mapCode]) ? $mapCode : $this->MAP_CAT_TABLE[$mapCode]),
312
				'obj_class' => (empty($this->MAP_OBJ_CLASS[$mapCode]) ? $mapCode : $this->MAP_OBJ_CLASS[$mapCode]),
313
				'obj_table' => (empty($this->MAP_OBJ_TABLE[$mapCode]) ? $mapCode : $this->MAP_OBJ_TABLE[$mapCode])
314
			);
315
		}
316
317
		return $mapList;
318
	}
319
320
	/**
321
	 * 	Load category into memory from database
322
	 *
323
	 * 	@param		int		$id      Id of category
324
	 *  @param		string	$label   Label of category
325
	 *  @param		string	$type    Type of category ('product', '...') or (0, 1, ...)
326
	 *  @param		string	$ref_ext External reference of object
327
	 * 	@return		int				<0 if KO, >0 if OK
328
	 */
329
	public function fetch($id, $label = '', $type = null, $ref_ext = '')
330
	{
331
		global $conf;
332
333
		// Check parameters
334
		if (empty($id) && empty($label) && empty($ref_ext)) {
335
			$this->error = "No category to search for";
336
			return -1;
337
		}
338
		if (!is_null($type) && !is_numeric($type)) {
339
			$type = $this->MAP_ID[$type];
340
		}
341
342
		$sql = "SELECT rowid, fk_parent, entity, label, description, color, fk_soc, visible, type, ref_ext";
343
		$sql .= ", date_creation, tms, fk_user_creat, fk_user_modif";
344
		$sql .= " FROM ".MAIN_DB_PREFIX."categorie";
345
		if ($id) {
346
			$sql .= " WHERE rowid = ".((int) $id);
347
		} elseif (!empty($ref_ext)) {
348
			$sql .= " WHERE ref_ext LIKE '".$this->db->escape($ref_ext)."'";
349
		} else {
350
			$sql .= " WHERE label = '".$this->db->escape($label)."' AND entity IN (".getEntity('category').")";
351
			if (!is_null($type)) {
352
				$sql .= " AND type = ".((int) $type);
353
			}
354
		}
355
356
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
357
		$resql = $this->db->query($sql);
358
		if ($resql) {
359
			if ($this->db->num_rows($resql) > 0) {
360
				$res = $this->db->fetch_array($resql);
361
362
				$this->id = $res['rowid'];
363
				//$this->ref = $res['rowid'];
364
				$this->fk_parent	= (int) $res['fk_parent'];
365
				$this->label		= $res['label'];
366
				$this->description = $res['description'];
367
				$this->color    	= $res['color'];
368
				$this->socid		= (int) $res['fk_soc'];
369
				$this->visible = (int) $res['visible'];
370
				$this->type = (int) $res['type'];
371
				$this->ref_ext = $res['ref_ext'];
372
				$this->entity = (int) $res['entity'];
373
				$this->date_creation = $this->db->jdate($res['date_creation']);
374
				$this->date_modification = $this->db->jdate($res['tms']);
375
				$this->user_creation_id = (int) $res['fk_user_creat'];
376
				$this->user_modification_id = (int) $res['fk_user_modif'];
377
				$this->user_creation = (int) $res['fk_user_creat'];
378
				$this->user_modification = (int) $res['fk_user_modif'];
379
380
				// Retrieve all extrafield
381
				// fetch optionals attributes and labels
382
				$this->fetch_optionals();
383
384
				$this->db->free($resql);
385
386
				// multilangs
387
				if (getDolGlobalInt('MAIN_MULTILANGS')) {
388
					$this->getMultiLangs();
389
				}
390
391
				return 1;
392
			} else {
393
				$this->error = "No category found";
394
				return 0;
395
			}
396
		} else {
397
			dol_print_error($this->db);
398
			$this->error = $this->db->lasterror;
399
			$this->errors[] = $this->db->lasterror;
400
			return -1;
401
		}
402
	}
403
404
	/**
405
	 *  Add category into database
406
	 *
407
	 *  @param	User	$user		Object user
408
	 *  @return	int 				-1 : SQL error
409
	 *          					-2 : new ID unknown
410
	 *          					-3 : Invalid category
411
	 * 								-4 : category already exists
412
	 */
413
	public function create($user)
414
	{
415
		global $conf, $langs, $hookmanager;
416
		$langs->load('categories');
417
418
		$type = $this->type;
419
420
		if (!is_numeric($type)) {
421
			$type = $this->MAP_ID[$type];
422
		}
423
424
		$error = 0;
425
426
		dol_syslog(get_class($this).'::create', LOG_DEBUG);
427
428
		// Clean parameters
429
		$this->label = trim($this->label);
430
		$this->description = trim($this->description);
431
		$this->color = trim($this->color);
432
		$this->import_key = trim($this->import_key);
433
		$this->ref_ext = trim($this->ref_ext);
434
		if (empty($this->visible)) {
435
			$this->visible = 0;
436
		}
437
		$this->fk_parent = ($this->fk_parent != "" ? intval($this->fk_parent) : 0);
438
439
		if ($this->already_exists()) {
440
			$this->error = $langs->trans("ImpossibleAddCat", $this->label);
441
			$this->error .= " : ".$langs->trans("CategoryExistsAtSameLevel");
442
			dol_syslog($this->error, LOG_WARNING);
443
			return -4;
444
		}
445
446
		$this->db->begin();
447
		$now = dol_now();
448
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."categorie (";
449
		$sql .= "fk_parent,";
450
		$sql .= " label,";
451
		$sql .= " description,";
452
		$sql .= " color,";
453
		if (!empty($conf->global->CATEGORY_ASSIGNED_TO_A_CUSTOMER)) {
454
			$sql .= "fk_soc,";
455
		}
456
		$sql .= " visible,";
457
		$sql .= " type,";
458
		$sql .= " import_key,";
459
		$sql .= " ref_ext,";
460
		$sql .= " entity,";
461
		$sql .= " date_creation,";
462
		$sql .= " fk_user_creat";
463
		$sql .= ") VALUES (";
464
		$sql .= (int) $this->fk_parent.",";
465
		$sql .= "'".$this->db->escape($this->label)."', ";
466
		$sql .= "'".$this->db->escape($this->description)."', ";
467
		$sql .= "'".$this->db->escape($this->color)."', ";
468
		if (!empty($conf->global->CATEGORY_ASSIGNED_TO_A_CUSTOMER)) {
469
			$sql .= ($this->socid > 0 ? $this->socid : 'null').", ";
470
		}
471
		$sql .= "'".$this->db->escape($this->visible)."', ";
472
		$sql .= ((int) $type).", ";
473
		$sql .= (!empty($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : 'null').", ";
474
		$sql .= (!empty($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : 'null').", ";
475
		$sql .= (int) $conf->entity.", ";
476
		$sql .= "'".$this->db->idate($now)."', ";
477
		$sql .= (int) $user->id;
478
		$sql .= ")";
479
480
		$res = $this->db->query($sql);
481
		if ($res) {
482
			$id = $this->db->last_insert_id(MAIN_DB_PREFIX."categorie");
483
484
			if ($id > 0) {
485
				$this->id = $id;
486
487
				$action = 'create';
488
489
				// Actions on extra fields
490
				if (!$error) {
491
					$result = $this->insertExtraFields();
492
					if ($result < 0) {
493
						$error++;
494
					}
495
				}
496
497
				if (!$error) {
498
					// Call trigger
499
					$result = $this->call_trigger('CATEGORY_CREATE', $user);
500
					if ($result < 0) {
501
						$error++;
502
					}
503
					// End call triggers
504
				}
505
506
				if (!$error) {
507
					$this->db->commit();
508
					return $id;
509
				} else {
510
					$this->db->rollback();
511
					return -3;
512
				}
513
			} else {
514
				$this->db->rollback();
515
				return -2;
516
			}
517
		} else {
518
			$this->error = $this->db->error();
519
			$this->db->rollback();
520
			return -1;
521
		}
522
	}
523
524
	/**
525
	 * 	Update category
526
	 *
527
	 *	@param	User	$user		Object user
528
	 * 	@return	int		 			1 : OK
529
	 *          					-1 : SQL error
530
	 *          					-2 : invalid category
531
	 */
532
	public function update(User $user)
533
	{
534
		global $conf, $langs, $hookmanager;
535
536
		$error = 0;
537
538
		// Clean parameters
539
		$this->label = trim($this->label);
540
		$this->description = trim($this->description);
541
		$this->ref_ext = trim($this->ref_ext);
542
		$this->fk_parent = ($this->fk_parent != "" ? intval($this->fk_parent) : 0);
543
		$this->visible = ($this->visible != "" ? intval($this->visible) : 0);
544
545
		if ($this->already_exists()) {
546
			$this->error = $langs->trans("ImpossibleUpdateCat");
547
			$this->error .= " : ".$langs->trans("CategoryExistsAtSameLevel");
548
			return -1;
549
		}
550
551
		$this->db->begin();
552
553
		$sql = "UPDATE ".MAIN_DB_PREFIX."categorie";
554
		$sql .= " SET label = '".$this->db->escape($this->label)."',";
555
		$sql .= " description = '".$this->db->escape($this->description)."',";
556
		$sql .= " ref_ext = '".$this->db->escape($this->ref_ext)."',";
557
		$sql .= " color = '".$this->db->escape($this->color)."'";
558
		if (!empty($conf->global->CATEGORY_ASSIGNED_TO_A_CUSTOMER)) {
559
			$sql .= ", fk_soc = ".($this->socid > 0 ? $this->socid : 'null');
560
		}
561
		$sql .= ", visible = ".(int) $this->visible;
562
		$sql .= ", fk_parent = ".(int) $this->fk_parent;
563
		$sql .= ", fk_user_modif = ".(int) $user->id;
564
		$sql .= " WHERE rowid = ".((int) $this->id);
565
566
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
567
		if ($this->db->query($sql)) {
568
			$action = 'update';
569
570
			// Actions on extra fields
571
			if (!$error) {
572
				$result = $this->insertExtraFields();
573
				if ($result < 0) {
574
					$error++;
575
				}
576
			}
577
578
			if (!$error) {
579
				// Call trigger
580
				$result = $this->call_trigger('CATEGORY_MODIFY', $user);
581
				if ($result < 0) {
582
					$error++; $this->db->rollback(); return -1;
583
				}
584
				// End call triggers
585
			}
586
587
			$this->db->commit();
588
589
			return 1;
590
		} else {
591
			$this->db->rollback();
592
			dol_print_error($this->db);
593
			return -1;
594
		}
595
	}
596
597
	/**
598
	 * 	Delete a category from database
599
	 *
600
	 * 	@param	User	$user		Object user that ask to delete
601
	 *	@param	int		$notrigger	1=Does not execute triggers, 0= execute triggers
602
	 *	@return	int                 <0 KO >0 OK
603
	 */
604
	public function delete($user, $notrigger = 0)
605
	{
606
		global $conf, $langs;
607
608
		$error = 0;
609
610
		// Clean parameters
611
		$this->fk_parent = ($this->fk_parent != "" ? intval($this->fk_parent) : 0);
612
613
		dol_syslog(get_class($this)."::remove");
614
615
		$this->db->begin();
616
617
		if (!$error && !$notrigger) {
618
			// Call trigger
619
			$result = $this->call_trigger('CATEGORY_DELETE', $user);
620
			if ($result < 0) {
621
				$error++;
622
			}
623
			// End call triggers
624
		}
625
626
		/* FIX #1317 : Check for child category and move up 1 level*/
627
		if (!$error) {
628
			$sql = "UPDATE ".MAIN_DB_PREFIX."categorie";
629
			$sql .= " SET fk_parent = ".((int) $this->fk_parent);
630
			$sql .= " WHERE fk_parent = ".((int) $this->id);
631
632
			if (!$this->db->query($sql)) {
633
				$this->error = $this->db->lasterror();
634
				$error++;
635
			}
636
		}
637
638
		$arraydelete = array(
639
			'categorie_account' => 'fk_categorie',
640
			'categorie_actioncomm' => 'fk_categorie',
641
			'categorie_contact' => 'fk_categorie',
642
			'categorie_fournisseur' => 'fk_categorie',
643
			'categorie_knowledgemanagement' => array('field' => 'fk_categorie', 'enabled' => isModEnabled('knowledgemanagement')),
644
			'categorie_member' => 'fk_categorie',
645
			'categorie_user' => 'fk_categorie',
646
			'categorie_product' => 'fk_categorie',
647
			'categorie_project' => 'fk_categorie',
648
			'categorie_societe' => 'fk_categorie',
649
			'categorie_ticket' => array('field' => 'fk_categorie', 'enabled' => isModEnabled('ticket')),
650
			'categorie_warehouse' => 'fk_categorie',
651
			'categorie_website_page' => array('field' => 'fk_categorie', 'enabled' => isModEnabled('website')),
652
			'bank_class' => 'fk_categ',
653
			'categorie_lang' => 'fk_category',
654
			'categorie' => 'rowid',
655
		);
656
		foreach ($arraydelete as $key => $value) {
657
			if (is_array($value)) {
658
				if (empty($value['enabled'])) {
659
					continue;
660
				}
661
				$value = $value['field'];
662
			}
663
			$sql  = "DELETE FROM ".MAIN_DB_PREFIX.$key;
664
			$sql .= " WHERE ".$value." = ".((int) $this->id);
665
			if (!$this->db->query($sql)) {
666
				$this->errors[] = $this->db->lasterror();
667
				dol_syslog("Error sql=".$sql." ".$this->error, LOG_ERR);
668
				$error++;
669
			}
670
		}
671
672
		// Removed extrafields
673
		if (!$error) {
674
			$result = $this->deleteExtraFields();
675
			if ($result < 0) {
676
				$error++;
677
				dol_syslog(get_class($this)."::delete erreur ".$this->error, LOG_ERR);
678
			}
679
		}
680
681
		if (!$error) {
682
			$this->db->commit();
683
			return 1;
684
		} else {
685
			$this->db->rollback();
686
			return -1;
687
		}
688
	}
689
690
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
691
	/**
692
	 * Link an object to the category
693
	 *
694
	 * @param   CommonObject 	$obj  	Object to link to category
695
	 * @param   string     		$type 	Type of category ('product', ...). Use '' to take $obj->element.
696
	 * @return  int                		1 : OK, -1 : erreur SQL, -2 : id not defined, -3 : Already linked
697
	 * @see del_type()
698
	 */
699
	public function add_type($obj, $type = '')
700
	{
701
		// phpcs:enable
702
		global $user, $langs, $conf;
703
704
		$error = 0;
705
706
		if ($this->id == -1) {
707
			return -2;
708
		}
709
710
		if (empty($type)) {
711
			$type = $obj->element;
712
		}
713
714
		dol_syslog(get_class($this).'::add_type', LOG_DEBUG);
715
716
		$this->db->begin();
717
718
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."categorie_".(empty($this->MAP_CAT_TABLE[$type]) ? $type : $this->MAP_CAT_TABLE[$type]);
719
		$sql .= " (fk_categorie, fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type]).")";
720
		$sql .= " VALUES (".((int) $this->id).", ".((int) $obj->id).")";
721
722
		if ($this->db->query($sql)) {
723
			if (!empty($conf->global->CATEGORIE_RECURSIV_ADD)) {
724
				$sql = 'SELECT fk_parent FROM '.MAIN_DB_PREFIX.'categorie';
725
				$sql .= " WHERE rowid = ".((int) $this->id);
726
727
				dol_syslog(get_class($this)."::add_type", LOG_DEBUG);
728
				$resql = $this->db->query($sql);
729
				if ($resql) {
730
					if ($this->db->num_rows($resql) > 0) {
731
						$objparent = $this->db->fetch_object($resql);
732
733
						if (!empty($objparent->fk_parent)) {
734
							$cat = new Categorie($this->db);
735
							$cat->id = $objparent->fk_parent;
736
							if (!$cat->containsObject($type, $obj->id)) {
737
								$result = $cat->add_type($obj, $type);
738
								if ($result < 0) {
739
									$this->error = $cat->error;
740
									$error++;
741
								}
742
							}
743
						}
744
					}
745
				} else {
746
					$error++;
747
					$this->error = $this->db->lasterror();
748
				}
749
750
				if ($error) {
751
					$this->db->rollback();
752
					return -1;
753
				}
754
			}
755
756
			// Call trigger
757
			$this->context = array('linkto'=>$obj); // Save object we want to link category to into category instance to provide information to trigger
758
			$result = $this->call_trigger('CATEGORY_LINK', $user);
759
			if ($result < 0) {
760
				$error++;
761
			}
762
			// End call triggers
763
764
			if (!$error) {
765
				$this->db->commit();
766
				return 1;
767
			} else {
768
				$this->db->rollback();
769
				return -2;
770
			}
771
		} else {
772
			$this->db->rollback();
773
			if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
774
				$this->error = $this->db->lasterrno();
775
				return -3;
776
			} else {
777
				$this->error = $this->db->lasterror();
778
			}
779
			return -1;
780
		}
781
	}
782
783
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
784
	/**
785
	 * Delete object from category
786
	 *
787
	 * @param   CommonObject $obj  Object
788
	 * @param   string       $type Type of category ('customer', 'supplier', 'contact', 'product', 'member')
789
	 * @return  int          1 if OK, -1 if KO
790
	 * @see add_type()
791
	 */
792
	public function del_type($obj, $type)
793
	{
794
		// phpcs:enable
795
		global $user, $langs, $conf;
796
797
		$error = 0;
798
799
		// For backward compatibility
800
		if ($type == 'societe') {
801
			$type = 'customer';
802
			dol_syslog(get_class($this)."::del_type(): type 'societe' is deprecated, please use 'customer' instead", LOG_WARNING);
803
		} elseif ($type == 'fournisseur') {
804
			$type = 'supplier';
805
			dol_syslog(get_class($this)."::del_type(): type 'fournisseur' is deprecated, please use 'supplier' instead", LOG_WARNING);
806
		}
807
808
		$this->db->begin();
809
810
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_".(empty($this->MAP_CAT_TABLE[$type]) ? $type : $this->MAP_CAT_TABLE[$type]);
811
		$sql .= " WHERE fk_categorie = ".((int) $this->id);
812
		$sql .= " AND fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])." = ".((int) $obj->id);
813
814
		dol_syslog(get_class($this).'::del_type', LOG_DEBUG);
815
		if ($this->db->query($sql)) {
816
			// Call trigger
817
			$this->context = array('unlinkoff'=>$obj); // Save object we want to link category to into category instance to provide information to trigger
818
			$result = $this->call_trigger('CATEGORY_UNLINK', $user);
819
			if ($result < 0) {
820
				$error++;
821
			}
822
			// End call triggers
823
824
			if (!$error) {
825
				$this->db->commit();
826
				return 1;
827
			} else {
828
				$this->db->rollback();
829
				return -2;
830
			}
831
		} else {
832
			$this->db->rollback();
833
			$this->error = $this->db->lasterror();
834
			return -1;
835
		}
836
	}
837
838
	/**
839
	 * Return list of fetched instance of elements having this category
840
	 *
841
	 * @param   string     	$type       Type of category ('customer', 'supplier', 'contact', 'product', 'member', 'knowledge_management', ...)
842
	 * @param   int        	$onlyids    Return only ids of objects (consume less memory)
843
	 * @param	int			$limit		Limit
844
	 * @param	int			$offset		Offset
845
	 * @param	string		$sortfield	Sort fields
846
	 * @param	string		$sortorder	Sort order ('ASC' or 'DESC');
847
	 * @return  array|int              	-1 if KO, array of instance of object if OK
848
	 * @see containsObject()
849
	 */
850
	public function getObjectsInCateg($type, $onlyids = 0, $limit = 0, $offset = 0, $sortfield = '', $sortorder = 'ASC')
851
	{
852
		global $user;
853
854
		$objs = array();
855
856
		$classnameforobj = $this->MAP_OBJ_CLASS[$type];
857
		$obj = new $classnameforobj($this->db);
858
859
		$sql = "SELECT c.fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type]);
860
		$sql .= " FROM ".MAIN_DB_PREFIX."categorie_".(empty($this->MAP_CAT_TABLE[$type]) ? $type : $this->MAP_CAT_TABLE[$type])." as c";
861
		$sql .= ", ".MAIN_DB_PREFIX.(empty($this->MAP_OBJ_TABLE[$type]) ? $type : $this->MAP_OBJ_TABLE[$type])." as o";
862
		$sql .= " WHERE o.entity IN (".getEntity($obj->element).")";
863
		$sql .= " AND c.fk_categorie = ".((int) $this->id);
864
		$sql .= " AND c.fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])." = o.rowid";
865
		// Protection for external users
866
		if (($type == 'customer' || $type == 'supplier') && $user->socid > 0) {
867
			$sql .= " AND o.rowid = ".((int) $user->socid);
868
		}
869
		if ($limit > 0 || $offset > 0) {
870
			$sql .= $this->db->plimit($limit + 1, $offset);
871
		}
872
		$sql .= $this->db->order($sortfield, $sortorder);
873
874
		dol_syslog(get_class($this)."::getObjectsInCateg", LOG_DEBUG);
875
		$resql = $this->db->query($sql);
876
		if ($resql) {
877
			while ($rec = $this->db->fetch_array($resql)) {
878
				if ($onlyids) {
879
					$objs[] = $rec['fk_'.(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])];
880
				} else {
881
					$classnameforobj = $this->MAP_OBJ_CLASS[$type];
882
883
					$obj = new $classnameforobj($this->db);
884
					$obj->fetch($rec['fk_'.(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])]);
885
886
					$objs[] = $obj;
887
				}
888
			}
889
			return $objs;
890
		} else {
891
			$this->error = $this->db->error().' sql='.$sql;
892
			return -1;
893
		}
894
	}
895
896
	/**
897
	 * Check for the presence of an object in a category
898
	 *
899
	 * @param   string $type      		Type of category ('customer', 'supplier', 'contact', 'product', 'member')
900
	 * @param   int    $object_id 		Id of the object to search
901
	 * @return  int                     Number of occurrences
902
	 * @see getObjectsInCateg()
903
	 */
904
	public function containsObject($type, $object_id)
905
	{
906
		$sql = "SELECT COUNT(*) as nb FROM ".MAIN_DB_PREFIX."categorie_".(empty($this->MAP_CAT_TABLE[$type]) ? $type : $this->MAP_CAT_TABLE[$type]);
907
		$sql .= " WHERE fk_categorie = ".((int) $this->id)." AND fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])." = ".((int) $object_id);
908
		dol_syslog(get_class($this)."::containsObject", LOG_DEBUG);
909
		$resql = $this->db->query($sql);
910
		if ($resql) {
911
			return $this->db->fetch_object($resql)->nb;
912
		} else {
913
			$this->error = $this->db->error().' sql='.$sql;
914
			return -1;
915
		}
916
	}
917
918
	/**
919
	 * List categories of an element id
920
	 *
921
	 * @param	int		$id			Id of element
922
	 * @param	string	$type		Type of category ('member', 'customer', 'supplier', 'product', 'contact')
923
	 * @param	string	$sortfield	Sort field
924
	 * @param	string	$sortorder	Sort order
925
	 * @param	int		$limit		Limit for list
926
	 * @param	int		$page		Page number
927
	 * @return	array|int			Array of categories, 0 if no cat, -1 on error
928
	 */
929
	public function getListForItem($id, $type = 'customer', $sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0)
930
	{
931
		global $conf;
932
933
		$categories = array();
934
935
		$type = sanitizeVal($type, 'aZ09');
936
937
		$sub_type = $type;
938
		$subcol_name = "fk_".$type;
939
		if ($type == "customer") {
940
			$sub_type = "societe";
941
			$subcol_name = "fk_soc";
942
		}
943
		if ($type == "supplier") {
944
			$sub_type = "fournisseur";
945
			$subcol_name = "fk_soc";
946
		}
947
		if ($type == "contact") {
948
			$subcol_name = "fk_socpeople";
949
		}
950
951
		$idoftype = array_search($type, self::$MAP_ID_TO_CODE);
952
953
		$sql = "SELECT s.rowid";
954
		$sql .= " FROM ".MAIN_DB_PREFIX."categorie as s, ".MAIN_DB_PREFIX."categorie_".$sub_type." as sub";
955
		$sql .= ' WHERE s.entity IN ('.getEntity('category').')';
956
		$sql .= ' AND s.type='.((int) $idoftype);
957
		$sql .= ' AND s.rowid = sub.fk_categorie';
958
		$sql .= " AND sub.".$subcol_name." = ".((int) $id);
959
960
		$sql .= $this->db->order($sortfield, $sortorder);
961
962
		$offset = 0;
963
		$nbtotalofrecords = '';
964
		if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) {
965
			$result = $this->db->query($sql);
966
			$nbtotalofrecords = $this->db->num_rows($result);
967
			if (($page * $limit) > $nbtotalofrecords) {	// if total resultset is smaller then paging size (filtering), goto and load page 0
968
				$page = 0;
969
				$offset = 0;
970
			}
971
		}
972
973
		if ($limit) {
974
			if ($page < 0) {
975
				$page = 0;
976
			}
977
			$offset = $limit * $page;
978
979
			$sql .= $this->db->plimit($limit + 1, $offset);
980
		}
981
982
		$result = $this->db->query($sql);
983
		if ($result) {
984
			$i = 0;
985
			$num = $this->db->num_rows($result);
986
			$min = min($num, ($limit <= 0 ? $num : $limit));
987
			while ($i < $min) {
988
				$obj = $this->db->fetch_object($result);
989
				$category_static = new Categorie($this->db);
990
				if ($category_static->fetch($obj->rowid)) {
991
					$categories[$i]['id'] = $category_static->id;
992
					$categories[$i]['fk_parent']		= $category_static->fk_parent;
993
					$categories[$i]['label']			= $category_static->label;
994
					$categories[$i]['description'] = $category_static->description;
995
					$categories[$i]['color']    		= $category_static->color;
996
					$categories[$i]['socid']			= $category_static->socid;
997
					$categories[$i]['ref_ext'] = $category_static->ref_ext;
998
					$categories[$i]['visible'] = $category_static->visible;
999
					$categories[$i]['type'] = $category_static->type;
1000
					$categories[$i]['entity'] = $category_static->entity;
1001
					$categories[$i]['array_options'] = $category_static->array_options;
1002
1003
					// multilangs
1004
					if (getDolGlobalInt('MAIN_MULTILANGS') && isset($category_static->multilangs)) {
1005
						$categories[$i]['multilangs'] = $category_static->multilangs;
1006
					}
1007
				}
1008
				$i++;
1009
			}
1010
		} else {
1011
			$this->error = $this->db->lasterror();
1012
			return -1;
1013
		}
1014
		if (!count($categories)) {
1015
			return 0;
1016
		}
1017
1018
		return $categories;
1019
	}
1020
1021
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1022
	/**
1023
	 * Return direct childs id of a category into an array
1024
	 *
1025
	 * @return	array|int   <0 KO, array ok
1026
	 */
1027
	public function get_filles()
1028
	{
1029
		// phpcs:enable
1030
		$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."categorie";
1031
		$sql .= " WHERE fk_parent = ".((int) $this->id);
1032
		$sql .= " AND entity IN (".getEntity('category').")";
1033
1034
		$res = $this->db->query($sql);
1035
		if ($res) {
1036
			$cats = array();
1037
			while ($rec = $this->db->fetch_array($res)) {
1038
				$cat = new Categorie($this->db);
1039
				$cat->fetch($rec['rowid']);
1040
				$cats[] = $cat;
1041
			}
1042
			return $cats;
1043
		} else {
1044
			dol_print_error($this->db);
1045
			return -1;
1046
		}
1047
	}
1048
1049
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1050
	/**
1051
	 * 	Load the array this->motherof that is array(id_son=>id_parent, ...)
1052
	 *
1053
	 *	@return		int		<0 if KO, >0 if OK
1054
	 */
1055
	protected function load_motherof()
1056
	{
1057
		// phpcs:enable
1058
		$this->motherof = array();
1059
1060
		// Load array[child]=parent
1061
		$sql = "SELECT fk_parent as id_parent, rowid as id_son";
1062
		$sql .= " FROM ".MAIN_DB_PREFIX."categorie";
1063
		$sql .= " WHERE fk_parent != 0";
1064
		$sql .= " AND entity IN (".getEntity('category').")";
1065
1066
		dol_syslog(get_class($this)."::load_motherof", LOG_DEBUG);
1067
		$resql = $this->db->query($sql);
1068
		if ($resql) {
1069
			while ($obj = $this->db->fetch_object($resql)) {
1070
				$this->motherof[$obj->id_son] = $obj->id_parent;
1071
			}
1072
			return 1;
1073
		} else {
1074
			dol_print_error($this->db);
1075
			return -1;
1076
		}
1077
	}
1078
1079
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1080
	/**
1081
	 * Rebuilding the category tree as an array
1082
	 * Return an array of table('id','id_mere',...) trie selon arbre et avec:
1083
	 *                id = id de la categorie
1084
	 *                id_mere = id de la categorie mere
1085
	 *                id_children = tableau des id enfant
1086
	 *                label = nom de la categorie
1087
	 *                fulllabel = nom avec chemin complet de la categorie
1088
	 *                fullpath = chemin complet compose des id
1089
	 *
1090
	 * @param   string                  $type                   Type of categories ('customer', 'supplier', 'contact', 'product', 'member', ...)
1091
	 * @param   int|string|array        $markafterid            Keep only or removed all categories including the leaf $markafterid in category tree (exclude) or Keep only of category is inside the leaf starting with this id.
1092
	 *                                                          $markafterid can be an :
1093
	 *                                                          - int (id of category)
1094
	 *                                                          - string (categories ids separated by comma)
1095
	 *                                                          - array (list of categories ids)
1096
	 * @param   int                     $include                [=0] Removed or 1=Keep only
1097
	 * @return  array|int               Array of categories. this->cats and this->motherof are set, -1 on error
1098
	 */
1099
	public function get_full_arbo($type, $markafterid = 0, $include = 0)
1100
	{
1101
		// phpcs:enable
1102
		global $conf, $langs;
1103
1104
		if (!is_numeric($type)) {
1105
			$type = $this->MAP_ID[$type];
1106
		}
1107
		if (is_null($type)) {
1108
			$this->error = 'BadValueForParameterType';
1109
			return -1;
1110
		}
1111
1112
		if (is_string($markafterid)) {
1113
			$markafterid = explode(',', $markafterid);
1114
		} elseif (is_numeric($markafterid)) {
1115
			if ($markafterid > 0) {
1116
				$markafterid = array($markafterid);
1117
			} else {
1118
				$markafterid = array();
1119
			}
1120
		} elseif (!is_array($markafterid)) {
1121
			$markafterid = array();
1122
		}
1123
1124
		$this->cats = array();
1125
1126
		// Init this->motherof that is array(id_son=>id_parent, ...)
1127
		$this->load_motherof();
1128
		$current_lang = $langs->getDefaultLang();
1129
1130
		// Init $this->cats array
1131
		$sql = "SELECT DISTINCT c.rowid, c.label, c.ref_ext, c.description, c.color, c.fk_parent, c.visible"; // Distinct reduce pb with old tables with duplicates
1132
		if (getDolGlobalInt('MAIN_MULTILANGS')) {
1133
			$sql .= ", t.label as label_trans, t.description as description_trans";
1134
		}
1135
		$sql .= " FROM ".MAIN_DB_PREFIX."categorie as c";
1136
		if (getDolGlobalInt('MAIN_MULTILANGS')) {
1137
			$sql .= " LEFT  JOIN ".MAIN_DB_PREFIX."categorie_lang as t ON t.fk_category=c.rowid AND t.lang='".$this->db->escape($current_lang)."'";
1138
		}
1139
		$sql .= " WHERE c.entity IN (".getEntity('category').")";
1140
		$sql .= " AND c.type = ".(int) $type;
1141
1142
		dol_syslog(get_class($this)."::get_full_arbo get category list", LOG_DEBUG);
1143
		$resql = $this->db->query($sql);
1144
		if ($resql) {
1145
			$i = 0;
1146
			while ($obj = $this->db->fetch_object($resql)) {
1147
				$this->cats[$obj->rowid]['rowid'] = $obj->rowid;
1148
				$this->cats[$obj->rowid]['id'] = $obj->rowid;
1149
				$this->cats[$obj->rowid]['fk_parent'] = $obj->fk_parent;
1150
				$this->cats[$obj->rowid]['label'] = !empty($obj->label_trans) ? $obj->label_trans : $obj->label;
1151
				$this->cats[$obj->rowid]['description'] = !empty($obj->description_trans) ? $obj->description_trans : $obj->description;
1152
				$this->cats[$obj->rowid]['color'] = $obj->color;
1153
				$this->cats[$obj->rowid]['visible'] = $obj->visible;
1154
				$this->cats[$obj->rowid]['ref_ext'] = $obj->ref_ext;
1155
				$this->cats[$obj->rowid]['picto'] = 'category';
1156
				$i++;
1157
			}
1158
		} else {
1159
			dol_print_error($this->db);
1160
			return -1;
1161
		}
1162
1163
		// We add the fullpath property to each elements of first level (no parent exists)
1164
		dol_syslog(get_class($this)."::get_full_arbo call to buildPathFromId", LOG_DEBUG);
1165
		foreach ($this->cats as $key => $val) {
1166
			//print 'key='.$key.'<br>'."\n";
1167
			$this->buildPathFromId($key, 0); // Process a branch from the root category key (this category has no parent)
1168
		}
1169
1170
		// Include or exclude leaf including $markafterid from tree
1171
		if (count($markafterid) > 0) {
1172
			$keyfiltercatid = '('.implode('|', $markafterid).')';
1173
1174
			//print "Look to discard category ".$markafterid."\n";
1175
			$keyfilter1 = '^'.$keyfiltercatid.'$';
1176
			$keyfilter2 = '_'.$keyfiltercatid.'$';
1177
			$keyfilter3 = '^'.$keyfiltercatid.'_';
1178
			$keyfilter4 = '_'.$keyfiltercatid.'_';
1179
			foreach ($this->cats as $key => $val) {
1180
				$test = (preg_match('/'.$keyfilter1.'/', $val['fullpath']) || preg_match('/'.$keyfilter2.'/', $val['fullpath'])
1181
					|| preg_match('/'.$keyfilter3.'/', $val['fullpath']) || preg_match('/'.$keyfilter4.'/', $val['fullpath']));
1182
1183
				if (($test && !$include) || (!$test && $include)) {
1184
					unset($this->cats[$key]);
1185
				}
1186
			}
1187
		}
1188
1189
		dol_syslog(get_class($this)."::get_full_arbo dol_sort_array", LOG_DEBUG);
1190
		$this->cats = dol_sort_array($this->cats, 'fulllabel', 'asc', true, false);
1191
1192
		return $this->cats;
1193
	}
1194
1195
	/**
1196
	 *	For category id_categ and its childs available in this->cats, define property fullpath and fulllabel.
1197
	 *  It is called by get_full_arbo()
1198
	 *  This function is a memory scan only from $this->cats and $this->motherof, no database access must be done here.
1199
	 *
1200
	 * 	@param		int		$id_categ		id_categ entry to update
1201
	 * 	@param		int		$protection		Deep counter to avoid infinite loop
1202
	 *	@return		int						<0 if KO, >0 if OK
1203
	 *  @see get_full_arbo()
1204
	 */
1205
	private function buildPathFromId($id_categ, $protection = 1000)
1206
	{
1207
		//dol_syslog(get_class($this)."::buildPathFromId id_categ=".$id_categ." protection=".$protection, LOG_DEBUG);
1208
1209
		if (!empty($this->cats[$id_categ]['fullpath'])) {
1210
			// Already defined
1211
			dol_syslog(get_class($this)."::buildPathFromId fullpath and fulllabel already defined", LOG_WARNING);
1212
			return -1;
1213
		}
1214
1215
		// First build full array $motherof
1216
		//$this->load_motherof();	// Disabled because already done by caller of buildPathFromId
1217
1218
		// $this->cats[$id_categ] is supposed to be already an array. We just want to complete it with property fullpath and fulllabel
1219
1220
		// Define fullpath and fulllabel
1221
		$this->cats[$id_categ]['fullpath'] = '_'.$id_categ;
1222
		$this->cats[$id_categ]['fulllabel'] = $this->cats[$id_categ]['label'];
1223
		$i = 0; $cursor_categ = $id_categ;
1224
		//print 'Work for id_categ='.$id_categ.'<br>'."\n";
1225
		while ((empty($protection) || $i < $protection) && !empty($this->motherof[$cursor_categ])) {
1226
			//print '&nbsp; cursor_categ='.$cursor_categ.' i='.$i.' '.$this->motherof[$cursor_categ].'<br>'."\n";
1227
			$this->cats[$id_categ]['fullpath'] = '_'.$this->motherof[$cursor_categ].$this->cats[$id_categ]['fullpath'];
1228
			$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'];
1229
			//print '&nbsp; Result for id_categ='.$id_categ.' : '.$this->cats[$id_categ]['fullpath'].' '.$this->cats[$id_categ]['fulllabel'].'<br>'."\n";
1230
			$i++;
1231
			$cursor_categ = $this->motherof[$cursor_categ];
1232
		}
1233
		//print 'Result for id_categ='.$id_categ.' : '.$this->cats[$id_categ]['fullpath'].'<br>'."\n";
1234
1235
		// We count number of _ to have level
1236
		$nbunderscore = substr_count($this->cats[$id_categ]['fullpath'], '_');
1237
		$this->cats[$id_categ]['level'] = ($nbunderscore ? $nbunderscore : null);
1238
1239
		return 1;
1240
	}
1241
1242
1243
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1244
	/**
1245
	 * 	Returns all categories
1246
	 *
1247
	 *	@param	int			$type		Type of category (0, 1, ...)
1248
	 *	@param	boolean		$parent		Just parent categories if true
1249
	 *	@return	array|int				Table of Object Category, -1 on error
1250
	 */
1251
	public function get_all_categories($type = null, $parent = false)
1252
	{
1253
		// phpcs:enable
1254
		if (!is_numeric($type)) {
1255
			$type = $this->MAP_ID[$type];
1256
		}
1257
1258
		$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."categorie";
1259
		$sql .= " WHERE entity IN (".getEntity('category').")";
1260
		if (!is_null($type)) {
1261
			$sql .= " AND type = ".(int) $type;
1262
		}
1263
		if ($parent) {
1264
			$sql .= " AND fk_parent = 0";
1265
		}
1266
1267
		$res = $this->db->query($sql);
1268
		if ($res) {
1269
			$cats = array();
1270
			while ($rec = $this->db->fetch_array($res)) {
1271
				$cat = new Categorie($this->db);
1272
				$cat->fetch($rec['rowid']);
1273
				$cats[$rec['rowid']] = $cat;
1274
			}
1275
			return $cats;
1276
		} else {
1277
			dol_print_error($this->db);
1278
			return -1;
1279
		}
1280
	}
1281
1282
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1283
	/**
1284
	 *	Returns the top level categories (which are not child)
1285
	 *
1286
	 *	@param		int		$type		Type of category (0, 1, ...)
1287
	 *	@return		array
1288
	 */
1289
	public function get_main_categories($type = null)
1290
	{
1291
		// phpcs:enable
1292
		return $this->get_all_categories($type, true);
1293
	}
1294
1295
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1296
	/**
1297
	 * 	Check if no category with same label already exists for this cat's parent or root and for this cat's type
1298
	 *
1299
	 * 	@return		integer		1 if already exist, 0 otherwise, -1 if error
1300
	 */
1301
	public function already_exists()
1302
	{
1303
		// phpcs:enable
1304
		$type = $this->type;
1305
1306
		if (!is_numeric($type)) {
1307
			$type = $this->MAP_ID[$type];
1308
		}
1309
1310
		/* We have to select any rowid from llx_categorie which category's mother and label
1311
		 * are equals to those of the calling category
1312
		 */
1313
		$sql = "SELECT c.rowid";
1314
		$sql .= " FROM ".MAIN_DB_PREFIX."categorie as c ";
1315
		$sql .= " WHERE c.entity IN (".getEntity('category').")";
1316
		$sql .= " AND c.type = ".((int) $type);
1317
		$sql .= " AND c.fk_parent = ".((int) $this->fk_parent);
1318
		$sql .= " AND c.label = '".$this->db->escape($this->label)."'";
1319
1320
		dol_syslog(get_class($this)."::already_exists", LOG_DEBUG);
1321
		$resql = $this->db->query($sql);
1322
		if ($resql) {
1323
			if ($this->db->num_rows($resql) > 0) {						// Checking for empty resql
1324
				$obj = $this->db->fetch_array($resql);
1325
				/* If object called create, obj cannot have is id.
1326
				 * If object called update, he mustn't have the same label as an other category for this mother.
1327
				 * So if the result have the same id, update is not for label, and if result have an other one,
1328
				 * update may be for label.
1329
				 */
1330
				if ($obj[0] > 0 && $obj[0] != $this->id) {
1331
					dol_syslog(get_class($this)."::already_exists category with name=".$this->label." and parent ".$this->fk_parent." exists: rowid=".$obj[0]." current_id=".$this->id, LOG_DEBUG);
1332
					return 1;
1333
				}
1334
			}
1335
			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);
1336
			return 0;
1337
		} else {
1338
			$this->error = $this->db->error();
1339
			return -1;
1340
		}
1341
	}
1342
1343
1344
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1345
	/**
1346
	 * Returns the path of the category, with the names of the categories
1347
	 * separated by $sep (" >> " by default)
1348
	 *
1349
	 * @param	string	$sep	     Separator
1350
	 * @param	string	$url	     Url ('', 'none' or 'urltouse')
1351
	 * @param   int     $nocolor     0
1352
	 * @param	string	$addpicto	 Add picto into link
1353
	 * @return	array
1354
	 */
1355
	public function print_all_ways($sep = '&gt;&gt;', $url = '', $nocolor = 0, $addpicto = 0)
1356
	{
1357
		// phpcs:enable
1358
		$ways = array();
1359
1360
		$allways = $this->get_all_ways(); // Load array of categories
1361
		foreach ($allways as $way) {
1362
			$w = array();
1363
			$i = 0;
1364
			$forced_color = '';
1365
			foreach ($way as $cat) {
1366
				$i++;
1367
1368
				if (empty($nocolor)) {
1369
					$forced_color = 'colortoreplace';
1370
					if ($i == count($way)) {	// Last category in hierarchy
1371
						// Check contrast with background and correct text color
1372
						$forced_color = 'categtextwhite';
1373
						if ($cat->color) {
1374
							if (colorIsLight($cat->color)) {
1375
								$forced_color = 'categtextblack';
1376
							}
1377
						}
1378
					}
1379
				}
1380
1381
				if ($url == '') {
1382
					$link = '<a href="'.DOL_URL_ROOT.'/categories/viewcat.php?id='.$cat->id.'&type='.$cat->type.'" class="'.$forced_color.'">';
1383
					$linkend = '</a>';
1384
					$w[] = $link.(($addpicto && $i == 1) ? img_object('', 'category', 'class="paddingright"') : '').$cat->label.$linkend;
1385
				} elseif ($url == 'none') {
1386
					$link = '<span class="'.$forced_color.'">';
1387
					$linkend = '</span>';
1388
					$w[] = $link.(($addpicto && $i == 1) ? img_object('', 'category', 'class="paddingright"') : '').$cat->label.$linkend;
1389
				} else {
1390
					$w[] = '<a class="'.$forced_color.'" href="'.DOL_URL_ROOT.'/'.$url.'?catid='.$cat->id.'">'.($addpicto ? img_object('', 'category') : '').$cat->label.'</a>';
1391
				}
1392
			}
1393
			$newcategwithpath = preg_replace('/colortoreplace/', $forced_color, implode('<span class="inline-block valignmiddle paddingleft paddingright '.$forced_color.'">'.$sep.'</span>', $w));
1394
1395
			$ways[] = $newcategwithpath;
1396
		}
1397
1398
		return $ways;
1399
	}
1400
1401
1402
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1403
	/**
1404
	 *	Returns an array containing the list of parent categories
1405
	 *
1406
	 *	@return	int|array <0 KO, array OK
1407
	 */
1408
	public function get_meres()
1409
	{
1410
		// phpcs:enable
1411
		$parents = array();
1412
1413
		$sql = "SELECT fk_parent FROM ".MAIN_DB_PREFIX."categorie";
1414
		$sql .= " WHERE rowid = ".((int) $this->id);
1415
1416
		$res = $this->db->query($sql);
1417
1418
		if ($res) {
1419
			while ($rec = $this->db->fetch_array($res)) {
1420
				if ($rec['fk_parent'] > 0) {
1421
					$cat = new Categorie($this->db);
1422
					$cat->fetch($rec['fk_parent']);
1423
					$parents[] = $cat;
1424
				}
1425
			}
1426
			return $parents;
1427
		} else {
1428
			dol_print_error($this->db);
1429
			return -1;
1430
		}
1431
	}
1432
1433
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1434
	/**
1435
	 * 	Returns in a table all possible paths to get to the category
1436
	 * 	starting with the major categories represented by Tables of categories
1437
	 *
1438
	 *	@return	array
1439
	 */
1440
	public function get_all_ways()
1441
	{
1442
		// phpcs:enable
1443
		$ways = array();
1444
1445
		$parents = $this->get_meres();
1446
		if (!empty($parents)) {
1447
			foreach ($parents as $parent) {
1448
				$allways = $parent->get_all_ways();
1449
				foreach ($allways as $way) {
1450
					$w = $way;
1451
					$w[] = $this;
1452
					$ways[] = $w;
1453
				}
1454
			}
1455
		}
1456
1457
		if (count($ways) == 0) {
1458
			$ways[0][0] = $this;
1459
		}
1460
1461
		return $ways;
1462
	}
1463
1464
	/**
1465
	 * Return list of categories (object instances or labels) linked to element of id $id and type $type
1466
	 * Should be named getListOfCategForObject
1467
	 *
1468
	 * @param   int    		$id     Id of element
1469
	 * @param   string|int	$type   Type of category ('customer', 'supplier', 'contact', 'product', 'member') or (0, 1, 2, ...)
1470
	 * @param   string 		$mode   'id'=Get array of category ids, 'object'=Get array of fetched category instances, 'label'=Get array of category
1471
	 *                      	    labels, 'id'= Get array of category IDs
1472
	 * @return  Categorie[]|int     Array of category objects or < 0 if KO
1473
	 */
1474
	public function containing($id, $type, $mode = 'object')
1475
	{
1476
		$cats = array();
1477
1478
		if (is_numeric($type)) {
1479
			$type = Categorie::$MAP_ID_TO_CODE[$type];
1480
		}
1481
1482
		if ($type === Categorie::TYPE_BANK_LINE) {   // TODO Remove this with standard category code after migration of llx_bank_categ into llx_categorie
1483
			// Load bank categories
1484
			$sql = "SELECT c.label, c.rowid";
1485
			$sql .= " FROM ".MAIN_DB_PREFIX."bank_class as a, ".MAIN_DB_PREFIX."bank_categ as c";
1486
			$sql .= " WHERE a.lineid=".((int) $id)." AND a.fk_categ = c.rowid";
1487
			$sql .= " AND c.entity IN (".getEntity('category').")";
1488
			$sql .= " ORDER BY c.label";
1489
1490
			$res = $this->db->query($sql);
1491
			if ($res) {
1492
				while ($obj = $this->db->fetch_object($res)) {
1493
					if ($mode == 'id') {
1494
						$cats[] = $obj->rowid;
1495
					} elseif ($mode == 'label') {
1496
						$cats[] = $obj->label;
1497
					} else {
1498
						$cat = new Categorie($this->db);
1499
						$cat->id = $obj->rowid;
1500
						$cat->label = $obj->label;
1501
						$cats[] = $cat;
1502
					}
1503
				}
1504
			} else {
1505
				dol_print_error($this->db);
1506
				return -1;
1507
			}
1508
		} else {
1509
			$sql = "SELECT ct.fk_categorie, c.label, c.rowid";
1510
			$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";
1511
			$sql .= " WHERE ct.fk_categorie = c.rowid AND ct.fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])." = ".(int) $id;
1512
			// This seems useless because the table already contains id of category of 1 unique type. So commented.
1513
			// So now it works also with external added categories.
1514
			//$sql .= " AND c.type = ".((int) $this->MAP_ID[$type]);
1515
			$sql .= " AND c.entity IN (".getEntity('category').")";
1516
1517
			$res = $this->db->query($sql);
1518
			if ($res) {
1519
				while ($obj = $this->db->fetch_object($res)) {
1520
					if ($mode == 'id') {
1521
						$cats[] = $obj->rowid;
1522
					} elseif ($mode == 'label') {
1523
						$cats[] = $obj->label;
1524
					} else {
1525
						$cat = new Categorie($this->db);
1526
						$cat->fetch($obj->fk_categorie);
1527
						$cats[] = $cat;
1528
					}
1529
				}
1530
			} else {
1531
				dol_print_error($this->db);
1532
				return -1;
1533
			}
1534
		}
1535
1536
		return $cats;
1537
	}
1538
1539
	/**
1540
	 * 	Returns categories whose id or name match
1541
	 * 	add wildcards in the name unless $exact = true
1542
	 *
1543
	 * 	@param		int			$id			Id
1544
	 * 	@param		string		$nom		Name
1545
	 * 	@param		string		$type		Type of category ('member', 'customer', 'supplier', 'product', 'contact'). Old mode (0, 1, 2, ...) is deprecated.
1546
	 * 	@param		boolean		$exact		Exact string search (true/false)
1547
	 * 	@param		boolean		$case		Case sensitive (true/false)
1548
	 * 	@return		Categorie[]|int			Array of Categorie, -1 if error
1549
	 */
1550
	public function rechercher($id, $nom, $type, $exact = false, $case = false)
1551
	{
1552
		// Deprecation warning
1553
		if (is_numeric($type)) {
1554
			dol_syslog(__METHOD__.': using numeric types is deprecated.', LOG_WARNING);
1555
		}
1556
1557
		$cats = array();
1558
1559
		// For backward compatibility
1560
		if (is_numeric($type)) {
1561
			// We want to reverse lookup
1562
			$map_type = array_flip($this->MAP_ID);
1563
			$type = $map_type[$type];
1564
			dol_syslog(get_class($this)."::rechercher(): numeric types are deprecated, please use string instead", LOG_WARNING);
1565
		}
1566
1567
		// Generation requete recherche
1568
		$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."categorie";
1569
		$sql .= " WHERE type = ".((int) $this->MAP_ID[$type]);
1570
		$sql .= " AND entity IN (".getEntity('category').")";
1571
		if ($nom) {
1572
			if (!$exact) {
1573
				$nom = '%'.$this->db->escape(str_replace('*', '%', $nom)).'%';
1574
			}
1575
			if (!$case) {
1576
				$sql .= " AND label LIKE '".$this->db->escape($nom)."'";
1577
			} else {
1578
				$sql .= " AND label LIKE BINARY '".$this->db->escape($nom)."'";
1579
			}
1580
		}
1581
		if ($id) {
1582
			$sql .= " AND rowid = ".((int) $id);
1583
		}
1584
1585
		$res = $this->db->query($sql);
1586
		if ($res) {
1587
			while ($rec = $this->db->fetch_array($res)) {
1588
				$cat = new Categorie($this->db);
1589
				$cat->fetch($rec['rowid']);
1590
				$cats[] = $cat;
1591
			}
1592
1593
			return $cats;
1594
		} else {
1595
			$this->error = $this->db->error().' sql='.$sql;
1596
			return -1;
1597
		}
1598
	}
1599
1600
	/**
1601
	 *	Return name and link of category (with picto)
1602
	 *  Use ->id, ->ref, ->label, ->color
1603
	 *
1604
	 *	@param		int		$withpicto		0=No picto, 1=Include picto into link, 2=Only picto
1605
	 *	@param		string	$option			Sur quoi pointe le lien ('', 'xyz')
1606
	 * 	@param		int		$maxlength		Max length of text
1607
	 *  @param		string	$moreparam		More param on URL link
1608
	 *	@return		string					Chaine avec URL
1609
	 */
1610
	public function getNomUrl($withpicto = 0, $option = '', $maxlength = 0, $moreparam = '')
1611
	{
1612
		global $langs, $hookmanager;
1613
1614
		$result = '';
1615
		$label = $langs->trans("ShowCategory").': '.($this->ref ? $this->ref : $this->label);
1616
1617
		// Check contrast with background and correct text color
1618
		$forced_color = 'categtextwhite';
1619
		if ($this->color) {
1620
			if (colorIsLight($this->color)) {
1621
				$forced_color = 'categtextblack';
1622
			}
1623
		}
1624
1625
		$link = '<a href="'.DOL_URL_ROOT.'/categories/viewcat.php?id='.$this->id.'&type='.$this->type.$moreparam.'&backtopage='.urlencode($_SERVER['PHP_SELF'].($moreparam ? '?'.$moreparam : '')).'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip '.$forced_color.'">';
1626
		$linkend = '</a>';
1627
1628
		$picto = 'category';
1629
1630
1631
		if ($withpicto) {
1632
			$result .= ($link.img_object($label, $picto, 'class="classfortooltip"').$linkend);
1633
		}
1634
		if ($withpicto && $withpicto != 2) {
1635
			$result .= ' ';
1636
		}
1637
		if ($withpicto != 2) {
1638
			$result .= $link.dol_trunc(($this->ref ? $this->ref : $this->label), $maxlength).$linkend;
1639
		}
1640
		global $action;
1641
		$hookmanager->initHooks(array($this->element . 'dao'));
1642
		$parameters = array('id'=>$this->id, 'getnomurl' => &$result);
1643
		$reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1644
		if ($reshook > 0) {
1645
			$result = $hookmanager->resPrint;
1646
		} else {
1647
			$result .= $hookmanager->resPrint;
1648
		}
1649
		return $result;
1650
	}
1651
1652
1653
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1654
	/**
1655
	 *  Deplace fichier uploade sous le nom $file dans le repertoire sdir
1656
	 *
1657
	 *  @param      string	$sdir       Final destination directory
1658
	 *  @param      array	$file		Uploaded file name
1659
	 *	@return		void
1660
	 */
1661
	public function add_photo($sdir, $file)
1662
	{
1663
		// phpcs:enable
1664
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1665
1666
		$dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, 'category').$this->id."/";
1667
		$dir .= "photos/";
1668
1669
		if (!file_exists($dir)) {
1670
			dol_mkdir($dir);
1671
		}
1672
1673
		if (file_exists($dir)) {
1674
			if (is_array($file['name']) && count($file['name']) > 0) {
1675
				$nbfile = count($file['name']);
1676
				for ($i = 0; $i <= $nbfile; $i++) {
1677
					$originImage = $dir.$file['name'][$i];
1678
1679
					// Cree fichier en taille origine
1680
					dol_move_uploaded_file($file['tmp_name'][$i], $originImage, 1, 0, 0);
1681
1682
					if (file_exists($originImage)) {
1683
						// Create thumbs
1684
						$this->addThumbs($originImage);
1685
					}
1686
				}
1687
			} else {
1688
				$originImage = $dir.$file['name'];
1689
1690
				// Cree fichier en taille origine
1691
				dol_move_uploaded_file($file['tmp_name'], $originImage, 1, 0, 0);
1692
1693
				if (file_exists($originImage)) {
1694
					// Create thumbs
1695
					$this->addThumbs($originImage);
1696
				}
1697
			}
1698
		}
1699
	}
1700
1701
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1702
	/**
1703
	 *    Return tableau de toutes les photos de la categorie
1704
	 *
1705
	 *    @param      string	$dir        Repertoire a scanner
1706
	 *    @param      int		$nbmax      Nombre maximum de photos (0=pas de max)
1707
	 *    @return     array       			Tableau de photos
1708
	 */
1709
	public function liste_photos($dir, $nbmax = 0)
1710
	{
1711
		// phpcs:enable
1712
		include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1713
1714
		$nbphoto = 0;
1715
		$tabobj = array();
1716
1717
		$dirthumb = $dir.'thumbs/';
1718
1719
		if (file_exists($dir)) {
1720
			$handle = opendir($dir);
1721
			if (is_resource($handle)) {
1722
				while (($file = readdir($handle)) !== false) {
1723
					if (dol_is_file($dir.$file) && preg_match('/(\.jpeg|\.jpg|\.bmp|\.gif|\.png|\.tiff)$/i', $dir.$file)) {
1724
						$nbphoto++;
1725
						$photo = $file;
1726
1727
						// On determine nom du fichier vignette
1728
						$photo_vignette = '';
1729
						if (preg_match('/(\.jpeg|\.jpg|\.bmp|\.gif|\.png|\.tiff)$/i', $photo, $regs)) {
1730
							$photo_vignette = preg_replace('/'.$regs[0].'/i', '', $photo).'_small'.$regs[0];
1731
						}
1732
1733
						// Objet
1734
						$obj = array();
1735
						$obj['photo'] = $photo;
1736
						if ($photo_vignette && is_file($dirthumb.$photo_vignette)) {
1737
							$obj['photo_vignette'] = 'thumbs/'.$photo_vignette;
1738
						} else {
1739
							$obj['photo_vignette'] = "";
1740
						}
1741
1742
						$tabobj[$nbphoto - 1] = $obj;
1743
1744
						// On continue ou on arrete de boucler
1745
						if ($nbmax && $nbphoto >= $nbmax) {
1746
							break;
1747
						}
1748
					}
1749
				}
1750
1751
				closedir($handle);
1752
			}
1753
		}
1754
1755
		return $tabobj;
1756
	}
1757
1758
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1759
	/**
1760
	 *    Efface la photo de la categorie et sa vignette
1761
	 *
1762
	 *    @param	string		$file		Path to file
1763
	 *    @return	void
1764
	 */
1765
	public function delete_photo($file)
1766
	{
1767
		// phpcs:enable
1768
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1769
1770
		$dir = dirname($file).'/'; // Chemin du dossier contenant l'image d'origine
1771
		$dirthumb = $dir.'/thumbs/'; // Chemin du dossier contenant la vignette
1772
		$filename = preg_replace('/'.preg_quote($dir, '/').'/i', '', $file); // Nom du fichier
1773
1774
		// On efface l'image d'origine
1775
		dol_delete_file($file, 1);
1776
1777
		// Si elle existe, on efface la vignette
1778
		if (preg_match('/(\.jpeg|\.jpg|\.bmp|\.gif|\.png|\.tiff)$/i', $filename, $regs)) {
1779
			$photo_vignette = preg_replace('/'.$regs[0].'/i', '', $filename).'_small'.$regs[0];
1780
			if (file_exists($dirthumb.$photo_vignette)) {
1781
				dol_delete_file($dirthumb.$photo_vignette, 1);
1782
			}
1783
		}
1784
	}
1785
1786
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1787
	/**
1788
	 *  Load size of image file
1789
	 *
1790
	 *  @param    	string	$file        Path to file
1791
	 *  @return		void
1792
	 */
1793
	public function get_image_size($file)
1794
	{
1795
		// phpcs:enable
1796
		$infoImg = getimagesize($file); // Recuperation des infos de l'image
1797
		$this->imgWidth = $infoImg[0]; // Largeur de l'image
1798
		$this->imgHeight = $infoImg[1]; // Hauteur de l'image
1799
	}
1800
1801
	/**
1802
	 *	Update ou cree les traductions des infos produits
1803
	 *
1804
	 *	@param	User	$user		Object user
1805
	 *
1806
	 *	@return		int		<0 if KO, >0 if OK
1807
	 */
1808
	public function setMultiLangs($user)
1809
	{
1810
		global $langs;
1811
1812
		$langs_available = $langs->get_available_languages();
1813
		$current_lang = $langs->getDefaultLang();
1814
1815
		foreach ($langs_available as $key => $value) {
1816
			$sql = "SELECT rowid";
1817
			$sql .= " FROM ".MAIN_DB_PREFIX."categorie_lang";
1818
			$sql .= " WHERE fk_category=".((int) $this->id);
1819
			$sql .= " AND lang = '".$this->db->escape($key)."'";
1820
1821
			$result = $this->db->query($sql);
1822
1823
			if ($key == $current_lang) {
1824
				if ($this->db->num_rows($result)) { // si aucune ligne dans la base
1825
					$sql2 = "UPDATE ".MAIN_DB_PREFIX."categorie_lang";
1826
					$sql2 .= " SET label = '".$this->db->escape($this->label)."',";
1827
					$sql2 .= " description = '".$this->db->escape($this->description)."'";
1828
					$sql2 .= " WHERE fk_category = ".((int) $this->id)." AND lang = '".$this->db->escape($key)."'";
1829
				} else {
1830
					$sql2 = "INSERT INTO ".MAIN_DB_PREFIX."categorie_lang (fk_category, lang, label, description)";
1831
					$sql2 .= " VALUES(".((int) $this->id).", '".$this->db->escape($key)."', '".$this->db->escape($this->label)."'";
1832
					$sql2 .= ", '".$this->db->escape($this->multilangs["$key"]["description"])."')";
1833
				}
1834
				dol_syslog(get_class($this).'::setMultiLangs', LOG_DEBUG);
1835
				if (!$this->db->query($sql2)) {
1836
					$this->error = $this->db->lasterror();
1837
					return -1;
1838
				}
1839
			} elseif (isset($this->multilangs["$key"])) {
1840
				if ($this->db->num_rows($result)) { // si aucune ligne dans la base
1841
					$sql2 = "UPDATE ".MAIN_DB_PREFIX."categorie_lang";
1842
					$sql2 .= " SET label='".$this->db->escape($this->multilangs["$key"]["label"])."',";
1843
					$sql2 .= " description='".$this->db->escape($this->multilangs["$key"]["description"])."'";
1844
					$sql2 .= " WHERE fk_category=".((int) $this->id)." AND lang='".$this->db->escape($key)."'";
1845
				} else {
1846
					$sql2 = "INSERT INTO ".MAIN_DB_PREFIX."categorie_lang (fk_category, lang, label, description)";
1847
					$sql2 .= " VALUES(".((int) $this->id).", '".$this->db->escape($key)."', '".$this->db->escape($this->multilangs["$key"]["label"])."'";
1848
					$sql2 .= ",'".$this->db->escape($this->multilangs["$key"]["description"])."')";
1849
				}
1850
1851
				// on ne sauvegarde pas des champs vides
1852
				if ($this->multilangs["$key"]["label"] || $this->multilangs["$key"]["description"] || $this->multilangs["$key"]["note"]) {
1853
					dol_syslog(get_class($this).'::setMultiLangs', LOG_DEBUG);
1854
				}
1855
				if (!$this->db->query($sql2)) {
1856
					$this->error = $this->db->lasterror();
1857
					return -1;
1858
				}
1859
			}
1860
		}
1861
1862
		// Call trigger
1863
		$result = $this->call_trigger('CATEGORY_SET_MULTILANGS', $user);
1864
		if ($result < 0) {
1865
			$this->error = $this->db->lasterror();
1866
			return -1;
1867
		}
1868
		// End call triggers
1869
1870
		return 1;
1871
	}
1872
1873
	/**
1874
	 *	Load array this->multilangs
1875
	 *
1876
	 *	@return		int		<0 if KO, >0 if OK
1877
	 */
1878
	public function getMultiLangs()
1879
	{
1880
		global $langs;
1881
1882
		$current_lang = $langs->getDefaultLang();
1883
1884
		$sql = "SELECT lang, label, description";
1885
		$sql .= " FROM ".MAIN_DB_PREFIX."categorie_lang";
1886
		$sql .= " WHERE fk_category=".((int) $this->id);
1887
1888
		$result = $this->db->query($sql);
1889
		if ($result) {
1890
			while ($obj = $this->db->fetch_object($result)) {
1891
				//print 'lang='.$obj->lang.' current='.$current_lang.'<br>';
1892
				if ($obj->lang == $current_lang) { // si on a les traduct. dans la langue courante on les charge en infos principales.
1893
					$this->label = $obj->label;
1894
					$this->description = $obj->description;
1895
				}
1896
				$this->multilangs["$obj->lang"]["label"] = $obj->label;
1897
				$this->multilangs["$obj->lang"]["description"] = $obj->description;
1898
			}
1899
			return 1;
1900
		} else {
1901
			$this->error = $langs->trans("Error")." : ".$this->db->error()." - ".$sql;
1902
			return -1;
1903
		}
1904
	}
1905
1906
	/**
1907
	 *	Return label of contact status
1908
	 *
1909
	 *	@param      int		$mode       0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto, 6=Long label + Picto
1910
	 * 	@return 	string				Label of contact status
1911
	 */
1912
	public function getLibStatut($mode)
1913
	{
1914
		return '';
1915
	}
1916
1917
1918
	/**
1919
	 *  Initialise an instance with random values.
1920
	 *  Used to build previews or test instances.
1921
	 *	id must be 0 if object instance is a specimen.
1922
	 *
1923
	 *  @return	void
1924
	 */
1925
	public function initAsSpecimen()
1926
	{
1927
		dol_syslog(get_class($this)."::initAsSpecimen");
1928
1929
		// Initialise parametres
1930
		$this->id = 0;
1931
		$this->fk_parent = 0;
1932
		$this->label = 'SPECIMEN';
1933
		$this->specimen = 1;
1934
		$this->description = 'This is a description';
1935
		$this->socid = 1;
1936
		$this->type = self::TYPE_PRODUCT;
1937
	}
1938
1939
	/**
1940
	 * Function used to replace a thirdparty id with another one.
1941
	 *
1942
	 * @param 	DoliDB 	$dbs 		Database handler, because function is static we name it $dbs not $db to avoid breaking coding test
1943
	 * @param 	int 	$origin_id 	Old thirdparty id
1944
	 * @param 	int 	$dest_id 	New thirdparty id
1945
	 * @return 	bool
1946
	 */
1947
	public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
1948
	{
1949
		$tables = array(
1950
			'categorie_societe'
1951
		);
1952
1953
		return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables, 1);
1954
	}
1955
1956
	/**
1957
	 * Return the addtional SQL JOIN query for filtering a list by a category
1958
	 *
1959
	 * @param string	$type			The category type (e.g Categorie::TYPE_WAREHOUSE)
1960
	 * @param string	$rowIdName		The name of the row id inside the whole sql query (e.g. "e.rowid")
1961
	 * @return string					A additional SQL JOIN query
1962
	 * @deprecated	search on some categories must be done using a WHERE EXISTS or NOT EXISTS and not a LEFT JOIN. @TODO Replace with getWhereQuery($type, $searchCategoryList)
1963
	 */
1964
	public static function getFilterJoinQuery($type, $rowIdName)
1965
	{
1966
		if ($type == 'bank_account') {
1967
			$type = 'account';
1968
		}
1969
1970
		return " LEFT JOIN ".MAIN_DB_PREFIX."categorie_".$type." as cp ON ".$rowIdName." = cp.fk_".$type;
1971
	}
1972
1973
	/**
1974
	 * Return the addtional SQL SELECT query for filtering a list by a category
1975
	 *
1976
	 * @param string	$type			The category type (e.g Categorie::TYPE_WAREHOUSE)
1977
	 * @param string	$rowIdName		The name of the row id inside the whole sql query (e.g. "e.rowid")
1978
	 * @param Array		$searchList		A list with the selected categories
1979
	 * @return string					A additional SQL SELECT query
1980
	 * @deprecated	search on some categories must be done using a WHERE EXISTS or NOT EXISTS and not a LEFT JOIN
1981
	 */
1982
	public static function getFilterSelectQuery($type, $rowIdName, $searchList)
1983
	{
1984
		if ($type == 'bank_account') {
1985
			$type = 'account';
1986
		}
1987
		if ($type == 'customer') {
1988
			$type = 'societe';
1989
		}
1990
		if ($type == 'supplier') {
1991
			$type = 'fournisseur';
1992
		}
1993
1994
		if (empty($searchList) && !is_array($searchList)) {
1995
			return "";
1996
		}
1997
1998
		$searchCategorySqlList = array();
1999
		foreach ($searchList as $searchCategory) {
2000
			if (intval($searchCategory) == -2) {
2001
				$searchCategorySqlList[] = " cp.fk_categorie IS NULL";
2002
			} elseif (intval($searchCategory) > 0) {
2003
				$searchCategorySqlList[] = " ".$rowIdName." IN (SELECT fk_".$type." FROM ".MAIN_DB_PREFIX."categorie_".$type." WHERE fk_categorie = ".((int) $searchCategory).")";
2004
			}
2005
		}
2006
2007
		if (!empty($searchCategorySqlList)) {
2008
			return " AND (".implode(' AND ', $searchCategorySqlList).")";
2009
		} else {
2010
			return "";
2011
		}
2012
	}
2013
2014
	/**
2015
	 *      Count all categories
2016
	 *
2017
	 *      @return int                             Number of categories, -1 on error
2018
	 */
2019
	public function countNbOfCategories()
2020
	{
2021
		dol_syslog(get_class($this)."::count_all_categories", LOG_DEBUG);
2022
		$sql = "SELECT COUNT(rowid) FROM ".MAIN_DB_PREFIX."categorie";
2023
		$sql .= " WHERE entity IN (".getEntity('category').")";
2024
2025
		$res = $this->db->query($sql);
2026
		if ($res) {
2027
			$obj = $this->db->fetch_object($res);
2028
			return $obj->count;
2029
		} else {
2030
			dol_print_error($this->db);
2031
			return -1;
2032
		}
2033
	}
2034
}
2035