Passed
Branch develop (5cbde9)
by
unknown
26:38
created

Categorie::print_all_ways()   B

Complexity

Conditions 8
Paths 12

Size

Total Lines 46
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 23
c 0
b 0
f 0
nc 12
nop 3
dl 0
loc 46
rs 8.4444
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-2019  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 <http://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.'/fourn/class/fournisseur.class.php';
38
require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
39
40
41
/**
42
 *	Class to manage categories
43
 */
44
class Categorie extends CommonObject
45
{
46
	// Categories types (we use string because we want to accept any modules/types in a future)
47
	const TYPE_PRODUCT   = 'product';
48
	const TYPE_SUPPLIER  = 'supplier';
49
	const TYPE_CUSTOMER  = 'customer';
50
	const TYPE_MEMBER    = 'member';
51
	const TYPE_CONTACT   = 'contact';
52
	const TYPE_USER      = 'user';
53
	const TYPE_PROJECT   = 'project';
54
	const TYPE_ACCOUNT   = 'bank_account';
55
    const TYPE_BANK_LINE = 'bank_line';
56
57
	/**
58
	 * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
59
	 */
60
	public $picto = 'category';
61
62
63
	/**
64
	 * @var array ID mapping from type string
65
	 *
66
	 * @note This array should be remove in future, once previous constants are moved to the string value. Deprecated
67
	 */
68
	protected $MAP_ID = array(
69
		'product'      => 0,
70
		'supplier'     => 1,
71
		'customer'     => 2,
72
		'member'       => 3,
73
		'contact'      => 4,
74
		'bank_account' => 5,
75
        'project'      => 6,
76
		'user'         => 7,
77
		'bank_line'    => 8,
78
	);
79
80
    /**
81
	 * @var array Code mapping from ID
82
	 */
83
	public static $MAP_ID_TO_CODE = array(
84
		0 => 'product',
85
		1 => 'supplier',
86
		2 => 'customer',
87
		3 => 'member',
88
		4 => 'contact',
89
		5 => 'bank_account',
90
		6 => 'project',
91
		7 => 'user',
92
		8 => 'bank_line',
93
	);
94
95
	/**
96
	 * @var array Foreign keys mapping from type string
97
	 *
98
	 * @note Move to const array when PHP 5.6 will be our minimum target
99
	 */
100
	protected $MAP_CAT_FK = array(
101
		'product'  => 'product',
102
		'customer' => 'soc',
103
		'supplier' => 'soc',
104
		'member'   => 'member',
105
		'contact'  => 'socpeople',
106
		'user'     => 'user',
107
        'account'  => 'account',		// old for bank_account
108
        'bank_account' => 'account',
109
        'project'  => 'project',
110
    );
111
112
    /**
113
	 * @var array Category tables mapping from type string
114
	 *
115
	 * @note Move to const array when PHP 5.6 will be our minimum target
116
	 */
117
	protected $MAP_CAT_TABLE = array(
118
		'product'  => 'product',
119
		'customer' => 'societe',
120
		'supplier' => 'fournisseur',
121
		'member'   => 'member',
122
		'contact'  => 'contact',
123
		'user'     => 'user',
124
        'account'  => 'account',		// old for bank_account
125
        'bank_account'=> 'account',
126
        'project'  => 'project',
127
	);
128
129
    /**
130
	 * @var array Object class mapping from type string
131
	 *
132
	 * @note Move to const array when PHP 5.6 will be our minimum target
133
	 */
134
	protected $MAP_OBJ_CLASS = array(
135
		'product'  => 'Product',
136
		'customer' => 'Societe',
137
		'supplier' => 'Fournisseur',
138
		'member'   => 'Adherent',
139
		'contact'  => 'Contact',
140
		'user'     => 'User',
141
		'account'  => 'Account',		// old for bank account
142
		'bank_account'  => 'Account',
143
        'project'  => 'Project',
144
	);
145
146
    /**
147
	 * @var array Object table mapping from type string
148
	 *
149
	 * @note Move to const array when PHP 5.6 will be our minimum target
150
	 */
151
	protected $MAP_OBJ_TABLE = array(
152
		'product'  => 'product',
153
		'customer' => 'societe',
154
		'supplier' => 'societe',
155
		'member'   => 'adherent',
156
		'contact'  => 'socpeople',
157
		'user'     => 'user',
158
        'account'  => 'bank_account',
159
        'project'  => 'projet',
160
	);
161
162
	/**
163
	 * @var string ID to identify managed object
164
	 */
165
	public $element='category';
166
167
	/**
168
	 * @var string Name of table without prefix where object is stored
169
	 */
170
	public $table_element='categorie';
171
172
	/**
173
     * @var int ID
174
     */
175
	public $fk_parent;
176
177
	/**
178
     * @var string Category label
179
     */
180
   	public $label;
181
182
	/**
183
	 * @var string description
184
	 */
185
	public $description;
186
187
	/**
188
	 * @var string     Color
189
	 */
190
	public $color;
191
	/**
192
	 * @var ???
0 ignored issues
show
Documentation Bug introduced by
The doc comment ??? at position 0 could not be parsed: Unknown type name '??' at position 0 in ???.
Loading history...
193
	 */
194
	public $socid;
195
	/**
196
	 * @var string	Category type
197
	 *
198
	 * @see Categorie::TYPE_PRODUCT
199
	 * @see Categorie::TYPE_SUPPLIER
200
	 * @see Categorie::TYPE_CUSTOMER
201
	 * @see Categorie::TYPE_MEMBER
202
	 * @see Categorie::TYPE_CONTACT
203
	 * @see Categorie::TYPE_USER
204
	 * @see Categorie::TYPE_ACCOUNT
205
	 * @see Categorie::TYPE_PROJECT
206
	 */
207
	public $type;
208
209
	/**
210
	 * @var array Categories table in memory
211
	 */
212
	public $cats = array();
213
214
    /**
215
	 * @var array Mother of table
216
	 */
217
	public $motherof = array();
218
219
	/**
220
	 *	Constructor
221
	 *
222
	 *  @param		DoliDB		$db     Database handler
223
	 */
224
	public function __construct($db)
225
	{
226
		$this->db = $db;
227
	}
228
229
	/**
230
	 * 	Load category into memory from database
231
	 *
232
	 * 	@param		int		$id		Id of category
233
	 *  @param		string	$label	Label of category
234
	 *  @param		string	$type	Type of category ('product', '...') or (0, 1, ...)
235
	 * 	@return		int				<0 if KO, >0 if OK
236
	 */
237
	public function fetch($id, $label = '', $type = null)
238
	{
239
		global $conf;
240
241
		// Check parameters
242
		if (empty($id) && empty($label)) return -1;
243
		if (! is_numeric($type)) $type=$this->MAP_ID[$type];
244
245
		$sql = "SELECT rowid, fk_parent, entity, label, description, color, fk_soc, visible, type";
246
		$sql.= " FROM ".MAIN_DB_PREFIX."categorie";
247
		if ($id > 0)
248
		{
249
			$sql.= " WHERE rowid = ".$id;
250
		}
251
		else
252
		{
253
			$sql.= " WHERE label = '".$this->db->escape($label)."' AND entity IN (".getEntity('category').")";
254
			if (! is_null($type)) $sql.= " AND type = ".$this->db->escape($type);
255
		}
256
257
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
258
		$resql = $this->db->query($sql);
259
		if ($resql)
260
		{
261
			if ($this->db->num_rows($resql) > 0)
262
			{
263
				$res = $this->db->fetch_array($resql);
264
265
				$this->id			= $res['rowid'];
266
				//$this->ref			= $res['rowid'];
267
				$this->fk_parent	= $res['fk_parent'];
268
				$this->label		= $res['label'];
269
				$this->description	= $res['description'];
270
				$this->color    	= $res['color'];
271
				$this->socid		= $res['fk_soc'];
272
				$this->visible		= $res['visible'];
273
				$this->type			= $res['type'];
274
				$this->entity		= $res['entity'];
275
276
				// Retreive all extrafield
277
				// fetch optionals attributes and labels
278
				$this->fetch_optionals();
279
280
				$this->db->free($resql);
281
282
				// multilangs
283
				if (! empty($conf->global->MAIN_MULTILANGS)) $this->getMultiLangs();
284
285
				return 1;
286
			}
287
			else
288
			{
289
				return 0;
290
			}
291
		}
292
		else
293
		{
294
			dol_print_error($this->db);
295
			return -1;
296
		}
297
	}
298
299
	/**
300
	 *  Add category into database
301
	 *
302
	 *  @param	User	$user		Object user
303
	 *  @return	int 				-1 : SQL error
304
	 *          					-2 : new ID unknown
305
	 *          					-3 : Invalid category
306
	 * 								-4 : category already exists
307
	 */
308
	public function create($user)
309
	{
310
		global $conf,$langs,$hookmanager;
311
		$langs->load('categories');
312
313
		$type=$this->type;
314
315
		if (! is_numeric($type)) $type=$this->MAP_ID[$type];
316
317
		$error=0;
318
319
		dol_syslog(get_class($this).'::create', LOG_DEBUG);
320
321
		// Clean parameters
322
		$this->label = trim($this->label);
323
		$this->description = trim($this->description);
324
		$this->color = trim($this->color);
325
		$this->import_key = trim($this->import_key);
326
		if (empty($this->visible)) $this->visible=0;
327
		$this->fk_parent = ($this->fk_parent != "" ? intval($this->fk_parent) : 0);
328
329
		if ($this->already_exists())
330
		{
331
			$this->error=$langs->trans("ImpossibleAddCat", $this->label);
332
			$this->error.=" : ".$langs->trans("CategoryExistsAtSameLevel");
333
			dol_syslog($this->error, LOG_WARNING);
334
			return -4;
335
		}
336
337
		$this->db->begin();
338
339
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."categorie (";
340
		$sql.= "fk_parent,";
341
		$sql.= " label,";
342
		$sql.= " description,";
343
		$sql.= " color,";
344
		if (! empty($conf->global->CATEGORY_ASSIGNED_TO_A_CUSTOMER))
345
		{
346
			$sql.= "fk_soc,";
347
		}
348
		$sql.= " visible,";
349
		$sql.= " type,";
350
		$sql.= " import_key,";
351
		$sql.= " entity";
352
		$sql.= ") VALUES (";
353
		$sql.= $this->db->escape($this->fk_parent).",";
354
		$sql.= "'".$this->db->escape($this->label)."',";
355
		$sql.= "'".$this->db->escape($this->description)."',";
356
		$sql.= "'".$this->db->escape($this->color)."',";
357
		if (! empty($conf->global->CATEGORY_ASSIGNED_TO_A_CUSTOMER))
358
		{
359
			$sql.= ($this->socid != -1 ? $this->socid : 'null').",";
360
		}
361
		$sql.= "'".$this->db->escape($this->visible)."',";
362
		$sql.= $this->db->escape($type).",";
363
		$sql.= (! empty($this->import_key)?"'".$this->db->escape($this->import_key)."'":'null').",";
364
		$sql.= $this->db->escape($conf->entity);
365
		$sql.= ")";
366
367
		$res = $this->db->query($sql);
368
		if ($res)
369
		{
370
			$id = $this->db->last_insert_id(MAIN_DB_PREFIX."categorie");
371
372
			if ($id > 0)
373
			{
374
				$this->id = $id;
375
376
				$action='create';
377
378
				// Actions on extra fields
379
				if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
380
				{
381
					$result=$this->insertExtraFields();
382
					if ($result < 0)
383
					{
384
						$error++;
385
					}
386
				}
387
388
				if (! $error)
389
				{
390
	                // Call trigger
391
    	            $result=$this->call_trigger('CATEGORY_CREATE', $user);
392
        	        if ($result < 0) { $error++; }
393
            	    // End call triggers
394
				}
395
396
                if ( ! $error )
397
                {
398
    				$this->db->commit();
399
    				return $id;
400
                }
401
                else
402
              	{
403
                	$this->db->rollback();
404
                    return -3;
405
                }
406
			}
407
			else
408
			{
409
				$this->db->rollback();
410
				return -2;
411
			}
412
		}
413
		else
414
		{
415
			$this->error=$this->db->error();
416
			$this->db->rollback();
417
			return -1;
418
		}
419
	}
420
421
	/**
422
	 * 	Update category
423
	 *
424
	 *	@param	User	$user		Object user
425
	 * 	@return	int		 			1 : OK
426
	 *          					-1 : SQL error
427
	 *          					-2 : invalid category
428
	 */
429
	public function update(User $user)
430
	{
431
		global $conf, $langs,$hookmanager;
432
433
		$error=0;
434
435
		// Clean parameters
436
		$this->label=trim($this->label);
437
		$this->description=trim($this->description);
438
		$this->fk_parent = ($this->fk_parent != "" ? intval($this->fk_parent) : 0);
439
		$this->visible = ($this->visible != "" ? intval($this->visible) : 0);
440
441
		if ($this->already_exists())
442
		{
443
			$this->error=$langs->trans("ImpossibleUpdateCat");
444
			$this->error.=" : ".$langs->trans("CategoryExistsAtSameLevel");
445
			return -1;
446
		}
447
448
		$this->db->begin();
449
450
		$sql = "UPDATE ".MAIN_DB_PREFIX."categorie";
451
		$sql.= " SET label = '".$this->db->escape($this->label)."',";
452
		$sql.= " description = '".$this->db->escape($this->description)."',";
453
		$sql.= " color = '".$this->db->escape($this->color)."'";
454
		if (! empty($conf->global->CATEGORY_ASSIGNED_TO_A_CUSTOMER))
455
		{
456
			$sql .= ", fk_soc = ".($this->socid != -1 ? $this->socid : 'null');
457
		}
458
		$sql .= ", visible = '".$this->db->escape($this->visible)."'";
459
		$sql .= ", fk_parent = ".$this->fk_parent;
460
		$sql .= " WHERE rowid = ".$this->id;
461
462
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
463
		if ($this->db->query($sql))
464
		{
465
			$action='update';
466
467
			// Actions on extra fields
468
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
469
			{
470
				$result=$this->insertExtraFields();
471
				if ($result < 0)
472
				{
473
					$error++;
474
				}
475
			}
476
477
			if (! $error)
478
			{
479
	            // Call trigger
480
    	        $result=$this->call_trigger('CATEGORY_MODIFY', $user);
481
        	    if ($result < 0) { $error++; $this->db->rollback(); return -1; }
482
            	// End call triggers
483
			}
484
485
			$this->db->commit();
486
487
			return 1;
488
		}
489
		else
490
		{
491
			$this->db->rollback();
492
			dol_print_error($this->db);
493
			return -1;
494
		}
495
	}
496
497
	/**
498
	 * 	Delete a category from database
499
	 *
500
	 * 	@param	User	$user		Object user that ask to delete
501
     *	@param	int		$notrigger	1=Does not execute triggers, 0= execute triggers
502
	 *	@return	int                 <0 KO >0 OK
503
	 */
504
	public function delete($user, $notrigger = 0)
505
	{
506
		global $conf,$langs;
507
508
		$error=0;
509
510
        // Clean parameters
511
		$this->fk_parent = ($this->fk_parent != "" ? intval($this->fk_parent) : 0);
512
513
		dol_syslog(get_class($this)."::remove");
514
515
		$this->db->begin();
516
517
		if (! $error && ! $notrigger)
518
		{
519
		    // Call trigger
520
		    $result=$this->call_trigger('CATEGORY_DELETE', $user);
521
		    if ($result < 0) $error++;
522
		    // End call triggers
523
		}
524
525
		/* FIX #1317 : Check for child category and move up 1 level*/
526
		if (! $error)
527
		{
528
			$sql = "UPDATE ".MAIN_DB_PREFIX."categorie";
529
			$sql.= " SET fk_parent = ".$this->fk_parent;
530
			$sql.= " WHERE fk_parent = ".$this->id;
531
532
			if (!$this->db->query($sql))
533
			{
534
				$this->error=$this->db->lasterror();
535
				$error++;
536
			}
537
		}
538
539
        $arraydelete = array(
540
            'categorie_societe' => 'fk_categorie',
541
            'categorie_fournisseur' => 'fk_categorie',
542
            'categorie_product' => 'fk_categorie',
543
            'categorie_member' => 'fk_categorie',
544
            'categorie_contact' => 'fk_categorie',
545
            'categorie_account' => 'fk_categorie',
546
            'bank_class' => 'fk_categ',
547
            'categorie_lang' => 'fk_category',
548
            'categorie' => 'rowid',
549
        );
550
        foreach ($arraydelete as $key => $value) {
551
            $sql  = "DELETE FROM " . MAIN_DB_PREFIX . $key;
552
            $sql .= " WHERE ".$value." = ".$this->id;
553
            if (!$this->db->query($sql)) {
554
                $this->errors[] = $this->db->lasterror();
555
                dol_syslog("Error sql=".$sql." ".$this->error, LOG_ERR);
556
                $error++;
557
            }
558
        }
559
560
		// Removed extrafields
561
		if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
562
		{
563
			$result=$this->deleteExtraFields();
564
			if ($result < 0)
565
			{
566
				$error++;
567
				dol_syslog(get_class($this)."::delete erreur ".$this->error, LOG_ERR);
568
			}
569
		}
570
571
		if (! $error)
572
		{
573
			$this->db->commit();
574
			return 1;
575
		}
576
		else
577
		{
578
			$this->db->rollback();
579
			return -1;
580
		}
581
	}
582
583
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
584
	/**
585
	 * Link an object to the category
586
	 *
587
	 * @param   CommonObject 	$obj  	Object to link to category
588
	 * @param   string     		$type 	Type of category ('product', ...)
589
	 * @return  int                		1 : OK, -1 : erreur SQL, -2 : id not defined, -3 : Already linked
590
	 */
591
	public function add_type($obj, $type)
592
	{
593
        // phpcs:enable
594
		global $user,$langs,$conf;
595
596
		$error=0;
597
598
		if ($this->id == -1) return -2;
599
600
        $this->db->begin();
601
602
		$sql = "INSERT INTO " . MAIN_DB_PREFIX . "categorie_" . $this->MAP_CAT_TABLE[$type];
603
		$sql .= " (fk_categorie, fk_" . $this->MAP_CAT_FK[$type] . ")";
604
		$sql .= " VALUES (" . $this->id . ", " . $obj->id . ")";
605
606
		dol_syslog(get_class($this).'::add_type', LOG_DEBUG);
607
		if ($this->db->query($sql))
608
		{
609
			if (! empty($conf->global->CATEGORIE_RECURSIV_ADD))
610
			{
611
				$sql = 'SELECT fk_parent FROM '.MAIN_DB_PREFIX.'categorie';
612
				$sql.= " WHERE rowid = ".$this->id;
613
614
				dol_syslog(get_class($this)."::add_type", LOG_DEBUG);
615
				$resql=$this->db->query($sql);
616
				if ($resql)
617
				{
618
					if ($this->db->num_rows($resql) > 0)
619
					{
620
                        $objparent = $this->db->fetch_object($resql);
621
622
						if (!empty($objparent->fk_parent))
623
						{
624
							$cat = new Categorie($this->db);
625
							$cat->id = $objparent->fk_parent;
626
							if (!$cat->containsObject($type, $obj->id)) {
627
								$result = $cat->add_type($obj, $type);
628
								if ($result < 0)
629
								{
630
									$this->error = $cat->error;
631
									$error++;
632
								}
633
							}
634
						}
635
					}
636
				}
637
				else
638
				{
639
					$error++;
640
					$this->error=$this->db->lasterror();
641
				}
642
643
				if ($error)
644
				{
645
				    $this->db->rollback();
646
					return -1;
647
				}
648
			}
649
650
651
652
            // Call trigger
653
			$this->context=array('linkto'=>$obj);	// Save object we want to link category to into category instance to provide information to trigger
654
			$result=$this->call_trigger('CATEGORY_LINK', $user);
655
            if ($result < 0) { $error++; }
656
            // End call triggers
657
658
			if (! $error)
659
			{
660
			    $this->db->commit();
661
			    return 1;
662
			}
663
			else
664
			{
665
			    $this->db->rollback();
666
			    return -2;
667
			}
668
		}
669
		else
670
		{
671
		    $this->db->rollback();
672
			if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS')
673
			{
674
				$this->error=$this->db->lasterrno();
675
				return -3;
676
			}
677
			else
678
			{
679
				$this->error=$this->db->lasterror();
680
			}
681
			return -1;
682
		}
683
	}
684
685
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
686
	/**
687
	 * Delete object from category
688
	 *
689
	 * @param   CommonObject $obj  Object
690
	 * @param   string       $type Type of category ('customer', 'supplier', 'contact', 'product', 'member')
691
	 *
692
	 * @return  int          1 if OK, -1 if KO
693
	 */
694
	public function del_type($obj, $type)
695
	{
696
        // phpcs:enable
697
		global $user,$langs,$conf;
698
699
		$error=0;
700
701
		// For backward compatibility
702
		if ($type == 'societe') {
703
			$type = 'customer';
704
			dol_syslog(get_class($this) . "::del_type(): type 'societe' is deprecated, please use 'customer' instead", LOG_WARNING);
705
		} elseif ($type == 'fournisseur') {
706
			$type = 'supplier';
707
			dol_syslog(get_class($this) . "::del_type(): type 'fournisseur' is deprecated, please use 'supplier' instead", LOG_WARNING);
708
		}
709
710
        $this->db->begin();
711
712
		$sql = "DELETE FROM " . MAIN_DB_PREFIX . "categorie_" . $this->MAP_CAT_TABLE[$type];
713
		$sql .= " WHERE fk_categorie = " . $this->id;
714
		$sql .= " AND   fk_" . $this->MAP_CAT_FK[$type] . "  = " . $obj->id;
715
716
		dol_syslog(get_class($this).'::del_type', LOG_DEBUG);
717
		if ($this->db->query($sql))
718
		{
719
            // Call trigger
720
			$this->context=array('unlinkoff'=>$obj);	// Save object we want to link category to into category instance to provide information to trigger
721
			$result=$this->call_trigger('CATEGORY_UNLINK', $user);
722
            if ($result < 0) { $error++; }
723
            // End call triggers
724
725
			if (! $error)
726
			{
727
			    $this->db->commit();
728
			    return 1;
729
			}
730
			else
731
			{
732
			    $this->db->rollback();
733
                return -2;
734
			}
735
		}
736
		else
737
		{
738
		    $this->db->rollback();
739
			$this->error=$this->db->lasterror();
740
			return -1;
741
		}
742
	}
743
744
	/**
745
	 * Return list of fetched instance of elements having this category
746
	 *
747
	 * @param   string     $type       Type of category ('customer', 'supplier', 'contact', 'product', 'member')
748
	 * @param   int        $onlyids    Return only ids of objects (consume less memory)
749
	 * @return  array|int              -1 if KO, array of instance of object if OK
750
	 * @see containsObject()
751
	 */
752
	public function getObjectsInCateg($type, $onlyids = 0)
753
	{
754
		$objs = array();
755
756
		$obj = new $this->MAP_OBJ_CLASS[$type]( $this->db );
757
758
		$sql = "SELECT c.fk_" . $this->MAP_CAT_FK[$type];
759
		$sql .= " FROM " . MAIN_DB_PREFIX . "categorie_" . $this->MAP_CAT_TABLE[$type] . " as c";
760
		$sql .= ", " . MAIN_DB_PREFIX . $this->MAP_OBJ_TABLE[$type] . " as o";
761
		$sql .= " WHERE o.entity IN (" . getEntity($obj->element).")";
762
		$sql.= " AND c.fk_categorie = ".$this->id;
763
		$sql .= " AND c.fk_" . $this->MAP_CAT_FK[$type] . " = o.rowid";
764
765
		dol_syslog(get_class($this)."::getObjectsInCateg", LOG_DEBUG);
766
		$resql = $this->db->query($sql);
767
		if ($resql)
768
		{
769
			while ($rec = $this->db->fetch_array($resql))
770
			{
771
			    if ($onlyids)
772
			    {
773
			        $objs[] = $rec['fk_' . $this->MAP_CAT_FK[$type]];
774
			    }
775
			    else
776
			    {
777
				    $obj = new $this->MAP_OBJ_CLASS[$type]( $this->db );
778
				    $obj->fetch($rec['fk_' . $this->MAP_CAT_FK[$type]]);
779
				    $objs[] = $obj;
780
			    }
781
			}
782
			return $objs;
783
		}
784
		else
785
		{
786
			$this->error=$this->db->error().' sql='.$sql;
787
			return -1;
788
		}
789
	}
790
791
	/**
792
	 * Check for the presence of an object in a category
793
	 *
794
	 * @param   string $type      		Type of category ('customer', 'supplier', 'contact', 'product', 'member')
795
	 * @param   int    $object_id 		Id of the object to search
796
	 * @return  int                     Number of occurrences
797
	 * @see getObjectsInCateg()
798
	 */
799
	public function containsObject($type, $object_id)
800
	{
801
		$sql = "SELECT COUNT(*) as nb FROM " . MAIN_DB_PREFIX . "categorie_" . $this->MAP_CAT_TABLE[$type];
802
		$sql .= " WHERE fk_categorie = " . $this->id . " AND fk_" . $this->MAP_CAT_FK[$type] . " = " . $object_id;
803
		dol_syslog(get_class($this)."::containsObject", LOG_DEBUG);
804
		$resql = $this->db->query($sql);
805
		if ($resql) {
806
			return $this->db->fetch_object($resql)->nb;
807
		} else {
808
			$this->error=$this->db->error().' sql='.$sql;
809
			return -1;
810
		}
811
	}
812
813
	/**
814
	 * List categories of an element id
815
	 *
816
	 * @param	int		$id			Id of element
817
	 * @param	string	$type		Type of category ('member', 'customer', 'supplier', 'product', 'contact')
818
	 * @param	string	$sortfield	Sort field
819
	 * @param	string	$sortorder	Sort order
820
	 * @param	int		$limit		Limit for list
821
	 * @param	int		$page		Page number
822
	 * @return	array|int			Array of categories, 0 if no cat, -1 on error
823
	 */
824
	public function getListForItem($id, $type = 'customer', $sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0)
825
	{
826
		global $conf;
827
828
		$categories = array();
829
830
		$sub_type = $type;
831
		$subcol_name = "fk_".$type;
832
		if ($type=="customer") {
833
			$sub_type="societe";
834
			$subcol_name="fk_soc";
835
		}
836
		if ($type=="supplier") {
837
			$sub_type="fournisseur";
838
			$subcol_name="fk_soc";
839
		}
840
		if ($type=="contact") {
841
			$subcol_name="fk_socpeople";
842
		}
843
		$sql = "SELECT s.rowid";
844
		$sql.= " FROM ".MAIN_DB_PREFIX."categorie as s";
845
		$sql.= " , ".MAIN_DB_PREFIX."categorie_".$sub_type." as sub ";
846
		$sql.= ' WHERE s.entity IN ('.getEntity('category').')';
847
		$sql.= ' AND s.type='.array_search($type, self::$MAP_ID_TO_CODE);
1 ignored issue
show
Bug introduced by
Are you sure array_search($type, self::MAP_ID_TO_CODE) of type false|integer|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

847
		$sql.= ' AND s.type='./** @scrutinizer ignore-type */ array_search($type, self::$MAP_ID_TO_CODE);
Loading history...
848
		$sql.= ' AND s.rowid = sub.fk_categorie';
849
		$sql.= ' AND sub.'.$subcol_name.' = '.$id;
850
851
		$sql.= $this->db->order($sortfield, $sortorder);
852
853
		$offset = 0;
854
		$nbtotalofrecords = '';
855
		if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
856
		{
857
			$result = $this->db->query($sql);
858
			$nbtotalofrecords = $this->db->num_rows($result);
859
			if (($page * $limit) > $nbtotalofrecords)	// if total resultset is smaller then paging size (filtering), goto and load page 0
860
			{
861
				$page = 0;
862
				$offset = 0;
863
			}
864
		}
865
866
		$sql.= $this->db->plimit($limit + 1, $offset);
867
868
		$result = $this->db->query($sql);
869
		if ($result)
870
		{
871
			$i=0;
872
			$num = $this->db->num_rows($result);
873
			$min = min($num, ($limit <= 0 ? $num : $limit));
874
			while ($i < $min)
875
			{
876
				$obj = $this->db->fetch_object($result);
877
				$category_static = new Categorie($this->db);
878
				if ($category_static->fetch($obj->rowid))
879
				{
880
					$categories[$i]['id'] 				= $category_static->id;
881
					$categories[$i]['fk_parent']		= $category_static->fk_parent;
882
					$categories[$i]['label']			= $category_static->label;
883
					$categories[$i]['description']		= $category_static->description;
884
					$categories[$i]['color']    		= $category_static->color;
885
					$categories[$i]['socid']			= $category_static->socid;
886
					$categories[$i]['visible']			= $category_static->visible;
887
					$categories[$i]['type']			= $category_static->type;
888
					$categories[$i]['entity']			= $category_static->entity;
889
					$categories[$i]['array_options']	= $category_static->array_options;
890
891
					// multilangs
892
					if (! empty($conf->global->MAIN_MULTILANGS)) 	{
893
						$categories[$i]['multilangs']	= $category_static->multilangs;
894
					}
895
				}
896
				$i++;
897
			}
898
		}
899
		else {
900
			$this->error = $this->db->lasterror();
901
			return -1;
902
		}
903
		if ( ! count($categories)) {
904
			return 0;
905
		}
906
907
		return $categories;
908
	}
909
910
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
911
	/**
912
	 * Return direct childs id of a category into an array
913
	 *
914
	 * @return	array|int   <0 KO, array ok
915
	 */
916
	public function get_filles()
917
	{
918
        // phpcs:enable
919
		$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."categorie";
920
		$sql.= " WHERE fk_parent = ".$this->id;
921
922
		$res  = $this->db->query($sql);
923
		if ($res)
924
		{
925
			$cats = array();
926
			while ($rec = $this->db->fetch_array($res))
927
			{
928
				$cat = new Categorie($this->db);
929
				$cat->fetch($rec['rowid']);
930
				$cats[] = $cat;
931
			}
932
			return $cats;
933
		}
934
		else
935
		{
936
			dol_print_error($this->db);
937
			return -1;
938
		}
939
	}
940
941
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
942
	/**
943
	 * 	Load the array this->motherof that is array(id_son=>id_parent, ...)
944
	 *
945
	 *	@return		int		<0 if KO, >0 if OK
946
	 */
947
	protected function load_motherof()
948
	{
949
        // phpcs:enable
950
	    $this->motherof=array();
951
952
		// Load array[child]=parent
953
		$sql = "SELECT fk_parent as id_parent, rowid as id_son";
954
		$sql.= " FROM ".MAIN_DB_PREFIX."categorie";
955
		$sql.= " WHERE fk_parent != 0";
956
		$sql.= " AND entity IN (".getEntity('category').")";
957
958
		dol_syslog(get_class($this)."::load_motherof", LOG_DEBUG);
959
		$resql = $this->db->query($sql);
960
		if ($resql)
961
		{
962
			while ($obj= $this->db->fetch_object($resql))
963
			{
964
				$this->motherof[$obj->id_son]=$obj->id_parent;
965
			}
966
			return 1;
967
		}
968
		else
969
		{
970
			dol_print_error($this->db);
971
			return -1;
972
		}
973
	}
974
975
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
976
	/**
977
	 * Rebuilding the category tree as an array
978
	 * Return an array of table('id','id_mere',...) trie selon arbre et avec:
979
	 *                id = id de la categorie
980
	 *                id_mere = id de la categorie mere
981
	 *                id_children = tableau des id enfant
982
	 *                label = nom de la categorie
983
	 *                fulllabel = nom avec chemin complet de la categorie
984
	 *                fullpath = chemin complet compose des id
985
	 *
986
	 * @param   string                  $type                   Type of categories ('customer', 'supplier', 'contact', 'product', 'member') or (0, 1, 2, ...).
987
	 * @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.
988
     *                                                          $markafterid can be an :
989
     *                                                          - int (id of category)
990
     *                                                          - string (categories ids seprated by comma)
991
     *                                                          - array (list of categories ids)
992
     * @param   int                     $include                [=0] Removed or 1=Keep only
993
	 * @return  array|int               Array of categories. this->cats and this->motherof are set, -1 on error
994
	 */
995
	public function get_full_arbo($type, $markafterid = 0, $include = 0)
996
	{
997
        // phpcs:enable
998
	    global $conf, $langs;
999
1000
		if (! is_numeric($type)) $type = $this->MAP_ID[$type];
1001
1002
        if (is_string($markafterid))
1003
        {
1004
            $markafterid = explode(',', $markafterid);
1005
        }
1006
        elseif (is_numeric($markafterid))
1007
        {
1008
            if ($markafterid > 0)
1009
            {
1010
                $markafterid = array($markafterid);
1011
            }
1012
            else
1013
            {
1014
                $markafterid = array();
1015
            }
1016
        }
1017
        elseif (!is_array($markafterid))
1 ignored issue
show
introduced by
The condition is_array($markafterid) is always true.
Loading history...
1018
        {
1019
            $markafterid = array();
1020
        }
1021
1022
        $this->cats = array();
1023
1024
		// Init this->motherof that is array(id_son=>id_parent, ...)
1025
		$this->load_motherof();
1026
		$current_lang = $langs->getDefaultLang();
1027
1028
		// Init $this->cats array
1029
		$sql = "SELECT DISTINCT c.rowid, c.label, c.description, c.color, c.fk_parent, c.visible";	// Distinct reduce pb with old tables with duplicates
1030
		if (! empty($conf->global->MAIN_MULTILANGS)) $sql.= ", t.label as label_trans, t.description as description_trans";
1031
		$sql.= " FROM ".MAIN_DB_PREFIX."categorie as c";
1032
		if (! empty($conf->global->MAIN_MULTILANGS)) $sql.= " LEFT  JOIN ".MAIN_DB_PREFIX."categorie_lang as t ON t.fk_category=c.rowid AND t.lang='".$this->db->escape($current_lang)."'";
1033
		$sql .= " WHERE c.entity IN (" . getEntity('category') . ")";
1034
		$sql .= " AND c.type = " . (int) $type;
1035
1036
		dol_syslog(get_class($this)."::get_full_arbo get category list", LOG_DEBUG);
1037
		$resql = $this->db->query($sql);
1038
		if ($resql)
1039
		{
1040
			$i=0;
1041
			while ($obj = $this->db->fetch_object($resql))
1042
			{
1043
				$this->cats[$obj->rowid]['rowid'] = $obj->rowid;
1044
				$this->cats[$obj->rowid]['id'] = $obj->rowid;
1045
				$this->cats[$obj->rowid]['fk_parent'] = $obj->fk_parent;
1046
				$this->cats[$obj->rowid]['label'] = ! empty($obj->label_trans) ? $obj->label_trans : $obj->label;
1047
				$this->cats[$obj->rowid]['description'] = ! empty($obj->description_trans) ? $obj->description_trans : $obj->description;
1048
				$this->cats[$obj->rowid]['color'] = $obj->color;
1049
				$this->cats[$obj->rowid]['visible'] = $obj->visible;
1050
				$i++;
1051
			}
1052
		}
1053
		else
1054
		{
1055
			dol_print_error($this->db);
1056
			return -1;
1057
		}
1058
1059
		// We add the fullpath property to each elements of first level (no parent exists)
1060
		dol_syslog(get_class($this)."::get_full_arbo call to build_path_from_id_categ", LOG_DEBUG);
1061
		foreach($this->cats as $key => $val)
1062
		{
1063
			//print 'key='.$key.'<br>'."\n";
1064
			$this->build_path_from_id_categ($key, 0);	// Process a branch from the root category key (this category has no parent)
1065
		}
1066
1067
        // Include or exclude leaf including $markafterid from tree
1068
        if (count($markafterid) > 0)
1069
        {
1070
            $keyfiltercatid = implode('|', $markafterid);
1071
1072
            //print "Look to discard category ".$markafterid."\n";
1073
            $keyfilter1 = '^' . $keyfiltercatid . '$';
1074
            $keyfilter2 = '_' . $keyfiltercatid . '$';
1075
            $keyfilter3 = '^' . $keyfiltercatid . '_';
1076
            $keyfilter4 = '_' . $keyfiltercatid . '_';
1077
            foreach($this->cats as $key => $val)
1078
            {
1079
                $test = (preg_match('/' . $keyfilter1 . '/', $val['fullpath']) || preg_match('/' . $keyfilter2 . '/', $val['fullpath'])
1080
                    || preg_match('/' . $keyfilter3 . '/', $val['fullpath']) || preg_match('/' . $keyfilter4 . '/', $val['fullpath']));
1081
1082
                if (($test && !$include) || (!$test && $include))
1083
                {
1084
                    unset($this->cats[$key]);
1085
                }
1086
            }
1087
        }
1088
1089
		dol_syslog(get_class($this)."::get_full_arbo dol_sort_array", LOG_DEBUG);
1090
		$this->cats=dol_sort_array($this->cats, 'fulllabel', 'asc', true, false);
1091
1092
		//$this->debug_cats();
1093
1094
		return $this->cats;
1095
	}
1096
1097
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1098
	/**
1099
	 *	For category id_categ and its childs available in this->cats, define property fullpath and fulllabel.
1100
	 *  This function is a memory scan only from $this->cats and $this->motherof, no database access must be done here.
1101
	 *
1102
	 * 	@param		int		$id_categ		id_categ entry to update
1103
	 * 	@param		int		$protection		Deep counter to avoid infinite loop
1104
	 *	@return		void
1105
	 */
1106
    public function build_path_from_id_categ($id_categ, $protection = 1000)
1107
    {
1108
        // phpcs:enable
1109
        dol_syslog(get_class($this)."::build_path_from_id_categ id_categ=".$id_categ." protection=".$protection, LOG_DEBUG);
1110
1111
		if (! empty($this->cats[$id_categ]['fullpath']))
1112
		{
1113
			// Already defined
1114
			dol_syslog(get_class($this)."::build_path_from_id_categ fullpath and fulllabel already defined", LOG_WARNING);
1115
			return;
1116
		}
1117
1118
		// First build full array $motherof
1119
		//$this->load_motherof();	// Disabled because already done by caller of build_path_from_id_categ
1120
1121
		// Define fullpath and fulllabel
1122
		$this->cats[$id_categ]['fullpath'] = '_'.$id_categ;
1123
		$this->cats[$id_categ]['fulllabel'] = $this->cats[$id_categ]['label'];
1124
		$i=0; $cursor_categ=$id_categ;
1125
		//print 'Work for id_categ='.$id_categ.'<br>'."\n";
1126
		while ((empty($protection) || $i < $protection) && ! empty($this->motherof[$cursor_categ]))
1127
		{
1128
			//print '&nbsp; cursor_categ='.$cursor_categ.' i='.$i.' '.$this->motherof[$cursor_categ].'<br>'."\n";
1129
			$this->cats[$id_categ]['fullpath'] = '_'.$this->motherof[$cursor_categ].$this->cats[$id_categ]['fullpath'];
1130
			$this->cats[$id_categ]['fulllabel'] = $this->cats[$this->motherof[$cursor_categ]]['label'].' >> '.$this->cats[$id_categ]['fulllabel'];
1131
			//print '&nbsp; Result for id_categ='.$id_categ.' : '.$this->cats[$id_categ]['fullpath'].' '.$this->cats[$id_categ]['fulllabel'].'<br>'."\n";
1132
			$i++; $cursor_categ=$this->motherof[$cursor_categ];
1133
		}
1134
		//print 'Result for id_categ='.$id_categ.' : '.$this->cats[$id_categ]['fullpath'].'<br>'."\n";
1135
1136
		// We count number of _ to have level
1137
		$this->cats[$id_categ]['level']=dol_strlen(preg_replace('/[^_]/i', '', $this->cats[$id_categ]['fullpath']));
1138
1139
        return;
1140
    }
1141
1142
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1143
	/**
1144
	 *	Display content of $this->cats
1145
	 *
1146
	 *	@return	void
1147
	 */
1148
	public function debug_cats()
1149
	{
1150
        // phpcs:enable
1151
		// Display $this->cats
1152
		foreach($this->cats as $key => $val)
1153
		{
1154
			print 'id: '.$this->cats[$key]['id'];
1155
			print ' label: '.$this->cats[$key]['label'];
1156
			print ' mother: '.$this->cats[$key]['fk_parent'];
1157
			//print ' children: '.(is_array($this->cats[$key]['id_children'])?join(',',$this->cats[$key]['id_children']):'');
1158
			print ' fullpath: '.$this->cats[$key]['fullpath'];
1159
			print ' fulllabel: '.$this->cats[$key]['fulllabel'];
1160
			print "<br>\n";
1161
		}
1162
	}
1163
1164
1165
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1166
	/**
1167
	 * 	Returns all categories
1168
	 *
1169
	 *	@param	int			$type		Type of category (0, 1, ...)
1170
	 *	@param	boolean		$parent		Just parent categories if true
1171
	 *	@return	array|int				Table of Object Category, -1 on error
1172
	 */
1173
	public function get_all_categories($type = null, $parent = false)
1174
	{
1175
        // phpcs:enable
1176
		if (! is_numeric($type)) $type = $this->MAP_ID[$type];
1177
1178
		$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."categorie";
1179
		$sql.= " WHERE entity IN (".getEntity('category').")";
1180
		if (! is_null($type))
1181
			$sql.= " AND type = ".$type;
1182
		if ($parent)
1183
			$sql.= " AND fk_parent = 0";
1184
1185
		$res = $this->db->query($sql);
1186
		if ($res)
1187
		{
1188
			$cats = array ();
1189
			while ($rec = $this->db->fetch_array($res))
1190
			{
1191
				$cat = new Categorie($this->db);
1192
				$cat->fetch($rec['rowid']);
1193
				$cats[$rec['rowid']] = $cat;
1194
			}
1195
			return $cats;
1196
		}
1197
		else
1198
		{
1199
			dol_print_error($this->db);
1200
			return -1;
1201
		}
1202
	}
1203
1204
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1205
	/**
1206
	 *	Returns the top level categories (which are not child)
1207
	 *
1208
	 *	@param		int		$type		Type of category (0, 1, ...)
1209
	 *	@return		array
1210
	 */
1211
	public function get_main_categories($type = null)
1212
	{
1213
	    // phpcs:enable
1214
	    return $this->get_all_categories($type, true);
1215
	}
1216
1217
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1218
	/**
1219
	 * 	Check if no category with same label already exists for this cat's parent or root and for this cat's type
1220
	 *
1221
	 * 	@return		integer		1 if already exist, 0 otherwise, -1 if error
1222
	 */
1223
	public function already_exists()
1224
	{
1225
        // phpcs:enable
1226
		$type=$this->type;
1227
1228
		if (! is_numeric($type)) $type=$this->MAP_ID[$type];
1229
1230
		/* We have to select any rowid from llx_categorie which category's mother and label
1231
		 * are equals to those of the calling category
1232
		 */
1233
		$sql = "SELECT c.rowid";
1234
		$sql.= " FROM ".MAIN_DB_PREFIX."categorie as c ";
1235
		$sql.= " WHERE c.entity IN (".getEntity('category').")";
1236
		$sql.= " AND c.type = ".$type;
1237
		$sql.= " AND c.fk_parent = ".$this->fk_parent;
1238
		$sql.= " AND c.label = '".$this->db->escape($this->label)."'";
1239
1240
		dol_syslog(get_class($this)."::already_exists", LOG_DEBUG);
1241
		$resql = $this->db->query($sql);
1242
		if ($resql)
1243
		{
1244
			if ($this->db->num_rows($resql) > 0)						// Checking for empty resql
1245
			{
1246
				$obj = $this->db->fetch_array($resql);
1247
				/* If object called create, obj cannot have is id.
1248
				 * If object called update, he mustn't have the same label as an other category for this mother.
1249
				 * So if the result have the same id, update is not for label, and if result have an other one,
1250
				 * update may be for label.
1251
				 */
1252
				if($obj[0] > 0 && $obj[0] != $this->id)
1253
				{
1254
					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);
1255
					return 1;
1256
				}
1257
			}
1258
			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);
1259
			return 0;
1260
		}
1261
		else
1262
		{
1263
			$this->error=$this->db->error();
1264
			return -1;
1265
		}
1266
	}
1267
1268
1269
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1270
	/**
1271
	 * Returns the path of the category, with the names of the categories
1272
	 * separated by $sep (" >> " by default)
1273
	 *
1274
	 * @param	string	$sep	     Separator
1275
	 * @param	string	$url	     Url
1276
	 * @param   int     $nocolor     0
1277
	 * @return	array
1278
	 */
1279
	public function print_all_ways($sep = " &gt;&gt; ", $url = '', $nocolor = 0)
1280
	{
1281
        // phpcs:enable
1282
		$ways = array();
1283
1284
		$allways = $this->get_all_ways(); // Load array of categories
1285
		foreach ($allways as $way)
1286
		{
1287
			$w = array();
1288
			$i = 0;
1289
			$forced_color='';
1290
			foreach ($way as $cat)
1291
			{
1292
			    $i++;
1293
1294
			    if (empty($nocolor))
1295
			    {
1296
    			    $forced_color='toreplace';
1297
    			    if ($i == count($way))
1298
    			    {
1299
    			        // Check contrast with background and correct text color
1300
    			        $forced_color='categtextwhite';
1301
    			        if ($cat->color)
1302
    			        {
1303
    			            if (colorIsLight($cat->color)) $forced_color='categtextblack';
1304
    			        }
1305
    			    }
1306
			    }
1307
1308
				if ($url == '')
1309
				{
1310
			        $link = '<a href="'.DOL_URL_ROOT.'/categories/viewcat.php?id='.$cat->id.'&type='.$cat->type.'" class="'.$forced_color .'">';
1311
			        $linkend='</a>';
1312
				    $w[] = $link.$cat->label.$linkend;
1313
				}
1314
				else
1315
				{
1316
					$w[] = "<a href='".DOL_URL_ROOT."/$url?catid=".$cat->id."'>".$cat->label."</a>";
1317
				}
1318
			}
1319
			$newcategwithpath = preg_replace('/toreplace/', $forced_color, implode($sep, $w));
1320
1321
			$ways[] = $newcategwithpath;
1322
		}
1323
1324
		return $ways;
1325
	}
1326
1327
1328
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1329
	/**
1330
	 *	Returns an array containing the list of parent categories
1331
	 *
1332
	 *	@return	int|array <0 KO, array OK
1333
	 */
1334
	public function get_meres()
1335
	{
1336
        // phpcs:enable
1337
		$parents = array();
1338
1339
		$sql = "SELECT fk_parent FROM ".MAIN_DB_PREFIX."categorie";
1340
		$sql.= " WHERE rowid = ".$this->id;
1341
1342
		$res  = $this->db->query($sql);
1343
1344
		if ($res)
1345
		{
1346
			while ($rec = $this->db->fetch_array($res))
1347
			{
1348
				if ($rec['fk_parent'] > 0)
1349
				{
1350
					$cat = new Categorie($this->db);
1351
					$cat->fetch($rec['fk_parent']);
1352
					$parents[] = $cat;
1353
				}
1354
			}
1355
			return $parents;
1356
		}
1357
		else
1358
		{
1359
			dol_print_error($this->db);
1360
			return -1;
1361
		}
1362
	}
1363
1364
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1365
	/**
1366
	 * 	Returns in a table all possible paths to get to the category
1367
	 * 	starting with the major categories represented by Tables of categories
1368
	 *
1369
	 *	@return	array
1370
	 */
1371
	public function get_all_ways()
1372
	{
1373
        // phpcs:enable
1374
		$ways = array();
1375
1376
		$parents=$this->get_meres();
1377
		if (! empty($parents))
1378
		{
1379
			foreach ($parents as $parent)
0 ignored issues
show
Bug introduced by
The expression $parents of type integer is not traversable.
Loading history...
1380
			{
1381
				$allways=$parent->get_all_ways();
1382
				foreach ($allways as $way)
1383
				{
1384
					$w		= $way;
1385
					$w[]	= $this;
1386
					$ways[]	= $w;
1387
				}
1388
			}
1389
		}
1390
1391
		if (count($ways) == 0)
1392
			$ways[0][0] = $this;
1393
1394
		return $ways;
1395
	}
1396
1397
	/**
1398
	 * Return list of categories (object instances or labels) linked to element of id $id and type $type
1399
	 * Should be named getListOfCategForObject
1400
	 *
1401
	 * @param   int    		$id     Id of element
1402
	 * @param   string|int	$type   Type of category ('customer', 'supplier', 'contact', 'product', 'member') or (0, 1, 2, ...)
1403
	 * @param   string 		$mode   'id'=Get array of category ids, 'object'=Get array of fetched category instances, 'label'=Get array of category
1404
	 *                      	    labels, 'id'= Get array of category IDs
1405
	 * @return  Categorie[]|int     Array of category objects or < 0 if KO
1406
	 */
1407
	public function containing($id, $type, $mode = 'object')
1408
	{
1409
		$cats = array();
1410
1411
		if (is_numeric($type)) $type = Categorie::$MAP_ID_TO_CODE[$type];
1412
1413
		if ($type === Categorie::TYPE_BANK_LINE)   // TODO Remove this with standard category code
1414
		{
1415
		    // Load bank groups
1416
		    $sql = "SELECT c.label, c.rowid";
1417
		    $sql.= " FROM ".MAIN_DB_PREFIX."bank_class as a, ".MAIN_DB_PREFIX."bank_categ as c";
1418
		    $sql.= " WHERE a.lineid=".$id." AND a.fk_categ = c.rowid";
1419
		    $sql.= " ORDER BY c.label";
1420
1421
		    $res = $this->db->query($sql);
1422
		    if ($res)
1423
		    {
1424
		        while ($obj = $this->db->fetch_object($res))
1425
		        {
1426
    				if ($mode == 'id') {
1427
    				    $cats[] = $obj->rowid;
1428
    				} elseif ($mode == 'label') {
1429
    				    $cats[] = $obj->label;
1430
    				} else {
1431
    				    $cat = new Categorie($this->db);
1432
    				    $cat->id = $obj->rowid;
1433
    				    $cat->label = $obj->label;
1434
    				    $cats[] = $cat;
1435
    				}
1436
		        }
1437
		    }
1438
		    else
1439
		    {
1440
		        dol_print_error($this->db);
1441
		        return -1;
1442
		    }
1443
		}
1444
        else
1445
        {
1446
    		$sql = "SELECT ct.fk_categorie, c.label, c.rowid";
1447
    		$sql .= " FROM " . MAIN_DB_PREFIX . "categorie_" . $this->MAP_CAT_TABLE[$type] . " as ct, " . MAIN_DB_PREFIX . "categorie as c";
1448
    		$sql .= " WHERE ct.fk_categorie = c.rowid AND ct.fk_" . $this->MAP_CAT_FK[$type] . " = " . (int) $id . " AND c.type = " . $this->MAP_ID[$type];
1449
    		$sql .= " AND c.entity IN (" . getEntity('category') . ")";
1450
1451
    		$res = $this->db->query($sql);
1452
    		if ($res)
1453
    		{
1454
    			while ($obj = $this->db->fetch_object($res))
1455
    			{
1456
    				if ($mode == 'id') {
1457
    					$cats[] = $obj->rowid;
1458
    				} elseif ($mode == 'label') {
1459
    					$cats[] = $obj->label;
1460
    				} else {
1461
    					$cat = new Categorie($this->db);
1462
    					$cat->fetch($obj->fk_categorie);
1463
    					$cats[] = $cat;
1464
    				}
1465
    			}
1466
    		}
1467
    		else
1468
    		{
1469
    			dol_print_error($this->db);
1470
    			return -1;
1471
    		}
1472
        }
1473
1474
        return $cats;
1475
	}
1476
1477
1478
	/**
1479
	 * 	Returns categories whose id or name match
1480
	 * 	add wildcards in the name unless $exact = true
1481
	 *
1482
	 * 	@param		int			$id			Id
1483
	 * 	@param		string		$nom		Name
1484
 	 * 	@param		string		$type		Type of category ('member', 'customer', 'supplier', 'product', 'contact'). Old mode (0, 1, 2, ...) is deprecated.
1485
	 * 	@param		boolean		$exact		Exact string search (true/false)
1486
	 * 	@param		boolean		$case		Case sensitive (true/false)
1487
	 * 	@return		Categorie[]|int			Array of Categorie, -1 if error
1488
	 */
1489
	public function rechercher($id, $nom, $type, $exact = false, $case = false)
1490
	{
1491
		// Deprecation warning
1492
		if (is_numeric($type)) {
1493
			dol_syslog(__METHOD__ . ': using numeric types is deprecated.', LOG_WARNING);
1494
		}
1495
1496
		$cats = array();
1497
1498
        // For backward compatibility
1499
        if (is_numeric($type)) {
1500
            // We want to reverse lookup
1501
            $map_type = array_flip($this->MAP_ID);
1502
            $type = $map_type[$type];
1503
            dol_syslog(get_class($this) . "::rechercher(): numeric types are deprecated, please use string instead", LOG_WARNING);
1504
        }
1505
1506
        // Generation requete recherche
1507
		$sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "categorie";
1508
		$sql .= " WHERE type = " . $this->MAP_ID[$type];
1509
		$sql .= " AND entity IN (" . getEntity('category') . ")";
1510
		if ($nom)
1511
		{
1512
			if (! $exact)
1513
				$nom = '%'.str_replace('*', '%', $nom).'%';
1514
			if (! $case)
1515
				$sql.= " AND label LIKE '".$this->db->escape($nom)."'";
1516
			else
1517
				$sql.= " AND label LIKE BINARY '".$this->db->escape($nom)."'";
1518
		}
1519
		if ($id)
1520
		{
1521
			$sql.=" AND rowid = '".$id."'";
1522
		}
1523
1524
		$res  = $this->db->query($sql);
1525
		if ($res)
1526
		{
1527
			while ($rec = $this->db->fetch_array($res))
1528
			{
1529
				$cat = new Categorie($this->db);
1530
				$cat->fetch($rec['rowid']);
1531
				$cats[] = $cat;
1532
			}
1533
1534
			return $cats;
1535
		}
1536
		else
1537
		{
1538
			$this->error=$this->db->error().' sql='.$sql;
1539
			return -1;
1540
		}
1541
	}
1542
1543
	/**
1544
	 *	Return name and link of category (with picto)
1545
	 *  Use ->id, ->ref, ->label, ->color
1546
	 *
1547
	 *	@param		int		$withpicto		0=No picto, 1=Include picto into link, 2=Only picto
1548
	 *	@param		string	$option			Sur quoi pointe le lien ('', 'xyz')
1549
	 * 	@param		int		$maxlength		Max length of text
1550
	 *	@return		string					Chaine avec URL
1551
	 */
1552
	public function getNomUrl($withpicto = 0, $option = '', $maxlength = 0)
1553
	{
1554
		global $langs;
1555
1556
		$result='';
1557
		$label=$langs->trans("ShowCategory").': '. ($this->ref?$this->ref:$this->label);
1558
1559
		// Check contrast with background and correct text color
1560
		$forced_color='categtextwhite';
1561
		if ($this->color)
1562
		{
1563
			if (colorIsLight($this->color)) $forced_color='categtextblack';
1564
		}
1565
1566
		$link = '<a href="'.DOL_URL_ROOT.'/categories/viewcat.php?id='.$this->id.'&type='.$this->type.'&backtopage='.urlencode($_SERVER['PHP_SELF']).'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip '.$forced_color .'">';
1567
		$linkend='</a>';
1568
1569
		$picto='category';
1570
1571
1572
        if ($withpicto) $result.=($link.img_object($label, $picto, 'class="classfortooltip"').$linkend);
1573
		if ($withpicto && $withpicto != 2) $result.=' ';
1574
		if ($withpicto != 2) $result.=$link.dol_trunc(($this->ref?$this->ref:$this->label), $maxlength).$linkend;
1575
		return $result;
1576
	}
1577
1578
1579
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1580
	/**
1581
	 *  Deplace fichier uploade sous le nom $files dans le repertoire sdir
1582
	 *
1583
	 *  @param      string	$sdir       Repertoire destination finale
1584
	 *  @param      string	$file		Nom du fichier uploade
1585
	 *	@return		void
1586
	 */
1587
	public function add_photo($sdir, $file)
1588
	{
1589
        // phpcs:enable
1590
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1591
1592
		$dir = $sdir .'/'. get_exdir($this->id, 2, 0, 0, $this, 'category') . $this->id ."/";
1593
		$dir .= "photos/";
1594
1595
		if (! file_exists($dir))
1596
		{
1597
			dol_mkdir($dir);
1598
		}
1599
1600
		if (file_exists($dir)) {
1601
			if (is_array($file['name']) && count($file['name']) > 0)
1602
			{
1603
				$nbfile = count($file['name']);
1604
				for ($i = 0; $i <= $nbfile; $i ++) {
1605
1606
					$originImage = $dir . $file['name'][$i];
1607
1608
					// Cree fichier en taille origine
1609
					dol_move_uploaded_file($file['tmp_name'][$i], $originImage, 1, 0, 0);
1610
1611
					if (file_exists($originImage)) {
1612
						// Create thumbs
1613
						$this->addThumbs($originImage);
1614
					}
1615
				}
1616
			} else {
1617
				$originImage = $dir . $file['name'];
1618
1619
				// Cree fichier en taille origine
1620
				dol_move_uploaded_file($file['tmp_name'], $originImage, 1, 0, 0);
1621
1622
				if (file_exists($originImage)) {
1623
					// Create thumbs
1624
					$this->addThumbs($originImage);
1625
				}
1626
			}
1627
		}
1628
	}
1629
1630
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1631
	/**
1632
	 *    Return tableau de toutes les photos de la categorie
1633
	 *
1634
	 *    @param      string	$dir        Repertoire a scanner
1635
	 *    @param      int		$nbmax      Nombre maximum de photos (0=pas de max)
1636
	 *    @return     array       			Tableau de photos
1637
	 */
1638
	public function liste_photos($dir, $nbmax = 0)
1639
	{
1640
        // phpcs:enable
1641
		include_once DOL_DOCUMENT_ROOT .'/core/lib/files.lib.php';
1642
1643
		$nbphoto=0;
1644
		$tabobj=array();
1645
1646
		$dirthumb = $dir.'thumbs/';
1647
1648
		if (file_exists($dir))
1649
		{
1650
			$handle=opendir($dir);
1651
            if (is_resource($handle))
1652
            {
1653
    			while (($file = readdir($handle)) !== false)
1654
    			{
1655
    				if (dol_is_file($dir.$file) && preg_match('/(\.jpeg|\.jpg|\.bmp|\.gif|\.png|\.tiff)$/i', $dir.$file))
1656
    				{
1657
    					$nbphoto++;
1658
    					$photo = $file;
1659
1660
    					// On determine nom du fichier vignette
1661
    					$photo_vignette='';
1662
    					if (preg_match('/(\.jpeg|\.jpg|\.bmp|\.gif|\.png|\.tiff)$/i', $photo, $regs))
1663
    					{
1664
    						$photo_vignette=preg_replace('/'.$regs[0].'/i', '', $photo).'_small'.$regs[0];
1665
    					}
1666
1667
    					// Objet
1668
    					$obj=array();
1669
    					$obj['photo']=$photo;
1670
    					if ($photo_vignette && is_file($dirthumb.$photo_vignette)) $obj['photo_vignette']='thumbs/' . $photo_vignette;
1671
    					else $obj['photo_vignette']="";
1672
1673
    					$tabobj[$nbphoto-1]=$obj;
1674
1675
    					// On continue ou on arrete de boucler
1676
    					if ($nbmax && $nbphoto >= $nbmax) break;
1677
    				}
1678
    			}
1679
1680
    			closedir($handle);
1681
            }
1682
		}
1683
1684
		return $tabobj;
1685
	}
1686
1687
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1688
	/**
1689
	 *    Efface la photo de la categorie et sa vignette
1690
	 *
1691
	 *    @param	string		$file		Path to file
1692
	 *    @return	void
1693
	 */
1694
	public function delete_photo($file)
1695
	{
1696
        // phpcs:enable
1697
        require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1698
1699
	    $dir = dirname($file).'/'; // Chemin du dossier contenant l'image d'origine
1700
		$dirthumb = $dir.'/thumbs/'; // Chemin du dossier contenant la vignette
1701
		$filename = preg_replace('/'.preg_quote($dir, '/').'/i', '', $file); // Nom du fichier
1702
1703
		// On efface l'image d'origine
1704
		dol_delete_file($file, 1);
1705
1706
		// Si elle existe, on efface la vignette
1707
		if (preg_match('/(\.jpeg|\.jpg|\.bmp|\.gif|\.png|\.tiff)$/i', $filename, $regs))
1708
		{
1709
			$photo_vignette=preg_replace('/'.$regs[0].'/i', '', $filename).'_small'.$regs[0];
1710
			if (file_exists($dirthumb.$photo_vignette))
1711
			{
1712
				dol_delete_file($dirthumb.$photo_vignette, 1);
1713
			}
1714
		}
1715
	}
1716
1717
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1718
	/**
1719
	 *  Load size of image file
1720
	 *
1721
	 *  @param    	string	$file        Path to file
1722
	 *  @return		void
1723
	 */
1724
	public function get_image_size($file)
1725
	{
1726
        // phpcs:enable
1727
		$infoImg = getimagesize($file); // Recuperation des infos de l'image
1728
		$this->imgWidth = $infoImg[0]; // Largeur de l'image
1729
		$this->imgHeight = $infoImg[1]; // Hauteur de l'image
1730
	}
1731
1732
	/**
1733
	 *	Update ou cree les traductions des infos produits
1734
	 *
1735
	 *	@param	User	$user		Object user
1736
	 *
1737
	 *	@return		int		<0 if KO, >0 if OK
1738
	 */
1739
	public function setMultiLangs($user)
1740
	{
1741
	    global $langs;
1742
1743
	    $langs_available = $langs->get_available_languages();
1744
	    $current_lang = $langs->getDefaultLang();
1745
1746
	    foreach ($langs_available as $key => $value)
1747
	    {
1748
	        $sql = "SELECT rowid";
1749
	        $sql.= " FROM ".MAIN_DB_PREFIX."categorie_lang";
1750
	        $sql.= " WHERE fk_category=".$this->id;
1751
	        $sql.= " AND lang='".$key."'";
1752
1753
	        $result = $this->db->query($sql);
1754
1755
	        if ($key == $current_lang)
1756
	        {
1757
	            if ($this->db->num_rows($result)) // si aucune ligne dans la base
1758
	            {
1759
	                $sql2 = "UPDATE ".MAIN_DB_PREFIX."categorie_lang";
1760
	                $sql2.= " SET label='".$this->db->escape($this->label)."',";
1761
	                $sql2.= " description='".$this->db->escape($this->description)."'";
1762
	                $sql2.= " WHERE fk_category=".$this->id." AND lang='".$this->db->escape($key)."'";
1763
	            }
1764
	            else
1765
	            {
1766
	                $sql2 = "INSERT INTO ".MAIN_DB_PREFIX."categorie_lang (fk_category, lang, label, description)";
1767
	                $sql2.= " VALUES(".$this->id.",'".$key."','". $this->db->escape($this->label);
1768
	                $sql2.= "','".$this->db->escape($this->multilangs["$key"]["description"])."')";
1769
	            }
1770
	            dol_syslog(get_class($this).'::setMultiLangs', LOG_DEBUG);
1771
	            if (! $this->db->query($sql2))
1772
	            {
1773
	                $this->error=$this->db->lasterror();
1774
	                return -1;
1775
	            }
1776
	        }
1777
	        elseif (isset($this->multilangs["$key"]))
1778
	        {
1779
	            if ($this->db->num_rows($result)) // si aucune ligne dans la base
1780
	            {
1781
	                $sql2 = "UPDATE ".MAIN_DB_PREFIX."categorie_lang";
1782
	                $sql2.= " SET label='".$this->db->escape($this->multilangs["$key"]["label"])."',";
1783
	                $sql2.= " description='".$this->db->escape($this->multilangs["$key"]["description"])."'";
1784
	                $sql2.= " WHERE fk_category=".$this->id." AND lang='".$this->db->escape($key)."'";
1785
	            }
1786
	            else
1787
	            {
1788
	                $sql2 = "INSERT INTO ".MAIN_DB_PREFIX."categorie_lang (fk_category, lang, label, description)";
1789
	                $sql2.= " VALUES(".$this->id.",'".$key."','". $this->db->escape($this->multilangs["$key"]["label"]);
1790
	                $sql2.= "','".$this->db->escape($this->multilangs["$key"]["description"])."')";
1791
	            }
1792
1793
	            // on ne sauvegarde pas des champs vides
1794
	            if ( $this->multilangs["$key"]["label"] || $this->multilangs["$key"]["description"] || $this->multilangs["$key"]["note"] )
1795
	                dol_syslog(get_class($this).'::setMultiLangs', LOG_DEBUG);
1796
	            if (! $this->db->query($sql2))
1797
	            {
1798
	                $this->error=$this->db->lasterror();
1799
	                return -1;
1800
	            }
1801
	        }
1802
	    }
1803
1804
			// Call trigger
1805
			$result = $this->call_trigger('CATEGORY_SET_MULTILANGS', $user);
1806
			if ($result < 0) {
1807
				$this->error = $this->db->lasterror();
1808
				return -1;
1809
			}
1810
			// End call triggers
1811
1812
	    return 1;
1813
	}
1814
1815
	/**
1816
	 *	Load array this->multilangs
1817
	 *
1818
	 *	@return		int		<0 if KO, >0 if OK
1819
	 */
1820
	public function getMultiLangs()
1821
	{
1822
	    global $langs;
1823
1824
	    $current_lang = $langs->getDefaultLang();
1825
1826
	    $sql = "SELECT lang, label, description";
1827
	    $sql.= " FROM ".MAIN_DB_PREFIX."categorie_lang";
1828
	    $sql.= " WHERE fk_category=".$this->id;
1829
1830
	    $result = $this->db->query($sql);
1831
	    if ($result)
1832
	    {
1833
	        while ( $obj = $this->db->fetch_object($result) )
1834
	        {
1835
	            //print 'lang='.$obj->lang.' current='.$current_lang.'<br>';
1836
	            if( $obj->lang == $current_lang ) // si on a les traduct. dans la langue courante on les charge en infos principales.
1837
	            {
1838
	                $this->label		= $obj->label;
1839
	                $this->description	= $obj->description;
1840
	            }
1841
	            $this->multilangs["$obj->lang"]["label"]		= $obj->label;
1842
	            $this->multilangs["$obj->lang"]["description"]	= $obj->description;
1843
	        }
1844
	        return 1;
1845
	    }
1846
	    else
1847
	    {
1848
	        $this->error=$langs->trans("Error")." : ".$this->db->error()." - ".$sql;
1849
	        return -1;
1850
	    }
1851
	}
1852
1853
	/**
1854
	 *	Return label of contact status
1855
	 *
1856
	 *	@param      int			$mode       0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto
1857
	 * 	@return 	string					Label of contact status
1858
	 */
1859
	public function getLibStatut($mode)
1860
	{
1861
	    return '';
1862
	}
1863
1864
1865
    /**
1866
     *  Initialise an instance with random values.
1867
     *  Used to build previews or test instances.
1868
     *	id must be 0 if object instance is a specimen.
1869
     *
1870
     *  @return	void
1871
     */
1872
    public function initAsSpecimen()
1873
    {
1874
        dol_syslog(get_class($this)."::initAsSpecimen");
1875
1876
        // Initialise parametres
1877
        $this->id=0;
1878
        $this->fk_parent=0;
1879
        $this->label = 'SPECIMEN';
1880
        $this->specimen=1;
1881
        $this->description = 'This is a description';
1882
        $this->socid = 1;
1883
        $this->type = self::TYPE_PRODUCT;
1884
    }
1885
1886
    /**
1887
     * Function used to replace a thirdparty id with another one.
1888
     *
1889
     * @param DoliDB $db Database handler
1890
     * @param int $origin_id Old thirdparty id
1891
     * @param int $dest_id New thirdparty id
1892
     * @return bool
1893
     */
1894
    public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
1895
    {
1896
        $tables = array(
1897
            'categorie_societe'
1898
        );
1899
1900
        return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables, 1);
1901
    }
1902
}
1903