categorie.class.php 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722
  1. <?php
  2. /* Copyright (C) 2005 Matthieu Valleton <mv@seeschloss.org>
  3. * Copyright (C) 2005 Davoleau Brice <brice.davoleau@gmail.com>
  4. * Copyright (C) 2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
  5. * Copyright (C) 2006-2012 Regis Houssin <regis.houssin@capnetworks.com>
  6. * Copyright (C) 2006-2012 Laurent Destailleur <eldy@users.sourceforge.net>
  7. * Copyright (C) 2007 Patrick Raguin <patrick.raguin@gmail.com>
  8. * Copyright (C) 2013 Juanjo Menent <jmenent@2byte.es>
  9. * Copyright (C) 2013-2016 Philippe Grand <philippe.grand@atoo-net.com>
  10. * Copyright (C) 2015 Marcos García <marcosgdf@gmail.com>
  11. * Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
  12. * Copyright (C) 2016 Charlie Benke <charlie@patas-monkey.com>
  13. *
  14. * This program is free software; you can redistribute it and/or modify
  15. * it under the terms of the GNU General Public License as published by
  16. * the Free Software Foundation; either version 3 of the License, or
  17. * (at your option) any later version.
  18. *
  19. * This program is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. * GNU General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU General Public License
  25. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  26. */
  27. /**
  28. * \file htdocs/categories/class/categorie.class.php
  29. * \ingroup categorie
  30. * \brief File of class to manage categories
  31. */
  32. require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
  33. require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
  34. require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.class.php';
  35. require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
  36. /**
  37. * Class to manage categories
  38. */
  39. class Categorie extends CommonObject
  40. {
  41. // Categories types
  42. const TYPE_PRODUCT = 0; // TODO Replace with value 'product'
  43. const TYPE_SUPPLIER = 1; // TODO Replace this value with 'supplier'
  44. const TYPE_CUSTOMER = 2; // TODO Replace this value with 'customer'
  45. const TYPE_MEMBER = 3; // TODO Replace this value with 'member'
  46. const TYPE_CONTACT = 4; // TODO Replace this value with 'contact'
  47. const TYPE_USER = 4; // categorie contact and user are same ! TODO Replace this value with 'user'
  48. const TYPE_ACCOUNT = 5; // for bank account TODO Replace this value with 'account'
  49. /**
  50. * @var array ID mapping from type string
  51. *
  52. * @note This array should be remove in future, once previous constants are moved to the string value.
  53. */
  54. private $MAP_ID = array(
  55. 'product' => 0,
  56. 'supplier' => 1,
  57. 'customer' => 2,
  58. 'member' => 3,
  59. 'contact' => 4,
  60. 'user' => 4,
  61. 'account' => 5,
  62. );
  63. /**
  64. * @var array Foreign keys mapping from type string
  65. *
  66. * @note Move to const array when PHP 5.6 will be our minimum target
  67. */
  68. private $MAP_CAT_FK = array(
  69. 'product' => 'product',
  70. 'customer' => 'soc',
  71. 'supplier' => 'soc',
  72. 'member' => 'member',
  73. 'contact' => 'socpeople',
  74. 'user' => 'user',
  75. 'account' => 'account',
  76. );
  77. /**
  78. * @var array Category tables mapping from type string
  79. *
  80. * @note Move to const array when PHP 5.6 will be our minimum target
  81. */
  82. private $MAP_CAT_TABLE = array(
  83. 'product' => 'product',
  84. 'customer' => 'societe',
  85. 'supplier' => 'fournisseur',
  86. 'member' => 'member',
  87. 'contact' => 'contact',
  88. 'user' => 'user',
  89. 'account' => 'account',
  90. );
  91. /**
  92. * @var array Object class mapping from type string
  93. *
  94. * @note Move to const array when PHP 5.6 will be our minimum target
  95. */
  96. private $MAP_OBJ_CLASS = array(
  97. 'product' => 'Product',
  98. 'customer' => 'Societe',
  99. 'supplier' => 'Fournisseur',
  100. 'member' => 'Adherent',
  101. 'contact' => 'Contact',
  102. 'user' => 'User',
  103. 'account' => 'Account',
  104. );
  105. /**
  106. * @var array Object table mapping from type string
  107. *
  108. * @note Move to const array when PHP 5.6 will be our minimum target
  109. */
  110. private $MAP_OBJ_TABLE = array(
  111. 'product' => 'product',
  112. 'customer' => 'societe',
  113. 'supplier' => 'societe',
  114. 'member' => 'adherent',
  115. 'contact' => 'socpeople',
  116. 'user' => 'user',
  117. 'account' => 'bank_account',
  118. );
  119. public $element='category';
  120. public $table_element='categories';
  121. var $fk_parent;
  122. var $label;
  123. var $description;
  124. /**
  125. * @var string Color
  126. */
  127. var $color;
  128. /**
  129. * @var ???
  130. */
  131. var $socid;
  132. /**
  133. * @var int Category type
  134. *
  135. * @see Categorie::TYPE_PRODUCT
  136. * @see Categorie::TYPE_SUPPLIER
  137. * @see Categorie::TYPE_CUSTOMER
  138. * @see Categorie::TYPE_MEMBER
  139. * @see Categorie::TYPE_CONTACT
  140. * @see Categorie::TYPE_USER
  141. * @see Categorie::TYPE_ACCOUNT
  142. */
  143. var $type;
  144. var $cats=array(); // Categories table in memory
  145. var $motherof=array();
  146. /**
  147. * Constructor
  148. *
  149. * @param DoliDB $db Database handler
  150. */
  151. function __construct($db)
  152. {
  153. $this->db = $db;
  154. }
  155. /**
  156. * Load category into memory from database
  157. *
  158. * @param int $id Id of category
  159. * @param string $label Label of category
  160. * @param string $type Type of category
  161. * @return int <0 if KO, >0 if OK
  162. */
  163. function fetch($id,$label='',$type='')
  164. {
  165. global $conf;
  166. // Check parameters
  167. if (empty($id) && empty($label)) return -1;
  168. $sql = "SELECT rowid, fk_parent, entity, label, description, color, fk_soc, visible, type";
  169. $sql.= " FROM ".MAIN_DB_PREFIX."categorie";
  170. if ($id)
  171. {
  172. $sql.= " WHERE rowid = '".$id."'";
  173. }
  174. else
  175. {
  176. $sql.= " WHERE label = '".$this->db->escape($label)."' AND entity IN (".getEntity('category',1).")";
  177. if ($type) $sql.= " AND type = '".$this->db->escape($type)."'";
  178. }
  179. dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
  180. $resql = $this->db->query($sql);
  181. if ($resql)
  182. {
  183. if ($this->db->num_rows($resql) > 0)
  184. {
  185. $res = $this->db->fetch_array($resql);
  186. $this->id = $res['rowid'];
  187. $this->fk_parent = $res['fk_parent'];
  188. $this->label = $res['label'];
  189. $this->description = $res['description'];
  190. $this->color = $res['color'];
  191. $this->socid = $res['fk_soc'];
  192. $this->visible = $res['visible'];
  193. $this->type = $res['type'];
  194. $this->entity = $res['entity'];
  195. $this->fetch_optionals($this->id,null);
  196. $this->db->free($resql);
  197. // multilangs
  198. if (! empty($conf->global->MAIN_MULTILANGS)) $this->getMultiLangs();
  199. return 1;
  200. }
  201. else
  202. {
  203. return 0;
  204. }
  205. }
  206. else
  207. {
  208. dol_print_error($this->db);
  209. return -1;
  210. }
  211. }
  212. /**
  213. * Add category into database
  214. *
  215. * @param User $user Object user
  216. * @return int -1 : SQL error
  217. * -2 : new ID unknown
  218. * -3 : Invalid category
  219. * -4 : category already exists
  220. */
  221. function create($user)
  222. {
  223. global $conf,$langs,$hookmanager;
  224. $langs->load('categories');
  225. $error=0;
  226. dol_syslog(get_class($this).'::create', LOG_DEBUG);
  227. // Clean parameters
  228. $this->label = trim($this->label);
  229. $this->description = trim($this->description);
  230. $this->color = trim($this->color);
  231. $this->import_key = trim($this->import_key);
  232. if (empty($this->visible)) $this->visible=0;
  233. $this->fk_parent = ($this->fk_parent != "" ? intval($this->fk_parent) : 0);
  234. if ($this->already_exists())
  235. {
  236. $this->error=$langs->trans("ImpossibleAddCat", $this->label);
  237. $this->error.=" : ".$langs->trans("CategoryExistsAtSameLevel");
  238. dol_syslog($this->error, LOG_WARNING);
  239. return -4;
  240. }
  241. $this->db->begin();
  242. $sql = "INSERT INTO ".MAIN_DB_PREFIX."categorie (";
  243. $sql.= "fk_parent,";
  244. $sql.= " label,";
  245. $sql.= " description,";
  246. $sql.= " color,";
  247. if (! empty($conf->global->CATEGORY_ASSIGNED_TO_A_CUSTOMER))
  248. {
  249. $sql.= "fk_soc,";
  250. }
  251. $sql.= " visible,";
  252. $sql.= " type,";
  253. $sql.= " import_key,";
  254. $sql.= " entity";
  255. $sql.= ") VALUES (";
  256. $sql.= $this->fk_parent.",";
  257. $sql.= "'".$this->db->escape($this->label)."',";
  258. $sql.= "'".$this->db->escape($this->description)."',";
  259. $sql.= "'".$this->db->escape($this->color)."',";
  260. if (! empty($conf->global->CATEGORY_ASSIGNED_TO_A_CUSTOMER))
  261. {
  262. $sql.= ($this->socid != -1 ? $this->socid : 'null').",";
  263. }
  264. $sql.= "'".$this->visible."',";
  265. $sql.= $this->type.",";
  266. $sql.= (! empty($this->import_key)?"'".$this->db->escape($this->import_key)."'":'null').",";
  267. $sql.= $conf->entity;
  268. $sql.= ")";
  269. $res = $this->db->query($sql);
  270. if ($res)
  271. {
  272. $id = $this->db->last_insert_id(MAIN_DB_PREFIX."categorie");
  273. if ($id > 0)
  274. {
  275. $this->id = $id;
  276. $action='create';
  277. // Actions on extra fields (by external module or standard code)
  278. // TODO the hook duplicates the trigger !!
  279. $hookmanager->initHooks(array('HookModuleNamedao'));
  280. $parameters=array('socid'=>$this->id);
  281. $reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks
  282. if (empty($reshook))
  283. {
  284. if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
  285. {
  286. $result=$this->insertExtraFields();
  287. if ($result < 0)
  288. {
  289. $error++;
  290. }
  291. }
  292. }
  293. else if ($reshook < 0) $error++;
  294. // Call trigger
  295. $result=$this->call_trigger('CATEGORY_CREATE',$user);
  296. if ($result < 0) { $error++; }
  297. // End call triggers
  298. if ( ! $error )
  299. {
  300. $this->db->commit();
  301. return $id;
  302. }
  303. else
  304. {
  305. $this->db->rollback();
  306. return -3;
  307. }
  308. }
  309. else
  310. {
  311. $this->db->rollback();
  312. return -2;
  313. }
  314. }
  315. else
  316. {
  317. $this->error=$this->db->error();
  318. $this->db->rollback();
  319. return -1;
  320. }
  321. }
  322. /**
  323. * Update category
  324. *
  325. * @param User $user Object user
  326. * @return int 1 : OK
  327. * -1 : SQL error
  328. * -2 : invalid category
  329. */
  330. function update($user='')
  331. {
  332. global $conf, $langs,$hookmanager;
  333. $error=0;
  334. // Clean parameters
  335. $this->label=trim($this->label);
  336. $this->description=trim($this->description);
  337. $this->fk_parent = ($this->fk_parent != "" ? intval($this->fk_parent) : 0);
  338. $this->visible = ($this->visible != "" ? intval($this->visible) : 0);
  339. if ($this->already_exists())
  340. {
  341. $this->error=$langs->trans("ImpossibleUpdateCat");
  342. $this->error.=" : ".$langs->trans("CategoryExistsAtSameLevel");
  343. return -1;
  344. }
  345. $this->db->begin();
  346. $sql = "UPDATE ".MAIN_DB_PREFIX."categorie";
  347. $sql.= " SET label = '".$this->db->escape($this->label)."',";
  348. $sql.= " description = '".$this->db->escape($this->description)."',";
  349. $sql.= " color = '".$this->db->escape($this->color)."'";
  350. if (! empty($conf->global->CATEGORY_ASSIGNED_TO_A_CUSTOMER))
  351. {
  352. $sql .= ", fk_soc = ".($this->socid != -1 ? $this->socid : 'null');
  353. }
  354. $sql .= ", visible = '".$this->visible."'";
  355. $sql .= ", fk_parent = ".$this->fk_parent;
  356. $sql .= " WHERE rowid = ".$this->id;
  357. dol_syslog(get_class($this)."::update", LOG_DEBUG);
  358. if ($this->db->query($sql))
  359. {
  360. $action='update';
  361. // Actions on extra fields (by external module or standard code)
  362. // TODO the hook duplicates the trigger !!
  363. $hookmanager->initHooks(array('HookCategorydao'));
  364. $parameters=array();
  365. $reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks
  366. if (empty($reshook))
  367. {
  368. if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
  369. {
  370. $result=$this->insertExtraFields();
  371. if ($result < 0)
  372. {
  373. $error++;
  374. }
  375. }
  376. }
  377. else if ($reshook < 0) $error++;
  378. // Call trigger
  379. $result=$this->call_trigger('CATEGORY_MODIFY',$user);
  380. if ($result < 0) { $error++; $this->db->rollback(); return -1; }
  381. // End call triggers
  382. $this->db->commit();
  383. return 1;
  384. }
  385. else
  386. {
  387. $this->db->rollback();
  388. dol_print_error($this->db);
  389. return -1;
  390. }
  391. }
  392. /**
  393. * Delete a category from database
  394. *
  395. * @param User $user Object user that ask to delete
  396. * @return int <0 KO >0 OK
  397. */
  398. function delete($user)
  399. {
  400. global $conf,$langs;
  401. $error=0;
  402. // Clean parameters
  403. $this->fk_parent = ($this->fk_parent != "" ? intval($this->fk_parent) : 0);
  404. dol_syslog(get_class($this)."::remove");
  405. $this->db->begin();
  406. /* FIX #1317 : Check for child cat and move up 1 level*/
  407. if (! $error)
  408. {
  409. $sql = "UPDATE ".MAIN_DB_PREFIX."categorie";
  410. $sql.= " SET fk_parent = ".$this->fk_parent;
  411. $sql.= " WHERE fk_parent = ".$this->id;
  412. if (!$this->db->query($sql))
  413. {
  414. $this->error=$this->db->lasterror();
  415. $error++;
  416. }
  417. }
  418. if (! $error)
  419. {
  420. $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_societe";
  421. $sql .= " WHERE fk_categorie = ".$this->id;
  422. if (!$this->db->query($sql))
  423. {
  424. $this->error=$this->db->lasterror();
  425. $error++;
  426. }
  427. }
  428. if (! $error)
  429. {
  430. $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_fournisseur";
  431. $sql .= " WHERE fk_categorie = ".$this->id;
  432. if (!$this->db->query($sql))
  433. {
  434. $this->error=$this->db->lasterror();
  435. $error++;
  436. }
  437. }
  438. if (! $error)
  439. {
  440. $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_product";
  441. $sql .= " WHERE fk_categorie = ".$this->id;
  442. if (!$this->db->query($sql))
  443. {
  444. $this->error=$this->db->lasterror();
  445. $error++;
  446. }
  447. }
  448. if (! $error)
  449. {
  450. $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_member";
  451. $sql .= " WHERE fk_categorie = ".$this->id;
  452. if (!$this->db->query($sql))
  453. {
  454. $this->error=$this->db->lasterror();
  455. $error++;
  456. }
  457. }
  458. if (! $error)
  459. {
  460. $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_contact";
  461. $sql .= " WHERE fk_categorie = ".$this->id;
  462. if (!$this->db->query($sql))
  463. {
  464. $this->error=$this->db->lasterror();
  465. $error++;
  466. }
  467. }
  468. if (! $error)
  469. {
  470. $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_lang";
  471. $sql .= " WHERE fk_category = ".$this->id;
  472. if (!$this->db->query($sql))
  473. {
  474. $this->error=$this->db->lasterror();
  475. dol_syslog("Error sql=".$sql." ".$this->error, LOG_ERR);
  476. $error++;
  477. }
  478. }
  479. // Delete category
  480. if (! $error)
  481. {
  482. $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie";
  483. $sql .= " WHERE rowid = ".$this->id;
  484. if (!$this->db->query($sql))
  485. {
  486. $this->error=$this->db->lasterror();
  487. $error++;
  488. }
  489. else
  490. {
  491. // Removed extrafields
  492. if (! $error)
  493. {
  494. if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
  495. {
  496. $result=$this->deleteExtraFields();
  497. if ($result < 0)
  498. {
  499. $error++;
  500. dol_syslog(get_class($this)."::delete erreur ".$this->error, LOG_ERR);
  501. }
  502. }
  503. }
  504. // Call trigger
  505. $result=$this->call_trigger('CATEGORY_DELETE',$user);
  506. if ($result < 0) { $error++; }
  507. // End call triggers
  508. }
  509. }
  510. if (! $error)
  511. {
  512. $this->db->commit();
  513. return 1;
  514. }
  515. else
  516. {
  517. $this->db->rollback();
  518. return -1;
  519. }
  520. }
  521. /**
  522. * Link an object to the category
  523. *
  524. * @param CommonObject $obj Object to link to category
  525. * @param string $type Type of category ('customer', 'supplier', 'contact', 'product', 'member')
  526. *
  527. * @return int 1 : OK, -1 : erreur SQL, -2 : id not defined, -3 : Already linked
  528. */
  529. function add_type($obj,$type)
  530. {
  531. global $user,$langs,$conf;
  532. $error=0;
  533. if ($this->id == -1) return -2;
  534. // For backward compatibility
  535. if ($type == 'societe')
  536. {
  537. $type = 'customer';
  538. dol_syslog(get_class($this) . "::add_type(): type 'societe' is deprecated, please use 'customer' instead", LOG_WARNING);
  539. }
  540. elseif ($type == 'fournisseur')
  541. {
  542. $type = 'supplier';
  543. dol_syslog(get_class($this) . "::add_type(): type 'fournisseur' is deprecated, please use 'supplier' instead", LOG_WARNING);
  544. }
  545. $this->db->begin();
  546. $sql = "INSERT INTO " . MAIN_DB_PREFIX . "categorie_" . $this->MAP_CAT_TABLE[$type];
  547. $sql .= " (fk_categorie, fk_" . $this->MAP_CAT_FK[$type] . ")";
  548. $sql .= " VALUES (" . $this->id . ", " . $obj->id . ")";
  549. dol_syslog(get_class($this).'::add_type', LOG_DEBUG);
  550. if ($this->db->query($sql))
  551. {
  552. if (! empty($conf->global->CATEGORIE_RECURSIV_ADD))
  553. {
  554. $sql = 'SELECT fk_parent FROM '.MAIN_DB_PREFIX.'categorie';
  555. $sql.= " WHERE rowid = ".$this->id;
  556. dol_syslog(get_class($this)."::add_type", LOG_DEBUG);
  557. $resql=$this->db->query($sql);
  558. if ($resql)
  559. {
  560. if ($this->db->num_rows($resql) > 0)
  561. {
  562. $objparent = $this->db->fetch_object($resql);
  563. if (!empty($objparent->fk_parent))
  564. {
  565. $cat = new Categorie($this->db);
  566. $cat->id=$objparent->fk_parent;
  567. $result=$cat->add_type($obj, $type);
  568. if ($result < 0)
  569. {
  570. $this->error=$cat->error;
  571. $error++;
  572. }
  573. }
  574. }
  575. }
  576. else
  577. {
  578. $error++;
  579. $this->error=$this->db->lasterror();
  580. }
  581. if ($error)
  582. {
  583. $this->db->rollback();
  584. return -1;
  585. }
  586. }
  587. // Save object we want to link category to into category instance to provide information to trigger
  588. $this->linkto=$obj;
  589. // Call trigger
  590. $result=$this->call_trigger('CATEGORY_LINK',$user);
  591. if ($result < 0) { $error++; }
  592. // End call triggers
  593. if (! $error)
  594. {
  595. $this->db->commit();
  596. return 1;
  597. }
  598. else
  599. {
  600. $this->db->rollback();
  601. return -2;
  602. }
  603. }
  604. else
  605. {
  606. $this->db->rollback();
  607. if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS')
  608. {
  609. $this->error=$this->db->lasterrno();
  610. return -3;
  611. }
  612. else
  613. {
  614. $this->error=$this->db->lasterror();
  615. }
  616. return -1;
  617. }
  618. }
  619. /**
  620. * Delete object from category
  621. *
  622. * @param CommonObject $obj Object
  623. * @param string $type Type of category ('customer', 'supplier', 'contact', 'product', 'member')
  624. *
  625. * @return int 1 if OK, -1 if KO
  626. */
  627. function del_type($obj,$type)
  628. {
  629. global $user,$langs,$conf;
  630. $error=0;
  631. // For backward compatibility
  632. if ($type == 'societe') {
  633. $type = 'customer';
  634. dol_syslog( get_class( $this ) . "::del_type(): type 'societe' is deprecated, please use 'customer' instead",
  635. LOG_WARNING );
  636. } elseif ($type == 'fournisseur') {
  637. $type = 'supplier';
  638. dol_syslog( get_class( $this ) . "::del_type(): type 'fournisseur' is deprecated, please use 'supplier' instead",
  639. LOG_WARNING );
  640. }
  641. $this->db->begin();
  642. $sql = "DELETE FROM " . MAIN_DB_PREFIX . "categorie_" . $this->MAP_CAT_TABLE[$type];
  643. $sql .= " WHERE fk_categorie = " . $this->id;
  644. $sql .= " AND fk_" . $this->MAP_CAT_FK[$type] . " = " . $obj->id;
  645. dol_syslog(get_class($this).'::del_type', LOG_DEBUG);
  646. if ($this->db->query($sql))
  647. {
  648. // Save object we want to unlink category off into category instance to provide information to trigger
  649. $this->unlinkoff=$obj;
  650. // Call trigger
  651. $result=$this->call_trigger('CATEGORY_UNLINK',$user);
  652. if ($result < 0) { $error++; }
  653. // End call triggers
  654. if (! $error)
  655. {
  656. $this->db->commit();
  657. return 1;
  658. }
  659. else
  660. {
  661. $this->db->rollback();
  662. return -2;
  663. }
  664. }
  665. else
  666. {
  667. $this->db->rollback();
  668. $this->error=$this->db->lasterror();
  669. return -1;
  670. }
  671. }
  672. /**
  673. * Return list of fetched instance of elements having this category
  674. *
  675. * @param string $type Type of category ('customer', 'supplier', 'contact', 'product', 'member')
  676. *
  677. * @return mixed -1 if KO, array of instance of object if OK
  678. */
  679. function getObjectsInCateg($type)
  680. {
  681. $objs = array();
  682. $obj = new $this->MAP_OBJ_CLASS[$type]( $this->db );
  683. $sql = "SELECT c.fk_" . $this->MAP_CAT_FK[$type];
  684. $sql .= " FROM " . MAIN_DB_PREFIX . "categorie_" . $this->MAP_CAT_TABLE[$type] . " as c";
  685. $sql .= ", " . MAIN_DB_PREFIX . $this->MAP_OBJ_TABLE[$type] . " as o";
  686. $sql .= " WHERE o.entity IN (" . getEntity( $obj->element, 1).")";
  687. $sql.= " AND c.fk_categorie = ".$this->id;
  688. $sql .= " AND c.fk_" . $this->MAP_CAT_FK[$type] . " = o.rowid";
  689. dol_syslog(get_class($this)."::getObjectsInCateg", LOG_DEBUG);
  690. $resql = $this->db->query($sql);
  691. if ($resql)
  692. {
  693. while ($rec = $this->db->fetch_array($resql)) {
  694. $obj = new $this->MAP_OBJ_CLASS[$type]( $this->db );
  695. $obj->fetch( $rec['fk_' . $this->MAP_CAT_FK[$type]]);
  696. $objs[] = $obj;
  697. }
  698. return $objs;
  699. }
  700. else
  701. {
  702. $this->error=$this->db->error().' sql='.$sql;
  703. return -1;
  704. }
  705. }
  706. /**
  707. * Check for the presence of an object in a category
  708. *
  709. * @param string $type Type of category ('customer', 'supplier', 'contact', 'product', 'member')
  710. * @param int $object_id id of the object to search
  711. *
  712. * @return int number of occurrences
  713. */
  714. function containsObject($type, $object_id )
  715. {
  716. $sql = "SELECT COUNT(*) as nb FROM " . MAIN_DB_PREFIX . "categorie_" . $this->MAP_CAT_TABLE[$type];
  717. $sql .= " WHERE fk_categorie = " . $this->id . " AND fk_" . $this->MAP_CAT_FK[$type] . " = " . $object_id;
  718. dol_syslog(get_class($this)."::containsObject", LOG_DEBUG);
  719. $resql = $this->db->query($sql);
  720. if ($resql) {
  721. return $this->db->fetch_object($resql)->nb;
  722. } else {
  723. $this->error=$this->db->error().' sql='.$sql;
  724. return -1;
  725. }
  726. }
  727. /**
  728. * Return childs of a category
  729. *
  730. * @return array|int <0 KO, array ok
  731. */
  732. function get_filles()
  733. {
  734. $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."categorie";
  735. $sql.= " WHERE fk_parent = ".$this->id;
  736. $res = $this->db->query($sql);
  737. if ($res)
  738. {
  739. $cats = array ();
  740. while ($rec = $this->db->fetch_array($res))
  741. {
  742. $cat = new Categorie($this->db);
  743. $cat->fetch($rec['rowid']);
  744. $cats[] = $cat;
  745. }
  746. return $cats;
  747. }
  748. else
  749. {
  750. dol_print_error($this->db);
  751. return -1;
  752. }
  753. }
  754. /**
  755. * Load this->motherof that is array(id_son=>id_parent, ...)
  756. *
  757. * @return int <0 if KO, >0 if OK
  758. */
  759. private function load_motherof()
  760. {
  761. global $conf;
  762. $this->motherof=array();
  763. // Load array[child]=parent
  764. $sql = "SELECT fk_parent as id_parent, rowid as id_son";
  765. $sql.= " FROM ".MAIN_DB_PREFIX."categorie";
  766. $sql.= " WHERE fk_parent != 0";
  767. $sql.= " AND entity IN (".getEntity('category',1).")";
  768. dol_syslog(get_class($this)."::load_motherof", LOG_DEBUG);
  769. $resql = $this->db->query($sql);
  770. if ($resql)
  771. {
  772. while ($obj= $this->db->fetch_object($resql))
  773. {
  774. $this->motherof[$obj->id_son]=$obj->id_parent;
  775. }
  776. return 1;
  777. }
  778. else
  779. {
  780. dol_print_error($this->db);
  781. return -1;
  782. }
  783. }
  784. /**
  785. * Rebuilding the category tree as an array
  786. * Return an array of table('id','id_mere',...) trie selon arbre et avec:
  787. * id = id de la categorie
  788. * id_mere = id de la categorie mere
  789. * id_children = tableau des id enfant
  790. * label = nom de la categorie
  791. * fulllabel = nom avec chemin complet de la categorie
  792. * fullpath = chemin complet compose des id
  793. *
  794. * @param string $type Type of categories ('customer', 'supplier', 'contact', 'product', 'member').
  795. * Old mode (0, 1, 2, ...) is deprecated.
  796. * @param int $markafterid Removed all categories including the leaf $markafterid in category tree.
  797. *
  798. * @return array Array of categories. this->cats and this->motherof are set.
  799. */
  800. function get_full_arbo($type,$markafterid=0)
  801. {
  802. global $conf, $langs;
  803. // For backward compatibility
  804. if (is_numeric($type))
  805. {
  806. // We want to reverse lookup
  807. $map_type = array_flip($this->MAP_ID);
  808. $type = $map_type[$type];
  809. dol_syslog( get_class( $this ) . "::get_full_arbo(): numeric types are deprecated, please use string instead", LOG_WARNING);
  810. }
  811. $this->cats = array();
  812. // Init this->motherof that is array(id_son=>id_parent, ...)
  813. $this->load_motherof();
  814. $current_lang = $langs->getDefaultLang();
  815. // Init $this->cats array
  816. $sql = "SELECT DISTINCT c.rowid, c.label, c.description, c.color, c.fk_parent"; // Distinct reduce pb with old tables with duplicates
  817. if (! empty($conf->global->MAIN_MULTILANGS)) $sql.= ", t.label as label_trans, t.description as description_trans";
  818. $sql.= " FROM ".MAIN_DB_PREFIX."categorie as c";
  819. 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='".$current_lang."'";
  820. $sql .= " WHERE c.entity IN (" . getEntity( 'category', 1 ) . ")";
  821. $sql .= " AND c.type = " . $this->MAP_ID[$type];
  822. dol_syslog(get_class($this)."::get_full_arbo get category list", LOG_DEBUG);
  823. $resql = $this->db->query($sql);
  824. if ($resql)
  825. {
  826. $i=0;
  827. while ($obj = $this->db->fetch_object($resql))
  828. {
  829. $this->cats[$obj->rowid]['rowid'] = $obj->rowid;
  830. $this->cats[$obj->rowid]['id'] = $obj->rowid;
  831. $this->cats[$obj->rowid]['fk_parent'] = $obj->fk_parent;
  832. $this->cats[$obj->rowid]['label'] = ! empty($obj->label_trans) ? $obj->label_trans : $obj->label;
  833. $this->cats[$obj->rowid]['description'] = ! empty($obj->description_trans) ? $obj->description_trans : $obj->description;
  834. $this->cats[$obj->rowid]['color'] = $obj->color;
  835. $i++;
  836. }
  837. }
  838. else
  839. {
  840. dol_print_error($this->db);
  841. return -1;
  842. }
  843. // We add the fullpath property to each elements of first level (no parent exists)
  844. dol_syslog(get_class($this)."::get_full_arbo call to build_path_from_id_categ", LOG_DEBUG);
  845. foreach($this->cats as $key => $val)
  846. {
  847. //print 'key='.$key.'<br>'."\n";
  848. $this->build_path_from_id_categ($key,0); // Process a branch from the root category key (this category has no parent)
  849. }
  850. // Exclude leaf including $markafterid from tree
  851. if ($markafterid)
  852. {
  853. //print "Look to discard category ".$markafterid."\n";
  854. $keyfilter1='^'.$markafterid.'$';
  855. $keyfilter2='_'.$markafterid.'$';
  856. $keyfilter3='^'.$markafterid.'_';
  857. $keyfilter4='_'.$markafterid.'_';
  858. foreach($this->cats as $key => $val)
  859. {
  860. if (preg_match('/'.$keyfilter1.'/',$val['fullpath']) || preg_match('/'.$keyfilter2.'/',$val['fullpath'])
  861. || preg_match('/'.$keyfilter3.'/',$val['fullpath']) || preg_match('/'.$keyfilter4.'/',$val['fullpath']))
  862. {
  863. unset($this->cats[$key]);
  864. }
  865. }
  866. }
  867. dol_syslog(get_class($this)."::get_full_arbo dol_sort_array", LOG_DEBUG);
  868. $this->cats=dol_sort_array($this->cats, 'fulllabel', 'asc', true, false);
  869. //$this->debug_cats();
  870. return $this->cats;
  871. }
  872. /**
  873. * For category id_categ and its childs available in this->cats, define property fullpath and fulllabel
  874. *
  875. * @param int $id_categ id_categ entry to update
  876. * @param int $protection Deep counter to avoid infinite loop
  877. * @return void
  878. */
  879. function build_path_from_id_categ($id_categ,$protection=1000)
  880. {
  881. dol_syslog(get_class($this)."::build_path_from_id_categ id_categ=".$id_categ." protection=".$protection, LOG_DEBUG);
  882. if (! empty($this->cats[$id_categ]['fullpath']))
  883. {
  884. // Already defined
  885. dol_syslog(get_class($this)."::build_path_from_id_categ fullpath and fulllabel already defined", LOG_WARNING);
  886. return;
  887. }
  888. // First build full array $motherof
  889. //$this->load_motherof(); // Disabled because already done by caller of build_path_from_id_categ
  890. // Define fullpath and fulllabel
  891. $this->cats[$id_categ]['fullpath'] = '_'.$id_categ;
  892. $this->cats[$id_categ]['fulllabel'] = $this->cats[$id_categ]['label'];
  893. $i=0; $cursor_categ=$id_categ;
  894. //print 'Work for id_categ='.$id_categ.'<br>'."\n";
  895. while ((empty($protection) || $i < $protection) && ! empty($this->motherof[$cursor_categ]))
  896. {
  897. //print '&nbsp; cursor_categ='.$cursor_categ.' i='.$i.' '.$this->motherof[$cursor_categ].'<br>'."\n";
  898. $this->cats[$id_categ]['fullpath'] = '_'.$this->motherof[$cursor_categ].$this->cats[$id_categ]['fullpath'];
  899. $this->cats[$id_categ]['fulllabel'] = $this->cats[$this->motherof[$cursor_categ]]['label'].' >> '.$this->cats[$id_categ]['fulllabel'];
  900. //print '&nbsp; Result for id_categ='.$id_categ.' : '.$this->cats[$id_categ]['fullpath'].' '.$this->cats[$id_categ]['fulllabel'].'<br>'."\n";
  901. $i++; $cursor_categ=$this->motherof[$cursor_categ];
  902. }
  903. //print 'Result for id_categ='.$id_categ.' : '.$this->cats[$id_categ]['fullpath'].'<br>'."\n";
  904. // We count number of _ to have level
  905. $this->cats[$id_categ]['level']=dol_strlen(preg_replace('/[^_]/i','',$this->cats[$id_categ]['fullpath']));
  906. return;
  907. }
  908. /**
  909. * Affiche contenu de $this->cats
  910. *
  911. * @return void
  912. */
  913. function debug_cats()
  914. {
  915. // Affiche $this->cats
  916. foreach($this->cats as $key => $val)
  917. {
  918. print 'id: '.$this->cats[$key]['id'];
  919. print ' label: '.$this->cats[$key]['label'];
  920. print ' mother: '.$this->cats[$key]['fk_parent'];
  921. //print ' children: '.(is_array($this->cats[$key]['id_children'])?join(',',$this->cats[$key]['id_children']):'');
  922. print ' fullpath: '.$this->cats[$key]['fullpath'];
  923. print ' fulllabel: '.$this->cats[$key]['fulllabel'];
  924. print "<br>\n";
  925. }
  926. }
  927. /**
  928. * Retourne toutes les categories
  929. *
  930. * @param int $type Type of category
  931. * @param boolean $parent Just parent categories if true
  932. * @return array Tableau d'objet Categorie
  933. */
  934. function get_all_categories($type=null, $parent=false)
  935. {
  936. $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."categorie";
  937. $sql.= " WHERE entity IN (".getEntity('category',1).")";
  938. if (! is_null($type))
  939. $sql.= " AND type = ".$type;
  940. if ($parent)
  941. $sql.= " AND fk_parent = 0";
  942. $res = $this->db->query($sql);
  943. if ($res)
  944. {
  945. $cats = array ();
  946. while ($rec = $this->db->fetch_array($res))
  947. {
  948. $cat = new Categorie($this->db);
  949. $cat->fetch($rec['rowid']);
  950. $cats[$rec['rowid']] = $cat;
  951. }
  952. return $cats;
  953. }
  954. else
  955. {
  956. dol_print_error($this->db);
  957. return -1;
  958. }
  959. }
  960. /**
  961. * Retourne le nombre total de categories
  962. *
  963. * @return int Nombre de categories
  964. * @deprecated function not used ?
  965. */
  966. function get_nb_categories()
  967. {
  968. $sql = "SELECT count(rowid)";
  969. $sql.= " FROM ".MAIN_DB_PREFIX."categorie";
  970. $sql.= " WHERE entity IN (".getEntity('category',1).")";
  971. $res = $this->db->query($sql);
  972. if ($res)
  973. {
  974. $res = $this->db->fetch_array($res);
  975. return $res[0];
  976. }
  977. else
  978. {
  979. dol_print_error($this->db);
  980. return -1;
  981. }
  982. }
  983. /**
  984. * Check if no category with same label already exists for this cat's parent or root and for this cat's type
  985. *
  986. * @return integer 1 if already exist, 0 otherwise, -1 if error
  987. */
  988. function already_exists()
  989. {
  990. /* We have to select any rowid from llx_categorie which category's mother and label
  991. * are equals to those of the calling category
  992. */
  993. $sql = "SELECT c.rowid";
  994. $sql.= " FROM ".MAIN_DB_PREFIX."categorie as c ";
  995. $sql.= " WHERE c.entity IN (".getEntity('category',1).")";
  996. $sql.= " AND c.type = ".$this->type;
  997. $sql.= " AND c.fk_parent = ".$this->fk_parent;
  998. $sql.= " AND c.label = '".$this->db->escape($this->label)."'";
  999. dol_syslog(get_class($this)."::already_exists", LOG_DEBUG);
  1000. $resql = $this->db->query($sql);
  1001. if ($resql)
  1002. {
  1003. if ($this->db->num_rows($resql) > 0) // Checking for empty resql
  1004. {
  1005. $obj = $this->db->fetch_array($resql);
  1006. /* If object called create, obj cannot have is id.
  1007. * If object called update, he mustn't have the same label as an other category for this mother.
  1008. * So if the result have the same id, update is not for label, and if result have an other one,
  1009. * update may be for label.
  1010. */
  1011. if($obj[0] > 0 && $obj[0] != $this->id)
  1012. {
  1013. dol_syslog(get_class($this)."::already_exists category with name=".$this->label." exist rowid=".$obj[0]." current_id=".$this->id, LOG_DEBUG);
  1014. return 1;
  1015. }
  1016. }
  1017. dol_syslog(get_class($this)."::already_exists no category with same name=".$this->label." rowid=".$obj[0]." current_id=".$this->id, LOG_DEBUG);
  1018. return 0;
  1019. }
  1020. else
  1021. {
  1022. $this->error=$this->db->error();
  1023. return -1;
  1024. }
  1025. }
  1026. /**
  1027. * Retourne les categories de premier niveau (qui ne sont pas filles)
  1028. *
  1029. * @param int $type Type of category
  1030. * @return array
  1031. */
  1032. function get_main_categories($type=null)
  1033. {
  1034. return $this->get_all_categories($type, true);
  1035. }
  1036. /**
  1037. * Retourne les chemin de la categorie, avec les noms des categories
  1038. * separes par $sep (" >> " par defaut)
  1039. *
  1040. * @param string $sep Separator
  1041. * @param string $url Url
  1042. * @param int $nocolor 0
  1043. * @return array
  1044. */
  1045. function print_all_ways($sep = " &gt;&gt; ", $url='', $nocolor=0)
  1046. {
  1047. $ways = array();
  1048. $allways = $this->get_all_ways(); // Load array of categories
  1049. foreach ($allways as $way)
  1050. {
  1051. $w = array();
  1052. $i = 0;
  1053. foreach ($way as $cat)
  1054. {
  1055. $i++;
  1056. if (empty($nocolor))
  1057. {
  1058. $forced_color='toreplace';
  1059. if ($i == count($way))
  1060. {
  1061. // Check contrast with background and correct text color
  1062. $forced_color='categtextwhite';
  1063. if ($cat->color)
  1064. {
  1065. $hex=$cat->color;
  1066. $r = hexdec($hex[0].$hex[1]);
  1067. $g = hexdec($hex[2].$hex[3]);
  1068. $b = hexdec($hex[4].$hex[5]);
  1069. $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
  1070. if ($bright >= 0.5) $forced_color='categtextblack'; // Higher than 60%
  1071. }
  1072. }
  1073. }
  1074. if ($url == '')
  1075. {
  1076. $link = '<a href="'.DOL_URL_ROOT.'/categories/viewcat.php?id='.$cat->id.'&type='.$cat->type.'" class="'.$forced_color .'">';
  1077. $linkend='</a>';
  1078. $w[] = $link.$cat->label.$linkend;
  1079. }
  1080. else
  1081. {
  1082. $w[] = "<a href='".DOL_URL_ROOT."/$url?catid=".$cat->id."'>".$cat->label."</a>";
  1083. }
  1084. }
  1085. $newcategwithpath = preg_replace('/toreplace/', $forced_color, implode($sep, $w));
  1086. $ways[] = $newcategwithpath;
  1087. }
  1088. return $ways;
  1089. }
  1090. /**
  1091. * Retourne un tableau contenant la liste des categories meres
  1092. *
  1093. * @return int|array <0 KO, array OK
  1094. */
  1095. function get_meres()
  1096. {
  1097. $parents = array();
  1098. $sql = "SELECT fk_parent FROM ".MAIN_DB_PREFIX."categorie";
  1099. $sql.= " WHERE rowid = ".$this->id;
  1100. $res = $this->db->query($sql);
  1101. if ($res)
  1102. {
  1103. while ($rec = $this->db->fetch_array($res))
  1104. {
  1105. if ($rec['fk_parent'] > 0)
  1106. {
  1107. $cat = new Categorie($this->db);
  1108. $cat->fetch($rec['fk_parent']);
  1109. $parents[] = $cat;
  1110. }
  1111. }
  1112. return $parents;
  1113. }
  1114. else
  1115. {
  1116. dol_print_error($this->db);
  1117. return -1;
  1118. }
  1119. }
  1120. /**
  1121. * Retourne dans un tableau tous les chemins possibles pour arriver a la categorie
  1122. * en partant des categories principales, representes par des tableaux de categories
  1123. *
  1124. * @return array
  1125. */
  1126. function get_all_ways()
  1127. {
  1128. $ways = array();
  1129. $parents=$this->get_meres();
  1130. if (! empty($parents))
  1131. {
  1132. foreach ($parents as $parent)
  1133. {
  1134. $allways=$parent->get_all_ways();
  1135. foreach ($allways as $way)
  1136. {
  1137. $w = $way;
  1138. $w[] = $this;
  1139. $ways[] = $w;
  1140. }
  1141. }
  1142. }
  1143. if (count($ways) == 0)
  1144. $ways[0][0] = $this;
  1145. return $ways;
  1146. }
  1147. /**
  1148. * Return list of categories (object instances or labels) linked to element of id $id and type $type
  1149. * Should be named getListOfCategForObject
  1150. *
  1151. * @param int $id Id of element
  1152. * @param string $type Type of category ('customer', 'supplier', 'contact', 'product', 'member'). Old mode (0, 1, 2, ...) is deprecated.
  1153. * @param string $mode 'id'=Get array of category ids, 'object'=Get array of fetched category instances, 'label'=Get array of category
  1154. * labels, 'id'= Get array of category IDs
  1155. * @return mixed Array of category objects or < 0 if KO
  1156. */
  1157. function containing($id, $type, $mode='object')
  1158. {
  1159. $cats = array();
  1160. // For backward compatibility
  1161. if (is_numeric($type))
  1162. {
  1163. dol_syslog(__METHOD__ . ': using numeric value for parameter type is deprecated. Use string code instead.', LOG_WARNING);
  1164. // We want to reverse lookup
  1165. $map_type = array_flip($this->MAP_ID);
  1166. $type = $map_type[$type];
  1167. }
  1168. $sql = "SELECT ct.fk_categorie, c.label, c.rowid";
  1169. $sql .= " FROM " . MAIN_DB_PREFIX . "categorie_" . $this->MAP_CAT_TABLE[$type] . " as ct, " . MAIN_DB_PREFIX . "categorie as c";
  1170. $sql .= " WHERE ct.fk_categorie = c.rowid AND ct.fk_" . $this->MAP_CAT_FK[$type] . " = " . (int) $id . " AND c.type = " . $this->MAP_ID[$type];
  1171. $sql .= " AND c.entity IN (" . getEntity( 'category', 1 ) . ")";
  1172. $res = $this->db->query($sql);
  1173. if ($res)
  1174. {
  1175. while ($obj = $this->db->fetch_object($res))
  1176. {
  1177. if ($mode == 'id') {
  1178. $cats[] = $obj->rowid;
  1179. } else if ($mode == 'label') {
  1180. $cats[] = $obj->label;
  1181. } else {
  1182. $cat = new Categorie($this->db);
  1183. $cat->fetch($obj->fk_categorie);
  1184. $cats[] = $cat;
  1185. }
  1186. }
  1187. return $cats;
  1188. }
  1189. else
  1190. {
  1191. dol_print_error($this->db);
  1192. return -1;
  1193. }
  1194. }
  1195. /**
  1196. * Retourne les categories dont l'id ou le nom correspond
  1197. * ajoute des wildcards au nom sauf si $exact = true
  1198. *
  1199. * @param int $id Id
  1200. * @param string $nom Name
  1201. * @param string $type Type of category ('member', 'customer', 'supplier', 'product', 'contact'). Old mode (0, 1, 2, ...) is deprecated.
  1202. * @param boolean $exact Exact string search (true/false)
  1203. * @param boolean $case Case sensitive (true/false)
  1204. * @return array Array of category id
  1205. */
  1206. function rechercher($id, $nom, $type, $exact = false, $case = false)
  1207. {
  1208. // Deprecation warning
  1209. if (is_numeric($type)) {
  1210. dol_syslog(__METHOD__ . ': using numeric types is deprecated.', LOG_WARNING);
  1211. }
  1212. $cats = array();
  1213. // For backward compatibility
  1214. if (is_numeric( $type )) {
  1215. // We want to reverse lookup
  1216. $map_type = array_flip( $this->MAP_ID );
  1217. $type = $map_type[$type];
  1218. dol_syslog( get_class( $this ) . "::rechercher(): numeric types are deprecated, please use string instead",
  1219. LOG_WARNING );
  1220. }
  1221. // Generation requete recherche
  1222. $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "categorie";
  1223. $sql .= " WHERE type = " . $this->MAP_ID[$type];
  1224. $sql .= " AND entity IN (" . getEntity( 'category', 1 ) . ")";
  1225. if ($nom)
  1226. {
  1227. if (! $exact)
  1228. $nom = '%'.str_replace('*', '%', $nom).'%';
  1229. if (! $case)
  1230. $sql.= " AND label LIKE '".$this->db->escape($nom)."'";
  1231. else
  1232. $sql.= " AND label LIKE BINARY '".$this->db->escape($nom)."'";
  1233. }
  1234. if ($id)
  1235. {
  1236. $sql.=" AND rowid = '".$id."'";
  1237. }
  1238. $res = $this->db->query($sql);
  1239. if ($res)
  1240. {
  1241. while ($rec = $this->db->fetch_array($res))
  1242. {
  1243. $cat = new Categorie($this->db);
  1244. $cat->fetch($rec['rowid']);
  1245. $cats[] = $cat;
  1246. }
  1247. return $cats;
  1248. }
  1249. else
  1250. {
  1251. $this->error=$this->db->error().' sql='.$sql;
  1252. return -1;
  1253. }
  1254. }
  1255. /**
  1256. * Return name and link of category (with picto)
  1257. * Use ->id, ->ref, ->label, ->color
  1258. *
  1259. * @param int $withpicto 0=No picto, 1=Include picto into link, 2=Only picto
  1260. * @param string $option Sur quoi pointe le lien ('', 'xyz')
  1261. * @param int $maxlength Max length of text
  1262. * @return string Chaine avec URL
  1263. */
  1264. function getNomUrl($withpicto=0,$option='',$maxlength=0)
  1265. {
  1266. global $langs;
  1267. $result='';
  1268. $label=$langs->trans("ShowCategory").': '. ($this->ref?$this->ref:$this->label);
  1269. // Check contrast with background and correct text color
  1270. $forced_color='categtextwhite';
  1271. if ($this->color)
  1272. {
  1273. $hex=$this->color;
  1274. $r = hexdec($hex[0].$hex[1]);
  1275. $g = hexdec($hex[2].$hex[3]);
  1276. $b = hexdec($hex[4].$hex[5]);
  1277. $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
  1278. if ($bright >= 0.5) $forced_color='categtextblack'; // Higher than 60%
  1279. }
  1280. $link = '<a href="'.DOL_URL_ROOT.'/categories/viewcat.php?id='.$this->id.'&type='.$this->type.'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip '.$forced_color .'">';
  1281. $linkend='</a>';
  1282. $picto='category';
  1283. if ($withpicto) $result.=($link.img_object($label, $picto, 'class="classfortooltip"').$linkend);
  1284. if ($withpicto && $withpicto != 2) $result.=' ';
  1285. if ($withpicto != 2) $result.=$link.dol_trunc(($this->ref?$this->ref:$this->label),$maxlength).$linkend;
  1286. return $result;
  1287. }
  1288. /**
  1289. * Deplace fichier uploade sous le nom $files dans le repertoire sdir
  1290. *
  1291. * @param string $sdir Repertoire destination finale
  1292. * @param string $file Nom du fichier uploade
  1293. * @return void
  1294. */
  1295. function add_photo($sdir, $file)
  1296. {
  1297. require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  1298. $dir = $sdir .'/'. get_exdir($this->id,2,0,0,$this,'category') . $this->id ."/";
  1299. $dir .= "photos/";
  1300. if (! file_exists($dir))
  1301. {
  1302. dol_mkdir($dir);
  1303. }
  1304. if (file_exists($dir))
  1305. {
  1306. $originImage = $dir . $file['name'];
  1307. // Cree fichier en taille origine
  1308. dol_move_uploaded_file($file['tmp_name'], $originImage, 1, 0, 0);
  1309. if (file_exists($originImage))
  1310. {
  1311. // Create thumbs
  1312. $this->addThumbs($originImage);
  1313. }
  1314. }
  1315. }
  1316. /**
  1317. * Return tableau de toutes les photos de la categorie
  1318. *
  1319. * @param string $dir Repertoire a scanner
  1320. * @param int $nbmax Nombre maximum de photos (0=pas de max)
  1321. * @return array Tableau de photos
  1322. */
  1323. function liste_photos($dir,$nbmax=0)
  1324. {
  1325. include_once DOL_DOCUMENT_ROOT .'/core/lib/files.lib.php';
  1326. $nbphoto=0;
  1327. $tabobj=array();
  1328. $dirthumb = $dir.'thumbs/';
  1329. if (file_exists($dir))
  1330. {
  1331. $handle=opendir($dir);
  1332. if (is_resource($handle))
  1333. {
  1334. while (($file = readdir($handle)) !== false)
  1335. {
  1336. if (dol_is_file($dir.$file) && preg_match('/(\.jpg|\.bmp|\.gif|\.png|\.tiff)$/i',$dir.$file))
  1337. {
  1338. $nbphoto++;
  1339. $photo = $file;
  1340. // On determine nom du fichier vignette
  1341. $photo_vignette='';
  1342. if (preg_match('/(\.jpg|\.bmp|\.gif|\.png|\.tiff)$/i',$photo,$regs))
  1343. {
  1344. $photo_vignette=preg_replace('/'.$regs[0].'/i','',$photo).'_small'.$regs[0];
  1345. }
  1346. // Objet
  1347. $obj=array();
  1348. $obj['photo']=$photo;
  1349. if ($photo_vignette && is_file($dirthumb.$photo_vignette)) $obj['photo_vignette']='thumbs/' . $photo_vignette;
  1350. else $obj['photo_vignette']="";
  1351. $tabobj[$nbphoto-1]=$obj;
  1352. // On continue ou on arrete de boucler
  1353. if ($nbmax && $nbphoto >= $nbmax) break;
  1354. }
  1355. }
  1356. closedir($handle);
  1357. }
  1358. }
  1359. return $tabobj;
  1360. }
  1361. /**
  1362. * Efface la photo de la categorie et sa vignette
  1363. *
  1364. * @param string $file Path to file
  1365. * @return void
  1366. */
  1367. function delete_photo($file)
  1368. {
  1369. require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  1370. $dir = dirname($file).'/'; // Chemin du dossier contenant l'image d'origine
  1371. $dirthumb = $dir.'/thumbs/'; // Chemin du dossier contenant la vignette
  1372. $filename = preg_replace('/'.preg_quote($dir,'/').'/i','',$file); // Nom du fichier
  1373. // On efface l'image d'origine
  1374. dol_delete_file($file,1);
  1375. // Si elle existe, on efface la vignette
  1376. if (preg_match('/(\.jpg|\.bmp|\.gif|\.png|\.tiff)$/i',$filename,$regs))
  1377. {
  1378. $photo_vignette=preg_replace('/'.$regs[0].'/i','',$filename).'_small'.$regs[0];
  1379. if (file_exists($dirthumb.$photo_vignette))
  1380. {
  1381. dol_delete_file($dirthumb.$photo_vignette,1);
  1382. }
  1383. }
  1384. }
  1385. /**
  1386. * Load size of image file
  1387. *
  1388. * @param string $file Path to file
  1389. * @return void
  1390. */
  1391. function get_image_size($file)
  1392. {
  1393. $infoImg = getimagesize($file); // Recuperation des infos de l'image
  1394. $this->imgWidth = $infoImg[0]; // Largeur de l'image
  1395. $this->imgHeight = $infoImg[1]; // Hauteur de l'image
  1396. }
  1397. /**
  1398. * Update ou cree les traductions des infos produits
  1399. *
  1400. * @param User $user Object user
  1401. *
  1402. * @return int <0 if KO, >0 if OK
  1403. */
  1404. function setMultiLangs($user)
  1405. {
  1406. global $langs;
  1407. $langs_available = $langs->get_available_languages();
  1408. $current_lang = $langs->getDefaultLang();
  1409. foreach ($langs_available as $key => $value)
  1410. {
  1411. $sql = "SELECT rowid";
  1412. $sql.= " FROM ".MAIN_DB_PREFIX."categorie_lang";
  1413. $sql.= " WHERE fk_category=".$this->id;
  1414. $sql.= " AND lang='".$key."'";
  1415. $result = $this->db->query($sql);
  1416. if ($key == $current_lang)
  1417. {
  1418. if ($this->db->num_rows($result)) // si aucune ligne dans la base
  1419. {
  1420. $sql2 = "UPDATE ".MAIN_DB_PREFIX."categorie_lang";
  1421. $sql2.= " SET label='".$this->db->escape($this->label)."',";
  1422. $sql2.= " description='".$this->db->escape($this->description)."'";
  1423. $sql2.= " WHERE fk_category=".$this->id." AND lang='".$key."'";
  1424. }
  1425. else
  1426. {
  1427. $sql2 = "INSERT INTO ".MAIN_DB_PREFIX."categorie_lang (fk_category, lang, label, description)";
  1428. $sql2.= " VALUES(".$this->id.",'".$key."','". $this->db->escape($this->label);
  1429. $sql2.= "','".$this->db->escape($this->multilangs["$key"]["description"])."')";
  1430. }
  1431. dol_syslog(get_class($this).'::setMultiLangs', LOG_DEBUG);
  1432. if (! $this->db->query($sql2))
  1433. {
  1434. $this->error=$this->db->lasterror();
  1435. return -1;
  1436. }
  1437. }
  1438. else if (isset($this->multilangs["$key"]))
  1439. {
  1440. if ($this->db->num_rows($result)) // si aucune ligne dans la base
  1441. {
  1442. $sql2 = "UPDATE ".MAIN_DB_PREFIX."categorie_lang";
  1443. $sql2.= " SET label='".$this->db->escape($this->multilangs["$key"]["label"])."',";
  1444. $sql2.= " description='".$this->db->escape($this->multilangs["$key"]["description"])."'";
  1445. $sql2.= " WHERE fk_category=".$this->id." AND lang='".$key."'";
  1446. }
  1447. else
  1448. {
  1449. $sql2 = "INSERT INTO ".MAIN_DB_PREFIX."categorie_lang (fk_category, lang, label, description)";
  1450. $sql2.= " VALUES(".$this->id.",'".$key."','". $this->db->escape($this->multilangs["$key"]["label"]);
  1451. $sql2.= "','".$this->db->escape($this->multilangs["$key"]["description"])."')";
  1452. }
  1453. // on ne sauvegarde pas des champs vides
  1454. if ( $this->multilangs["$key"]["label"] || $this->multilangs["$key"]["description"] || $this->multilangs["$key"]["note"] )
  1455. dol_syslog(get_class($this).'::setMultiLangs', LOG_DEBUG);
  1456. if (! $this->db->query($sql2))
  1457. {
  1458. $this->error=$this->db->lasterror();
  1459. return -1;
  1460. }
  1461. }
  1462. }
  1463. // Call trigger
  1464. $result = $this->call_trigger('CATEGORY_SET_MULTILANGS',$user);
  1465. if ($result < 0) {
  1466. $this->error = $this->db->lasterror();
  1467. return -1;
  1468. }
  1469. // End call triggers
  1470. return 1;
  1471. }
  1472. /**
  1473. * Load array this->multilangs
  1474. *
  1475. * @return int <0 if KO, >0 if OK
  1476. */
  1477. function getMultiLangs()
  1478. {
  1479. global $langs;
  1480. $current_lang = $langs->getDefaultLang();
  1481. $sql = "SELECT lang, label, description";
  1482. $sql.= " FROM ".MAIN_DB_PREFIX."categorie_lang";
  1483. $sql.= " WHERE fk_category=".$this->id;
  1484. $result = $this->db->query($sql);
  1485. if ($result)
  1486. {
  1487. while ( $obj = $this->db->fetch_object($result) )
  1488. {
  1489. //print 'lang='.$obj->lang.' current='.$current_lang.'<br>';
  1490. if( $obj->lang == $current_lang ) // si on a les traduct. dans la langue courante on les charge en infos principales.
  1491. {
  1492. $this->label = $obj->label;
  1493. $this->description = $obj->description;
  1494. }
  1495. $this->multilangs["$obj->lang"]["label"] = $obj->label;
  1496. $this->multilangs["$obj->lang"]["description"] = $obj->description;
  1497. }
  1498. return 1;
  1499. }
  1500. else
  1501. {
  1502. $this->error=$langs->trans("Error")." : ".$this->db->error()." - ".$sql;
  1503. return -1;
  1504. }
  1505. }
  1506. /**
  1507. * Initialise an instance with random values.
  1508. * Used to build previews or test instances.
  1509. * id must be 0 if object instance is a specimen.
  1510. *
  1511. * @return void
  1512. */
  1513. function initAsSpecimen()
  1514. {
  1515. dol_syslog(get_class($this)."::initAsSpecimen");
  1516. // Initialise parametres
  1517. $this->id=0;
  1518. $this->fk_parent=0;
  1519. $this->label = 'SPECIMEN';
  1520. $this->specimen=1;
  1521. $this->description = 'This is a description';
  1522. $this->socid = 1;
  1523. $this->type = self::TYPE_PRODUCT;
  1524. }
  1525. /**
  1526. * Function used to replace a thirdparty id with another one.
  1527. *
  1528. * @param DoliDB $db Database handler
  1529. * @param int $origin_id Old thirdparty id
  1530. * @param int $dest_id New thirdparty id
  1531. * @return bool
  1532. */
  1533. public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
  1534. {
  1535. $tables = array(
  1536. 'categorie_societe'
  1537. );
  1538. return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables, 1);
  1539. }
  1540. }