functions2.lib.php 87 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386
  1. <?php
  2. /* Copyright (C) 2008-2011 Laurent Destailleur <eldy@users.sourceforge.net>
  3. * Copyright (C) 2008-2012 Regis Houssin <regis.houssin@inodbox.com>
  4. * Copyright (C) 2008 Raphael Bertrand (Resultic) <raphael.bertrand@resultic.fr>
  5. * Copyright (C) 2014-2016 Marcos García <marcosgdf@gmail.com>
  6. * Copyright (C) 2015 Ferran Marcet <fmarcet@2byte.es>
  7. * Copyright (C) 2015-2016 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
  8. * Copyright (C) 2017 Juanjo Menent <jmenent@2byte.es>
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 3 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  22. * or see https://www.gnu.org/
  23. */
  24. /**
  25. * \file htdocs/core/lib/functions2.lib.php
  26. * \brief A set of functions for Dolibarr
  27. * This file contains all rare functions.
  28. */
  29. // Enable this line to trace path when function is called.
  30. //print xdebug_print_function_stack('Functions2.lib was called');exit;
  31. /**
  32. * Same function than javascript unescape() function but in PHP.
  33. *
  34. * @param string $source String to decode
  35. * @return string Unescaped string
  36. */
  37. function jsUnEscape($source)
  38. {
  39. $decodedStr = "";
  40. $pos = 0;
  41. $len = strlen($source);
  42. while ($pos < $len) {
  43. $charAt = substr($source, $pos, 1);
  44. if ($charAt == '%') {
  45. $pos++;
  46. $charAt = substr($source, $pos, 1);
  47. if ($charAt == 'u') {
  48. // we got a unicode character
  49. $pos++;
  50. $unicodeHexVal = substr($source, $pos, 4);
  51. $unicode = hexdec($unicodeHexVal);
  52. $entity = "&#".$unicode.';';
  53. $decodedStr .= utf8_encode($entity);
  54. $pos += 4;
  55. } else {
  56. // we have an escaped ascii character
  57. $hexVal = substr($source, $pos, 2);
  58. $decodedStr .= chr(hexdec($hexVal));
  59. $pos += 2;
  60. }
  61. } else {
  62. $decodedStr .= $charAt;
  63. $pos++;
  64. }
  65. }
  66. return dol_html_entity_decode($decodedStr, ENT_COMPAT);
  67. }
  68. /**
  69. * Return list of modules directories. We detect directories that contains a subdirectory /core/modules
  70. * We discard directory modules that contains 'disabled' into their name.
  71. *
  72. * @param string $subdir Sub directory (Example: '/mailings')
  73. * @return array Array of directories that can contains module descriptors
  74. */
  75. function dolGetModulesDirs($subdir = '')
  76. {
  77. global $conf;
  78. $modulesdir = array();
  79. foreach ($conf->file->dol_document_root as $type => $dirroot)
  80. {
  81. // Default core/modules dir
  82. if ($type === 'main') {
  83. $modulesdir[$dirroot.'/core/modules'.$subdir.'/'] = $dirroot.'/core/modules'.$subdir.'/';
  84. }
  85. // Scan dir from external modules
  86. $handle = @opendir($dirroot);
  87. if (is_resource($handle))
  88. {
  89. while (($file = readdir($handle)) !== false)
  90. {
  91. if (preg_match('/disabled/', $file)) continue; // We discard module if it contains disabled into name.
  92. if (is_dir($dirroot.'/'.$file) && substr($file, 0, 1) <> '.' && substr($file, 0, 3) <> 'CVS' && $file != 'includes')
  93. {
  94. if (is_dir($dirroot.'/'.$file.'/core/modules'.$subdir.'/'))
  95. {
  96. $modulesdir[$dirroot.'/'.$file.'/core/modules'.$subdir.'/'] = $dirroot.'/'.$file.'/core/modules'.$subdir.'/';
  97. }
  98. }
  99. }
  100. closedir($handle);
  101. }
  102. }
  103. return $modulesdir;
  104. }
  105. /**
  106. * Try to guess default paper format according to language into $langs
  107. *
  108. * @param Translate $outputlangs Output lang to use to autodetect output format if setup not done
  109. * @return string Default paper format code
  110. */
  111. function dol_getDefaultFormat(Translate $outputlangs = null)
  112. {
  113. global $langs;
  114. $selected = 'EUA4';
  115. if (!$outputlangs) {
  116. $outputlangs = $langs;
  117. }
  118. if ($outputlangs->defaultlang == 'ca_CA') $selected = 'CAP4'; // Canada
  119. if ($outputlangs->defaultlang == 'en_US') $selected = 'USLetter'; // US
  120. return $selected;
  121. }
  122. /**
  123. * Output content of a file $filename in version of current language (otherwise may use an alternate language)
  124. *
  125. * @param Translate $langs Object language to use for output
  126. * @param string $filename Relative filename to output
  127. * @param int $searchalt 1=Search also in alternative languages
  128. * @return boolean true if OK, false if KO
  129. */
  130. function dol_print_file($langs, $filename, $searchalt = 0)
  131. {
  132. global $conf;
  133. // Test if file is in lang directory
  134. foreach ($langs->dir as $searchdir)
  135. {
  136. $formfile = ($searchdir."/langs/".$langs->defaultlang."/".$filename);
  137. dol_syslog('functions2::dol_print_file search file '.$formfile, LOG_DEBUG);
  138. if (is_readable($formfile))
  139. {
  140. $content = file_get_contents($formfile);
  141. $isutf8 = utf8_check($content);
  142. if (!$isutf8 && $conf->file->character_set_client == 'UTF-8') print utf8_encode($content);
  143. elseif ($isutf8 && $conf->file->character_set_client == 'ISO-8859-1') print utf8_decode($content);
  144. else print $content;
  145. return true;
  146. } else dol_syslog('functions2::dol_print_file not found', LOG_DEBUG);
  147. if ($searchalt) {
  148. // Test si fichier dans repertoire de la langue alternative
  149. if ($langs->defaultlang != "en_US") $formfilealt = $searchdir."/langs/en_US/".$filename;
  150. else $formfilealt = $searchdir."/langs/fr_FR/".$filename;
  151. dol_syslog('functions2::dol_print_file search alt file '.$formfilealt, LOG_DEBUG);
  152. //print 'getcwd='.getcwd().' htmlfilealt='.$formfilealt.' X '.file_exists(getcwd().'/'.$formfilealt);
  153. if (is_readable($formfilealt))
  154. {
  155. $content = file_get_contents($formfilealt);
  156. $isutf8 = utf8_check($content);
  157. if (!$isutf8 && $conf->file->character_set_client == 'UTF-8') print utf8_encode($content);
  158. elseif ($isutf8 && $conf->file->character_set_client == 'ISO-8859-1') print utf8_decode($content);
  159. else print $content;
  160. return true;
  161. } else dol_syslog('functions2::dol_print_file not found', LOG_DEBUG);
  162. }
  163. }
  164. return false;
  165. }
  166. /**
  167. * Show informations on an object
  168. * TODO Move this into html.formother
  169. *
  170. * @param object $object Objet to show
  171. * @param int $usetable Output into a table
  172. * @return void
  173. */
  174. function dol_print_object_info($object, $usetable = 0)
  175. {
  176. global $langs, $db;
  177. // Load translation files required by the page
  178. $langs->loadLangs(array('other', 'admin'));
  179. include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
  180. $deltadateforserver = getServerTimeZoneInt('now');
  181. $deltadateforclient = ((int) $_SESSION['dol_tz'] + (int) $_SESSION['dol_dst']);
  182. //$deltadateforcompany=((int) $_SESSION['dol_tz'] + (int) $_SESSION['dol_dst']);
  183. $deltadateforuser = round($deltadateforclient - $deltadateforserver);
  184. //print "x".$deltadateforserver." - ".$deltadateforclient." - ".$deltadateforuser;
  185. if ($usetable) print '<table class="border tableforfield centpercent">';
  186. // Import key
  187. if (!empty($object->import_key))
  188. {
  189. if ($usetable) print '<tr><td class="titlefield">';
  190. print $langs->trans("ImportedWithSet");
  191. if ($usetable) print '</td><td>';
  192. else print ': ';
  193. print $object->import_key;
  194. if ($usetable) print '</td></tr>';
  195. else print '<br>';
  196. }
  197. // User creation (old method using already loaded object and not id is kept for backward compatibility)
  198. if (!empty($object->user_creation) || !empty($object->user_creation_id))
  199. {
  200. if ($usetable) print '<tr><td class="titlefield">';
  201. print $langs->trans("CreatedBy");
  202. if ($usetable) print '</td><td>';
  203. else print ': ';
  204. if (is_object($object->user_creation))
  205. {
  206. if ($object->user_creation->id) print $object->user_creation->getNomUrl(1, '', 0, 0, 0);
  207. else print $langs->trans("Unknown");
  208. } else {
  209. $userstatic = new User($db);
  210. $userstatic->fetch($object->user_creation_id ? $object->user_creation_id : $object->user_creation);
  211. if ($userstatic->id) print $userstatic->getNomUrl(1, '', 0, 0, 0);
  212. else print $langs->trans("Unknown");
  213. }
  214. if ($usetable) print '</td></tr>';
  215. else print '<br>';
  216. }
  217. // Date creation
  218. if (!empty($object->date_creation))
  219. {
  220. if ($usetable) print '<tr><td class="titlefield">';
  221. print $langs->trans("DateCreation");
  222. if ($usetable) print '</td><td>';
  223. else print ': ';
  224. print dol_print_date($object->date_creation, 'dayhour');
  225. if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_creation + ($deltadateforuser * 3600), "dayhour").' &nbsp;'.$langs->trans("ClientHour");
  226. if ($usetable) print '</td></tr>';
  227. else print '<br>';
  228. }
  229. // User change (old method using already loaded object and not id is kept for backward compatibility)
  230. if (!empty($object->user_modification) || !empty($object->user_modification_id))
  231. {
  232. if ($usetable) print '<tr><td class="titlefield">';
  233. print $langs->trans("ModifiedBy");
  234. if ($usetable) print '</td><td>';
  235. else print ': ';
  236. if (is_object($object->user_modification))
  237. {
  238. if ($object->user_modification->id) print $object->user_modification->getNomUrl(1, '', 0, 0, 0);
  239. else print $langs->trans("Unknown");
  240. } else {
  241. $userstatic = new User($db);
  242. $userstatic->fetch($object->user_modification_id ? $object->user_modification_id : $object->user_modification);
  243. if ($userstatic->id) print $userstatic->getNomUrl(1, '', 0, 0, 0);
  244. else print $langs->trans("Unknown");
  245. }
  246. if ($usetable) print '</td></tr>';
  247. else print '<br>';
  248. }
  249. // Date change
  250. if (!empty($object->date_modification))
  251. {
  252. if ($usetable) print '<tr><td class="titlefield">';
  253. print $langs->trans("DateLastModification");
  254. if ($usetable) print '</td><td>';
  255. else print ': ';
  256. print dol_print_date($object->date_modification, 'dayhour');
  257. if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_modification + ($deltadateforuser * 3600), "dayhour").' &nbsp;'.$langs->trans("ClientHour");
  258. if ($usetable) print '</td></tr>';
  259. else print '<br>';
  260. }
  261. // User validation (old method using already loaded object and not id is kept for backward compatibility)
  262. if (!empty($object->user_validation) || !empty($object->user_validation_id))
  263. {
  264. if ($usetable) print '<tr><td class="titlefield">';
  265. print $langs->trans("ValidatedBy");
  266. if ($usetable) print '</td><td>';
  267. else print ': ';
  268. if (is_object($object->user_validation))
  269. {
  270. if ($object->user_validation->id) print $object->user_validation->getNomUrl(1, '', 0, 0, 0);
  271. else print $langs->trans("Unknown");
  272. } else {
  273. $userstatic = new User($db);
  274. $userstatic->fetch($object->user_validation_id ? $object->user_validation_id : $object->user_validation);
  275. if ($userstatic->id) print $userstatic->getNomUrl(1, '', 0, 0, 0);
  276. else print $langs->trans("Unknown");
  277. }
  278. if ($usetable) print '</td></tr>';
  279. else print '<br>';
  280. }
  281. // Date validation
  282. if (!empty($object->date_validation))
  283. {
  284. if ($usetable) print '<tr><td class="titlefield">';
  285. print $langs->trans("DateValidation");
  286. if ($usetable) print '</td><td>';
  287. else print ': ';
  288. print dol_print_date($object->date_validation, 'dayhour');
  289. if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_validation + ($deltadateforuser * 3600), "dayhour").' &nbsp;'.$langs->trans("ClientHour");
  290. if ($usetable) print '</td></tr>';
  291. else print '<br>';
  292. }
  293. // User approve (old method using already loaded object and not id is kept for backward compatibility)
  294. if (!empty($object->user_approve) || !empty($object->user_approve_id))
  295. {
  296. if ($usetable) print '<tr><td class="titlefield">';
  297. print $langs->trans("ApprovedBy");
  298. if ($usetable) print '</td><td>';
  299. else print ': ';
  300. if (is_object($object->user_approve))
  301. {
  302. if ($object->user_approve->id) print $object->user_approve->getNomUrl(1, '', 0, 0, 0);
  303. else print $langs->trans("Unknown");
  304. } else {
  305. $userstatic = new User($db);
  306. $userstatic->fetch($object->user_approve_id ? $object->user_approve_id : $object->user_approve);
  307. if ($userstatic->id) print $userstatic->getNomUrl(1, '', 0, 0, 0);
  308. else print $langs->trans("Unknown");
  309. }
  310. if ($usetable) print '</td></tr>';
  311. else print '<br>';
  312. }
  313. // Date approve
  314. if (!empty($object->date_approve))
  315. {
  316. if ($usetable) print '<tr><td class="titlefield">';
  317. print $langs->trans("DateApprove");
  318. if ($usetable) print '</td><td>';
  319. else print ': ';
  320. print dol_print_date($object->date_approve, 'dayhour');
  321. if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_approve + ($deltadateforuser * 3600), "dayhour").' &nbsp;'.$langs->trans("ClientHour");
  322. if ($usetable) print '</td></tr>';
  323. else print '<br>';
  324. }
  325. // User approve
  326. if (!empty($object->user_approve_id2))
  327. {
  328. if ($usetable) print '<tr><td class="titlefield">';
  329. print $langs->trans("ApprovedBy");
  330. if ($usetable) print '</td><td>';
  331. else print ': ';
  332. $userstatic = new User($db);
  333. $userstatic->fetch($object->user_approve_id2);
  334. if ($userstatic->id) print $userstatic->getNomUrl(1, '', 0, 0, 0);
  335. else print $langs->trans("Unknown");
  336. if ($usetable) print '</td></tr>';
  337. else print '<br>';
  338. }
  339. // Date approve
  340. if (!empty($object->date_approve2))
  341. {
  342. if ($usetable) print '<tr><td class="titlefield">';
  343. print $langs->trans("DateApprove2");
  344. if ($usetable) print '</td><td>';
  345. else print ': ';
  346. print dol_print_date($object->date_approve2, 'dayhour');
  347. if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_approve2 + ($deltadateforuser * 3600), "dayhour").' &nbsp;'.$langs->trans("ClientHour");
  348. if ($usetable) print '</td></tr>';
  349. else print '<br>';
  350. }
  351. // User close
  352. if (!empty($object->user_cloture) || !empty($object->user_closing))
  353. {
  354. if (isset($object->user_cloture) && !empty($object->user_cloture)) $object->user_closing = $object->user_cloture;
  355. if ($usetable) print '<tr><td class="titlefield">';
  356. print $langs->trans("ClosedBy");
  357. if ($usetable) print '</td><td>';
  358. else print ': ';
  359. if (is_object($object->user_closing))
  360. {
  361. if ($object->user_closing->id) print $object->user_closing->getNomUrl(1, '', 0, 0, 0);
  362. else print $langs->trans("Unknown");
  363. } else {
  364. $userstatic = new User($db);
  365. $userstatic->fetch($object->user_closing);
  366. if ($userstatic->id) print $userstatic->getNomUrl(1, '', 0, 0, 0);
  367. else print $langs->trans("Unknown");
  368. }
  369. if ($usetable) print '</td></tr>';
  370. else print '<br>';
  371. }
  372. // Date close
  373. if (!empty($object->date_cloture) || !empty($object->date_closing))
  374. {
  375. if (isset($object->date_cloture) && !empty($object->date_cloture)) $object->date_closing = $object->date_cloture;
  376. if ($usetable) print '<tr><td class="titlefield">';
  377. print $langs->trans("DateClosing");
  378. if ($usetable) print '</td><td>';
  379. else print ': ';
  380. print dol_print_date($object->date_closing, 'dayhour');
  381. if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_closing + ($deltadateforuser * 3600), "dayhour").' &nbsp;'.$langs->trans("ClientHour");
  382. if ($usetable) print '</td></tr>';
  383. else print '<br>';
  384. }
  385. // User conciliate
  386. if (!empty($object->user_rappro))
  387. {
  388. if ($usetable) print '<tr><td class="titlefield">';
  389. print $langs->trans("ConciliatedBy");
  390. if ($usetable) print '</td><td>';
  391. else print ': ';
  392. if (is_object($object->user_rappro))
  393. {
  394. if ($object->user_rappro->id) print $object->user_rappro->getNomUrl(1, '', 0, 0, 0);
  395. else print $langs->trans("Unknown");
  396. } else {
  397. $userstatic = new User($db);
  398. $userstatic->fetch($object->user_rappro);
  399. if ($userstatic->id) print $userstatic->getNomUrl(1, '', 0, 0, 0);
  400. else print $langs->trans("Unknown");
  401. }
  402. if ($usetable) print '</td></tr>';
  403. else print '<br>';
  404. }
  405. // Date conciliate
  406. if (!empty($object->date_rappro))
  407. {
  408. if ($usetable) print '<tr><td class="titlefield">';
  409. print $langs->trans("DateConciliating");
  410. if ($usetable) print '</td><td>';
  411. else print ': ';
  412. print dol_print_date($object->date_rappro, 'dayhour');
  413. if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_rappro + ($deltadateforuser * 3600), "dayhour").' &nbsp;'.$langs->trans("ClientHour");
  414. if ($usetable) print '</td></tr>';
  415. else print '<br>';
  416. }
  417. // Date send
  418. if (!empty($object->date_envoi))
  419. {
  420. if ($usetable) print '<tr><td class="titlefield">';
  421. print $langs->trans("DateLastSend");
  422. if ($usetable) print '</td><td>';
  423. else print ': ';
  424. print dol_print_date($object->date_envoi, 'dayhour');
  425. if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_envoi + ($deltadateforuser * 3600), "dayhour").' &nbsp;'.$langs->trans("ClientHour");
  426. if ($usetable) print '</td></tr>';
  427. else print '<br>';
  428. }
  429. if ($usetable) print '</table>';
  430. }
  431. /**
  432. * Return an email formatted to include a tracking id
  433. * For example myemail@example.com becom myemail+trackingid@example.com
  434. *
  435. * @param string $email Email address (Ex: "toto@example.com", "John Do <johndo@example.com>")
  436. * @param string $trackingid Tracking id (Ex: thi123 for thirdparty with id 123)
  437. * @return string Return email tracker string
  438. */
  439. function dolAddEmailTrackId($email, $trackingid)
  440. {
  441. $tmp = explode('@', $email);
  442. return $tmp[0].'+'.$trackingid.'@'.(isset($tmp[1]) ? $tmp[1] : '');
  443. }
  444. /**
  445. * Return true if email has a domain name that can't be resolved
  446. *
  447. * @param string $mail Email address (Ex: "toto@example.com", "John Do <johndo@example.com>")
  448. * @return boolean True if domain email is OK, False if KO
  449. */
  450. function isValidMailDomain($mail)
  451. {
  452. list($user, $domain) = explode("@", $mail, 2);
  453. return checkdnsrr($domain, "MX");
  454. }
  455. /**
  456. * Url string validation
  457. * <http[s]> :// [user[:pass]@] hostname [port] [/path] [?getquery] [anchor]
  458. *
  459. * @param string $url Url
  460. * @param int $http 1: verify http is provided, 0: not verify http
  461. * @param int $pass 1: verify user and pass is provided, 0: not verify user and pass
  462. * @param int $port 1: verify port is provided, 0: not verify port
  463. * @param int $path 1: verify a path is provided "/" or "/..." or "/.../", 0: not verify path
  464. * @param int $query 1: verify query is provided, 0: not verify query
  465. * @param int $anchor 1: verify anchor is provided, 0: not verify anchor
  466. * @return int 1=Check is OK, 0=Check is KO
  467. */
  468. function isValidUrl($url, $http = 0, $pass = 0, $port = 0, $path = 0, $query = 0, $anchor = 0)
  469. {
  470. $ValidUrl = 0;
  471. $urlregex = '';
  472. // SCHEME
  473. if ($http) $urlregex .= "^(http:\/\/|https:\/\/)";
  474. // USER AND PASS
  475. if ($pass) $urlregex .= "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)?@)";
  476. // HOSTNAME OR IP
  477. //$urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)*"; // x allowed (ex. http://localhost, http://routerlogin)
  478. //$urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)+"; // x.x
  479. $urlregex .= "([a-z0-9+\$_\\\:-])+(\.[a-z0-9+\$_-][a-z0-9+\$_-]+)*"; // x ou x.xx (2 x ou plus)
  480. //use only one of the above
  481. // PORT
  482. if ($port) $urlregex .= "(\:[0-9]{2,5})";
  483. // PATH
  484. if ($path) $urlregex .= "(\/([a-z0-9+\$_-]\.?)+)*\/";
  485. // GET Query
  486. if ($query) $urlregex .= "(\?[a-z+&\$_.-][a-z0-9;:@\/&%=+\$_.-]*)";
  487. // ANCHOR
  488. if ($anchor) $urlregex .= "(#[a-z_.-][a-z0-9+\$_.-]*)$";
  489. // check
  490. if (preg_match('/'.$urlregex.'/i', $url))
  491. {
  492. $ValidUrl = 1;
  493. }
  494. //print $urlregex.' - '.$url.' - '.$ValidUrl;
  495. return $ValidUrl;
  496. }
  497. /**
  498. * Check if VAT numero is valid (check done on syntax only, no database or remote access)
  499. *
  500. * @param Societe $company VAT number
  501. * @return int 1=Check is OK, 0=Check is KO
  502. */
  503. function isValidVATID($company)
  504. {
  505. if ($company->isInEEC()) // Syntax check rules for EEC countries
  506. {
  507. /* Disabled because some companies can have an address in Irland and a vat number in France.
  508. $vatprefix = $company->country_code;
  509. if ($vatprefix == 'GR') $vatprefix = '(EL|GR)';
  510. elseif ($vatprefix == 'MC') $vatprefix = 'FR'; // Monaco is using french VAT numbers
  511. else $vatprefix = preg_quote($vatprefix, '/');*/
  512. $vatprefix = '[a-zA-Z][a-zA-Z]';
  513. if (!preg_match('/^'.$vatprefix.'[a-zA-Z0-9\-\.]{5,14}$/i', str_replace(' ', '', $company->tva_intra)))
  514. {
  515. return 0;
  516. }
  517. }
  518. return 1;
  519. }
  520. /**
  521. * Clean an url string
  522. *
  523. * @param string $url Url
  524. * @param integer $http 1 = keep both http:// and https://, 0: remove http:// but not https://
  525. * @return string Cleaned url
  526. */
  527. function clean_url($url, $http = 1)
  528. {
  529. // Fixed by Matelli (see http://matelli.fr/showcases/patchs-dolibarr/fix-cleaning-url.html)
  530. // To include the minus sign in a char class, we must not escape it but put it at the end of the class
  531. // Also, there's no need of escape a dot sign in a class
  532. $regs = array();
  533. if (preg_match('/^(https?:[\\/]+)?([0-9A-Z.-]+\.[A-Z]{2,4})(:[0-9]+)?/i', $url, $regs))
  534. {
  535. $proto = $regs[1];
  536. $domain = $regs[2];
  537. $port = isset($regs[3]) ? $regs[3] : '';
  538. //print $url." -> ".$proto." - ".$domain." - ".$port;
  539. //$url = dol_string_nospecial(trim($url));
  540. $url = trim($url);
  541. // Si http: defini on supprime le http (Si https on ne supprime pas)
  542. $newproto = $proto;
  543. if ($http == 0)
  544. {
  545. if (preg_match('/^http:[\\/]+/i', $url))
  546. {
  547. $url = preg_replace('/^http:[\\/]+/i', '', $url);
  548. $newproto = '';
  549. }
  550. }
  551. // On passe le nom de domaine en minuscule
  552. $CleanUrl = preg_replace('/^'.preg_quote($proto.$domain, '/').'/i', $newproto.strtolower($domain), $url);
  553. return $CleanUrl;
  554. } else return $url;
  555. }
  556. /**
  557. * Returns an email value with obfuscated parts.
  558. *
  559. * @param string $mail Email
  560. * @param string $replace Replacement character (defaul: *)
  561. * @param int $nbreplace Number of replacement character (default: 8)
  562. * @param int $nbdisplaymail Number of character unchanged (default: 4)
  563. * @param int $nbdisplaydomain Number of character unchanged of domain (default: 3)
  564. * @param bool $displaytld Display tld (default: true)
  565. * @return string Return email with hidden parts or '';
  566. */
  567. function dolObfuscateEmail($mail, $replace = "*", $nbreplace = 8, $nbdisplaymail = 4, $nbdisplaydomain = 3, $displaytld = true)
  568. {
  569. if (!isValidEmail($mail))return '';
  570. $tab = explode('@', $mail);
  571. $tab2 = explode('.', $tab[1]);
  572. $string_replace = '';
  573. $mail_name = $tab[0];
  574. $mail_domaine = $tab2[0];
  575. $mail_tld = '';
  576. $nbofelem = count($tab2);
  577. for ($i = 1; $i < $nbofelem && $displaytld; $i++)
  578. {
  579. $mail_tld .= '.'.$tab2[$i];
  580. }
  581. for ($i = 0; $i < $nbreplace; $i++) {
  582. $string_replace .= $replace;
  583. }
  584. if (strlen($mail_name) > $nbdisplaymail) {
  585. $mail_name = substr($mail_name, 0, $nbdisplaymail);
  586. }
  587. if (strlen($mail_domaine) > $nbdisplaydomain) {
  588. $mail_domaine = substr($mail_domaine, strlen($mail_domaine) - $nbdisplaydomain);
  589. }
  590. return $mail_name.$string_replace.$mail_domaine.$mail_tld;
  591. }
  592. /**
  593. * Return lines of an html table from an array
  594. * Used by array2table function only
  595. *
  596. * @param array $data Array of data
  597. * @param string $troptions Options for tr
  598. * @param string $tdoptions Options for td
  599. * @return string
  600. */
  601. function array2tr($data, $troptions = '', $tdoptions = '')
  602. {
  603. $text = '<tr '.$troptions.'>';
  604. foreach ($data as $key => $item) {
  605. $text .= '<td '.$tdoptions.'>'.$item.'</td>';
  606. }
  607. $text .= '</tr>';
  608. return $text;
  609. }
  610. /**
  611. * Return an html table from an array
  612. *
  613. * @param array $data Array of data
  614. * @param int $tableMarkup Table markup
  615. * @param string $tableoptions Options for table
  616. * @param string $troptions Options for tr
  617. * @param string $tdoptions Options for td
  618. * @return string
  619. */
  620. function array2table($data, $tableMarkup = 1, $tableoptions = '', $troptions = '', $tdoptions = '')
  621. {
  622. $text = '';
  623. if ($tableMarkup) $text = '<table '.$tableoptions.'>';
  624. foreach ($data as $key => $item) {
  625. if (is_array($item)) {
  626. $text .= array2tr($item, $troptions, $tdoptions);
  627. } else {
  628. $text .= '<tr '.$troptions.'>';
  629. $text .= '<td '.$tdoptions.'>'.$key.'</td>';
  630. $text .= '<td '.$tdoptions.'>'.$item.'</td>';
  631. $text .= '</tr>';
  632. }
  633. }
  634. if ($tableMarkup) $text .= '</table>';
  635. return $text;
  636. }
  637. /**
  638. * Return last or next value for a mask (according to area we should not reset)
  639. *
  640. * @param DoliDB $db Database handler
  641. * @param string $mask Mask to use
  642. * @param string $table Table containing field with counter
  643. * @param string $field Field containing already used values of counter
  644. * @param string $where To add a filter on selection (for exemple to filter on invoice types)
  645. * @param Societe $objsoc The company that own the object we need a counter for
  646. * @param string $date Date to use for the {y},{m},{d} tags.
  647. * @param string $mode 'next' for next value or 'last' for last value
  648. * @param bool $bentityon Activate the entity filter. Default is true (for modules not compatible with multicompany)
  649. * @param User $objuser Object user we need data from.
  650. * @param int $forceentity Entity id to force
  651. * @return string New value (numeric) or error message
  652. */
  653. function get_next_value($db, $mask, $table, $field, $where = '', $objsoc = '', $date = '', $mode = 'next', $bentityon = true, $objuser = null, $forceentity = null)
  654. {
  655. global $conf, $user;
  656. if (!is_object($objsoc)) $valueforccc = $objsoc;
  657. elseif ($table == "commande_fournisseur" || $table == "facture_fourn") $valueforccc = dol_string_unaccent($objsoc->code_fournisseur);
  658. else $valueforccc = dol_string_unaccent($objsoc->code_client);
  659. $sharetable = $table;
  660. if ($table == 'facture' || $table == 'invoice') $sharetable = 'invoicenumber'; // for getEntity function
  661. // Clean parameters
  662. if ($date == '') $date = dol_now(); // We use local year and month of PHP server to search numbers
  663. // but we should use local year and month of user
  664. // For debugging
  665. //dol_syslog("mask=".$mask, LOG_DEBUG);
  666. //include_once(DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php');
  667. //$mask='FA{yy}{mm}-{0000@99}';
  668. //$date=dol_mktime(12, 0, 0, 1, 1, 1900);
  669. //$date=dol_stringtotime('20130101');
  670. $hasglobalcounter = false;
  671. $reg = array();
  672. // Extract value for mask counter, mask raz and mask offset
  673. if (preg_match('/\{(0+)([@\+][0-9\-\+\=]+)?([@\+][0-9\-\+\=]+)?\}/i', $mask, $reg))
  674. {
  675. $masktri = $reg[1].(!empty($reg[2]) ? $reg[2] : '').(!empty($reg[3]) ? $reg[3] : '');
  676. $maskcounter = $reg[1];
  677. $hasglobalcounter = true;
  678. } else {
  679. // setting some defaults so the rest of the code won't fail if there is a third party counter
  680. $masktri = '00000';
  681. $maskcounter = '00000';
  682. }
  683. $maskraz = -1;
  684. $maskoffset = 0;
  685. $resetEveryMonth = false;
  686. if (dol_strlen($maskcounter) < 3 && empty($conf->global->MAIN_COUNTER_WITH_LESS_3_DIGITS)) return 'ErrorCounterMustHaveMoreThan3Digits';
  687. // Extract value for third party mask counter
  688. $regClient = array();
  689. if (preg_match('/\{(c+)(0*)\}/i', $mask, $regClientRef))
  690. {
  691. $maskrefclient = $regClientRef[1].$regClientRef[2];
  692. $maskrefclient_maskclientcode = $regClientRef[1];
  693. $maskrefclient_maskcounter = $regClientRef[2];
  694. $maskrefclient_maskoffset = 0; //default value of maskrefclient_counter offset
  695. $maskrefclient_clientcode = substr($valueforccc, 0, dol_strlen($maskrefclient_maskclientcode)); //get n first characters of client code where n is length in mask
  696. $maskrefclient_clientcode = str_pad($maskrefclient_clientcode, dol_strlen($maskrefclient_maskclientcode), "#", STR_PAD_RIGHT); //padding maskrefclient_clientcode for having exactly n characters in maskrefclient_clientcode
  697. $maskrefclient_clientcode = dol_string_nospecial($maskrefclient_clientcode); //sanitize maskrefclient_clientcode for sql insert and sql select like
  698. if (dol_strlen($maskrefclient_maskcounter) > 0 && dol_strlen($maskrefclient_maskcounter) < 3) return 'ErrorCounterMustHaveMoreThan3Digits';
  699. } else $maskrefclient = '';
  700. // fail if there is neither a global nor a third party counter
  701. if (!$hasglobalcounter && ($maskrefclient_maskcounter == ''))
  702. {
  703. return 'ErrorBadMask';
  704. }
  705. // Extract value for third party type
  706. $regType = array();
  707. if (preg_match('/\{(t+)\}/i', $mask, $regType))
  708. {
  709. $masktype = $regType[1];
  710. $masktype_value = substr(preg_replace('/^TE_/', '', $objsoc->typent_code), 0, dol_strlen($regType[1])); // get n first characters of thirdpaty typent_code (where n is length in mask)
  711. $masktype_value = str_pad($masktype_value, dol_strlen($regType[1]), "#", STR_PAD_RIGHT); // we fill on right with # to have same number of char than into mask
  712. } else {
  713. $masktype = '';
  714. $masktype_value = '';
  715. }
  716. // Extract value for user
  717. if (preg_match('/\{(u+)\}/i', $mask, $regType))
  718. {
  719. $lastname = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
  720. if (is_object($objuser)) $lastname = $objuser->lastname;
  721. $maskuser = $regType[1];
  722. $maskuser_value = substr($lastname, 0, dol_strlen($regType[1])); // get n first characters of user firstname (where n is length in mask)
  723. $maskuser_value = str_pad($maskuser_value, dol_strlen($regType[1]), "#", STR_PAD_RIGHT); // we fill on right with # to have same number of char than into mask
  724. } else {
  725. $maskuser = '';
  726. $maskuser_value = '';
  727. }
  728. // Personalized field {XXX-1} à {XXX-9}
  729. $maskperso = array();
  730. $maskpersonew = array();
  731. $tmpmask = $mask;
  732. $regKey = array();
  733. while (preg_match('/\{([A-Z]+)\-([1-9])\}/', $tmpmask, $regKey))
  734. {
  735. $maskperso[$regKey[1]] = '{'.$regKey[1].'-'.$regKey[2].'}';
  736. $maskpersonew[$regKey[1]] = str_pad('', $regKey[2], '_', STR_PAD_RIGHT);
  737. $tmpmask = preg_replace('/\{'.$regKey[1].'\-'.$regKey[2].'\}/i', $maskpersonew[$regKey[1]], $tmpmask);
  738. }
  739. if (strstr($mask, 'user_extra_'))
  740. {
  741. $start = "{user_extra_";
  742. $end = "\}";
  743. $extra = get_string_between($mask, "user_extra_", "}");
  744. if (!empty($user->array_options['options_'.$extra])) {
  745. $mask = preg_replace('#('.$start.')(.*?)('.$end.')#si', $user->array_options['options_'.$extra], $mask);
  746. }
  747. }
  748. $maskwithonlyymcode = $mask;
  749. $maskwithonlyymcode = preg_replace('/\{(0+)([@\+][0-9\-\+\=]+)?([@\+][0-9\-\+\=]+)?\}/i', $maskcounter, $maskwithonlyymcode);
  750. $maskwithonlyymcode = preg_replace('/\{dd\}/i', 'dd', $maskwithonlyymcode);
  751. $maskwithonlyymcode = preg_replace('/\{(c+)(0*)\}/i', $maskrefclient, $maskwithonlyymcode);
  752. $maskwithonlyymcode = preg_replace('/\{(t+)\}/i', $masktype_value, $maskwithonlyymcode);
  753. $maskwithonlyymcode = preg_replace('/\{(u+)\}/i', $maskuser_value, $maskwithonlyymcode);
  754. foreach ($maskperso as $key => $val)
  755. {
  756. $maskwithonlyymcode = preg_replace('/'.preg_quote($val, '/').'/i', $maskpersonew[$key], $maskwithonlyymcode);
  757. }
  758. $maskwithnocode = $maskwithonlyymcode;
  759. $maskwithnocode = preg_replace('/\{yyyy\}/i', 'yyyy', $maskwithnocode);
  760. $maskwithnocode = preg_replace('/\{yy\}/i', 'yy', $maskwithnocode);
  761. $maskwithnocode = preg_replace('/\{y\}/i', 'y', $maskwithnocode);
  762. $maskwithnocode = preg_replace('/\{mm\}/i', 'mm', $maskwithnocode);
  763. // Now maskwithnocode = 0000ddmmyyyyccc for example
  764. // and maskcounter = 0000 for example
  765. //print "maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode."\n<br>";
  766. //var_dump($reg);
  767. // If an offset is asked
  768. if (!empty($reg[2]) && preg_match('/^\+/', $reg[2])) $maskoffset = preg_replace('/^\+/', '', $reg[2]);
  769. if (!empty($reg[3]) && preg_match('/^\+/', $reg[3])) $maskoffset = preg_replace('/^\+/', '', $reg[3]);
  770. // Define $sqlwhere
  771. $sqlwhere = '';
  772. $yearoffset = 0; // Use year of current $date by default
  773. $yearoffsettype = false; // false: no reset, 0,-,=,+: reset at offset SOCIETE_FISCAL_MONTH_START, x=reset at offset x
  774. // If a restore to zero after a month is asked we check if there is already a value for this year.
  775. if (!empty($reg[2]) && preg_match('/^@/', $reg[2])) $yearoffsettype = preg_replace('/^@/', '', $reg[2]);
  776. if (!empty($reg[3]) && preg_match('/^@/', $reg[3])) $yearoffsettype = preg_replace('/^@/', '', $reg[3]);
  777. //print "yearoffset=".$yearoffset." yearoffsettype=".$yearoffsettype;
  778. if (is_numeric($yearoffsettype) && $yearoffsettype >= 1)
  779. $maskraz = $yearoffsettype; // For backward compatibility
  780. elseif ($yearoffsettype === '0' || (!empty($yearoffsettype) && !is_numeric($yearoffsettype) && $conf->global->SOCIETE_FISCAL_MONTH_START > 1))
  781. $maskraz = $conf->global->SOCIETE_FISCAL_MONTH_START;
  782. //print "maskraz=".$maskraz; // -1=no reset
  783. if ($maskraz > 0) { // A reset is required
  784. if ($maskraz == 99) {
  785. $maskraz = date('m', $date);
  786. $resetEveryMonth = true;
  787. }
  788. if ($maskraz > 12) return 'ErrorBadMaskBadRazMonth';
  789. // Define posy, posm and reg
  790. if ($maskraz > 1) // if reset is not first month, we need month and year into mask
  791. {
  792. if (preg_match('/^(.*)\{(y+)\}\{(m+)\}/i', $maskwithonlyymcode, $reg)) { $posy = 2; $posm = 3; } elseif (preg_match('/^(.*)\{(m+)\}\{(y+)\}/i', $maskwithonlyymcode, $reg)) { $posy = 3; $posm = 2; } else return 'ErrorCantUseRazInStartedYearIfNoYearMonthInMask';
  793. if (dol_strlen($reg[$posy]) < 2) return 'ErrorCantUseRazWithYearOnOneDigit';
  794. } else // if reset is for a specific month in year, we need year
  795. {
  796. if (preg_match('/^(.*)\{(m+)\}\{(y+)\}/i', $maskwithonlyymcode, $reg)) { $posy = 3; $posm = 2; } elseif (preg_match('/^(.*)\{(y+)\}\{(m+)\}/i', $maskwithonlyymcode, $reg)) { $posy = 2; $posm = 3; } elseif (preg_match('/^(.*)\{(y+)\}/i', $maskwithonlyymcode, $reg)) { $posy = 2; $posm = 0; } else return 'ErrorCantUseRazIfNoYearInMask';
  797. }
  798. // Define length
  799. $yearlen = $posy ?dol_strlen($reg[$posy]) : 0;
  800. $monthlen = $posm ?dol_strlen($reg[$posm]) : 0;
  801. // Define pos
  802. $yearpos = (dol_strlen($reg[1]) + 1);
  803. $monthpos = ($yearpos + $yearlen);
  804. if ($posy == 3 && $posm == 2) { // if month is before year
  805. $monthpos = (dol_strlen($reg[1]) + 1);
  806. $yearpos = ($monthpos + $monthlen);
  807. }
  808. //print "xxx ".$maskwithonlyymcode." maskraz=".$maskraz." posy=".$posy." yearlen=".$yearlen." yearpos=".$yearpos." posm=".$posm." monthlen=".$monthlen." monthpos=".$monthpos." yearoffsettype=".$yearoffsettype." resetEveryMonth=".$resetEveryMonth."\n";
  809. // Define $yearcomp and $monthcomp (that will be use in the select where to search max number)
  810. $monthcomp = $maskraz;
  811. $yearcomp = 0;
  812. if (!empty($yearoffsettype) && !is_numeric($yearoffsettype) && $yearoffsettype != '=') // $yearoffsettype is - or +
  813. {
  814. $currentyear = date("Y", $date);
  815. $fiscaldate = dol_mktime('0', '0', '0', $maskraz, '1', $currentyear);
  816. $newyeardate = dol_mktime('0', '0', '0', '1', '1', $currentyear);
  817. $nextnewyeardate = dol_mktime('0', '0', '0', '1', '1', $currentyear + 1);
  818. //echo 'currentyear='.$currentyear.' date='.dol_print_date($date, 'day').' fiscaldate='.dol_print_date($fiscaldate, 'day').'<br>';
  819. // If after or equal of current fiscal date
  820. if ($date >= $fiscaldate)
  821. {
  822. // If before of next new year date
  823. if ($date < $nextnewyeardate && $yearoffsettype == '+') $yearoffset = 1;
  824. } // If after or equal of current new year date
  825. elseif ($date >= $newyeardate && $yearoffsettype == '-') $yearoffset = -1;
  826. } // For backward compatibility
  827. elseif (date("m", $date) < $maskraz && empty($resetEveryMonth)) { $yearoffset = -1; } // If current month lower that month of return to zero, year is previous year
  828. if ($yearlen == 4) $yearcomp = sprintf("%04d", date("Y", $date) + $yearoffset);
  829. elseif ($yearlen == 2) $yearcomp = sprintf("%02d", date("y", $date) + $yearoffset);
  830. elseif ($yearlen == 1) $yearcomp = substr(date("y", $date), 2, 1) + $yearoffset;
  831. if ($monthcomp > 1 && empty($resetEveryMonth)) // Test with month is useless if monthcomp = 0 or 1 (0 is same as 1) (regis: $monthcomp can't equal 0)
  832. {
  833. if ($yearlen == 4) $yearcomp1 = sprintf("%04d", date("Y", $date) + $yearoffset + 1);
  834. elseif ($yearlen == 2) $yearcomp1 = sprintf("%02d", date("y", $date) + $yearoffset + 1);
  835. $sqlwhere .= "(";
  836. $sqlwhere .= " (SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp)."'";
  837. $sqlwhere .= " AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") >= '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."')";
  838. $sqlwhere .= " OR";
  839. $sqlwhere .= " (SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp1)."'";
  840. $sqlwhere .= " AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") < '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."') ";
  841. $sqlwhere .= ')';
  842. } elseif ($resetEveryMonth)
  843. {
  844. $sqlwhere .= "(SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp)."'";
  845. $sqlwhere .= " AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") = '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."')";
  846. } else // reset is done on january
  847. {
  848. $sqlwhere .= '(SUBSTRING('.$field.', '.$yearpos.', '.$yearlen.") = '".$db->escape($yearcomp)."')";
  849. }
  850. }
  851. //print "sqlwhere=".$sqlwhere." yearcomp=".$yearcomp."<br>\n"; // sqlwhere and yearcomp defined only if we ask a reset
  852. //print "masktri=".$masktri." maskcounter=".$maskcounter." maskraz=".$maskraz." maskoffset=".$maskoffset."<br>\n";
  853. // Define $sqlstring
  854. if (function_exists('mb_strrpos'))
  855. {
  856. $posnumstart = mb_strrpos($maskwithnocode, $maskcounter, 'UTF-8');
  857. } else {
  858. $posnumstart = strrpos($maskwithnocode, $maskcounter);
  859. } // Pos of counter in final string (from 0 to ...)
  860. if ($posnumstart < 0) return 'ErrorBadMaskFailedToLocatePosOfSequence';
  861. $sqlstring = 'SUBSTRING('.$field.', '.($posnumstart + 1).', '.dol_strlen($maskcounter).')';
  862. // Define $maskLike
  863. $maskLike = dol_string_nospecial($mask);
  864. $maskLike = str_replace("%", "_", $maskLike);
  865. // Replace protected special codes with matching number of _ as wild card caracter
  866. $maskLike = preg_replace('/\{yyyy\}/i', '____', $maskLike);
  867. $maskLike = preg_replace('/\{yy\}/i', '__', $maskLike);
  868. $maskLike = preg_replace('/\{y\}/i', '_', $maskLike);
  869. $maskLike = preg_replace('/\{mm\}/i', '__', $maskLike);
  870. $maskLike = preg_replace('/\{dd\}/i', '__', $maskLike);
  871. $maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'), str_pad("", dol_strlen($maskcounter), "_"), $maskLike);
  872. if ($maskrefclient) $maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'), str_pad("", dol_strlen($maskrefclient), "_"), $maskLike);
  873. if ($masktype) $maskLike = str_replace(dol_string_nospecial('{'.$masktype.'}'), $masktype_value, $maskLike);
  874. if ($maskuser) $maskLike = str_replace(dol_string_nospecial('{'.$maskuser.'}'), $maskuser_value, $maskLike);
  875. foreach ($maskperso as $key => $val)
  876. {
  877. $maskLike = str_replace(dol_string_nospecial($maskperso[$key]), $maskpersonew[$key], $maskLike);
  878. }
  879. // Get counter in database
  880. $counter = 0;
  881. $sql = "SELECT MAX(".$sqlstring.") as val";
  882. $sql .= " FROM ".MAIN_DB_PREFIX.$table;
  883. $sql .= " WHERE ".$field." LIKE '".$db->escape($maskLike)."'";
  884. $sql .= " AND ".$field." NOT LIKE '(PROV%)'";
  885. if ($bentityon) // only if entity enable
  886. $sql .= " AND entity IN (".getEntity($sharetable).")";
  887. elseif (!empty($forceentity))
  888. $sql .= " AND entity IN (".$forceentity.")";
  889. if ($where) $sql .= $where;
  890. if ($sqlwhere) $sql .= ' AND '.$sqlwhere;
  891. //print $sql.'<br>';
  892. dol_syslog("functions2::get_next_value mode=".$mode."", LOG_DEBUG);
  893. $resql = $db->query($sql);
  894. if ($resql)
  895. {
  896. $obj = $db->fetch_object($resql);
  897. $counter = $obj->val;
  898. } else dol_print_error($db);
  899. // Check if we must force counter to maskoffset
  900. if (empty($counter)) $counter = $maskoffset;
  901. elseif (preg_match('/[^0-9]/i', $counter))
  902. {
  903. $counter = 0;
  904. dol_syslog("Error, the last counter found is '".$counter."' so is not a numeric value. We will restart to 1.", LOG_ERR);
  905. } elseif ($counter < $maskoffset && empty($conf->global->MAIN_NUMBERING_OFFSET_ONLY_FOR_FIRST)) $counter = $maskoffset;
  906. if ($mode == 'last') // We found value for counter = last counter value. Now need to get corresponding ref of invoice.
  907. {
  908. $counterpadded = str_pad($counter, dol_strlen($maskcounter), "0", STR_PAD_LEFT);
  909. // Define $maskLike
  910. $maskLike = dol_string_nospecial($mask);
  911. $maskLike = str_replace("%", "_", $maskLike);
  912. // Replace protected special codes with matching number of _ as wild card caracter
  913. $maskLike = preg_replace('/\{yyyy\}/i', '____', $maskLike);
  914. $maskLike = preg_replace('/\{yy\}/i', '__', $maskLike);
  915. $maskLike = preg_replace('/\{y\}/i', '_', $maskLike);
  916. $maskLike = preg_replace('/\{mm\}/i', '__', $maskLike);
  917. $maskLike = preg_replace('/\{dd\}/i', '__', $maskLike);
  918. $maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'), $counterpadded, $maskLike);
  919. if ($maskrefclient) $maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'), str_pad("", dol_strlen($maskrefclient), "_"), $maskLike);
  920. if ($masktype) $maskLike = str_replace(dol_string_nospecial('{'.$masktype.'}'), $masktype_value, $maskLike);
  921. if ($maskuser) $maskLike = str_replace(dol_string_nospecial('{'.$maskuser.'}'), $maskuser_value, $maskLike);
  922. $ref = '';
  923. $sql = "SELECT ".$field." as ref";
  924. $sql .= " FROM ".MAIN_DB_PREFIX.$table;
  925. $sql .= " WHERE ".$field." LIKE '".$db->escape($maskLike)."'";
  926. $sql .= " AND ".$field." NOT LIKE '%PROV%'";
  927. if ($bentityon) // only if entity enable
  928. $sql .= " AND entity IN (".getEntity($sharetable).")";
  929. elseif (!empty($forceentity))
  930. $sql .= " AND entity IN (".$forceentity.")";
  931. if ($where) $sql .= $where;
  932. if ($sqlwhere) $sql .= ' AND '.$sqlwhere;
  933. dol_syslog("functions2::get_next_value mode=".$mode."", LOG_DEBUG);
  934. $resql = $db->query($sql);
  935. if ($resql)
  936. {
  937. $obj = $db->fetch_object($resql);
  938. if ($obj) $ref = $obj->ref;
  939. } else dol_print_error($db);
  940. $numFinal = $ref;
  941. } elseif ($mode == 'next')
  942. {
  943. $counter++;
  944. // If value for $counter has a length higher than $maskcounter chars
  945. if ($counter >= pow(10, dol_strlen($maskcounter)))
  946. {
  947. $counter = 'ErrorMaxNumberReachForThisMask';
  948. }
  949. if (!empty($maskrefclient_maskcounter))
  950. {
  951. //print "maskrefclient_maskcounter=".$maskrefclient_maskcounter." maskwithnocode=".$maskwithnocode." maskrefclient=".$maskrefclient."\n<br>";
  952. // Define $sqlstring
  953. $maskrefclient_posnumstart = strpos($maskwithnocode, $maskrefclient_maskcounter, strpos($maskwithnocode, $maskrefclient)); // Pos of counter in final string (from 0 to ...)
  954. if ($maskrefclient_posnumstart <= 0) return 'ErrorBadMask';
  955. $maskrefclient_sqlstring = 'SUBSTRING('.$field.', '.($maskrefclient_posnumstart + 1).', '.dol_strlen($maskrefclient_maskcounter).')';
  956. //print "x".$sqlstring;
  957. // Define $maskrefclient_maskLike
  958. $maskrefclient_maskLike = dol_string_nospecial($mask);
  959. $maskrefclient_maskLike = str_replace("%", "_", $maskrefclient_maskLike);
  960. // Replace protected special codes with matching number of _ as wild card caracter
  961. $maskrefclient_maskLike = str_replace(dol_string_nospecial('{yyyy}'), '____', $maskrefclient_maskLike);
  962. $maskrefclient_maskLike = str_replace(dol_string_nospecial('{yy}'), '__', $maskrefclient_maskLike);
  963. $maskrefclient_maskLike = str_replace(dol_string_nospecial('{y}'), '_', $maskrefclient_maskLike);
  964. $maskrefclient_maskLike = str_replace(dol_string_nospecial('{mm}'), '__', $maskrefclient_maskLike);
  965. $maskrefclient_maskLike = str_replace(dol_string_nospecial('{dd}'), '__', $maskrefclient_maskLike);
  966. $maskrefclient_maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'), str_pad("", dol_strlen($maskcounter), "_"), $maskrefclient_maskLike);
  967. $maskrefclient_maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'), $maskrefclient_clientcode.str_pad("", dol_strlen($maskrefclient_maskcounter), "_"), $maskrefclient_maskLike);
  968. // Get counter in database
  969. $maskrefclient_counter = 0;
  970. $maskrefclient_sql = "SELECT MAX(".$maskrefclient_sqlstring.") as val";
  971. $maskrefclient_sql .= " FROM ".MAIN_DB_PREFIX.$table;
  972. //$sql.= " WHERE ".$field." not like '(%'";
  973. $maskrefclient_sql .= " WHERE ".$field." LIKE '".$db->escape($maskrefclient_maskLike)."'";
  974. if ($bentityon) // only if entity enable
  975. $maskrefclient_sql .= " AND entity IN (".getEntity($sharetable).")";
  976. elseif (!empty($forceentity))
  977. $sql .= " AND entity IN (".$forceentity.")";
  978. if ($where) $maskrefclient_sql .= $where; //use the same optional where as general mask
  979. if ($sqlwhere) $maskrefclient_sql .= ' AND '.$sqlwhere; //use the same sqlwhere as general mask
  980. $maskrefclient_sql .= ' AND (SUBSTRING('.$field.', '.(strpos($maskwithnocode, $maskrefclient) + 1).', '.dol_strlen($maskrefclient_maskclientcode).")='".$db->escape($maskrefclient_clientcode)."')";
  981. dol_syslog("functions2::get_next_value maskrefclient", LOG_DEBUG);
  982. $maskrefclient_resql = $db->query($maskrefclient_sql);
  983. if ($maskrefclient_resql)
  984. {
  985. $maskrefclient_obj = $db->fetch_object($maskrefclient_resql);
  986. $maskrefclient_counter = $maskrefclient_obj->val;
  987. } else dol_print_error($db);
  988. if (empty($maskrefclient_counter) || preg_match('/[^0-9]/i', $maskrefclient_counter)) $maskrefclient_counter = $maskrefclient_maskoffset;
  989. $maskrefclient_counter++;
  990. }
  991. // Build numFinal
  992. $numFinal = $mask;
  993. // We replace special codes except refclient
  994. if (!empty($yearoffsettype) && !is_numeric($yearoffsettype) && $yearoffsettype != '=') // yearoffsettype is - or +, so we don't want current year
  995. {
  996. $numFinal = preg_replace('/\{yyyy\}/i', date("Y", $date) + $yearoffset, $numFinal);
  997. $numFinal = preg_replace('/\{yy\}/i', date("y", $date) + $yearoffset, $numFinal);
  998. $numFinal = preg_replace('/\{y\}/i', substr(date("y", $date), 1, 1) + $yearoffset, $numFinal);
  999. } else // we want yyyy to be current year
  1000. {
  1001. $numFinal = preg_replace('/\{yyyy\}/i', date("Y", $date), $numFinal);
  1002. $numFinal = preg_replace('/\{yy\}/i', date("y", $date), $numFinal);
  1003. $numFinal = preg_replace('/\{y\}/i', substr(date("y", $date), 1, 1), $numFinal);
  1004. }
  1005. $numFinal = preg_replace('/\{mm\}/i', date("m", $date), $numFinal);
  1006. $numFinal = preg_replace('/\{dd\}/i', date("d", $date), $numFinal);
  1007. // Now we replace the counter
  1008. $maskbefore = '{'.$masktri.'}';
  1009. $maskafter = str_pad($counter, dol_strlen($maskcounter), "0", STR_PAD_LEFT);
  1010. //print 'x'.$maskbefore.'-'.$maskafter.'y';
  1011. $numFinal = str_replace($maskbefore, $maskafter, $numFinal);
  1012. // Now we replace the refclient
  1013. if ($maskrefclient)
  1014. {
  1015. //print "maskrefclient=".$maskrefclient." maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode." maskrefclient_clientcode=".$maskrefclient_clientcode."\n<br>";exit;
  1016. $maskrefclient_maskbefore = '{'.$maskrefclient.'}';
  1017. $maskrefclient_maskafter = $maskrefclient_clientcode.str_pad($maskrefclient_counter, dol_strlen($maskrefclient_maskcounter), "0", STR_PAD_LEFT);
  1018. $numFinal = str_replace($maskrefclient_maskbefore, $maskrefclient_maskafter, $numFinal);
  1019. }
  1020. // Now we replace the type
  1021. if ($masktype)
  1022. {
  1023. $masktype_maskbefore = '{'.$masktype.'}';
  1024. $masktype_maskafter = $masktype_value;
  1025. $numFinal = str_replace($masktype_maskbefore, $masktype_maskafter, $numFinal);
  1026. }
  1027. // Now we replace the user
  1028. if ($maskuser)
  1029. {
  1030. $maskuser_maskbefore = '{'.$maskuser.'}';
  1031. $maskuser_maskafter = $maskuser_value;
  1032. $numFinal = str_replace($maskuser_maskbefore, $maskuser_maskafter, $numFinal);
  1033. }
  1034. }
  1035. dol_syslog("functions2::get_next_value return ".$numFinal, LOG_DEBUG);
  1036. return $numFinal;
  1037. }
  1038. /**
  1039. * Get string between
  1040. *
  1041. * @param string $string String to test
  1042. * @param int $start Value for start
  1043. * @param int $end Value for end
  1044. * @return string Return part of string
  1045. */
  1046. function get_string_between($string, $start, $end)
  1047. {
  1048. $string = " ".$string;
  1049. $ini = strpos($string, $start);
  1050. if ($ini == 0) return "";
  1051. $ini += strlen($start);
  1052. $len = strpos($string, $end, $ini) - $ini;
  1053. return substr($string, $ini, $len);
  1054. }
  1055. /**
  1056. * Check value
  1057. *
  1058. * @param string $mask Mask to use
  1059. * @param string $value Value
  1060. * @return int|string <0 or error string if KO, 0 if OK
  1061. */
  1062. function check_value($mask, $value)
  1063. {
  1064. $result = 0;
  1065. $hasglobalcounter = false;
  1066. // Extract value for mask counter, mask raz and mask offset
  1067. $reg = array();
  1068. if (preg_match('/\{(0+)([@\+][0-9]+)?([@\+][0-9]+)?\}/i', $mask, $reg))
  1069. {
  1070. $masktri = $reg[1].(isset($reg[2]) ? $reg[2] : '').(isset($reg[3]) ? $reg[3] : '');
  1071. $maskcounter = $reg[1];
  1072. $hasglobalcounter = true;
  1073. } else {
  1074. // setting some defaults so the rest of the code won't fail if there is a third party counter
  1075. $masktri = '00000';
  1076. $maskcounter = '00000';
  1077. }
  1078. $maskraz = -1;
  1079. $maskoffset = 0;
  1080. if (dol_strlen($maskcounter) < 3) return 'ErrorCounterMustHaveMoreThan3Digits';
  1081. // Extract value for third party mask counter
  1082. $regClientRef = array();
  1083. if (preg_match('/\{(c+)(0*)\}/i', $mask, $regClientRef))
  1084. {
  1085. $maskrefclient = $regClientRef[1].$regClientRef[2];
  1086. $maskrefclient_maskclientcode = $regClientRef[1];
  1087. $maskrefclient_maskcounter = $regClientRef[2];
  1088. $maskrefclient_maskoffset = 0; //default value of maskrefclient_counter offset
  1089. $maskrefclient_clientcode = substr('', 0, dol_strlen($maskrefclient_maskclientcode)); //get n first characters of client code to form maskrefclient_clientcode
  1090. $maskrefclient_clientcode = str_pad($maskrefclient_clientcode, dol_strlen($maskrefclient_maskclientcode), "#", STR_PAD_RIGHT); //padding maskrefclient_clientcode for having exactly n characters in maskrefclient_clientcode
  1091. $maskrefclient_clientcode = dol_string_nospecial($maskrefclient_clientcode); //sanitize maskrefclient_clientcode for sql insert and sql select like
  1092. if (dol_strlen($maskrefclient_maskcounter) > 0 && dol_strlen($maskrefclient_maskcounter) < 3) return 'ErrorCounterMustHaveMoreThan3Digits';
  1093. } else $maskrefclient = '';
  1094. // fail if there is neither a global nor a third party counter
  1095. if (!$hasglobalcounter && ($maskrefclient_maskcounter == ''))
  1096. {
  1097. return 'ErrorBadMask';
  1098. }
  1099. $maskwithonlyymcode = $mask;
  1100. $maskwithonlyymcode = preg_replace('/\{(0+)([@\+][0-9]+)?([@\+][0-9]+)?\}/i', $maskcounter, $maskwithonlyymcode);
  1101. $maskwithonlyymcode = preg_replace('/\{dd\}/i', 'dd', $maskwithonlyymcode);
  1102. $maskwithonlyymcode = preg_replace('/\{(c+)(0*)\}/i', $maskrefclient, $maskwithonlyymcode);
  1103. $maskwithnocode = $maskwithonlyymcode;
  1104. $maskwithnocode = preg_replace('/\{yyyy\}/i', 'yyyy', $maskwithnocode);
  1105. $maskwithnocode = preg_replace('/\{yy\}/i', 'yy', $maskwithnocode);
  1106. $maskwithnocode = preg_replace('/\{y\}/i', 'y', $maskwithnocode);
  1107. $maskwithnocode = preg_replace('/\{mm\}/i', 'mm', $maskwithnocode);
  1108. // Now maskwithnocode = 0000ddmmyyyyccc for example
  1109. // and maskcounter = 0000 for example
  1110. //print "maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode."\n<br>";
  1111. // If an offset is asked
  1112. if (!empty($reg[2]) && preg_match('/^\+/', $reg[2])) $maskoffset = preg_replace('/^\+/', '', $reg[2]);
  1113. if (!empty($reg[3]) && preg_match('/^\+/', $reg[3])) $maskoffset = preg_replace('/^\+/', '', $reg[3]);
  1114. // Define $sqlwhere
  1115. // If a restore to zero after a month is asked we check if there is already a value for this year.
  1116. if (!empty($reg[2]) && preg_match('/^@/', $reg[2])) $maskraz = preg_replace('/^@/', '', $reg[2]);
  1117. if (!empty($reg[3]) && preg_match('/^@/', $reg[3])) $maskraz = preg_replace('/^@/', '', $reg[3]);
  1118. if ($maskraz >= 0)
  1119. {
  1120. if ($maskraz == 99) {
  1121. $maskraz = date('m');
  1122. $resetEveryMonth = true;
  1123. }
  1124. if ($maskraz > 12) return 'ErrorBadMaskBadRazMonth';
  1125. // Define reg
  1126. if ($maskraz > 1 && !preg_match('/^(.*)\{(y+)\}\{(m+)\}/i', $maskwithonlyymcode, $reg)) return 'ErrorCantUseRazInStartedYearIfNoYearMonthInMask';
  1127. if ($maskraz <= 1 && !preg_match('/^(.*)\{(y+)\}/i', $maskwithonlyymcode, $reg)) return 'ErrorCantUseRazIfNoYearInMask';
  1128. //print "x".$maskwithonlyymcode." ".$maskraz;
  1129. }
  1130. //print "masktri=".$masktri." maskcounter=".$maskcounter." maskraz=".$maskraz." maskoffset=".$maskoffset."<br>\n";
  1131. // Check we have a number in ($posnumstart+1).', '.dol_strlen($maskcounter)
  1132. //
  1133. // Check length
  1134. $len = dol_strlen($maskwithnocode);
  1135. if (dol_strlen($value) != $len) $result = -1;
  1136. // Define $maskLike
  1137. /* seems not used
  1138. $maskLike = dol_string_nospecial($mask);
  1139. $maskLike = str_replace("%","_",$maskLike);
  1140. // Replace protected special codes with matching number of _ as wild card caracter
  1141. $maskLike = str_replace(dol_string_nospecial('{yyyy}'),'____',$maskLike);
  1142. $maskLike = str_replace(dol_string_nospecial('{yy}'),'__',$maskLike);
  1143. $maskLike = str_replace(dol_string_nospecial('{y}'),'_',$maskLike);
  1144. $maskLike = str_replace(dol_string_nospecial('{mm}'),'__',$maskLike);
  1145. $maskLike = str_replace(dol_string_nospecial('{dd}'),'__',$maskLike);
  1146. $maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'),str_pad("",dol_strlen($maskcounter),"_"),$maskLike);
  1147. if ($maskrefclient) $maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'),str_pad("",strlen($maskrefclient),"_"),$maskLike);
  1148. */
  1149. dol_syslog("functions2::check_value result=".$result, LOG_DEBUG);
  1150. return $result;
  1151. }
  1152. /**
  1153. * Convert a binary data to string that represent hexadecimal value
  1154. *
  1155. * @param string $bin Value to convert
  1156. * @param boolean $pad Add 0
  1157. * @param boolean $upper Convert to tupper
  1158. * @return string x
  1159. */
  1160. function binhex($bin, $pad = false, $upper = false)
  1161. {
  1162. $last = dol_strlen($bin) - 1;
  1163. for ($i = 0; $i <= $last; $i++) { $x += $bin[$last - $i] * pow(2, $i); }
  1164. $x = dechex($x);
  1165. if ($pad) { while (dol_strlen($x) < intval(dol_strlen($bin)) / 4) { $x = "0$x"; } }
  1166. if ($upper) { $x = strtoupper($x); }
  1167. return $x;
  1168. }
  1169. /**
  1170. * Convert an hexadecimal string into a binary string
  1171. *
  1172. * @param string $hexa Hexadecimal string to convert (example: 'FF')
  1173. * @return string bin
  1174. */
  1175. function hexbin($hexa)
  1176. {
  1177. $bin = '';
  1178. $strLength = dol_strlen($hexa);
  1179. for ($i = 0; $i < $strLength; $i++)
  1180. {
  1181. $bin .= str_pad(decbin(hexdec($hexa[$i])), 4, '0', STR_PAD_LEFT);
  1182. }
  1183. return $bin;
  1184. }
  1185. /**
  1186. * Retourne le numero de la semaine par rapport a une date
  1187. *
  1188. * @param string $time Date au format 'timestamp'
  1189. * @return string Number of week
  1190. */
  1191. function numero_semaine($time)
  1192. {
  1193. $stime = strftime('%Y-%m-%d', $time);
  1194. if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/i', $stime, $reg))
  1195. {
  1196. // Date est au format 'YYYY-MM-DD' ou 'YYYY-MM-DD HH:MM:SS'
  1197. $annee = $reg[1];
  1198. $mois = $reg[2];
  1199. $jour = $reg[3];
  1200. }
  1201. /*
  1202. * Norme ISO-8601:
  1203. * - La semaine 1 de toute annee est celle qui contient le 4 janvier ou que la semaine 1 de toute annee est celle qui contient le 1er jeudi de janvier.
  1204. * - La majorite des annees ont 52 semaines mais les annees qui commence un jeudi et les annees bissextiles commencant un mercredi en possede 53.
  1205. * - Le 1er jour de la semaine est le Lundi
  1206. */
  1207. // Definition du Jeudi de la semaine
  1208. if (date("w", mktime(12, 0, 0, $mois, $jour, $annee)) == 0) // Dimanche
  1209. $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee) - 3 * 24 * 60 * 60;
  1210. elseif (date("w", mktime(12, 0, 0, $mois, $jour, $annee)) < 4) // du Lundi au Mercredi
  1211. $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee) + (4 - date("w", mktime(12, 0, 0, $mois, $jour, $annee))) * 24 * 60 * 60;
  1212. elseif (date("w", mktime(12, 0, 0, $mois, $jour, $annee)) > 4) // du Vendredi au Samedi
  1213. $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee) - (date("w", mktime(12, 0, 0, $mois, $jour, $annee)) - 4) * 24 * 60 * 60;
  1214. else // Jeudi
  1215. $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee);
  1216. // Definition du premier Jeudi de l'annee
  1217. if (date("w", mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine))) == 0) // Dimanche
  1218. {
  1219. $premierJeudiAnnee = mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine)) + 4 * 24 * 60 * 60;
  1220. } elseif (date("w", mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine))) < 4) // du Lundi au Mercredi
  1221. {
  1222. $premierJeudiAnnee = mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine)) + (4 - date("w", mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine)))) * 24 * 60 * 60;
  1223. } elseif (date("w", mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine))) > 4) // du Vendredi au Samedi
  1224. {
  1225. $premierJeudiAnnee = mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine)) + (7 - (date("w", mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine))) - 4)) * 24 * 60 * 60;
  1226. } else // Jeudi
  1227. {
  1228. $premierJeudiAnnee = mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine));
  1229. }
  1230. // Definition du numero de semaine: nb de jours entre "premier Jeudi de l'annee" et "Jeudi de la semaine";
  1231. $numeroSemaine = (
  1232. (
  1233. date("z", mktime(12, 0, 0, date("m", $jeudiSemaine), date("d", $jeudiSemaine), date("Y", $jeudiSemaine)))
  1234. -
  1235. date("z", mktime(12, 0, 0, date("m", $premierJeudiAnnee), date("d", $premierJeudiAnnee), date("Y", $premierJeudiAnnee)))
  1236. ) / 7
  1237. ) + 1;
  1238. // Cas particulier de la semaine 53
  1239. if ($numeroSemaine == 53)
  1240. {
  1241. // Les annees qui commence un Jeudi et les annees bissextiles commencant un Mercredi en possede 53
  1242. if (date("w", mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine))) == 4 || (date("w", mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine))) == 3 && date("z", mktime(12, 0, 0, 12, 31, date("Y", $jeudiSemaine))) == 365))
  1243. {
  1244. $numeroSemaine = 53;
  1245. } else {
  1246. $numeroSemaine = 1;
  1247. }
  1248. }
  1249. //echo $jour."-".$mois."-".$annee." (".date("d-m-Y",$premierJeudiAnnee)." - ".date("d-m-Y",$jeudiSemaine).") -> ".$numeroSemaine."<BR>";
  1250. return sprintf("%02d", $numeroSemaine);
  1251. }
  1252. /**
  1253. * Convertit une masse d'une unite vers une autre unite
  1254. *
  1255. * @param float $weight Masse a convertir
  1256. * @param int $from_unit Unite originale en puissance de 10
  1257. * @param int $to_unit Nouvelle unite en puissance de 10
  1258. * @return float Masse convertie
  1259. */
  1260. function weight_convert($weight, &$from_unit, $to_unit)
  1261. {
  1262. /* Pour convertire 320 gr en Kg appeler
  1263. * $f = -3
  1264. * weigh_convert(320, $f, 0) retournera 0.32
  1265. *
  1266. */
  1267. while ($from_unit <> $to_unit)
  1268. {
  1269. if ($from_unit > $to_unit)
  1270. {
  1271. $weight = $weight * 10;
  1272. $from_unit = $from_unit - 1;
  1273. $weight = weight_convert($weight, $from_unit, $to_unit);
  1274. }
  1275. if ($from_unit < $to_unit)
  1276. {
  1277. $weight = $weight / 10;
  1278. $from_unit = $from_unit + 1;
  1279. $weight = weight_convert($weight, $from_unit, $to_unit);
  1280. }
  1281. }
  1282. return $weight;
  1283. }
  1284. /**
  1285. * Save personnal parameter
  1286. *
  1287. * @param DoliDB $db Handler database
  1288. * @param Conf $conf Object conf
  1289. * @param User $user Object user
  1290. * @param array $tab Array (key=>value) with all parameters to save
  1291. * @return int <0 if KO, >0 if OK
  1292. *
  1293. * @see dolibarr_get_const(), dolibarr_set_const(), dolibarr_del_const()
  1294. */
  1295. function dol_set_user_param($db, $conf, &$user, $tab)
  1296. {
  1297. // Verification parametres
  1298. if (count($tab) < 1) return -1;
  1299. $db->begin();
  1300. // We remove old parameters for all keys in $tab
  1301. $sql = "DELETE FROM ".MAIN_DB_PREFIX."user_param";
  1302. $sql .= " WHERE fk_user = ".$user->id;
  1303. $sql .= " AND entity = ".$conf->entity;
  1304. $sql .= " AND param in (";
  1305. $i = 0;
  1306. foreach ($tab as $key => $value)
  1307. {
  1308. if ($i > 0) $sql .= ',';
  1309. $sql .= "'".$db->escape($key)."'";
  1310. $i++;
  1311. }
  1312. $sql .= ")";
  1313. dol_syslog("functions2.lib::dol_set_user_param", LOG_DEBUG);
  1314. $resql = $db->query($sql);
  1315. if (!$resql)
  1316. {
  1317. dol_print_error($db);
  1318. $db->rollback();
  1319. return -1;
  1320. }
  1321. foreach ($tab as $key => $value)
  1322. {
  1323. // Set new parameters
  1324. if ($value)
  1325. {
  1326. $sql = "INSERT INTO ".MAIN_DB_PREFIX."user_param(fk_user,entity,param,value)";
  1327. $sql .= " VALUES (".$user->id.",".$conf->entity.",";
  1328. $sql .= " '".$db->escape($key)."','".$db->escape($value)."')";
  1329. dol_syslog("functions2.lib::dol_set_user_param", LOG_DEBUG);
  1330. $result = $db->query($sql);
  1331. if (!$result)
  1332. {
  1333. dol_print_error($db);
  1334. $db->rollback();
  1335. return -1;
  1336. }
  1337. $user->conf->$key = $value;
  1338. //print "key=".$key." user->conf->key=".$user->conf->$key;
  1339. } else {
  1340. unset($user->conf->$key);
  1341. }
  1342. }
  1343. $db->commit();
  1344. return 1;
  1345. }
  1346. /**
  1347. * Returns formated reduction
  1348. *
  1349. * @param int $reduction Reduction percentage
  1350. * @param Translate $langs Output language
  1351. * @return string Formated reduction
  1352. */
  1353. function dol_print_reduction($reduction, $langs)
  1354. {
  1355. $string = '';
  1356. if ($reduction == 100)
  1357. {
  1358. $string = $langs->transnoentities("Offered");
  1359. } else {
  1360. $string = vatrate($reduction, true);
  1361. }
  1362. return $string;
  1363. }
  1364. /**
  1365. * Return OS version.
  1366. * Note that PHP_OS returns only OS (not version) and OS PHP was built on, not necessarly OS PHP runs on.
  1367. *
  1368. * @param string $option Option string
  1369. * @return string OS version
  1370. */
  1371. function version_os($option = '')
  1372. {
  1373. if ($option == 'smr') $osversion = php_uname('s').' '.php_uname('m').' '.php_uname('r');
  1374. else $osversion = php_uname();
  1375. return $osversion;
  1376. }
  1377. /**
  1378. * Return PHP version
  1379. *
  1380. * @return string PHP version
  1381. * @see versionphparray()
  1382. */
  1383. function version_php()
  1384. {
  1385. return phpversion();
  1386. }
  1387. /**
  1388. * Return Dolibarr version
  1389. *
  1390. * @return string Dolibarr version
  1391. * @see versiondolibarrarray()
  1392. */
  1393. function version_dolibarr()
  1394. {
  1395. return DOL_VERSION;
  1396. }
  1397. /**
  1398. * Return web server version
  1399. *
  1400. * @return string Web server version
  1401. */
  1402. function version_webserver()
  1403. {
  1404. return $_SERVER["SERVER_SOFTWARE"];
  1405. }
  1406. /**
  1407. * Return list of activated modules usable for document generation
  1408. *
  1409. * @param DoliDB $db Database handler
  1410. * @param string $type Type of models (company, invoice, ...)
  1411. * @param int $maxfilenamelength Max length of value to show
  1412. * @return mixed 0 if no module is activated, or array(key=>label). For modules that need directory scan, key is completed with ":filename".
  1413. */
  1414. function getListOfModels($db, $type, $maxfilenamelength = 0)
  1415. {
  1416. global $conf, $langs;
  1417. $liste = array();
  1418. $found = 0;
  1419. $dirtoscan = '';
  1420. $sql = "SELECT nom as id, nom as doc_template_name, libelle as label, description as description";
  1421. $sql .= " FROM ".MAIN_DB_PREFIX."document_model";
  1422. $sql .= " WHERE type = '".$db->escape($type)."'";
  1423. $sql .= " AND entity IN (0,".$conf->entity.")";
  1424. $sql .= " ORDER BY description DESC";
  1425. dol_syslog('/core/lib/function2.lib.php::getListOfModels', LOG_DEBUG);
  1426. $resql = $db->query($sql);
  1427. if ($resql)
  1428. {
  1429. $num = $db->num_rows($resql);
  1430. $i = 0;
  1431. while ($i < $num)
  1432. {
  1433. $found = 1;
  1434. $obj = $db->fetch_object($resql);
  1435. // If this generation module needs to scan a directory, then description field is filled
  1436. // with the constant that contains list of directories to scan (COMPANY_ADDON_PDF_ODT_PATH, ...).
  1437. if (!empty($obj->description)) // A list of directories to scan is defined
  1438. {
  1439. include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  1440. $const = $obj->description;
  1441. //irtoscan.=($dirtoscan?',':'').preg_replace('/[\r\n]+/',',',trim($conf->global->$const));
  1442. $dirtoscan = preg_replace('/[\r\n]+/', ',', trim($conf->global->$const));
  1443. $listoffiles = array();
  1444. // Now we add models found in directories scanned
  1445. $listofdir = explode(',', $dirtoscan);
  1446. foreach ($listofdir as $key=>$tmpdir)
  1447. {
  1448. $tmpdir = trim($tmpdir);
  1449. $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
  1450. if (!$tmpdir) { unset($listofdir[$key]); continue; }
  1451. if (is_dir($tmpdir))
  1452. {
  1453. // all type of template is allowed
  1454. $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '', '', 'name', SORT_ASC, 0);
  1455. if (count($tmpfiles)) $listoffiles = array_merge($listoffiles, $tmpfiles);
  1456. }
  1457. }
  1458. if (count($listoffiles))
  1459. {
  1460. foreach ($listoffiles as $record)
  1461. {
  1462. $max = ($maxfilenamelength ? $maxfilenamelength : 28);
  1463. $liste[$obj->id.':'.$record['fullname']] = dol_trunc($record['name'], $max, 'middle');
  1464. }
  1465. } else {
  1466. $liste[0] = $obj->label.': '.$langs->trans("None");
  1467. }
  1468. } else {
  1469. if ($type == 'member' && $obj->doc_template_name == 'standard') // Special case, if member template, we add variant per format
  1470. {
  1471. global $_Avery_Labels;
  1472. include_once DOL_DOCUMENT_ROOT.'/core/lib/format_cards.lib.php';
  1473. foreach ($_Avery_Labels as $key => $val)
  1474. {
  1475. $liste[$obj->id.':'.$key] = ($obj->label ? $obj->label : $obj->doc_template_name).' '.$val['name'];
  1476. }
  1477. } else // Common usage
  1478. {
  1479. $liste[$obj->id] = $obj->label ? $obj->label : $obj->doc_template_name;
  1480. }
  1481. }
  1482. $i++;
  1483. }
  1484. } else {
  1485. dol_print_error($db);
  1486. return -1;
  1487. }
  1488. if ($found) return $liste;
  1489. else return 0;
  1490. }
  1491. /**
  1492. * This function evaluates a string that should be a valid IPv4
  1493. * Note: For ip 169.254.0.0, it returns 0 with some PHP (5.6.24) and 2 with some minor patchs of PHP (5.6.25). See https://github.com/php/php-src/pull/1954.
  1494. *
  1495. * @param string $ip IP Address
  1496. * @return int 0 if not valid or reserved range, 1 if valid and public IP, 2 if valid and private range IP
  1497. */
  1498. function is_ip($ip)
  1499. {
  1500. // First we test if it is a valid IPv4
  1501. if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
  1502. // Then we test if it is a private range
  1503. if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) return 2;
  1504. // Then we test if it is a reserved range
  1505. if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE)) return 0;
  1506. return 1;
  1507. }
  1508. return 0;
  1509. }
  1510. /**
  1511. * Build a login from lastname, firstname
  1512. *
  1513. * @param string $lastname Lastname
  1514. * @param string $firstname Firstname
  1515. * @return string Login
  1516. */
  1517. function dol_buildlogin($lastname, $firstname)
  1518. {
  1519. $login = strtolower(dol_string_unaccent($firstname));
  1520. $login .= ($login ? '.' : '');
  1521. $login .= strtolower(dol_string_unaccent($lastname));
  1522. $login = dol_string_nospecial($login, ''); // For special names
  1523. return $login;
  1524. }
  1525. /**
  1526. * Return array to use for SoapClient constructor
  1527. *
  1528. * @return array
  1529. */
  1530. function getSoapParams()
  1531. {
  1532. global $conf;
  1533. $params = array();
  1534. $proxyuse = (empty($conf->global->MAIN_PROXY_USE) ?false:true);
  1535. $proxyhost = (empty($conf->global->MAIN_PROXY_USE) ?false:$conf->global->MAIN_PROXY_HOST);
  1536. $proxyport = (empty($conf->global->MAIN_PROXY_USE) ?false:$conf->global->MAIN_PROXY_PORT);
  1537. $proxyuser = (empty($conf->global->MAIN_PROXY_USE) ?false:$conf->global->MAIN_PROXY_USER);
  1538. $proxypass = (empty($conf->global->MAIN_PROXY_USE) ?false:$conf->global->MAIN_PROXY_PASS);
  1539. $timeout = (empty($conf->global->MAIN_USE_CONNECT_TIMEOUT) ? 10 : $conf->global->MAIN_USE_CONNECT_TIMEOUT); // Connection timeout
  1540. $response_timeout = (empty($conf->global->MAIN_USE_RESPONSE_TIMEOUT) ? 30 : $conf->global->MAIN_USE_RESPONSE_TIMEOUT); // Response timeout
  1541. //print extension_loaded('soap');
  1542. if ($proxyuse)
  1543. {
  1544. $params = array('connection_timeout'=>$timeout,
  1545. 'response_timeout'=>$response_timeout,
  1546. 'proxy_use' => 1,
  1547. 'proxy_host' => $proxyhost,
  1548. 'proxy_port' => $proxyport,
  1549. 'proxy_login' => $proxyuser,
  1550. 'proxy_password' => $proxypass,
  1551. 'trace' => 1
  1552. );
  1553. } else {
  1554. $params = array('connection_timeout'=>$timeout,
  1555. 'response_timeout'=>$response_timeout,
  1556. 'proxy_use' => 0,
  1557. 'proxy_host' => false,
  1558. 'proxy_port' => false,
  1559. 'proxy_login' => false,
  1560. 'proxy_password' => false,
  1561. 'trace' => 1
  1562. );
  1563. }
  1564. return $params;
  1565. }
  1566. /**
  1567. * Return link url to an object
  1568. *
  1569. * @param int $objectid Id of record
  1570. * @param string $objecttype Type of object ('invoice', 'order', 'expedition_bon', 'myobject@mymodule', ...)
  1571. * @param int $withpicto Picto to show
  1572. * @param string $option More options
  1573. * @return string URL of link to object id/type
  1574. */
  1575. function dolGetElementUrl($objectid, $objecttype, $withpicto = 0, $option = '')
  1576. {
  1577. global $db, $conf, $langs;
  1578. $ret = '';
  1579. $regs = array();
  1580. // If we ask a resource form external module (instead of default path)
  1581. if (preg_match('/^([^@]+)@([^@]+)$/i', $objecttype, $regs)) {
  1582. $myobject = $regs[1];
  1583. $module = $regs[2];
  1584. }
  1585. else {
  1586. // Parse $objecttype (ex: project_task)
  1587. $module = $myobject = $objecttype;
  1588. if (preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs))
  1589. {
  1590. $module = $regs[1];
  1591. $myobject = $regs[2];
  1592. }
  1593. }
  1594. // Generic case for $classpath
  1595. $classpath = $module.'/class';
  1596. // Special cases, to work with non standard path
  1597. if ($objecttype == 'facture' || $objecttype == 'invoice') {
  1598. $classpath = 'compta/facture/class';
  1599. $module = 'facture';
  1600. $myobject = 'facture';
  1601. } elseif ($objecttype == 'commande' || $objecttype == 'order') {
  1602. $classpath = 'commande/class';
  1603. $module = 'commande';
  1604. $myobject = 'commande';
  1605. } elseif ($objecttype == 'propal') {
  1606. $classpath = 'comm/propal/class';
  1607. } elseif ($objecttype == 'supplier_proposal') {
  1608. $classpath = 'supplier_proposal/class';
  1609. } elseif ($objecttype == 'shipping') {
  1610. $classpath = 'expedition/class';
  1611. $myobject = 'expedition';
  1612. $module = 'expedition_bon';
  1613. } elseif ($objecttype == 'delivery') {
  1614. $classpath = 'delivery/class';
  1615. $myobject = 'delivery';
  1616. $module = 'livraison_bon';
  1617. } elseif ($objecttype == 'contract') {
  1618. $classpath = 'contrat/class';
  1619. $module = 'contrat';
  1620. $myobject = 'contrat';
  1621. } elseif ($objecttype == 'member') {
  1622. $classpath = 'adherents/class';
  1623. $module = 'adherent';
  1624. $myobject = 'adherent';
  1625. } elseif ($objecttype == 'cabinetmed_cons') {
  1626. $classpath = 'cabinetmed/class';
  1627. $module = 'cabinetmed';
  1628. $myobject = 'cabinetmedcons';
  1629. } elseif ($objecttype == 'fichinter') {
  1630. $classpath = 'fichinter/class';
  1631. $module = 'ficheinter';
  1632. $myobject = 'fichinter';
  1633. } elseif ($objecttype == 'task') {
  1634. $classpath = 'projet/class';
  1635. $module = 'projet';
  1636. $myobject = 'task';
  1637. } elseif ($objecttype == 'stock') {
  1638. $classpath = 'product/stock/class';
  1639. $module = 'stock';
  1640. $myobject = 'stock';
  1641. } elseif ($objecttype == 'inventory') {
  1642. $classpath = 'product/inventory/class';
  1643. $module = 'stock';
  1644. $myobject = 'inventory';
  1645. } elseif ($objecttype == 'mo') {
  1646. $classpath = 'mrp/class';
  1647. $module = 'mrp';
  1648. $myobject = 'mo';
  1649. }
  1650. // Generic case for $classfile and $classname
  1651. $classfile = strtolower($myobject); $classname = ucfirst($myobject);
  1652. //print "objecttype=".$objecttype." module=".$module." subelement=".$subelement." classfile=".$classfile." classname=".$classname." classpath=".$classpath;
  1653. if ($objecttype == 'invoice_supplier') {
  1654. $classfile = 'fournisseur.facture';
  1655. $classname = 'FactureFournisseur';
  1656. $classpath = 'fourn/class';
  1657. $module = 'fournisseur';
  1658. } elseif ($objecttype == 'order_supplier') {
  1659. $classfile = 'fournisseur.commande';
  1660. $classname = 'CommandeFournisseur';
  1661. $classpath = 'fourn/class';
  1662. $module = 'fournisseur';
  1663. } elseif ($objecttype == 'stock') {
  1664. $classpath = 'product/stock/class';
  1665. $classfile = 'entrepot';
  1666. $classname = 'Entrepot';
  1667. }
  1668. if (!empty($conf->$module->enabled))
  1669. {
  1670. $res = dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
  1671. if ($res)
  1672. {
  1673. if (class_exists($classname))
  1674. {
  1675. $object = new $classname($db);
  1676. $res = $object->fetch($objectid);
  1677. if ($res > 0) {
  1678. $ret = $object->getNomUrl($withpicto, $option);
  1679. } elseif ($res == 0) {
  1680. $ret = $langs->trans('Deleted');
  1681. }
  1682. unset($object);
  1683. } else dol_syslog("Class with classname ".$classname." is unknown even after the include", LOG_ERR);
  1684. }
  1685. }
  1686. return $ret;
  1687. }
  1688. /**
  1689. * Clean corrupted tree (orphelins linked to a not existing parent), record linked to themself and child-parent loop
  1690. *
  1691. * @param DoliDB $db Database handler
  1692. * @param string $tabletocleantree Table to clean
  1693. * @param string $fieldfkparent Field name that contains id of parent
  1694. * @return int Nb of records fixed/deleted
  1695. */
  1696. function cleanCorruptedTree($db, $tabletocleantree, $fieldfkparent)
  1697. {
  1698. $totalnb = 0;
  1699. $listofid = array();
  1700. $listofparentid = array();
  1701. // Get list of all id in array listofid and all parents in array listofparentid
  1702. $sql = 'SELECT rowid, '.$fieldfkparent.' as parent_id FROM '.MAIN_DB_PREFIX.$tabletocleantree;
  1703. $resql = $db->query($sql);
  1704. if ($resql)
  1705. {
  1706. $num = $db->num_rows($resql);
  1707. $i = 0;
  1708. while ($i < $num)
  1709. {
  1710. $obj = $db->fetch_object($resql);
  1711. $listofid[] = $obj->rowid;
  1712. if ($obj->parent_id > 0) $listofparentid[$obj->rowid] = $obj->parent_id;
  1713. $i++;
  1714. }
  1715. } else {
  1716. dol_print_error($db);
  1717. }
  1718. if (count($listofid))
  1719. {
  1720. print 'Code requested to clean tree (may be to solve data corruption), so we check/clean orphelins and loops.'."<br>\n";
  1721. // Check loops on each other
  1722. $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree." SET ".$fieldfkparent." = 0 WHERE ".$fieldfkparent." = rowid"; // So we update only records linked to themself
  1723. $resql = $db->query($sql);
  1724. if ($resql)
  1725. {
  1726. $nb = $db->affected_rows($sql);
  1727. if ($nb > 0)
  1728. {
  1729. print '<br>Some record that were parent of themself were cleaned.';
  1730. }
  1731. $totalnb += $nb;
  1732. }
  1733. //else dol_print_error($db);
  1734. // Check other loops
  1735. $listofidtoclean = array();
  1736. foreach ($listofparentid as $id => $pid)
  1737. {
  1738. // Check depth
  1739. //print 'Analyse record id='.$id.' with parent '.$pid.'<br>';
  1740. $cursor = $id; $arrayidparsed = array(); // We start from child $id
  1741. while ($cursor > 0)
  1742. {
  1743. $arrayidparsed[$cursor] = 1;
  1744. if ($arrayidparsed[$listofparentid[$cursor]]) // We detect a loop. A record with a parent that was already into child
  1745. {
  1746. print 'Found a loop between id '.$id.' - '.$cursor.'<br>';
  1747. unset($arrayidparsed);
  1748. $listofidtoclean[$cursor] = $id;
  1749. break;
  1750. }
  1751. $cursor = $listofparentid[$cursor];
  1752. }
  1753. if (count($listofidtoclean)) break;
  1754. }
  1755. $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree;
  1756. $sql .= " SET ".$fieldfkparent." = 0";
  1757. $sql .= " WHERE rowid IN (".join(',', $listofidtoclean).")"; // So we update only records detected wrong
  1758. $resql = $db->query($sql);
  1759. if ($resql)
  1760. {
  1761. $nb = $db->affected_rows($sql);
  1762. if ($nb > 0)
  1763. {
  1764. // Removed orphelins records
  1765. print '<br>Some records were detected to have parent that is a child, we set them as root record for id: ';
  1766. print join(',', $listofidtoclean);
  1767. }
  1768. $totalnb += $nb;
  1769. }
  1770. //else dol_print_error($db);
  1771. // Check and clean orphelins
  1772. $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree;
  1773. $sql .= " SET ".$fieldfkparent." = 0";
  1774. $sql .= " WHERE ".$fieldfkparent." NOT IN (".join(',', $listofid).")"; // So we update only records linked to a non existing parent
  1775. $resql = $db->query($sql);
  1776. if ($resql)
  1777. {
  1778. $nb = $db->affected_rows($sql);
  1779. if ($nb > 0)
  1780. {
  1781. // Removed orphelins records
  1782. print '<br>Some orphelins were found and modified to be parent so records are visible again for id: ';
  1783. print join(',', $listofid);
  1784. }
  1785. $totalnb += $nb;
  1786. }
  1787. //else dol_print_error($db);
  1788. print '<br>We fixed '.$totalnb.' record(s). Some records may still be corrupted. New check may be required.';
  1789. return $totalnb;
  1790. }
  1791. }
  1792. /**
  1793. * Convert an array with RGB value into hex RGB value.
  1794. * This is the opposite function of colorStringToArray
  1795. *
  1796. * @param array $arraycolor Array
  1797. * @param string $colorifnotfound Color code to return if entry not defined or not a RGB format
  1798. * @return string RGB hex value (without # before). For example: 'FF00FF', '01FF02'
  1799. * @see colorStringToArray(), colorHexToRgb()
  1800. */
  1801. function colorArrayToHex($arraycolor, $colorifnotfound = '888888')
  1802. {
  1803. if (!is_array($arraycolor)) return $colorifnotfound;
  1804. if (empty($arraycolor)) return $colorifnotfound;
  1805. return sprintf("%02s", dechex($arraycolor[0])).sprintf("%02s", dechex($arraycolor[1])).sprintf("%02s", dechex($arraycolor[2]));
  1806. }
  1807. /**
  1808. * Convert a string RGB value ('FFFFFF', '255,255,255') into an array RGB array(255,255,255).
  1809. * This is the opposite function of colorArrayToHex.
  1810. * If entry is already an array, return it.
  1811. *
  1812. * @param string $stringcolor String with hex (FFFFFF) or comma RGB ('255,255,255')
  1813. * @param array $colorifnotfound Color code array to return if entry not defined
  1814. * @return array RGB hex value (without # before). For example: FF00FF
  1815. * @see colorArrayToHex(), colorHexToRgb()
  1816. */
  1817. function colorStringToArray($stringcolor, $colorifnotfound = array(88, 88, 88))
  1818. {
  1819. if (is_array($stringcolor)) return $stringcolor; // If already into correct output format, we return as is
  1820. $reg = array();
  1821. $tmp = preg_match('/^#?([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])$/', $stringcolor, $reg);
  1822. if (!$tmp)
  1823. {
  1824. $tmp = explode(',', $stringcolor);
  1825. if (count($tmp) < 3) return $colorifnotfound;
  1826. return $tmp;
  1827. }
  1828. return array(hexdec($reg[1]), hexdec($reg[2]), hexdec($reg[3]));
  1829. }
  1830. /**
  1831. * @param string $color the color you need to valid
  1832. * @param boolean $allow_white in case of white isn't valid
  1833. * @return boolean
  1834. */
  1835. function colorValidateHex($color, $allow_white = true)
  1836. {
  1837. if (!$allow_white && ($color === '#fff' || $color === '#ffffff')) return false;
  1838. if (preg_match('/^#[a-f0-9]{6}$/i', $color)) //hex color is valid
  1839. {
  1840. return true;
  1841. }
  1842. return false;
  1843. }
  1844. /**
  1845. * Change color to make it less aggressive (ratio is negative) or more aggressive (ratio is positive)
  1846. *
  1847. * @param string $hex Color in hex ('#AA1122' or 'AA1122' or '#a12' or 'a12')
  1848. * @param integer $ratio Default=-50. Note: 0=Component color is unchanged, -100=Component color become 88, +100=Component color become 00 or FF
  1849. * @param integer $brightness Default=0. Adjust brightness. -100=Decrease brightness by 100%, +100=Increase of 100%.
  1850. * @return string New string of color
  1851. * @see colorAdjustBrightness()
  1852. */
  1853. function colorAgressiveness($hex, $ratio = -50, $brightness = 0)
  1854. {
  1855. if (empty($ratio)) $ratio = 0; // To avoid null
  1856. // Steps should be between -255 and 255. Negative = darker, positive = lighter
  1857. $ratio = max(-100, min(100, $ratio));
  1858. // Normalize into a six character long hex string
  1859. $hex = str_replace('#', '', $hex);
  1860. if (strlen($hex) == 3) {
  1861. $hex = str_repeat(substr($hex, 0, 1), 2).str_repeat(substr($hex, 1, 1), 2).str_repeat(substr($hex, 2, 1), 2);
  1862. }
  1863. // Split into three parts: R, G and B
  1864. $color_parts = str_split($hex, 2);
  1865. $return = '#';
  1866. foreach ($color_parts as $color) {
  1867. $color = hexdec($color); // Convert to decimal
  1868. if ($ratio > 0) // We increase aggressivity
  1869. {
  1870. if ($color > 127) $color += ((255 - $color) * ($ratio / 100));
  1871. if ($color < 128) $color -= ($color * ($ratio / 100));
  1872. } else // We decrease agressiveness
  1873. {
  1874. if ($color > 128) $color -= (($color - 128) * (abs($ratio) / 100));
  1875. if ($color < 127) $color += ((128 - $color) * (abs($ratio) / 100));
  1876. }
  1877. if ($brightness > 0)
  1878. {
  1879. $color = ($color * (100 + abs($brightness)) / 100);
  1880. } else {
  1881. $color = ($color * (100 - abs($brightness)) / 100);
  1882. }
  1883. $color = max(0, min(255, $color)); // Adjust color to stay into valid range
  1884. $return .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT); // Make two char hex code
  1885. }
  1886. //var_dump($hex.' '.$ratio.' -> '.$return);
  1887. return $return;
  1888. }
  1889. /**
  1890. * @param string $hex Color in hex ('#AA1122' or 'AA1122' or '#a12' or 'a12')
  1891. * @param integer $steps Step/offset added to each color component. It should be between -255 and 255. Negative = darker, positive = lighter
  1892. * @return string New color with format '#AA1122'
  1893. * @see colorAgressiveness()
  1894. */
  1895. function colorAdjustBrightness($hex, $steps)
  1896. {
  1897. // Steps should be between -255 and 255. Negative = darker, positive = lighter
  1898. $steps = max(-255, min(255, $steps));
  1899. // Normalize into a six character long hex string
  1900. $hex = str_replace('#', '', $hex);
  1901. if (strlen($hex) == 3) {
  1902. $hex = str_repeat(substr($hex, 0, 1), 2).str_repeat(substr($hex, 1, 1), 2).str_repeat(substr($hex, 2, 1), 2);
  1903. }
  1904. // Split into three parts: R, G and B
  1905. $color_parts = str_split($hex, 2);
  1906. $return = '#';
  1907. foreach ($color_parts as $color) {
  1908. $color = hexdec($color); // Convert to decimal
  1909. $color = max(0, min(255, $color + $steps)); // Adjust color
  1910. $return .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT); // Make two char hex code
  1911. }
  1912. return $return;
  1913. }
  1914. /**
  1915. * @param string $hex color in hex
  1916. * @param integer $percent 0 to 100
  1917. * @return string
  1918. */
  1919. function colorDarker($hex, $percent)
  1920. {
  1921. $steps = intval(255 * $percent / 100) * -1;
  1922. return colorAdjustBrightness($hex, $steps);
  1923. }
  1924. /**
  1925. * @param string $hex color in hex
  1926. * @param integer $percent 0 to 100
  1927. * @return string
  1928. */
  1929. function colorLighten($hex, $percent)
  1930. {
  1931. $steps = intval(255 * $percent / 100);
  1932. return colorAdjustBrightness($hex, $steps);
  1933. }
  1934. /**
  1935. * @param string $hex color in hex
  1936. * @param float $alpha 0 to 1 to add alpha channel
  1937. * @param bool $returnArray true=return an array instead, false=return string
  1938. * @return string|array String or array
  1939. */
  1940. function colorHexToRgb($hex, $alpha = false, $returnArray = false)
  1941. {
  1942. $string = '';
  1943. $hex = str_replace('#', '', $hex);
  1944. $length = strlen($hex);
  1945. $rgb = array();
  1946. $rgb['r'] = hexdec($length == 6 ? substr($hex, 0, 2) : ($length == 3 ? str_repeat(substr($hex, 0, 1), 2) : 0));
  1947. $rgb['g'] = hexdec($length == 6 ? substr($hex, 2, 2) : ($length == 3 ? str_repeat(substr($hex, 1, 1), 2) : 0));
  1948. $rgb['b'] = hexdec($length == 6 ? substr($hex, 4, 2) : ($length == 3 ? str_repeat(substr($hex, 2, 1), 2) : 0));
  1949. if ($alpha !== false) {
  1950. $rgb['a'] = floatval($alpha);
  1951. $string = 'rgba('.implode(',', $rgb).')';
  1952. } else {
  1953. $string = 'rgb('.implode(',', $rgb).')';
  1954. }
  1955. if ($returnArray) {
  1956. return $rgb;
  1957. } else {
  1958. return $string;
  1959. }
  1960. }
  1961. /**
  1962. * Applies the Cartesian product algorithm to an array
  1963. * Source: http://stackoverflow.com/a/15973172
  1964. *
  1965. * @param array $input Array of products
  1966. * @return array Array of combinations
  1967. */
  1968. function cartesianArray(array $input)
  1969. {
  1970. // filter out empty values
  1971. $input = array_filter($input);
  1972. $result = array(array());
  1973. foreach ($input as $key => $values) {
  1974. $append = array();
  1975. foreach ($result as $product) {
  1976. foreach ($values as $item) {
  1977. $product[$key] = $item;
  1978. $append[] = $product;
  1979. }
  1980. }
  1981. $result = $append;
  1982. }
  1983. return $result;
  1984. }
  1985. /**
  1986. * Get name of directory where the api_...class.php file is stored
  1987. *
  1988. * @param string $moduleobject Module object name
  1989. * @return string Directory name
  1990. */
  1991. function getModuleDirForApiClass($moduleobject)
  1992. {
  1993. $moduledirforclass = $moduleobject;
  1994. if ($moduledirforclass != 'api') $moduledirforclass = preg_replace('/api$/i', '', $moduledirforclass);
  1995. if ($moduleobject == 'contracts') {
  1996. $moduledirforclass = 'contrat';
  1997. } elseif (in_array($moduleobject, array('admin', 'login', 'setup', 'access', 'status', 'tools', 'documents'))) {
  1998. $moduledirforclass = 'api';
  1999. } elseif ($moduleobject == 'contact' || $moduleobject == 'contacts' || $moduleobject == 'customer' || $moduleobject == 'thirdparty' || $moduleobject == 'thirdparties') {
  2000. $moduledirforclass = 'societe';
  2001. } elseif ($moduleobject == 'propale' || $moduleobject == 'proposals') {
  2002. $moduledirforclass = 'comm/propal';
  2003. } elseif ($moduleobject == 'agenda' || $moduleobject == 'agendaevents') {
  2004. $moduledirforclass = 'comm/action';
  2005. } elseif ($moduleobject == 'adherent' || $moduleobject == 'members' || $moduleobject == 'memberstypes' || $moduleobject == 'subscriptions') {
  2006. $moduledirforclass = 'adherents';
  2007. } elseif ($moduleobject == 'don' || $moduleobject == 'donations') {
  2008. $moduledirforclass = 'don';
  2009. } elseif ($moduleobject == 'banque' || $moduleobject == 'bankaccounts') {
  2010. $moduledirforclass = 'compta/bank';
  2011. } elseif ($moduleobject == 'category' || $moduleobject == 'categorie') {
  2012. $moduledirforclass = 'categories';
  2013. } elseif ($moduleobject == 'order' || $moduleobject == 'orders') {
  2014. $moduledirforclass = 'commande';
  2015. } elseif ($moduleobject == 'shipments') {
  2016. $moduledirforclass = 'expedition';
  2017. } elseif ($moduleobject == 'facture' || $moduleobject == 'invoice' || $moduleobject == 'invoices') {
  2018. $moduledirforclass = 'compta/facture';
  2019. } elseif ($moduleobject == 'project' || $moduleobject == 'projects' || $moduleobject == 'task' || $moduleobject == 'tasks') {
  2020. $moduledirforclass = 'projet';
  2021. } elseif ($moduleobject == 'stock' || $moduleobject == 'stockmovements' || $moduleobject == 'warehouses') {
  2022. $moduledirforclass = 'product/stock';
  2023. } elseif ($moduleobject == 'supplierproposals' || $moduleobject == 'supplierproposal' || $moduleobject == 'supplier_proposal') {
  2024. $moduledirforclass = 'supplier_proposal';
  2025. } elseif ($moduleobject == 'fournisseur' || $moduleobject == 'supplierinvoices' || $moduleobject == 'supplierorders') {
  2026. $moduledirforclass = 'fourn';
  2027. } elseif ($moduleobject == 'ficheinter' || $moduleobject == 'interventions') {
  2028. $moduledirforclass = 'fichinter';
  2029. } elseif ($moduleobject == 'mos') {
  2030. $moduledirforclass = 'mrp';
  2031. } elseif (in_array($moduleobject, array('products', 'expensereports', 'users', 'tickets', 'boms'))) {
  2032. $moduledirforclass = preg_replace('/s$/', '', $moduleobject);
  2033. }
  2034. return $moduledirforclass;
  2035. }
  2036. /**
  2037. * Return 2 hexa code randomly
  2038. *
  2039. * @param int $min Between 0 and 255
  2040. * @param int $max Between 0 and 255
  2041. * @return string A color string '12'
  2042. */
  2043. function randomColorPart($min = 0, $max = 255)
  2044. {
  2045. return str_pad(dechex(mt_rand($min, $max)), 2, '0', STR_PAD_LEFT);
  2046. }
  2047. /**
  2048. * Return hexadecimal color randomly
  2049. *
  2050. * @param int $min Between 0 and 255
  2051. * @param int $max Between 0 and 255
  2052. * @return string A color string '123456'
  2053. */
  2054. function randomColor($min = 0, $max = 255)
  2055. {
  2056. return randomColorPart($min, $max).randomColorPart($min, $max).randomColorPart($min, $max);
  2057. }
  2058. if (!function_exists('dolEscapeXML'))
  2059. {
  2060. /**
  2061. * Encode string for xml usage
  2062. *
  2063. * @param string $string String to encode
  2064. * @return string String encoded
  2065. */
  2066. function dolEscapeXML($string)
  2067. {
  2068. return strtr($string, array('\''=>'&apos;', '"'=>'&quot;', '&'=>'&amp;', '<'=>'&lt;', '>'=>'&gt;'));
  2069. }
  2070. }
  2071. /**
  2072. * Return automatic or manual in current language
  2073. *
  2074. * @param string $automaticmanual Value to test (1, 'automatic', 'true' or 0, 'manual', 'false')
  2075. * @param integer $case 1=Yes/No, 0=yes/no, 2=Disabled checkbox, 3=Disabled checkbox + Automatic/Manual
  2076. * @param int $color 0=texte only, 1=Text is formated with a color font style ('ok' or 'error'), 2=Text is formated with 'ok' color.
  2077. * @return string HTML string
  2078. */
  2079. function autoOrManual($automaticmanual, $case = 1, $color = 0)
  2080. {
  2081. global $langs;
  2082. $result = 'unknown'; $classname = '';
  2083. if ($automaticmanual == 1 || strtolower($automaticmanual) == 'automatic' || strtolower($automaticmanual) == 'true') // A mettre avant test sur no a cause du == 0
  2084. {
  2085. $result = $langs->trans('automatic');
  2086. if ($case == 1 || $case == 3) $result = $langs->trans("Automatic");
  2087. if ($case == 2) $result = '<input type="checkbox" value="1" checked disabled>';
  2088. if ($case == 3) $result = '<input type="checkbox" value="1" checked disabled> '.$result;
  2089. $classname = 'ok';
  2090. } elseif ($automaticmanual == 0 || strtolower($automaticmanual) == 'manual' || strtolower($automaticmanual) == 'false')
  2091. {
  2092. $result = $langs->trans("manual");
  2093. if ($case == 1 || $case == 3) $result = $langs->trans("Manual");
  2094. if ($case == 2) $result = '<input type="checkbox" value="0" disabled>';
  2095. if ($case == 3) $result = '<input type="checkbox" value="0" disabled> '.$result;
  2096. if ($color == 2) $classname = 'ok';
  2097. else $classname = 'error';
  2098. }
  2099. if ($color) return '<font class="'.$classname.'">'.$result.'</font>';
  2100. return $result;
  2101. }
  2102. /**
  2103. * Convert links to local wrapper to medias files into a string into a public external URL readable on internet
  2104. *
  2105. * @param string $notetoshow Text to convert
  2106. * @return string String
  2107. */
  2108. function convertBackOfficeMediasLinksToPublicLinks($notetoshow)
  2109. {
  2110. global $dolibarr_main_url_root;
  2111. // Define $urlwithroot
  2112. $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
  2113. $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
  2114. //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
  2115. $notetoshow = preg_replace('/src="[a-zA-Z0-9_\/\-\.]*(viewimage\.php\?modulepart=medias[^"]*)"/', 'src="'.$urlwithroot.'/\1"', $notetoshow);
  2116. return $notetoshow;
  2117. }
  2118. /**
  2119. * Function to format a value into a defined format for French administration (no thousand separator & decimal separator force to ',' with two decimals)
  2120. * Function used into accountancy FEC export
  2121. *
  2122. * @param float $amount Amount to format
  2123. * @return string Chain with formatted upright
  2124. * @see price2num() Format a numeric into a price for FEC files
  2125. */
  2126. function price2fec($amount)
  2127. {
  2128. global $conf;
  2129. // Clean parameters
  2130. if (empty($amount)) $amount = 0; // To have a numeric value if amount not defined or = ''
  2131. $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
  2132. // Output decimal number by default
  2133. $nbdecimal = (empty($conf->global->ACCOUNTING_FEC_DECIMAL_LENGTH) ? 2 : $conf->global->ACCOUNTING_FEC_DECIMAL_LENGTH);
  2134. // Output separators by default
  2135. $dec = (empty($conf->global->ACCOUNTING_FEC_DECIMAL_SEPARATOR) ? ',' : $conf->global->ACCOUNTING_FEC_DECIMAL_SEPARATOR);
  2136. $thousand = (empty($conf->global->ACCOUNTING_FEC_THOUSAND_SEPARATOR) ? '' : $conf->global->ACCOUNTING_FEC_THOUSAND_SEPARATOR);
  2137. // Format number
  2138. $output = number_format($amount, $nbdecimal, $dec, $thousand);
  2139. return $output;
  2140. }