extrafields.class.php 105 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441
  1. <?php
  2. /* Copyright (C) 2002-2003 Rodolphe Quiedeville <rodolphe@quiedeville.org>
  3. * Copyright (C) 2002-2003 Jean-Louis Bergamo <jlb@j1b.org>
  4. * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
  5. * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
  6. * Copyright (C) 2009-2012 Laurent Destailleur <eldy@users.sourceforge.net>
  7. * Copyright (C) 2009-2012 Regis Houssin <regis.houssin@inodbox.com>
  8. * Copyright (C) 2013 Florian Henry <forian.henry@open-concept.pro>
  9. * Copyright (C) 2015-2023 Charlene BENKE <charlene@patas-monkey.com>
  10. * Copyright (C) 2016 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
  11. * Copyright (C) 2017 Nicolas ZABOURI <info@inovea-conseil.com>
  12. * Copyright (C) 2018-2022 Frédéric France <frederic.france@netlogic.fr>
  13. * Copyright (C) 2022 Antonin MARCHAL <antonin@letempledujeu.fr>
  14. *
  15. * This program is free software; you can redistribute it and/or modify
  16. * it under the terms of the GNU General Public License as published by
  17. * the Free Software Foundation; either version 3 of the License, or
  18. * (at your option) any later version.
  19. *
  20. * This program is distributed in the hope that it will be useful,
  21. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23. * GNU General Public License for more details.
  24. *
  25. * You should have received a copy of the GNU General Public License
  26. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  27. */
  28. /**
  29. * \file htdocs/core/class/extrafields.class.php
  30. * \ingroup core
  31. * \brief File of class to manage extra fields
  32. */
  33. /**
  34. * Class to manage standard extra fields
  35. */
  36. class ExtraFields
  37. {
  38. /**
  39. * @var DoliDB Database handler.
  40. */
  41. public $db;
  42. /**
  43. * @var array New array to store extrafields definition
  44. */
  45. public $attributes;
  46. /**
  47. * @var array Array with boolean of status of groups
  48. */
  49. public $expand_display;
  50. /**
  51. * @var string Error code (or message)
  52. */
  53. public $error = '';
  54. /**
  55. * @var string[] Array of Error code (or message)
  56. */
  57. public $errors = array();
  58. /**
  59. * @var string DB Error number
  60. */
  61. public $errno;
  62. /**
  63. * @var array Array of type to label
  64. */
  65. public static $type2label = array(
  66. 'varchar'=>'String1Line',
  67. 'text'=>'TextLongNLines',
  68. 'html'=>'HtmlText',
  69. 'int'=>'Int',
  70. 'double'=>'Float',
  71. 'date'=>'Date',
  72. 'datetime'=>'DateAndTime',
  73. //'datetimegmt'=>'DateAndTimeUTC',
  74. 'boolean'=>'Boolean',
  75. 'price'=>'ExtrafieldPrice',
  76. 'pricecy'=>'ExtrafieldPriceWithCurrency',
  77. 'phone'=>'ExtrafieldPhone',
  78. 'mail'=>'ExtrafieldMail',
  79. 'url'=>'ExtrafieldUrl',
  80. 'ip'=>'ExtrafieldIP',
  81. 'icon'=>'Icon',
  82. 'password' => 'ExtrafieldPassword',
  83. 'select' => 'ExtrafieldSelect',
  84. 'sellist' => 'ExtrafieldSelectList',
  85. 'radio' => 'ExtrafieldRadio',
  86. 'checkbox' => 'ExtrafieldCheckBox',
  87. 'chkbxlst' => 'ExtrafieldCheckBoxFromList',
  88. 'link' => 'ExtrafieldLink',
  89. 'separate' => 'ExtrafieldSeparator',
  90. );
  91. /**
  92. * Constructor
  93. *
  94. * @param DoliDB $db Database handler
  95. */
  96. public function __construct($db)
  97. {
  98. $this->db = $db;
  99. $this->error = '';
  100. $this->errors = array();
  101. $this->attributes = array();
  102. }
  103. /**
  104. * Add a new extra field parameter
  105. *
  106. * @param string $attrname Code of attribute
  107. * @param string $label label of attribute
  108. * @param string $type Type of attribute ('boolean','int','varchar','text','html','date','datetime','price', 'pricecy', 'phone','mail','password','url','select','checkbox','separate',...)
  109. * @param int $pos Position of attribute
  110. * @param string $size Size/length definition of attribute ('5', '24,8', ...). For float, it contains 2 numeric separated with a comma.
  111. * @param string $elementtype Element type. Same value than object->table_element (Example 'member', 'product', 'thirdparty', ...)
  112. * @param int $unique Is field unique or not
  113. * @param int $required Is field required or not
  114. * @param string $default_value Defaulted value (In database. use the default_value feature for default value on screen. Example: '', '0', 'null', 'avalue')
  115. * @param array|string $param Params for field (ex for select list : array('options' => array(value'=>'label of option')) )
  116. * @param int $alwayseditable Is attribute always editable regardless of the document status
  117. * @param string $perms Permission to check
  118. * @param string $list Visibility ('0'=never visible, '1'=visible on list+forms, '2'=list only, '3'=form only or 'eval string')
  119. * @param string $help Text with help tooltip
  120. * @param string $computed Computed value
  121. * @param string $entity Entity of extrafields (for multicompany modules)
  122. * @param string $langfile Language file
  123. * @param string $enabled Condition to have the field enabled or not
  124. * @param int $totalizable Is a measure. Must show a total on lists
  125. * @param int $printable Is extrafield displayed on PDF
  126. * @param array $moreparams More parameters. Example: array('css'=>, 'csslist'=>Css on list, 'cssview'=>...)
  127. * @return int Return integer <=0 if KO, >0 if OK
  128. */
  129. public function addExtraField($attrname, $label, $type, $pos, $size, $elementtype, $unique = 0, $required = 0, $default_value = '', $param = '', $alwayseditable = 0, $perms = '', $list = '-1', $help = '', $computed = '', $entity = '', $langfile = '', $enabled = '1', $totalizable = 0, $printable = 0, $moreparams = array())
  130. {
  131. if (empty($attrname)) {
  132. return -1;
  133. }
  134. if (empty($label)) {
  135. return -1;
  136. }
  137. $result = 0;
  138. if ($type == 'separate') {
  139. $unique = 0;
  140. $required = 0;
  141. } // Force unique and not required if this is a separator field to avoid troubles.
  142. if ($elementtype == 'thirdparty') {
  143. $elementtype = 'societe';
  144. }
  145. if ($elementtype == 'contact') {
  146. $elementtype = 'socpeople';
  147. }
  148. // Create field into database except for separator type which is not stored in database
  149. if ($type != 'separate') {
  150. $result = $this->create($attrname, $type, $size, $elementtype, $unique, $required, $default_value, $param, $perms, $list, $computed, $help, $moreparams);
  151. }
  152. $err1 = $this->errno;
  153. if ($result > 0 || $err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' || $type == 'separate') {
  154. // Add declaration of field into table
  155. $result2 = $this->create_label($attrname, $label, $type, $pos, $size, $elementtype, $unique, $required, $param, $alwayseditable, $perms, $list, $help, $default_value, $computed, $entity, $langfile, $enabled, $totalizable, $printable, $moreparams);
  156. $err2 = $this->errno;
  157. if ($result2 > 0 || ($err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' && $err2 == 'DB_ERROR_RECORD_ALREADY_EXISTS')) {
  158. $this->error = '';
  159. $this->errno = 0;
  160. return 1;
  161. } else {
  162. return -2;
  163. }
  164. } else {
  165. return -1;
  166. }
  167. }
  168. /**
  169. * Add a new optional attribute.
  170. * This is a private method. For public method, use addExtraField.
  171. *
  172. * @param string $attrname code of attribute
  173. * @param string $type Type of attribute ('boolean', 'int', 'varchar', 'text', 'html', 'date', 'datetime', 'price', 'pricecy', 'phone', 'mail', 'password', 'url', 'select', 'checkbox', ...)
  174. * @param string $length Size/length of attribute ('5', '24,8', ...)
  175. * @param string $elementtype Element type ('member', 'product', 'thirdparty', 'contact', ...)
  176. * @param int $unique Is field unique or not
  177. * @param int $required Is field required or not
  178. * @param string $default_value Default value for field (in database)
  179. * @param array $param Params for field (ex for select list : array('options'=>array('value'=>'label of option'))
  180. * @param string $perms Permission
  181. * @param string $list Into list view by default
  182. * @param string $computed Computed value
  183. * @param string $help Help on tooltip
  184. * @param array $moreparams More parameters. Example: array('css'=>, 'csslist'=>, 'cssview'=>...)
  185. * @return int Return integer <=0 if KO, >0 if OK
  186. */
  187. private function create($attrname, $type = 'varchar', $length = '255', $elementtype = 'member', $unique = 0, $required = 0, $default_value = '', $param = array(), $perms = '', $list = '0', $computed = '', $help = '', $moreparams = array())
  188. {
  189. if ($elementtype == 'thirdparty') {
  190. $elementtype = 'societe';
  191. }
  192. if ($elementtype == 'contact') {
  193. $elementtype = 'socpeople';
  194. }
  195. $table = $elementtype.'_extrafields';
  196. if ($elementtype == 'categorie') {
  197. $table = 'categories_extrafields';
  198. }
  199. if (!empty($attrname) && preg_match("/^\w[a-zA-Z0-9_]*$/", $attrname) && !is_numeric($attrname)) {
  200. if ($type == 'boolean') {
  201. $typedb = 'int';
  202. $lengthdb = '1';
  203. } elseif ($type == 'price') {
  204. $typedb = 'double';
  205. $lengthdb = '24,8';
  206. } elseif ($type == 'pricecy') {
  207. $typedb = 'varchar';
  208. $lengthdb = '64';
  209. } elseif ($type == 'phone') {
  210. $typedb = 'varchar';
  211. $lengthdb = '20';
  212. } elseif ($type == 'mail' || $type == 'ip' || $type == 'icon') {
  213. $typedb = 'varchar';
  214. $lengthdb = '128';
  215. } elseif ($type == 'url') {
  216. $typedb = 'varchar';
  217. $lengthdb = '255';
  218. } elseif (($type == 'select') || ($type == 'sellist') || ($type == 'radio') || ($type == 'checkbox') || ($type == 'chkbxlst')) {
  219. $typedb = 'varchar';
  220. $lengthdb = '255';
  221. } elseif ($type == 'link') {
  222. $typedb = 'int';
  223. $lengthdb = '11';
  224. } elseif ($type == 'html') {
  225. $typedb = 'text';
  226. $lengthdb = $length;
  227. } elseif ($type == 'password') {
  228. $typedb = 'varchar';
  229. $lengthdb = '128';
  230. } else {
  231. $typedb = $type;
  232. $lengthdb = $length;
  233. if ($type == 'varchar' && empty($lengthdb)) {
  234. $lengthdb = '255';
  235. }
  236. }
  237. $field_desc = array(
  238. 'type'=>$typedb,
  239. 'value'=>$lengthdb,
  240. 'null'=>($required ? 'NOT NULL' : 'NULL'),
  241. 'default' => $default_value
  242. );
  243. $result = $this->db->DDLAddField($this->db->prefix().$table, $attrname, $field_desc);
  244. if ($result > 0) {
  245. if ($unique) {
  246. $sql = "ALTER TABLE ".$this->db->prefix().$table." ADD UNIQUE INDEX uk_".$table."_".$attrname." (".$attrname.")";
  247. $resql = $this->db->query($sql, 1, 'dml');
  248. }
  249. return 1;
  250. } else {
  251. $this->error = $this->db->lasterror();
  252. $this->errno = $this->db->lasterrno();
  253. return -1;
  254. }
  255. } else {
  256. return 0;
  257. }
  258. }
  259. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  260. /**
  261. * Add description of a new optional attribute
  262. *
  263. * @param string $attrname code of attribute
  264. * @param string $label label of attribute
  265. * @param string $type Type of attribute ('int', 'varchar', 'text', 'html', 'date', 'datehour', 'float')
  266. * @param int $pos Position of attribute
  267. * @param string $size Size/length of attribute ('5', '24,8', ...)
  268. * @param string $elementtype Element type ('member', 'product', 'thirdparty', ...)
  269. * @param int $unique Is field unique or not
  270. * @param int $required Is field required or not
  271. * @param array|string $param Params for field (ex for select list : array('options' => array(value'=>'label of option')) )
  272. * @param int $alwayseditable Is attribute always editable regardless of the document status
  273. * @param string $perms Permission to check
  274. * @param string $list Visibily
  275. * @param string $help Help on tooltip
  276. * @param string $default Default value (in database. use the default_value feature for default value on screen).
  277. * @param string $computed Computed value
  278. * @param string $entity Entity of extrafields
  279. * @param string $langfile Language file
  280. * @param string $enabled Condition to have the field enabled or not
  281. * @param int $totalizable Is a measure. Must show a total on lists
  282. * @param int $printable Is extrafield displayed on PDF
  283. * @param array $moreparams More parameters. Example: array('css'=>, 'csslist'=>, 'cssview'=>...)
  284. * @return int Return integer <=0 if KO, >0 if OK
  285. * @throws Exception
  286. */
  287. private function create_label($attrname, $label = '', $type = '', $pos = 0, $size = '', $elementtype = 'member', $unique = 0, $required = 0, $param = '', $alwayseditable = 0, $perms = '', $list = '-1', $help = '', $default = '', $computed = '', $entity = '', $langfile = '', $enabled = '1', $totalizable = 0, $printable = 0, $moreparams = array())
  288. {
  289. // phpcs:enable
  290. global $conf, $user;
  291. if ($elementtype == 'thirdparty') {
  292. $elementtype = 'societe';
  293. }
  294. if ($elementtype == 'contact') {
  295. $elementtype = 'socpeople';
  296. }
  297. // Clean parameters
  298. if (empty($pos)) {
  299. $pos = 0;
  300. }
  301. if (empty($list)) {
  302. $list = '0';
  303. }
  304. if (empty($required)) {
  305. $required = 0;
  306. }
  307. if (empty($unique)) {
  308. $unique = 0;
  309. }
  310. if (empty($printable)) {
  311. $printable = 0;
  312. }
  313. if (empty($alwayseditable)) {
  314. $alwayseditable = 0;
  315. }
  316. if (empty($totalizable)) {
  317. $totalizable = 0;
  318. }
  319. $css = '';
  320. if (!empty($moreparams) && !empty($moreparams['css'])) {
  321. $css = $moreparams['css'];
  322. }
  323. $csslist = '';
  324. if (!empty($moreparams) && !empty($moreparams['csslist'])) {
  325. $csslist = $moreparams['csslist'];
  326. }
  327. $cssview = '';
  328. if (!empty($moreparams) && !empty($moreparams['cssview'])) {
  329. $cssview = $moreparams['cssview'];
  330. }
  331. if (!empty($attrname) && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname) && !is_numeric($attrname)) {
  332. if (is_array($param) && count($param) > 0) {
  333. $params = serialize($param);
  334. } elseif (strlen($param) > 0) {
  335. $params = trim($param);
  336. } else {
  337. $params = '';
  338. }
  339. $sql = "INSERT INTO ".$this->db->prefix()."extrafields(";
  340. $sql .= " name,";
  341. $sql .= " label,";
  342. $sql .= " type,";
  343. $sql .= " pos,";
  344. $sql .= " size,";
  345. $sql .= " entity,";
  346. $sql .= " elementtype,";
  347. $sql .= " fieldunique,";
  348. $sql .= " fieldrequired,";
  349. $sql .= " param,";
  350. $sql .= " alwayseditable,";
  351. $sql .= " perms,";
  352. $sql .= " langs,";
  353. $sql .= " list,";
  354. $sql .= " printable,";
  355. $sql .= " fielddefault,";
  356. $sql .= " fieldcomputed,";
  357. $sql .= " fk_user_author,";
  358. $sql .= " fk_user_modif,";
  359. $sql .= " datec,";
  360. $sql .= " enabled,";
  361. $sql .= " help,";
  362. $sql .= " totalizable,";
  363. $sql .= " css,";
  364. $sql .= " csslist,";
  365. $sql .= " cssview";
  366. $sql .= " )";
  367. $sql .= " VALUES('".$this->db->escape($attrname)."',";
  368. $sql .= " '".$this->db->escape($label)."',";
  369. $sql .= " '".$this->db->escape($type)."',";
  370. $sql .= " ".((int) $pos).",";
  371. $sql .= " '".$this->db->escape($size)."',";
  372. $sql .= " ".($entity === '' ? $conf->entity : $entity).",";
  373. $sql .= " '".$this->db->escape($elementtype)."',";
  374. $sql .= " ".((int) $unique).",";
  375. $sql .= " ".((int) $required).",";
  376. $sql .= " '".$this->db->escape($params)."',";
  377. $sql .= " ".((int) $alwayseditable).",";
  378. $sql .= " ".($perms ? "'".$this->db->escape($perms)."'" : "null").",";
  379. $sql .= " ".($langfile ? "'".$this->db->escape($langfile)."'" : "null").",";
  380. $sql .= " '".$this->db->escape($list)."',";
  381. $sql .= " '".$this->db->escape($printable)."',";
  382. $sql .= " ".($default ? "'".$this->db->escape($default)."'" : "null").",";
  383. $sql .= " ".($computed ? "'".$this->db->escape($computed)."'" : "null").",";
  384. $sql .= " ".(is_object($user) ? $user->id : 0).",";
  385. $sql .= " ".(is_object($user) ? $user->id : 0).",";
  386. $sql .= "'".$this->db->idate(dol_now())."',";
  387. $sql .= " ".($enabled ? "'".$this->db->escape($enabled)."'" : "1").",";
  388. $sql .= " ".($help ? "'".$this->db->escape($help)."'" : "null").",";
  389. $sql .= " ".($totalizable ? 'TRUE' : 'FALSE').",";
  390. $sql .= " ".($css ? "'".$this->db->escape($css)."'" : "null").",";
  391. $sql .= " ".($csslist ? "'".$this->db->escape($csslist)."'" : "null").",";
  392. $sql .= " ".($cssview ? "'".$this->db->escape($cssview)."'" : "null");
  393. $sql .= ')';
  394. dol_syslog(get_class($this)."::create_label", LOG_DEBUG);
  395. if ($this->db->query($sql)) {
  396. return 1;
  397. } else {
  398. $this->error = $this->db->lasterror();
  399. $this->errno = $this->db->lasterrno();
  400. return -1;
  401. }
  402. }
  403. return -1;
  404. }
  405. /**
  406. * Delete an optional attribute
  407. *
  408. * @param string $attrname Code of attribute to delete
  409. * @param string $elementtype Element type ('member', 'product', 'thirdparty', 'contact', ...)
  410. * @return int Return integer < 0 if KO, 0 if nothing is done, 1 if OK
  411. */
  412. public function delete($attrname, $elementtype = 'member')
  413. {
  414. if ($elementtype == 'thirdparty') {
  415. $elementtype = 'societe';
  416. }
  417. if ($elementtype == 'contact') {
  418. $elementtype = 'socpeople';
  419. }
  420. $table = $elementtype.'_extrafields';
  421. if ($elementtype == 'categorie') {
  422. $table = 'categories_extrafields';
  423. }
  424. $error = 0;
  425. if (!empty($attrname) && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
  426. $result = $this->delete_label($attrname, $elementtype);
  427. if ($result < 0) {
  428. $this->error = $this->db->lasterror();
  429. $this->errors[] = $this->db->lasterror();
  430. $error++;
  431. }
  432. if (!$error) {
  433. $sql = "SELECT COUNT(rowid) as nb";
  434. $sql .= " FROM ".$this->db->prefix()."extrafields";
  435. $sql .= " WHERE elementtype = '".$this->db->escape($elementtype)."'";
  436. $sql .= " AND name = '".$this->db->escape($attrname)."'";
  437. //$sql.= " AND entity IN (0,".$conf->entity.")"; Do not test on entity here. We want to see if there is still on field remaning in other entities before deleting field in table
  438. $resql = $this->db->query($sql);
  439. if ($resql) {
  440. $obj = $this->db->fetch_object($resql);
  441. if ($obj->nb <= 0) {
  442. $result = $this->db->DDLDropField($this->db->prefix().$table, $attrname); // This also drop the unique key
  443. if ($result < 0) {
  444. $this->error = $this->db->lasterror();
  445. $this->errors[] = $this->db->lasterror();
  446. $error++;
  447. }
  448. }
  449. }
  450. }
  451. return $result;
  452. } else {
  453. return 0;
  454. }
  455. }
  456. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  457. /**
  458. * Delete description of an optional attribute
  459. *
  460. * @param string $attrname Code of attribute to delete
  461. * @param string $elementtype Element type ('member', 'product', 'thirdparty', ...)
  462. * @return int Return integer < 0 if KO, 0 if nothing is done, 1 if OK
  463. */
  464. private function delete_label($attrname, $elementtype = 'member')
  465. {
  466. // phpcs:enable
  467. global $conf;
  468. if ($elementtype == 'thirdparty') {
  469. $elementtype = 'societe';
  470. }
  471. if ($elementtype == 'contact') {
  472. $elementtype = 'socpeople';
  473. }
  474. if (isset($attrname) && $attrname != '' && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
  475. $sql = "DELETE FROM ".$this->db->prefix()."extrafields";
  476. $sql .= " WHERE name = '".$this->db->escape($attrname)."'";
  477. $sql .= " AND entity IN (0,".$conf->entity.')';
  478. $sql .= " AND elementtype = '".$this->db->escape($elementtype)."'";
  479. dol_syslog(get_class($this)."::delete_label", LOG_DEBUG);
  480. $resql = $this->db->query($sql);
  481. if ($resql) {
  482. return 1;
  483. } else {
  484. dol_print_error($this->db);
  485. return -1;
  486. }
  487. } else {
  488. return 0;
  489. }
  490. }
  491. /**
  492. * Modify type of a personalized attribute
  493. *
  494. * @param string $attrname Name of attribute
  495. * @param string $label Label of attribute
  496. * @param string $type Type of attribute ('boolean', 'int', 'varchar', 'text', 'html', 'date', 'datetime','price','phone','mail','password','url','select','checkbox', ...)
  497. * @param int $length Length of attribute
  498. * @param string $elementtype Element type ('member', 'product', 'thirdparty', 'contact', ...)
  499. * @param int $unique Is field unique or not
  500. * @param int $required Is field required or not
  501. * @param int $pos Position of attribute
  502. * @param array $param Params for field (ex for select list : array('options' => array(value'=>'label of option')) )
  503. * @param int $alwayseditable Is attribute always editable regardless of the document status
  504. * @param string $perms Permission to check
  505. * @param string $list Visibility
  506. * @param string $help Help on tooltip
  507. * @param string $default Default value (in database. use the default_value feature for default value on screen).
  508. * @param string $computed Computed value
  509. * @param string $entity Entity of extrafields
  510. * @param string $langfile Language file
  511. * @param string $enabled Condition to have the field enabled or not
  512. * @param int $totalizable Is extrafield totalizable on list
  513. * @param int $printable Is extrafield displayed on PDF
  514. * @param array $moreparams More parameters. Example: array('css'=>, 'csslist'=>, 'cssview'=>...)
  515. * @return int >0 if OK, <=0 if KO
  516. * @throws Exception
  517. */
  518. public function update($attrname, $label, $type, $length, $elementtype, $unique = 0, $required = 0, $pos = 0, $param = array(), $alwayseditable = 0, $perms = '', $list = '', $help = '', $default = '', $computed = '', $entity = '', $langfile = '', $enabled = '1', $totalizable = 0, $printable = 0, $moreparams = array())
  519. {
  520. global $action, $hookmanager;
  521. if ($elementtype == 'thirdparty') {
  522. $elementtype = 'societe';
  523. }
  524. if ($elementtype == 'contact') {
  525. $elementtype = 'socpeople';
  526. }
  527. $table = $elementtype.'_extrafields';
  528. if ($elementtype == 'categorie') {
  529. $table = 'categories_extrafields';
  530. }
  531. if (isset($attrname) && $attrname != '' && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
  532. if ($type == 'boolean') {
  533. $typedb = 'int';
  534. $lengthdb = '1';
  535. } elseif ($type == 'price') {
  536. $typedb = 'double';
  537. $lengthdb = '24,8';
  538. } elseif ($type == 'pricecy') {
  539. $typedb = 'varchar';
  540. $lengthdb = '64';
  541. } elseif ($type == 'phone') {
  542. $typedb = 'varchar';
  543. $lengthdb = '20';
  544. } elseif ($type == 'mail' || $type == 'ip' || $type == 'icon') {
  545. $typedb = 'varchar';
  546. $lengthdb = '128';
  547. } elseif ($type == 'url') {
  548. $typedb = 'varchar';
  549. $lengthdb = '255';
  550. } elseif (($type == 'select') || ($type == 'sellist') || ($type == 'radio') || ($type == 'checkbox') || ($type == 'chkbxlst')) {
  551. $typedb = 'varchar';
  552. $lengthdb = '255';
  553. } elseif ($type == 'html') {
  554. $typedb = 'text';
  555. $lengthdb = $length;
  556. } elseif ($type == 'link') {
  557. $typedb = 'int';
  558. $lengthdb = '11';
  559. } elseif ($type == 'password') {
  560. $typedb = 'varchar';
  561. $lengthdb = '128';
  562. } else {
  563. $typedb = $type;
  564. $lengthdb = $length;
  565. }
  566. $field_desc = array('type'=>$typedb, 'value'=>$lengthdb, 'null'=>($required ? 'NOT NULL' : 'NULL'), 'default'=>$default);
  567. if (is_object($hookmanager)) {
  568. $hookmanager->initHooks(array('extrafieldsdao'));
  569. $parameters = array('field_desc'=>&$field_desc, 'table'=>$table, 'attr_name'=>$attrname, 'label'=>$label, 'type'=>$type, 'length'=>$length, 'unique'=>$unique, 'required'=>$required, 'pos'=>$pos, 'param'=>$param, 'alwayseditable'=>$alwayseditable, 'perms'=>$perms, 'list'=>$list, 'help'=>$help, 'default'=>$default, 'computed'=>$computed, 'entity'=>$entity, 'langfile'=>$langfile, 'enabled'=>$enabled, 'totalizable'=>$totalizable, 'printable'=>$printable);
  570. $reshook = $hookmanager->executeHooks('updateExtrafields', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
  571. if ($reshook < 0) {
  572. $this->error = $this->db->lasterror();
  573. return -1;
  574. }
  575. }
  576. if ($type != 'separate') { // No table update when separate type
  577. $result = $this->db->DDLUpdateField($this->db->prefix().$table, $attrname, $field_desc);
  578. }
  579. if ($result > 0 || $type == 'separate') {
  580. if ($label) {
  581. $result = $this->update_label($attrname, $label, $type, $length, $elementtype, $unique, $required, $pos, $param, $alwayseditable, $perms, $list, $help, $default, $computed, $entity, $langfile, $enabled, $totalizable, $printable, $moreparams);
  582. }
  583. if ($result > 0) {
  584. $sql = '';
  585. if ($unique) {
  586. $sql = "ALTER TABLE ".$this->db->prefix().$table." ADD UNIQUE INDEX uk_".$table."_".$attrname." (".$attrname.")";
  587. } else {
  588. $sql = "ALTER TABLE ".$this->db->prefix().$table." DROP INDEX IF EXISTS uk_".$table."_".$attrname;
  589. }
  590. dol_syslog(get_class($this).'::update', LOG_DEBUG);
  591. $resql = $this->db->query($sql, 1, 'dml');
  592. /*if ($resql < 0) {
  593. $this->error = $this->db->lasterror();
  594. return -1;
  595. }*/
  596. return 1;
  597. } else {
  598. $this->error = $this->db->lasterror();
  599. return -1;
  600. }
  601. } else {
  602. $this->error = $this->db->lasterror();
  603. return -1;
  604. }
  605. } else {
  606. return 0;
  607. }
  608. }
  609. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  610. /**
  611. * Modify description of personalized attribute
  612. *
  613. * @param string $attrname Name of attribute
  614. * @param string $label Label of attribute
  615. * @param string $type Type of attribute
  616. * @param int $size Length of attribute
  617. * @param string $elementtype Element type ('member', 'product', 'thirdparty', ...)
  618. * @param int $unique Is field unique or not
  619. * @param int $required Is field required or not
  620. * @param int $pos Position of attribute
  621. * @param array $param Params for field (ex for select list : array('options' => array(value'=>'label of option')) )
  622. * @param int $alwayseditable Is attribute always editable regardless of the document status
  623. * @param string $perms Permission to check
  624. * @param string $list Visiblity
  625. * @param string $help Help on tooltip.
  626. * @param string $default Default value (in database. use the default_value feature for default value on screen).
  627. * @param string $computed Computed value
  628. * @param string $entity Entity of extrafields
  629. * @param string $langfile Language file
  630. * @param string $enabled Condition to have the field enabled or not
  631. * @param int $totalizable Is extrafield totalizable on list
  632. * @param int $printable Is extrafield displayed on PDF
  633. * @param array $moreparams More parameters. Example: array('css'=>, 'csslist'=>, 'cssview'=>...)
  634. * @return int Return integer <=0 if KO, >0 if OK
  635. * @throws Exception
  636. */
  637. private function update_label($attrname, $label, $type, $size, $elementtype, $unique = 0, $required = 0, $pos = 0, $param = array(), $alwayseditable = 0, $perms = '', $list = '0', $help = '', $default = '', $computed = '', $entity = '', $langfile = '', $enabled = '1', $totalizable = 0, $printable = 0, $moreparams = array())
  638. {
  639. // phpcs:enable
  640. global $conf, $user;
  641. dol_syslog(get_class($this)."::update_label ".$attrname.", ".$label.", ".$type.", ".$size.", ".$elementtype.", ".$unique.", ".$required.", ".$pos.", ".$alwayseditable.", ".$perms.", ".$list.", ".$default.", ".$computed.", ".$entity.", ".$langfile.", ".$enabled.", ".$totalizable.", ".$printable);
  642. // Clean parameters
  643. if ($elementtype == 'thirdparty') {
  644. $elementtype = 'societe';
  645. }
  646. if ($elementtype == 'contact') {
  647. $elementtype = 'socpeople';
  648. }
  649. if (empty($pos)) {
  650. $pos = 0;
  651. }
  652. if (empty($list)) {
  653. $list = '0';
  654. }
  655. if (empty($totalizable)) {
  656. $totalizable = 0;
  657. }
  658. if (empty($required)) {
  659. $required = 0;
  660. }
  661. if (empty($unique)) {
  662. $unique = 0;
  663. }
  664. if (empty($alwayseditable)) {
  665. $alwayseditable = 0;
  666. }
  667. $css = '';
  668. if (!empty($moreparams) && !empty($moreparams['css'])) {
  669. $css = $moreparams['css'];
  670. }
  671. $csslist = '';
  672. if (!empty($moreparams) && !empty($moreparams['csslist'])) {
  673. $csslist = $moreparams['csslist'];
  674. }
  675. $cssview = '';
  676. if (!empty($moreparams) && !empty($moreparams['cssview'])) {
  677. $cssview = $moreparams['cssview'];
  678. }
  679. if (isset($attrname) && $attrname != '' && preg_match("/^\w[a-zA-Z0-9-_]*$/", $attrname)) {
  680. $this->db->begin();
  681. if (is_array($param) && count($param) > 0) {
  682. $params = serialize($param);
  683. } elseif (is_array($param)) {
  684. $params = '';
  685. } elseif (strlen($param) > 0) {
  686. $params = trim($param);
  687. } else {
  688. $params = '';
  689. }
  690. if ($entity === '' || $entity != '0') {
  691. // We dont want on all entities, we delete all and current
  692. $sql_del = "DELETE FROM ".$this->db->prefix()."extrafields";
  693. $sql_del .= " WHERE name = '".$this->db->escape($attrname)."'";
  694. $sql_del .= " AND entity IN (0, ".($entity === '' ? $conf->entity : $entity).")";
  695. $sql_del .= " AND elementtype = '".$this->db->escape($elementtype)."'";
  696. } else {
  697. // We want on all entities ($entities = '0'), we delete on all only (we keep setup specific to each entity)
  698. $sql_del = "DELETE FROM ".$this->db->prefix()."extrafields";
  699. $sql_del .= " WHERE name = '".$this->db->escape($attrname)."'";
  700. $sql_del .= " AND entity = 0";
  701. $sql_del .= " AND elementtype = '".$this->db->escape($elementtype)."'";
  702. }
  703. $resql1 = $this->db->query($sql_del);
  704. $sql = "INSERT INTO ".$this->db->prefix()."extrafields(";
  705. $sql .= " name,"; // This is code
  706. $sql .= " entity,";
  707. $sql .= " label,";
  708. $sql .= " type,";
  709. $sql .= " size,";
  710. $sql .= " elementtype,";
  711. $sql .= " fieldunique,";
  712. $sql .= " fieldrequired,";
  713. $sql .= " perms,";
  714. $sql .= " langs,";
  715. $sql .= " pos,";
  716. $sql .= " alwayseditable,";
  717. $sql .= " param,";
  718. $sql .= " list,";
  719. $sql .= " printable,";
  720. $sql .= " totalizable,";
  721. $sql .= " fielddefault,";
  722. $sql .= " fieldcomputed,";
  723. $sql .= " fk_user_author,";
  724. $sql .= " fk_user_modif,";
  725. $sql .= " datec,";
  726. $sql .= " enabled,";
  727. $sql .= " help,";
  728. $sql .= " css,";
  729. $sql .= " csslist,";
  730. $sql .= " cssview";
  731. $sql .= ") VALUES (";
  732. $sql .= "'".$this->db->escape($attrname)."',";
  733. $sql .= " ".($entity === '' ? $conf->entity : $entity).",";
  734. $sql .= " '".$this->db->escape($label)."',";
  735. $sql .= " '".$this->db->escape($type)."',";
  736. $sql .= " '".$this->db->escape($size)."',";
  737. $sql .= " '".$this->db->escape($elementtype)."',";
  738. $sql .= " ".$unique.",";
  739. $sql .= " ".$required.",";
  740. $sql .= " ".($perms ? "'".$this->db->escape($perms)."'" : "null").",";
  741. $sql .= " ".($langfile ? "'".$this->db->escape($langfile)."'" : "null").",";
  742. $sql .= " ".$pos.",";
  743. $sql .= " '".$this->db->escape($alwayseditable)."',";
  744. $sql .= " '".$this->db->escape($params)."',";
  745. $sql .= " '".$this->db->escape($list)."', ";
  746. $sql .= " '".$this->db->escape($printable)."', ";
  747. $sql .= " ".($totalizable ? 'TRUE' : 'FALSE').",";
  748. $sql .= " ".(($default != '') ? "'".$this->db->escape($default)."'" : "null").",";
  749. $sql .= " ".($computed ? "'".$this->db->escape($computed)."'" : "null").",";
  750. $sql .= " ".$user->id.",";
  751. $sql .= " ".$user->id.",";
  752. $sql .= "'".$this->db->idate(dol_now())."',";
  753. $sql .= "'".$this->db->escape($enabled)."',";
  754. $sql .= " ".($help ? "'".$this->db->escape($help)."'" : "null").",";
  755. $sql .= " ".($css ? "'".$this->db->escape($css)."'" : "null").",";
  756. $sql .= " ".($csslist ? "'".$this->db->escape($csslist)."'" : "null").",";
  757. $sql .= " ".($cssview ? "'".$this->db->escape($cssview)."'" : "null");
  758. $sql .= ")";
  759. $resql2 = $this->db->query($sql);
  760. if ($resql1 && $resql2) {
  761. $this->db->commit();
  762. return 1;
  763. } else {
  764. $this->db->rollback();
  765. dol_print_error($this->db);
  766. return -1;
  767. }
  768. } else {
  769. return 0;
  770. }
  771. }
  772. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  773. /**
  774. * Load the array of extrafields defintion $this->attributes
  775. *
  776. * @param string $elementtype Type of element ('all' = all or $object->table_element like 'adherent', 'commande', 'thirdparty', 'facture', 'propal', 'product', ...).
  777. * @param boolean $forceload Force load of extra fields whatever is status of cache.
  778. * @return array Array of attributes keys+label for all extra fields.
  779. */
  780. public function fetch_name_optionals_label($elementtype, $forceload = false)
  781. {
  782. // phpcs:enable
  783. global $conf;
  784. if (empty($elementtype)) {
  785. return array();
  786. }
  787. if ($elementtype == 'thirdparty') {
  788. $elementtype = 'societe';
  789. }
  790. if ($elementtype == 'contact') {
  791. $elementtype = 'socpeople';
  792. }
  793. if ($elementtype == 'order_supplier') {
  794. $elementtype = 'commande_fournisseur';
  795. }
  796. // Test cache $this->attributes[$elementtype]['loaded'] to see if we must do something
  797. // TODO
  798. $array_name_label = array();
  799. // We should not have several time this request. If we have, there is some optimization to do by calling a simple $extrafields->fetch_optionals() in top of code and not into subcode
  800. $sql = "SELECT rowid, name, label, type, size, elementtype, fieldunique, fieldrequired, param, pos, alwayseditable, perms, langs, list, printable, totalizable, fielddefault, fieldcomputed, entity, enabled, help,";
  801. $sql .= " css, cssview, csslist";
  802. $sql .= " FROM ".$this->db->prefix()."extrafields";
  803. //$sql.= " WHERE entity IN (0,".$conf->entity.")"; // Filter is done later
  804. if ($elementtype && $elementtype != 'all') {
  805. $sql .= " WHERE elementtype = '".$this->db->escape($elementtype)."'"; // Filed with object->table_element
  806. }
  807. $sql .= " ORDER BY pos";
  808. $resql = $this->db->query($sql);
  809. if ($resql) {
  810. $count = 0;
  811. if ($this->db->num_rows($resql)) {
  812. while ($tab = $this->db->fetch_object($resql)) {
  813. if ($tab->entity != 0 && $tab->entity != $conf->entity) {
  814. // This field is not in current entity. We discard but before we save it into the array of mandatory fields if it is a mandatory field without default value
  815. if ($tab->fieldrequired && is_null($tab->fielddefault)) {
  816. $this->attributes[$tab->elementtype]['mandatoryfieldsofotherentities'][$tab->name] = $tab->type;
  817. }
  818. continue;
  819. }
  820. // We can add this attribute to object. TODO Remove this and return $this->attributes[$elementtype]['label']
  821. if ($tab->type != 'separate') {
  822. $array_name_label[$tab->name] = $tab->label;
  823. }
  824. $this->attributes[$tab->elementtype]['type'][$tab->name] = $tab->type;
  825. $this->attributes[$tab->elementtype]['label'][$tab->name] = $tab->label;
  826. $this->attributes[$tab->elementtype]['size'][$tab->name] = $tab->size;
  827. $this->attributes[$tab->elementtype]['elementtype'][$tab->name] = $tab->elementtype;
  828. $this->attributes[$tab->elementtype]['default'][$tab->name] = $tab->fielddefault;
  829. $this->attributes[$tab->elementtype]['computed'][$tab->name] = $tab->fieldcomputed;
  830. $this->attributes[$tab->elementtype]['unique'][$tab->name] = $tab->fieldunique;
  831. $this->attributes[$tab->elementtype]['required'][$tab->name] = $tab->fieldrequired;
  832. $this->attributes[$tab->elementtype]['param'][$tab->name] = ($tab->param ? jsonOrUnserialize($tab->param) : '');
  833. $this->attributes[$tab->elementtype]['pos'][$tab->name] = $tab->pos;
  834. $this->attributes[$tab->elementtype]['alwayseditable'][$tab->name] = $tab->alwayseditable;
  835. $this->attributes[$tab->elementtype]['perms'][$tab->name] = ((is_null($tab->perms) || strlen($tab->perms) == 0) ? 1 : $tab->perms);
  836. $this->attributes[$tab->elementtype]['langfile'][$tab->name] = $tab->langs;
  837. $this->attributes[$tab->elementtype]['list'][$tab->name] = $tab->list;
  838. $this->attributes[$tab->elementtype]['printable'][$tab->name] = $tab->printable;
  839. $this->attributes[$tab->elementtype]['totalizable'][$tab->name] = ($tab->totalizable ? 1 : 0);
  840. $this->attributes[$tab->elementtype]['entityid'][$tab->name] = $tab->entity;
  841. $this->attributes[$tab->elementtype]['enabled'][$tab->name] = $tab->enabled;
  842. $this->attributes[$tab->elementtype]['help'][$tab->name] = $tab->help;
  843. $this->attributes[$tab->elementtype]['css'][$tab->name] = $tab->css;
  844. $this->attributes[$tab->elementtype]['cssview'][$tab->name] = $tab->cssview;
  845. $this->attributes[$tab->elementtype]['csslist'][$tab->name] = $tab->csslist;
  846. $this->attributes[$tab->elementtype]['loaded'] = 1;
  847. $count++;
  848. }
  849. }
  850. if ($elementtype) {
  851. $this->attributes[$elementtype]['loaded'] = 1; // Note: If nothing is found, we also set the key 'loaded' to 1.
  852. $this->attributes[$elementtype]['count'] = $count;
  853. }
  854. } else {
  855. $this->error = $this->db->lasterror();
  856. dol_syslog(get_class($this)."::fetch_name_optionals_label ".$this->error, LOG_ERR);
  857. }
  858. return $array_name_label;
  859. }
  860. /**
  861. * Return HTML string to put an input field into a page
  862. * Code very similar with showInputField of common object
  863. *
  864. * @param string $key Key of attribute
  865. * @param string|array $value Preselected value to show (for date type it must be in timestamp format, for amount or price it must be a php numeric value); for dates in filter mode, a range array('start'=><timestamp>, 'end'=><timestamp>) should be provided
  866. * @param string $moreparam To add more parameters on html input tag
  867. * @param string $keysuffix Prefix string to add after name and id of field (can be used to avoid duplicate names)
  868. * @param string $keyprefix Suffix string to add before name and id of field (can be used to avoid duplicate names)
  869. * @param string $morecss More css (to defined size of field. Old behaviour: may also be a numeric)
  870. * @param int $objectid Current object id
  871. * @param string $extrafieldsobjectkey The key to use to store retreived data (commonly $object->table_element)
  872. * @param int $mode 1=Used for search filters
  873. * @return string
  874. */
  875. public function showInputField($key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '', $objectid = 0, $extrafieldsobjectkey = '', $mode = 0)
  876. {
  877. global $conf, $langs, $form;
  878. if (!is_object($form)) {
  879. require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
  880. $form = new Form($this->db);
  881. }
  882. $out = '';
  883. if (!preg_match('/options_$/', $keyprefix)) { // Because we work on extrafields, we add 'options_' to prefix if not already added
  884. $keyprefix = $keyprefix.'options_';
  885. }
  886. if (empty($extrafieldsobjectkey)) {
  887. dol_syslog(get_class($this).'::showInputField extrafieldsobjectkey required', LOG_ERR);
  888. return 'BadValueForParamExtraFieldsObjectKey';
  889. }
  890. $label = $this->attributes[$extrafieldsobjectkey]['label'][$key];
  891. $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
  892. $size = $this->attributes[$extrafieldsobjectkey]['size'][$key];
  893. $default = $this->attributes[$extrafieldsobjectkey]['default'][$key];
  894. $computed = $this->attributes[$extrafieldsobjectkey]['computed'][$key];
  895. $unique = $this->attributes[$extrafieldsobjectkey]['unique'][$key];
  896. $required = $this->attributes[$extrafieldsobjectkey]['required'][$key];
  897. $param = $this->attributes[$extrafieldsobjectkey]['param'][$key];
  898. $perms = dol_eval($this->attributes[$extrafieldsobjectkey]['perms'][$key], 1, 1, '2');
  899. $langfile = $this->attributes[$extrafieldsobjectkey]['langfile'][$key];
  900. $list = dol_eval($this->attributes[$extrafieldsobjectkey]['list'][$key], 1, 1, '2');
  901. $totalizable = $this->attributes[$extrafieldsobjectkey]['totalizable'][$key];
  902. $help = $this->attributes[$extrafieldsobjectkey]['help'][$key];
  903. $hidden = (empty($list) ? 1 : 0); // If empty, we are sure it is hidden, otherwise we show. If it depends on mode (view/create/edit form or list, this must be filtered by caller)
  904. //var_dump('key='.$key.' '.$value.' '.$moreparam.' '.$keysuffix.' '.$keyprefix.' '.$objectid.' '.$extrafieldsobjectkey.' '.$mode);
  905. //var_dump('label='.$label.' type='.$type.' param='.var_export($param, 1));
  906. if ($computed) {
  907. if (!preg_match('/^search_/', $keyprefix)) {
  908. return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
  909. } else {
  910. return '';
  911. }
  912. }
  913. //
  914. // 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'css'=>'minwidth300 maxwidth500 widthcentpercentminusx', 'cssview'=>'wordbreak', 'csslist'=>'tdoverflowmax200'
  915. if (empty($morecss)) {
  916. // Add automatic css
  917. if ($type == 'date') {
  918. $morecss = 'minwidth100imp';
  919. } elseif ($type == 'datetime' || $type == 'datetimegmt' || $type == 'link') {
  920. $morecss = 'minwidth200imp';
  921. } elseif (in_array($type, array('int', 'integer', 'double', 'price'))) {
  922. $morecss = 'maxwidth75';
  923. } elseif ($type == 'password') {
  924. $morecss = 'maxwidth100';
  925. } elseif ($type == 'url') {
  926. $morecss = 'minwidth400';
  927. } elseif ($type == 'boolean') {
  928. $morecss = '';
  929. } elseif ($type == 'radio') {
  930. $morecss = 'width25';
  931. } else {
  932. if (empty($size) || round((float) $size) < 12) {
  933. $morecss = 'minwidth100';
  934. } elseif (round((float) $size) <= 48) {
  935. $morecss = 'minwidth200';
  936. } else {
  937. $morecss = 'minwidth400';
  938. }
  939. }
  940. // If css forced in attribute, we use this one
  941. if (!empty($this->attributes[$extrafieldsobjectkey]['css'][$key])) {
  942. $morecss = $this->attributes[$extrafieldsobjectkey]['css'][$key];
  943. }
  944. }
  945. if (in_array($type, array('date'))) {
  946. $tmp = explode(',', $size);
  947. $newsize = $tmp[0];
  948. $showtime = 0;
  949. // Do not show current date when field not required (see selectDate() method)
  950. if (!$required && $value == '') {
  951. $value = '-1';
  952. }
  953. if ($mode == 1) {
  954. // search filter on a date extrafield shows two inputs to select a date range
  955. $prefill = array(
  956. 'start' => isset($value['start']) ? $value['start'] : '',
  957. 'end' => isset($value['end']) ? $value['end'] : ''
  958. );
  959. $out = '<div ' . ($moreparam ? $moreparam : '') . '><div class="nowrap">';
  960. $out .= $form->selectDate($prefill['start'], $keyprefix.$key.$keysuffix.'_start', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("From"));
  961. $out .= '</div><div class="nowrap">';
  962. $out .= $form->selectDate($prefill['end'], $keyprefix.$key.$keysuffix.'_end', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"));
  963. $out .= '</div></div>';
  964. } else {
  965. // TODO Must also support $moreparam
  966. $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
  967. }
  968. } elseif (in_array($type, array('datetime', 'datetimegmt'))) {
  969. $tmp = explode(',', $size);
  970. $newsize = $tmp[0];
  971. $showtime = 1;
  972. // Do not show current date when field not required (see selectDate() method)
  973. if (!$required && $value == '') {
  974. $value = '-1';
  975. }
  976. if ($mode == 1) {
  977. // search filter on a date extrafield shows two inputs to select a date range
  978. $prefill = array(
  979. 'start' => isset($value['start']) ? $value['start'] : '',
  980. 'end' => isset($value['end']) ? $value['end'] : ''
  981. );
  982. $out = '<div ' . ($moreparam ? $moreparam : '') . '><div class="nowrap">';
  983. $out .= $form->selectDate($prefill['start'], $keyprefix.$key.$keysuffix.'_start', 1, 1, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("From"), 'tzuserrel');
  984. $out .= '</div><div class="nowrap">';
  985. $out .= $form->selectDate($prefill['end'], $keyprefix.$key.$keysuffix.'_end', 1, 1, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
  986. $out .= '</div></div>';
  987. } else {
  988. // TODO Must also support $moreparam
  989. $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
  990. }
  991. } elseif (in_array($type, array('int', 'integer'))) {
  992. $tmp = explode(',', $size);
  993. $newsize = $tmp[0];
  994. $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$newsize.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').'>';
  995. } elseif (preg_match('/varchar/', $type)) {
  996. $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$size.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').'>';
  997. } elseif (in_array($type, array('mail', 'ip', 'phone', 'url'))) {
  998. $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
  999. } elseif ($type == 'icon') {
  1000. /* External lib inclusion are not allowed in backoffice. Also lib is included several time if there is several icon file.
  1001. Some code must be added into main when MAIN_ADD_ICONPICKER_JS is set to add of lib in html header
  1002. $out ='<link rel="stylesheet" href="'.dol_buildpath('/myfield/css/fontawesome-iconpicker.min.css', 1).'">';
  1003. $out.='<script src="'.dol_buildpath('/myfield/js/fontawesome-iconpicker.min.js', 1).'"></script>';
  1004. */
  1005. $out.= '<input type="text" class="form-control icp icp-auto iconpicker-element iconpicker-input flat '.$morecss.' maxwidthonsmartphone"';
  1006. $out.= ' name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
  1007. if (getDolGlobalInt('MAIN_ADD_ICONPICKER_JS')) {
  1008. $out.='<script>';
  1009. $options="{ title: '<b>".$langs->trans("IconFieldSelector")."</b>', placement: 'right', showFooter: false, templates: {";
  1010. $options.="iconpicker: '<div class=\"iconpicker\"><div style=\"background-color:#EFEFEF;\" class=\"iconpicker-items\"></div></div>',";
  1011. $options.="iconpickerItem: '<a role=\"button\" href=\"#\" class=\"iconpicker-item\" style=\"background-color:#DDDDDD;\"><i></i></a>',";
  1012. // $options.="buttons: '<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-cancel btn btn-default btn-sm\">".$langs->trans("Cancel")."</button>";
  1013. // $options.="<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-accept btn btn-primary btn-sm\">".$langs->trans("Save")."</button>',";
  1014. $options.="footer: '<div class=\"popover-footer\" style=\"background-color:#EFEFEF;\"></div>',";
  1015. $options.="search: '<input type=\"search\" class\"form-control iconpicker-search\" placeholder=\"".$langs->trans("TypeToFilter")."\" />',";
  1016. $options.="popover: '<div class=\"iconpicker-popover popover\">";
  1017. $options.=" <div class=\"arrow\" ></div>";
  1018. $options.=" <div class=\"popover-title\" style=\"text-align:center;background-color:#EFEFEF;\"></div>";
  1019. $options.=" <div class=\"popover-content \" ></div>";
  1020. $options.="</div>'}}";
  1021. $out.="$('#".$keyprefix.$key.$keysuffix."').iconpicker(".$options.");";
  1022. $out.='</script>';
  1023. }
  1024. } elseif ($type == 'text') {
  1025. if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
  1026. require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
  1027. $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
  1028. $out = $doleditor->Create(1);
  1029. } else {
  1030. $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
  1031. }
  1032. } elseif ($type == 'html') {
  1033. if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
  1034. require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
  1035. $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_5, '90%');
  1036. $out = $doleditor->Create(1);
  1037. } else {
  1038. $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
  1039. }
  1040. } elseif ($type == 'boolean') {
  1041. if (empty($mode)) {
  1042. $checked = '';
  1043. if (!empty($value)) {
  1044. $checked = ' checked value="1" ';
  1045. } else {
  1046. $checked = ' value="1" ';
  1047. }
  1048. $out = '<input type="checkbox" class="flat valignmiddle'.($morecss ? ' '.$morecss : '').' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>';
  1049. } else {
  1050. $out = $form->selectyesno($keyprefix.$key.$keysuffix, $value, 1, false, 1);
  1051. }
  1052. $out .= '<input type="hidden" name="'.$keyprefix.$key.$keysuffix.'_boolean" value="1">'; // A hidden field ending with "_boolean" that is always set to 1.
  1053. } elseif ($type == 'price') {
  1054. if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
  1055. $value = price($value);
  1056. }
  1057. $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> '.$langs->getCurrencySymbol($conf->currency);
  1058. } elseif ($type == 'pricecy') {
  1059. $currency = $conf->currency;
  1060. if (!empty($value)) {
  1061. // $value in memory is a php string like '10.01:USD'
  1062. $pricetmp = explode(':', $value);
  1063. $currency = !empty($pricetmp[1]) ? $pricetmp[1] : $conf->currency;
  1064. $value = price($pricetmp[0]);
  1065. }
  1066. $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
  1067. $out .= $form->selectCurrency($currency, $keyprefix.$key.$keysuffix.'currency_id');
  1068. } elseif ($type == 'double') {
  1069. if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
  1070. $value = price($value);
  1071. }
  1072. $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
  1073. } elseif ($type == 'select') {
  1074. $out = '';
  1075. if ($mode) {
  1076. $options = array();
  1077. foreach ($param['options'] as $okey => $val) {
  1078. if ((string) $okey == '') {
  1079. continue;
  1080. }
  1081. $valarray = explode('|', $val);
  1082. $val = $valarray[0];
  1083. if ($langfile && $val) {
  1084. $options[$okey] = $langs->trans($val);
  1085. } else {
  1086. $options[$okey] = $val;
  1087. }
  1088. }
  1089. $selected = array();
  1090. if (!is_array($value)) {
  1091. $selected = explode(',', $value);
  1092. }
  1093. $out .= $form->multiselectarray($keyprefix.$key.$keysuffix, $options, $selected, 0, 0, $morecss, 0, 0, '', '', '', !empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2'));
  1094. } else {
  1095. if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
  1096. include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
  1097. $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
  1098. }
  1099. $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
  1100. $out .= '<option value="0">&nbsp;</option>';
  1101. foreach ($param['options'] as $key2 => $val2) {
  1102. if ((string) $key2 == '') {
  1103. continue;
  1104. }
  1105. $valarray = explode('|', $val2);
  1106. $val2 = $valarray[0];
  1107. $parent = '';
  1108. if (!empty($valarray[1])) {
  1109. $parent = $valarray[1];
  1110. }
  1111. $out .= '<option value="'.$key2.'"';
  1112. $out .= (((string) $value == (string) $key2) ? ' selected' : '');
  1113. $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
  1114. $out .= '>';
  1115. if ($langfile && $val2) {
  1116. $out .= $langs->trans($val2);
  1117. } else {
  1118. $out .= $val2;
  1119. }
  1120. $out .= '</option>';
  1121. }
  1122. $out .= '</select>';
  1123. }
  1124. } elseif ($type == 'sellist') {
  1125. $out = '';
  1126. if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
  1127. include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
  1128. $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
  1129. }
  1130. $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
  1131. if (is_array($param['options'])) {
  1132. $param_list = array_keys($param['options']);
  1133. $InfoFieldList = explode(":", $param_list[0]);
  1134. $parentName = '';
  1135. $parentField = '';
  1136. // 0 : tableName
  1137. // 1 : label field name
  1138. // 2 : key fields name (if differ of rowid)
  1139. // 3 : key field parent (for dependent lists)
  1140. // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
  1141. // 5 : id category type
  1142. // 6 : ids categories list separated by comma for category root
  1143. // 7 : sort by (to be close to common object)
  1144. $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
  1145. if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
  1146. if (strpos($InfoFieldList[4], 'extra.') !== false) {
  1147. $keyList = 'main.'.$InfoFieldList[2].' as rowid';
  1148. } else {
  1149. $keyList = $InfoFieldList[2].' as rowid';
  1150. }
  1151. }
  1152. if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
  1153. list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
  1154. $keyList .= ', '.$parentField;
  1155. }
  1156. $filter_categorie = false;
  1157. if (count($InfoFieldList) > 5) {
  1158. if ($InfoFieldList[0] == 'categorie') {
  1159. $filter_categorie = true;
  1160. }
  1161. }
  1162. if ($filter_categorie === false) {
  1163. $fields_label = explode('|', $InfoFieldList[1]);
  1164. if (is_array($fields_label)) {
  1165. $keyList .= ', ';
  1166. $keyList .= implode(', ', $fields_label);
  1167. }
  1168. $sqlwhere = '';
  1169. $sql = "SELECT ".$keyList;
  1170. $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
  1171. if (!empty($InfoFieldList[4])) {
  1172. // can use current entity filter
  1173. if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
  1174. $InfoFieldList[4] = str_replace('$ENTITY$', $conf->entity, $InfoFieldList[4]);
  1175. }
  1176. // can use SELECT request
  1177. if (strpos($InfoFieldList[4], '$SEL$') !== false) {
  1178. $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
  1179. }
  1180. // current object id can be use into filter
  1181. if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
  1182. $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
  1183. } else {
  1184. $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
  1185. }
  1186. //We have to join on extrafield table
  1187. if (strpos($InfoFieldList[4], 'extra.') !== false) {
  1188. $sql .= ' as main, '.$this->db->prefix().$InfoFieldList[0].'_extrafields as extra';
  1189. $sqlwhere .= " WHERE extra.fk_object=main.".$InfoFieldList[2]." AND ".$InfoFieldList[4];
  1190. } else {
  1191. $sqlwhere .= " WHERE ".$InfoFieldList[4];
  1192. }
  1193. } else {
  1194. $sqlwhere .= ' WHERE 1=1';
  1195. }
  1196. // Some tables may have field, some other not. For the moment we disable it.
  1197. if (in_array($InfoFieldList[0], array('tablewithentity'))) {
  1198. $sqlwhere .= ' AND entity = '.((int) $conf->entity);
  1199. }
  1200. $sql .= $sqlwhere;
  1201. //print $sql;
  1202. $sql .= ' ORDER BY '.implode(', ', $fields_label);
  1203. dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
  1204. $resql = $this->db->query($sql);
  1205. if ($resql) {
  1206. $out .= '<option value="0">&nbsp;</option>';
  1207. $num = $this->db->num_rows($resql);
  1208. $i = 0;
  1209. while ($i < $num) {
  1210. $labeltoshow = '';
  1211. $obj = $this->db->fetch_object($resql);
  1212. // Several field into label (eq table:code|label:rowid)
  1213. $notrans = false;
  1214. $fields_label = explode('|', $InfoFieldList[1]);
  1215. if (is_array($fields_label) && count($fields_label) > 1) {
  1216. $notrans = true;
  1217. foreach ($fields_label as $field_toshow) {
  1218. $labeltoshow .= $obj->$field_toshow.' ';
  1219. }
  1220. } else {
  1221. $labeltoshow = $obj->{$InfoFieldList[1]};
  1222. }
  1223. $labeltoshow = $labeltoshow;
  1224. if ($value == $obj->rowid) {
  1225. if (!$notrans) {
  1226. foreach ($fields_label as $field_toshow) {
  1227. $translabel = $langs->trans($obj->$field_toshow);
  1228. $labeltoshow = $translabel.' ';
  1229. }
  1230. }
  1231. $out .= '<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
  1232. } else {
  1233. if (!$notrans) {
  1234. $translabel = $langs->trans($obj->{$InfoFieldList[1]});
  1235. $labeltoshow = $translabel;
  1236. }
  1237. if (empty($labeltoshow)) {
  1238. $labeltoshow = '(not defined)';
  1239. }
  1240. if (!empty($InfoFieldList[3]) && $parentField) {
  1241. $parent = $parentName.':'.$obj->{$parentField};
  1242. }
  1243. $out .= '<option value="'.$obj->rowid.'"';
  1244. $out .= ($value == $obj->rowid ? ' selected' : '');
  1245. $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
  1246. $out .= '>'.$labeltoshow.'</option>';
  1247. }
  1248. $i++;
  1249. }
  1250. $this->db->free($resql);
  1251. } else {
  1252. print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
  1253. }
  1254. } else {
  1255. require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
  1256. $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
  1257. $out .= '<option value="0">&nbsp;</option>';
  1258. if (is_array($data)) {
  1259. foreach ($data as $data_key => $data_value) {
  1260. $out .= '<option value="'.$data_key.'"';
  1261. $out .= ($value == $data_key ? ' selected' : '');
  1262. $out .= '>'.$data_value.'</option>';
  1263. }
  1264. }
  1265. }
  1266. }
  1267. $out .= '</select>';
  1268. } elseif ($type == 'checkbox') {
  1269. $value_arr = $value;
  1270. if (!is_array($value)) {
  1271. $value_arr = explode(',', $value);
  1272. }
  1273. $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ? null : $param['options']), $value_arr, '', 0, '', 0, '100%');
  1274. } elseif ($type == 'radio') {
  1275. $out = '';
  1276. foreach ($param['options'] as $keyopt => $val) {
  1277. $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
  1278. $out .= ' value="'.$keyopt.'"';
  1279. $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
  1280. $out .= ($value == $keyopt ? 'checked' : '');
  1281. $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$langs->trans($val).'</label><br>';
  1282. }
  1283. } elseif ($type == 'chkbxlst') {
  1284. if (is_array($value)) {
  1285. $value_arr = $value;
  1286. } else {
  1287. $value_arr = explode(',', $value);
  1288. }
  1289. if (is_array($param['options'])) {
  1290. $param_list = array_keys($param['options']);
  1291. $InfoFieldList = explode(":", $param_list[0]);
  1292. $parentName = '';
  1293. $parentField = '';
  1294. // 0 : tableName
  1295. // 1 : label field name
  1296. // 2 : key fields name (if differ of rowid)
  1297. // 3 : key field parent (for dependent lists)
  1298. // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
  1299. // 5 : id category type
  1300. // 6 : ids categories list separated by comma for category root
  1301. // 7 : sort by (to be close to common object)
  1302. $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
  1303. if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
  1304. list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
  1305. $keyList .= ', '.$parentField;
  1306. }
  1307. if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
  1308. if (strpos($InfoFieldList[4], 'extra.') !== false) {
  1309. $keyList = 'main.'.$InfoFieldList[2].' as rowid';
  1310. } else {
  1311. $keyList = $InfoFieldList[2].' as rowid';
  1312. }
  1313. }
  1314. $filter_categorie = false;
  1315. if (count($InfoFieldList) > 5) {
  1316. if ($InfoFieldList[0] == 'categorie') {
  1317. $filter_categorie = true;
  1318. }
  1319. }
  1320. if ($filter_categorie === false) {
  1321. $fields_label = explode('|', $InfoFieldList[1]);
  1322. if (is_array($fields_label)) {
  1323. $keyList .= ', ';
  1324. $keyList .= implode(', ', $fields_label);
  1325. }
  1326. $sqlwhere = '';
  1327. $sql = "SELECT ".$keyList;
  1328. $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
  1329. if (!empty($InfoFieldList[4])) {
  1330. // can use current entity filter
  1331. if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
  1332. $InfoFieldList[4] = str_replace('$ENTITY$', $conf->entity, $InfoFieldList[4]);
  1333. }
  1334. // can use SELECT request
  1335. if (strpos($InfoFieldList[4], '$SEL$') !== false) {
  1336. $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
  1337. }
  1338. // current object id can be use into filter
  1339. if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
  1340. $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
  1341. } elseif (preg_match("#^.*list.php$#", $_SERVER["PHP_SELF"])) {
  1342. // Pattern for word=$ID$
  1343. $word = '\b[a-zA-Z0-9-\.-_]+\b=\$ID\$';
  1344. // Removing space arount =, ( and )
  1345. $InfoFieldList[4] = preg_replace('# *(=|\(|\)) *#', '$1', $InfoFieldList[4]);
  1346. $nbPreg = 1;
  1347. // While we have parenthesis
  1348. while ($nbPreg != 0) {
  1349. // Init des compteurs
  1350. $nbPregRepl = $nbPregSel = 0;
  1351. // On retire toutes les parenthèses sans = avant
  1352. $InfoFieldList[4] = preg_replace('#([^=])(\([^)^(]*('.$word.')[^)^(]*\))#', '$1 $3 ', $InfoFieldList[4], -1, $nbPregRepl);
  1353. // On retire les espaces autour des = et parenthèses
  1354. $InfoFieldList[4] = preg_replace('# *(=|\(|\)) *#', '$1', $InfoFieldList[4]);
  1355. // On retire toutes les parenthèses avec = avant
  1356. $InfoFieldList[4] = preg_replace('#\b[a-zA-Z0-9-\.-_]+\b=\([^)^(]*('.$word.')[^)^(]*\)#', '$1 ', $InfoFieldList[4], -1, $nbPregSel);
  1357. // On retire les espaces autour des = et parenthèses
  1358. $InfoFieldList[4] = preg_replace('# *(=|\(|\)) *#', '$1', $InfoFieldList[4]);
  1359. // Calcul du compteur général pour la boucle
  1360. $nbPreg = $nbPregRepl + $nbPregSel;
  1361. }
  1362. // Si l'on a un AND ou un OR, avant ou après
  1363. $matchCondition = array();
  1364. preg_match('#(AND|OR|) *('.$word.') *(AND|OR|)#', $InfoFieldList[4], $matchCondition);
  1365. while (!empty($matchCondition[0])) {
  1366. // If the two sides differ but are not empty
  1367. if (!empty($matchCondition[1]) && !empty($matchCondition[3]) && $matchCondition[1] != $matchCondition[3]) {
  1368. // Nobody sain would do that without parentheses
  1369. $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
  1370. } else {
  1371. if (!empty($matchCondition[1])) {
  1372. $boolCond = (($matchCondition[1] == "AND") ? ' AND TRUE ' : ' OR FALSE ');
  1373. $InfoFieldList[4] = str_replace($matchCondition[0], $boolCond.$matchCondition[3], $InfoFieldList[4]);
  1374. } elseif (!empty($matchCondition[3])) {
  1375. $boolCond = (($matchCondition[3] == "AND") ? ' TRUE AND ' : ' FALSE OR');
  1376. $InfoFieldList[4] = str_replace($matchCondition[0], $boolCond, $InfoFieldList[4]);
  1377. } else {
  1378. $InfoFieldList[4] = " TRUE ";
  1379. }
  1380. }
  1381. // Si l'on a un AND ou un OR, avant ou après
  1382. preg_match('#(AND|OR|) *('.$word.') *(AND|OR|)#', $InfoFieldList[4], $matchCondition);
  1383. }
  1384. } else {
  1385. $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
  1386. }
  1387. // We have to join on extrafield table
  1388. if (strpos($InfoFieldList[4], 'extra.') !== false) {
  1389. $sql .= ' as main, '.$this->db->prefix().$InfoFieldList[0].'_extrafields as extra';
  1390. $sqlwhere .= " WHERE extra.fk_object=main.".$InfoFieldList[2]." AND ".$InfoFieldList[4];
  1391. } else {
  1392. $sqlwhere .= " WHERE ".$InfoFieldList[4];
  1393. }
  1394. } else {
  1395. $sqlwhere .= ' WHERE 1=1';
  1396. }
  1397. // Some tables may have field, some other not. For the moment we disable it.
  1398. if (in_array($InfoFieldList[0], array('tablewithentity'))) {
  1399. $sqlwhere .= " AND entity = ".((int) $conf->entity);
  1400. }
  1401. // $sql.=preg_replace('/^ AND /','',$sqlwhere);
  1402. // print $sql;
  1403. $sql .= $sqlwhere;
  1404. dol_syslog(get_class($this).'::showInputField type=chkbxlst', LOG_DEBUG);
  1405. $resql = $this->db->query($sql);
  1406. if ($resql) {
  1407. $num = $this->db->num_rows($resql);
  1408. $i = 0;
  1409. $data = array();
  1410. while ($i < $num) {
  1411. $labeltoshow = '';
  1412. $obj = $this->db->fetch_object($resql);
  1413. $notrans = false;
  1414. // Several field into label (eq table:code|label:rowid)
  1415. $fields_label = explode('|', $InfoFieldList[1]);
  1416. if (is_array($fields_label)) {
  1417. $notrans = true;
  1418. foreach ($fields_label as $field_toshow) {
  1419. $labeltoshow .= $obj->$field_toshow.' ';
  1420. }
  1421. } else {
  1422. $labeltoshow = $obj->{$InfoFieldList[1]};
  1423. }
  1424. $labeltoshow = dol_trunc($labeltoshow, 45);
  1425. if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
  1426. $labeltoshow = '';
  1427. foreach ($fields_label as $field_toshow) {
  1428. $translabel = $langs->trans($obj->$field_toshow);
  1429. if ($translabel != $obj->$field_toshow) {
  1430. $labeltoshow .= ' '.dol_trunc($translabel, 18).' ';
  1431. } else {
  1432. $labeltoshow .= ' '.dol_trunc($obj->$field_toshow, 18).' ';
  1433. }
  1434. }
  1435. $data[$obj->rowid] = $labeltoshow;
  1436. } else {
  1437. if (!$notrans) {
  1438. $translabel = $langs->trans($obj->{$InfoFieldList[1]});
  1439. if ($translabel != $obj->{$InfoFieldList[1]}) {
  1440. $labeltoshow = dol_trunc($translabel, 18);
  1441. } else {
  1442. $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
  1443. }
  1444. }
  1445. if (empty($labeltoshow)) {
  1446. $labeltoshow = '(not defined)';
  1447. }
  1448. if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
  1449. $data[$obj->rowid] = $labeltoshow;
  1450. }
  1451. if (!empty($InfoFieldList[3]) && $parentField) {
  1452. $parent = $parentName.':'.$obj->{$parentField};
  1453. }
  1454. $data[$obj->rowid] = $labeltoshow;
  1455. }
  1456. $i++;
  1457. }
  1458. $this->db->free($resql);
  1459. $out = $form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, '', 0, '', 0, '100%');
  1460. } else {
  1461. print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
  1462. }
  1463. } else {
  1464. require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
  1465. $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
  1466. $out = $form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, '', 0, '', 0, '100%');
  1467. }
  1468. }
  1469. } elseif ($type == 'link') {
  1470. $param_list = array_keys($param['options']); // $param_list[0] = 'ObjectName:classPath' but can also be 'ObjectName:classPath:1:(status:=:1)'
  1471. /* Removed.
  1472. The selectForForms is called with parameter $objectfield defined, so the app can retrieve the filter inside the ajax component instead of being provided as parameters. The
  1473. filter was used to pass SQL requests leading to serious SQL injection problem. This should not be possible. Also the call of the ajax was broken by some WAF.
  1474. if (strpos($param_list[0], '$ID$') !== false && !empty($objectid)) {
  1475. $param_list[0] = str_replace('$ID$', $objectid, $param_list[0]);
  1476. }*/
  1477. $showempty = (($required && $default != '') ? 0 : 1);
  1478. $tmparray = explode(':', $param_list[0]);
  1479. $element = $extrafieldsobjectkey; // $extrafieldsobjectkey comes from $object->table_element but we need $object->element
  1480. if ($element == 'socpeople') {
  1481. $element = 'contact';
  1482. } elseif ( $element == 'projet' ) {
  1483. $element = 'project';
  1484. }
  1485. //$objectdesc = $param_list[0]; // Example: 'ObjectName:classPath:1:(status:=:1)' Replaced by next line: this was propagated also a filter by ajax call that was blocked by some WAF
  1486. $objectdesc = $tmparray[0]; // Example: 'ObjectName:classPath' To not propagate any filter (selectForForms do ajax call and propagating SQL filter is blocked by some WAF). Also we should use the one into the definition in the ->fields of $elem if found.
  1487. $objectfield = $element.':options_'.$key; // Example: 'actioncomm:options_fff' To be used in priority to know object linked with all its definition (including filters)
  1488. $out = $form->selectForForms($objectdesc, $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, '', 0, 0, '', $objectfield);
  1489. } elseif ($type == 'password') {
  1490. // If prefix is 'search_', field is used as a filter, we use a common text field.
  1491. $out = '<input style="display:none" type="text" name="fakeusernameremembered">'; // Hidden field to reduce impact of evil Google Chrome autopopulate bug.
  1492. $out .= '<input autocomplete="new-password" type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
  1493. }
  1494. if (!empty($hidden)) {
  1495. $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
  1496. }
  1497. /* Add comments
  1498. if ($type == 'date') $out.=' (YYYY-MM-DD)';
  1499. elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
  1500. */
  1501. /*if (!empty($help) && $keyprefix != 'search_options_') {
  1502. $out .= $form->textwithpicto('', $help, 1, 'help', '', 0, 3);
  1503. }*/
  1504. return $out;
  1505. }
  1506. /**
  1507. * Return HTML string to put an output field into a page
  1508. *
  1509. * @param string $key Key of attribute
  1510. * @param string $value Value to show
  1511. * @param string $moreparam To add more parameters on html input tag (only checkbox use html input for output rendering)
  1512. * @param string $extrafieldsobjectkey Required (for example $object->table_element).
  1513. * @param Translate $outputlangs Output language
  1514. * @return string Formated value
  1515. */
  1516. public function showOutputField($key, $value, $moreparam = '', $extrafieldsobjectkey = '', $outputlangs = null)
  1517. {
  1518. global $conf, $langs;
  1519. if (is_null($outputlangs) || !is_object($outputlangs)) {
  1520. $outputlangs = $langs;
  1521. }
  1522. if (empty($extrafieldsobjectkey)) {
  1523. dol_syslog(get_class($this).'::showOutputField extrafieldsobjectkey required', LOG_ERR);
  1524. return 'BadValueForParamExtraFieldsObjectKey';
  1525. }
  1526. $label = $this->attributes[$extrafieldsobjectkey]['label'][$key];
  1527. $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
  1528. $size = $this->attributes[$extrafieldsobjectkey]['size'][$key]; // Can be '255', '24,8'...
  1529. $default = $this->attributes[$extrafieldsobjectkey]['default'][$key];
  1530. $computed = $this->attributes[$extrafieldsobjectkey]['computed'][$key];
  1531. $unique = $this->attributes[$extrafieldsobjectkey]['unique'][$key];
  1532. $required = $this->attributes[$extrafieldsobjectkey]['required'][$key];
  1533. $param = $this->attributes[$extrafieldsobjectkey]['param'][$key];
  1534. $perms = dol_eval($this->attributes[$extrafieldsobjectkey]['perms'][$key], 1, 1, '2');
  1535. $langfile = $this->attributes[$extrafieldsobjectkey]['langfile'][$key];
  1536. $list = dol_eval($this->attributes[$extrafieldsobjectkey]['list'][$key], 1, 1, '2');
  1537. $help = $this->attributes[$extrafieldsobjectkey]['help'][$key];
  1538. $hidden = (empty($list) ? 1 : 0); // If $list empty, we are sure it is hidden, otherwise we show. If it depends on mode (view/create/edit form or list, this must be filtered by caller)
  1539. if ($hidden) {
  1540. return ''; // This is a protection. If field is hidden, we should just not call this method.
  1541. }
  1542. //if ($computed) $value = // $value is already calculated into $value before calling this method
  1543. $showsize = 0;
  1544. if ($type == 'date') {
  1545. $showsize = 10;
  1546. if ($value !== '') {
  1547. $value = dol_print_date($value, 'day'); // For date without hour, date is always GMT for storage and output
  1548. }
  1549. } elseif ($type == 'datetime') {
  1550. $showsize = 19;
  1551. if ($value !== '') {
  1552. $value = dol_print_date($value, 'dayhour', 'tzuserrel');
  1553. }
  1554. } elseif ($type == 'datetimegmt') {
  1555. $showsize = 19;
  1556. if ($value !== '') {
  1557. $value = dol_print_date($value, 'dayhour', 'gmt');
  1558. }
  1559. } elseif ($type == 'int') {
  1560. $showsize = 10;
  1561. } elseif ($type == 'double') {
  1562. if (!empty($value)) {
  1563. //$value=price($value);
  1564. $sizeparts = explode(",", $size);
  1565. $number_decimals = array_key_exists(1, $sizeparts) ? $sizeparts[1] : 0;
  1566. $value = price($value, 0, $outputlangs, 0, 0, $number_decimals, '');
  1567. }
  1568. } elseif ($type == 'boolean') {
  1569. $checked = '';
  1570. if (!empty($value)) {
  1571. $checked = ' checked ';
  1572. }
  1573. $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
  1574. } elseif ($type == 'mail') {
  1575. $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
  1576. } elseif ($type == 'ip') {
  1577. $value = dol_print_ip($value, 0);
  1578. } elseif ($type == 'icon') {
  1579. $value = '<span class="'.$value.'"></span>';
  1580. } elseif ($type == 'url') {
  1581. $value = dol_print_url($value, '_blank', 32, 1);
  1582. } elseif ($type == 'phone') {
  1583. $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
  1584. } elseif ($type == 'price') {
  1585. //$value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
  1586. if ($value || $value == '0') {
  1587. $value = price($value, 0, $outputlangs, 0, $conf->global->MAIN_MAX_DECIMALS_TOT, -1).' '.$outputlangs->getCurrencySymbol($conf->currency);
  1588. }
  1589. } elseif ($type == 'pricecy') {
  1590. $currency = $conf->currency;
  1591. if (!empty($value)) {
  1592. // $value in memory is a php string like '0.01:EUR'
  1593. $pricetmp = explode(':', $value);
  1594. $currency = !empty($pricetmp[1]) ? $pricetmp[1] : $conf->currency;
  1595. $value = $pricetmp[0];
  1596. }
  1597. if ($value || $value == '0') {
  1598. $value = price($value, 0, $outputlangs, 0, $conf->global->MAIN_MAX_DECIMALS_TOT, -1, $currency);
  1599. }
  1600. } elseif ($type == 'select') {
  1601. $valstr = (!empty($param['options'][$value]) ? $param['options'][$value] : '');
  1602. if (($pos = strpos($valstr, "|")) !== false) {
  1603. $valstr = substr($valstr, 0, $pos);
  1604. }
  1605. if ($langfile && $valstr) {
  1606. $value = $outputlangs->trans($valstr);
  1607. } else {
  1608. $value = $valstr;
  1609. }
  1610. } elseif ($type == 'sellist') {
  1611. $param_list = array_keys($param['options']);
  1612. $InfoFieldList = explode(":", $param_list[0]);
  1613. $selectkey = "rowid";
  1614. $keyList = 'rowid';
  1615. if (count($InfoFieldList) >= 3) {
  1616. $selectkey = $InfoFieldList[2];
  1617. $keyList = $InfoFieldList[2].' as rowid';
  1618. }
  1619. $fields_label = explode('|', $InfoFieldList[1]);
  1620. if (is_array($fields_label)) {
  1621. $keyList .= ', ';
  1622. $keyList .= implode(', ', $fields_label);
  1623. }
  1624. $filter_categorie = false;
  1625. if (count($InfoFieldList) > 5) {
  1626. if ($InfoFieldList[0] == 'categorie') {
  1627. $filter_categorie = true;
  1628. }
  1629. }
  1630. $sql = "SELECT ".$keyList;
  1631. $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
  1632. if (!empty($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra.') !== false) {
  1633. $sql .= ' as main';
  1634. }
  1635. if ($selectkey == 'rowid' && empty($value)) {
  1636. $sql .= " WHERE ".$selectkey." = 0";
  1637. } elseif ($selectkey == 'rowid') {
  1638. $sql .= " WHERE ".$selectkey." = ".((int) $value);
  1639. } else {
  1640. $sql .= " WHERE ".$selectkey." = '".$this->db->escape($value)."'";
  1641. }
  1642. //$sql.= ' AND entity = '.$conf->entity;
  1643. dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
  1644. $resql = $this->db->query($sql);
  1645. if ($resql) {
  1646. if ($filter_categorie === false) {
  1647. $value = ''; // value was used, so now we reste it to use it to build final output
  1648. $obj = $this->db->fetch_object($resql);
  1649. // Several field into label (eq table:code|label:rowid)
  1650. $fields_label = explode('|', $InfoFieldList[1]);
  1651. if (is_array($fields_label) && count($fields_label) > 1) {
  1652. foreach ($fields_label as $field_toshow) {
  1653. $translabel = '';
  1654. if (!empty($obj->$field_toshow)) {
  1655. $translabel = $outputlangs->trans($obj->$field_toshow);
  1656. if ($translabel != $obj->$field_toshow) {
  1657. $value .= dol_trunc($translabel, 24) . ' ';
  1658. } else {
  1659. $value .= $obj->$field_toshow . ' ';
  1660. }
  1661. }
  1662. }
  1663. } else {
  1664. $translabel = '';
  1665. $tmppropname = $InfoFieldList[1];
  1666. //$obj->$tmppropname = '';
  1667. if (!empty(isset($obj->$tmppropname) ? $obj->$tmppropname : '')) {
  1668. $translabel = $outputlangs->trans($obj->$tmppropname);
  1669. }
  1670. if ($translabel != (isset($obj->$tmppropname) ? $obj->$tmppropname : '')) {
  1671. $value = dol_trunc($translabel, 18);
  1672. } else {
  1673. $value = isset($obj->$tmppropname) ? $obj->$tmppropname : '';
  1674. }
  1675. }
  1676. } else {
  1677. $toprint = array();
  1678. $obj = $this->db->fetch_object($resql);
  1679. if ($obj->rowid) {
  1680. require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
  1681. $c = new Categorie($this->db);
  1682. $result = $c->fetch($obj->rowid);
  1683. if ($result > 0) {
  1684. $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
  1685. foreach ($ways as $way) {
  1686. $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #bbb"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
  1687. }
  1688. }
  1689. }
  1690. $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
  1691. }
  1692. } else {
  1693. dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
  1694. }
  1695. } elseif ($type == 'radio') {
  1696. if (!isset($param['options'][$value])) {
  1697. $outputlangs->load('errors');
  1698. $value = $outputlangs->trans('ErrorNoValueForRadioType');
  1699. } else {
  1700. $value = $outputlangs->trans($param['options'][$value]);
  1701. }
  1702. } elseif ($type == 'checkbox') {
  1703. $value_arr = explode(',', $value);
  1704. $value = '';
  1705. $toprint = array();
  1706. if (is_array($value_arr)) {
  1707. foreach ($value_arr as $keyval => $valueval) {
  1708. if (!empty($valueval)) {
  1709. $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$param['options'][$valueval].'</li>';
  1710. }
  1711. }
  1712. }
  1713. $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
  1714. } elseif ($type == 'chkbxlst') {
  1715. $value_arr = explode(',', $value);
  1716. $param_list = array_keys($param['options']);
  1717. $InfoFieldList = explode(":", $param_list[0]);
  1718. $selectkey = "rowid";
  1719. $keyList = 'rowid';
  1720. if (count($InfoFieldList) >= 3) {
  1721. $selectkey = $InfoFieldList[2];
  1722. $keyList = $InfoFieldList[2].' as rowid';
  1723. }
  1724. $fields_label = explode('|', $InfoFieldList[1]);
  1725. if (is_array($fields_label)) {
  1726. $keyList .= ', ';
  1727. $keyList .= implode(', ', $fields_label);
  1728. }
  1729. $filter_categorie = false;
  1730. if (count($InfoFieldList) > 5) {
  1731. if ($InfoFieldList[0] == 'categorie') {
  1732. $filter_categorie = true;
  1733. }
  1734. }
  1735. $sql = "SELECT ".$keyList;
  1736. $sql .= " FROM ".$this->db->prefix().$InfoFieldList[0];
  1737. if (strpos($InfoFieldList[4], 'extra.') !== false) {
  1738. $sql .= ' as main';
  1739. }
  1740. // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
  1741. // $sql.= ' AND entity = '.$conf->entity;
  1742. dol_syslog(get_class($this).':showOutputField:$type=chkbxlst', LOG_DEBUG);
  1743. $resql = $this->db->query($sql);
  1744. if ($resql) {
  1745. if ($filter_categorie === false) {
  1746. $value = ''; // value was used, so now we reste it to use it to build final output
  1747. $toprint = array();
  1748. while ($obj = $this->db->fetch_object($resql)) {
  1749. // Several field into label (eq table:code|label:rowid)
  1750. $fields_label = explode('|', $InfoFieldList[1]);
  1751. if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
  1752. if (is_array($fields_label) && count($fields_label) > 1) {
  1753. $label = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">';
  1754. foreach ($fields_label as $field_toshow) {
  1755. $translabel = '';
  1756. if (!empty($obj->$field_toshow)) {
  1757. $translabel = $outputlangs->trans($obj->$field_toshow);
  1758. }
  1759. if ($translabel != $field_toshow) {
  1760. $label .= ' '.dol_trunc($translabel, 18);
  1761. } else {
  1762. $label .= ' '.$obj->$field_toshow;
  1763. }
  1764. }
  1765. $label .= '</li>';
  1766. $toprint[] = $label;
  1767. } else {
  1768. $translabel = '';
  1769. if (!empty($obj->{$InfoFieldList[1]})) {
  1770. $translabel = $outputlangs->trans($obj->{$InfoFieldList[1]});
  1771. }
  1772. if ($translabel != $obj->{$InfoFieldList[1]}) {
  1773. $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.dol_trunc($translabel, 18).'</li>';
  1774. } else {
  1775. $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$obj->{$InfoFieldList[1]}.'</li>';
  1776. }
  1777. }
  1778. }
  1779. }
  1780. } else {
  1781. require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
  1782. $toprint = array();
  1783. while ($obj = $this->db->fetch_object($resql)) {
  1784. if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
  1785. $c = new Categorie($this->db);
  1786. $c->fetch($obj->rowid);
  1787. $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
  1788. foreach ($ways as $way) {
  1789. $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"'.($c->color ? ' style="background: #'.$c->color.';"' : ' style="background: #bbb"').'>'.img_object('', 'category').' '.$way.'</li>';
  1790. }
  1791. }
  1792. }
  1793. }
  1794. if (!empty($toprint)) {
  1795. $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
  1796. }
  1797. } else {
  1798. dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
  1799. }
  1800. } elseif ($type == 'link') {
  1801. $out = '';
  1802. // Only if something to display (perf)
  1803. if ($value) { // If we have -1 here, pb is into insert, not into ouptut (fix insert instead of changing code here to compensate)
  1804. $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
  1805. $InfoFieldList = explode(":", $param_list[0]);
  1806. $classname = $InfoFieldList[0];
  1807. $classpath = $InfoFieldList[1];
  1808. if (!empty($classpath)) {
  1809. dol_include_once($InfoFieldList[1]);
  1810. if ($classname && class_exists($classname)) {
  1811. $object = new $classname($this->db);
  1812. $object->fetch($value);
  1813. $value = $object->getNomUrl(3);
  1814. }
  1815. } else {
  1816. dol_syslog('Error bad setup of extrafield', LOG_WARNING);
  1817. return 'Error bad setup of extrafield';
  1818. }
  1819. }
  1820. } elseif ($type == 'text') {
  1821. $value = dol_htmlentitiesbr($value);
  1822. } elseif ($type == 'html') {
  1823. $value = dol_htmlentitiesbr($value);
  1824. } elseif ($type == 'password') {
  1825. $value = dol_trunc(preg_replace('/./i', '*', $value), 8, 'right', 'UTF-8', 1);
  1826. } else {
  1827. $showsize = round((float) $size);
  1828. if ($showsize > 48) {
  1829. $showsize = 48;
  1830. }
  1831. }
  1832. //print $type.'-'.$size;
  1833. $out = $value;
  1834. return $out;
  1835. }
  1836. /**
  1837. * Return the CSS to use for this extrafield into list
  1838. *
  1839. * @param string $key Key of attribute
  1840. * @param string $extrafieldsobjectkey If defined, use the new method to get extrafields data
  1841. * @return string Formated value
  1842. */
  1843. public function getAlignFlag($key, $extrafieldsobjectkey = '')
  1844. {
  1845. global $conf, $langs;
  1846. $type = 'varchar';
  1847. if (!empty($extrafieldsobjectkey)) {
  1848. $type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
  1849. }
  1850. $cssstring = '';
  1851. if (in_array($type, array('date', 'datetime', 'datetimegmt',))) {
  1852. $cssstring = "center";
  1853. } elseif (in_array($type, array('int', 'price', 'double'))) {
  1854. $cssstring = "right";
  1855. } elseif (in_array($type, array('boolean', 'radio', 'checkbox', 'ip', 'icon'))) {
  1856. $cssstring = "center";
  1857. }
  1858. if (!empty($this->attributes[$extrafieldsobjectkey]['csslist'][$key])) {
  1859. $cssstring .= ($cssstring ? ' ' : '').$this->attributes[$extrafieldsobjectkey]['csslist'][$key];
  1860. } else {
  1861. if (in_array($type, array('ip'))) {
  1862. $cssstring .= ($cssstring ? ' ' : '').'tdoverflowmax150';
  1863. }
  1864. }
  1865. return $cssstring;
  1866. }
  1867. /**
  1868. * Return HTML string to print separator extrafield
  1869. *
  1870. * @param string $key Key of attribute
  1871. * @param object $object Object
  1872. * @param int $colspan Value of colspan to use (it must includes the first column with title)
  1873. * @param string $display_type "card" for form display, "line" for document line display (extrafields on propal line, order line, etc...)
  1874. * @param string $mode Show output ('view') or input ('create' or 'edit') for extrafield
  1875. * @return string HTML code with line for separator
  1876. */
  1877. public function showSeparator($key, $object, $colspan = 2, $display_type = 'card', $mode = '')
  1878. {
  1879. global $conf, $langs;
  1880. $tagtype='tr';
  1881. $tagtype_dyn='td';
  1882. if ($display_type=='line') {
  1883. $tagtype='div';
  1884. $tagtype_dyn='span';
  1885. $colspan=0;
  1886. }
  1887. $extrafield_param = $this->attributes[$object->table_element]['param'][$key];
  1888. $extrafield_param_list = array();
  1889. if (!empty($extrafield_param) && is_array($extrafield_param)) {
  1890. $extrafield_param_list = array_keys($extrafield_param['options']);
  1891. }
  1892. // Set $extrafield_collapse_display_value (do we have to collapse/expand the group after the separator)
  1893. $extrafield_collapse_display_value = -1;
  1894. $expand_display = false;
  1895. if (is_array($extrafield_param_list) && count($extrafield_param_list) > 0) {
  1896. $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
  1897. $expand_display = ((isset($_COOKIE['DOLCOLLAPSE_'.$object->table_element.'_extrafields_'.$key]) || GETPOST('ignorecollapsesetup', 'int')) ? (empty($_COOKIE['DOLCOLLAPSE_'.$object->table_element.'_extrafields_'.$key]) ? false : true) : ($extrafield_collapse_display_value == 2 ? false : true));
  1898. }
  1899. $disabledcookiewrite = 0;
  1900. if ($mode == 'create') {
  1901. // On create mode, force separator group to not be collapsable
  1902. $extrafield_collapse_display_value = 1;
  1903. $expand_display = true; // We force group to be shown expanded
  1904. $disabledcookiewrite = 1; // We keep status of group unchanged into the cookie
  1905. }
  1906. $out = '<'.$tagtype.' id="trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'" class="trextrafieldseparator trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'">';
  1907. $out .= '<'.$tagtype_dyn.' '.(!empty($colspan) ? 'colspan="' . $colspan . '"' : '').'>';
  1908. // Some js code will be injected here to manage the collapsing of extrafields
  1909. // Output the picto
  1910. $out .= '<span class="'.($extrafield_collapse_display_value ? 'cursorpointer ' : '').($extrafield_collapse_display_value == 0 ? 'fas fa-square opacitymedium' : 'far fa-'.(($expand_display ? 'minus' : 'plus').'-square')).'"></span>';
  1911. $out .= '&nbsp;';
  1912. $out .= '<strong>';
  1913. $out .= $langs->trans($this->attributes[$object->table_element]['label'][$key]);
  1914. $out .= '</strong>';
  1915. $out .= '</'.$tagtype_dyn.'>';
  1916. $out .= '</'.$tagtype.'>';
  1917. $collapse_group = $key.(!empty($object->id) ? '_'.$object->id : '');
  1918. //$extrafields_collapse_num = $this->attributes[$object->table_element]['pos'][$key].(!empty($object->id)?'_'.$object->id:'');
  1919. if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
  1920. // Set the collapse_display status to cookie in priority or if ignorecollapsesetup is 1, if cookie and ignorecollapsesetup not defined, use the setup.
  1921. $this->expand_display[$collapse_group] = $expand_display;
  1922. if (!empty($conf->use_javascript_ajax)) {
  1923. $out .= '<!-- Add js script to manage the collapse/uncollapse of extrafields separators '.$key.' -->'."\n";
  1924. $out .= '<script nonce="'.getNonce().'" type="text/javascript">'."\n";
  1925. $out .= 'jQuery(document).ready(function(){'."\n";
  1926. if (empty($disabledcookiewrite)) {
  1927. if ($expand_display === false) {
  1928. $out .= ' console.log("Inject js for the collapsing of extrafield '.$key.' - hide");'."\n";
  1929. $out .= ' jQuery(".trextrafields_collapse'.$collapse_group.'").hide();'."\n";
  1930. } else {
  1931. $out .= ' console.log("Inject js for collapsing of extrafield '.$key.' - keep visible and set cookie");'."\n";
  1932. $out .= ' document.cookie = "DOLCOLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=1; path='.$_SERVER["PHP_SELF"].'"'."\n";
  1933. }
  1934. }
  1935. $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').'").click(function(){'."\n";
  1936. $out .= ' console.log("We click on collapse/uncollapse to hide/show .trextrafields_collapse'.$collapse_group.'");'."\n";
  1937. $out .= ' jQuery(".trextrafields_collapse'.$collapse_group.'").toggle(100, function(){'."\n";
  1938. $out .= ' if (jQuery(".trextrafields_collapse'.$collapse_group.'").is(":hidden")) {'."\n";
  1939. $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').' '.$tagtype_dyn.' span").addClass("fa-plus-square").removeClass("fa-minus-square");'."\n";
  1940. $out .= ' document.cookie = "DOLCOLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=0; path='.$_SERVER["PHP_SELF"].'"'."\n";
  1941. $out .= ' } else {'."\n";
  1942. $out .= ' jQuery("#trextrafieldseparator'.$key.(!empty($object->id) ? '_'.$object->id : '').' '.$tagtype_dyn.' span").addClass("fa-minus-square").removeClass("fa-plus-square");'."\n";
  1943. $out .= ' document.cookie = "DOLCOLLAPSE_'.$object->table_element.'_extrafields_'.$key.'=1; path='.$_SERVER["PHP_SELF"].'"'."\n";
  1944. $out .= ' }'."\n";
  1945. $out .= ' });'."\n";
  1946. $out .= ' });'."\n";
  1947. $out .= '});'."\n";
  1948. $out .= '</script>'."\n";
  1949. }
  1950. } else {
  1951. $this->expand_display[$collapse_group] = 1;
  1952. }
  1953. return $out;
  1954. }
  1955. /**
  1956. * Fill array_options property of object by extrafields value (using for data sent by forms)
  1957. *
  1958. * @param array|null $extralabels Deprecated (old $array of extrafields, now set this to null)
  1959. * @param object $object Object
  1960. * @param string $onlykey Only some keys are filled:
  1961. * 'string' => When we make update of only one extrafield ($action = 'update_extras'), calling page can set this to avoid to have other extrafields being reset.
  1962. * '@GETPOSTISSET' => When we make update of several extrafields ($action = 'update'), calling page can set this to avoid to have fields not into POST being reset.
  1963. * @param int $todefaultifmissing 1=Set value to the default value in database if value is mandatory and missing
  1964. * @return int 1 if array_options set, 0 if no value, -1 if error (field required missing for example)
  1965. */
  1966. public function setOptionalsFromPost($extralabels, &$object, $onlykey = '', $todefaultifmissing = 0)
  1967. {
  1968. global $conf, $_POST, $langs;
  1969. $nofillrequired = 0; // For error when required field left blank
  1970. $error_field_required = array();
  1971. if (isset($this->attributes[$object->table_element]['label']) && is_array($this->attributes[$object->table_element]['label'])) {
  1972. $extralabels = $this->attributes[$object->table_element]['label'];
  1973. }
  1974. if (is_array($extralabels)) {
  1975. // Get extra fields
  1976. foreach ($extralabels as $key => $value) {
  1977. if (!empty($onlykey) && $onlykey != '@GETPOSTISSET' && $key != $onlykey) {
  1978. continue;
  1979. }
  1980. if (!empty($onlykey) && $onlykey == '@GETPOSTISSET' && !GETPOSTISSET('options_'.$key) && (! in_array($this->attributes[$object->table_element]['type'][$key], array('boolean', 'checkbox', 'chkbxlst')))) {
  1981. //when unticking boolean field, it's not set in POST
  1982. continue;
  1983. }
  1984. $key_type = $this->attributes[$object->table_element]['type'][$key];
  1985. if ($key_type == 'separate') {
  1986. continue;
  1987. }
  1988. $enabled = 1;
  1989. if (isset($this->attributes[$object->table_element]['enabled'][$key])) { // 'enabled' is often a condition on module enabled or not
  1990. $enabled = dol_eval($this->attributes[$object->table_element]['enabled'][$key], 1, 1, '2');
  1991. }
  1992. $visibility = 1;
  1993. if (isset($this->attributes[$object->table_element]['list'][$key])) { // 'list' is option for visibility
  1994. $visibility = intval(dol_eval($this->attributes[$object->table_element]['list'][$key], 1, 1, '2'));
  1995. }
  1996. $perms = 1;
  1997. if (isset($this->attributes[$object->table_element]['perms'][$key])) {
  1998. $perms = dol_eval($this->attributes[$object->table_element]['perms'][$key], 1, 1, '2');
  1999. }
  2000. if (empty($enabled)
  2001. || (
  2002. $onlykey === '@GETPOSTISSET'
  2003. && in_array($this->attributes[$object->table_element]['type'][$key], array('boolean', 'checkbox', 'chkbxlst'))
  2004. && in_array(abs($enabled), array(2, 5))
  2005. && ! GETPOSTISSET('options_' . $key) // Update hidden checkboxes and multiselect only if they are provided
  2006. )
  2007. ) {
  2008. continue;
  2009. }
  2010. $visibility_abs = abs($visibility);
  2011. // not modify if extra field is not in update form (0 : never, 2 or -2 : list only, 5 or - 5 : list and view only)
  2012. if (empty($visibility_abs) || $visibility_abs == 2 || $visibility_abs == 5) {
  2013. continue;
  2014. }
  2015. if (empty($perms)) {
  2016. continue;
  2017. }
  2018. if ($this->attributes[$object->table_element]['required'][$key]) { // Value is required
  2019. // Check if functionally empty without using GETPOST (depending on the type of extrafield, a
  2020. // technically non-empty value may be treated as empty functionally).
  2021. // value can be alpha, int, array, etc...
  2022. $v = $_POST["options_".$key] ?? null;
  2023. $type = $this->attributes[$object->table_element]['type'][$key];
  2024. if (self::isEmptyValue($v, $type)) {
  2025. //print 'ccc'.$value.'-'.$this->attributes[$object->table_element]['required'][$key];
  2026. // Field is not defined. We mark this as an error. We may fix it later if there is a default value and $todefaultifmissing is set.
  2027. $nofillrequired++;
  2028. if (!empty($this->attributes[$object->table_element]['langfile'][$key])) {
  2029. $langs->load($this->attributes[$object->table_element]['langfile'][$key]);
  2030. }
  2031. $error_field_required[$key] = $langs->transnoentitiesnoconv($value);
  2032. }
  2033. }
  2034. if (in_array($key_type, array('date'))) {
  2035. // Clean parameters
  2036. $value_key = dol_mktime(12, 0, 0, GETPOST("options_".$key."month", 'int'), GETPOST("options_".$key."day", 'int'), GETPOST("options_".$key."year", 'int'));
  2037. } elseif (in_array($key_type, array('datetime'))) {
  2038. // Clean parameters
  2039. $value_key = dol_mktime(GETPOST("options_".$key."hour", 'int'), GETPOST("options_".$key."min", 'int'), GETPOST("options_".$key."sec", 'int'), GETPOST("options_".$key."month", 'int'), GETPOST("options_".$key."day", 'int'), GETPOST("options_".$key."year", 'int'), 'tzuserrel');
  2040. } elseif (in_array($key_type, array('datetimegmt'))) {
  2041. // Clean parameters
  2042. $value_key = dol_mktime(GETPOST("options_".$key."hour", 'int'), GETPOST("options_".$key."min", 'int'), GETPOST("options_".$key."sec", 'int'), GETPOST("options_".$key."month", 'int'), GETPOST("options_".$key."day", 'int'), GETPOST("options_".$key."year", 'int'), 'gmt');
  2043. } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
  2044. $value_arr = GETPOST("options_".$key, 'array'); // check if an array
  2045. if (!empty($value_arr)) {
  2046. $value_key = implode(',', $value_arr);
  2047. } else {
  2048. $value_key = '';
  2049. }
  2050. } elseif (in_array($key_type, array('price', 'double'))) {
  2051. $value_arr = GETPOST("options_".$key, 'alpha');
  2052. $value_key = price2num($value_arr);
  2053. } elseif (in_array($key_type, array('pricecy', 'double'))) {
  2054. $value_key = price2num(GETPOST("options_".$key, 'alpha')).':'.GETPOST("options_".$key."currency_id", 'alpha');
  2055. } elseif (in_array($key_type, array('html'))) {
  2056. $value_key = GETPOST("options_".$key, 'restricthtml');
  2057. } elseif (in_array($key_type, array('text'))) {
  2058. $label_security_check = 'alphanohtml';
  2059. // by default 'alphanohtml' (better security); hidden conf MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML allows basic html
  2060. if (getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_REF_LABELS')) {
  2061. $label_security_check = 'nohtml';
  2062. } else {
  2063. $label_security_check = !getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML') ? 'alphanohtml' : 'restricthtml';
  2064. }
  2065. $value_key = GETPOST("options_".$key, $label_security_check);
  2066. } else {
  2067. $value_key = GETPOST("options_".$key);
  2068. if (in_array($key_type, array('link')) && $value_key == '-1') {
  2069. $value_key = '';
  2070. }
  2071. }
  2072. if (!empty($error_field_required[$key]) && $todefaultifmissing) {
  2073. // Value is required but we have a default value and we asked to set empty value to the default value
  2074. if (!empty($this->attributes[$object->table_element]['default']) && !is_null($this->attributes[$object->table_element]['default'][$key])) {
  2075. $value_key = $this->attributes[$object->table_element]['default'][$key];
  2076. unset($error_field_required[$key]);
  2077. $nofillrequired--;
  2078. }
  2079. }
  2080. $object->array_options["options_".$key] = $value_key;
  2081. }
  2082. if ($nofillrequired) {
  2083. $langs->load('errors');
  2084. $this->error = $langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required);
  2085. setEventMessages($this->error, null, 'errors');
  2086. return -1;
  2087. } else {
  2088. return 1;
  2089. }
  2090. } else {
  2091. return 0;
  2092. }
  2093. }
  2094. /**
  2095. * return array_options array of data of extrafields value of object sent by a search form
  2096. *
  2097. * @param array|string $extrafieldsobjectkey array of extrafields (old usage) or value of object->table_element (new usage)
  2098. * @param string $keyprefix Prefix string to add into name and id of field (can be used to avoid duplicate names)
  2099. * @param string $keysuffix Suffix string to add into name and id of field (can be used to avoid duplicate names)
  2100. * @return array|int array_options set or 0 if no value
  2101. */
  2102. public function getOptionalsFromPost($extrafieldsobjectkey, $keyprefix = '', $keysuffix = '')
  2103. {
  2104. global $_POST;
  2105. if (is_string($extrafieldsobjectkey) && !empty($this->attributes[$extrafieldsobjectkey]['label']) && is_array($this->attributes[$extrafieldsobjectkey]['label'])) {
  2106. $extralabels = $this->attributes[$extrafieldsobjectkey]['label'];
  2107. } else {
  2108. $extralabels = $extrafieldsobjectkey;
  2109. }
  2110. if (is_array($extralabels)) {
  2111. $array_options = array();
  2112. // Get extra fields
  2113. foreach ($extralabels as $key => $value) {
  2114. $key_type = '';
  2115. if (is_string($extrafieldsobjectkey)) {
  2116. $key_type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
  2117. }
  2118. if (in_array($key_type, array('date'))) {
  2119. $dateparamname_start = $keysuffix . 'options_' . $key . $keyprefix . '_start';
  2120. $dateparamname_end = $keysuffix . 'options_' . $key . $keyprefix . '_end';
  2121. if (GETPOST($dateparamname_start . 'year') || GETPOST($dateparamname_end . 'year')) {
  2122. $value_key = array();
  2123. // values provided as a component year, month, day, etc.
  2124. if (GETPOST($dateparamname_start . 'year')) {
  2125. $value_key['start'] = dol_mktime(0, 0, 0, GETPOST($dateparamname_start . 'month', 'int'), GETPOST($dateparamname_start . 'day', 'int'), GETPOST($dateparamname_start . 'year', 'int'));
  2126. }
  2127. if (GETPOST($dateparamname_start . 'year')) {
  2128. $value_key['end'] = dol_mktime(23, 59, 59, GETPOST($dateparamname_end . 'month', 'int'), GETPOST($dateparamname_end . 'day', 'int'), GETPOST($dateparamname_end . 'year', 'int'));
  2129. }
  2130. } elseif (GETPOST($keysuffix."options_".$key.$keyprefix."year")) {
  2131. // Clean parameters
  2132. $value_key = dol_mktime(12, 0, 0, GETPOST($keysuffix."options_".$key.$keyprefix."month", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."day", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."year", 'int'));
  2133. } else {
  2134. continue; // Value was not provided, we should not set it.
  2135. }
  2136. } elseif (in_array($key_type, array('datetime', 'datetimegmt'))) {
  2137. $dateparamname_start = $keysuffix . 'options_' . $key . $keyprefix . '_start';
  2138. $dateparamname_end = $keysuffix . 'options_' . $key . $keyprefix . '_end';
  2139. if (GETPOST($dateparamname_start . 'year') && GETPOST($dateparamname_end . 'year')) {
  2140. // values provided as a date pair (start date + end date), each date being broken down as year, month, day, etc.
  2141. $dateparamname_end_hour = GETPOST($dateparamname_end . 'hour', 'int') != '-1' ? GETPOST($dateparamname_end . 'hour', 'int') : '23';
  2142. $dateparamname_end_min = GETPOST($dateparamname_end . 'min', 'int') != '-1' ? GETPOST($dateparamname_end . 'min', 'int') : '59';
  2143. $dateparamname_end_sec = GETPOST($dateparamname_end . 'sec', 'int') != '-1' ? GETPOST($dateparamname_end . 'sec', 'int') : '59';
  2144. if ($key_type == 'datetimegmt') {
  2145. $value_key = array(
  2146. 'start' => dol_mktime(GETPOST($dateparamname_start . 'hour', 'int'), GETPOST($dateparamname_start . 'min', 'int'), GETPOST($dateparamname_start . 'sec', 'int'), GETPOST($dateparamname_start . 'month', 'int'), GETPOST($dateparamname_start . 'day', 'int'), GETPOST($dateparamname_start . 'year', 'int'), 'gmt'),
  2147. 'end' => dol_mktime($dateparamname_end_hour, $dateparamname_end_min, $dateparamname_end_sec, GETPOST($dateparamname_end . 'month', 'int'), GETPOST($dateparamname_end . 'day', 'int'), GETPOST($dateparamname_end . 'year', 'int'), 'gmt')
  2148. );
  2149. } else {
  2150. $value_key = array(
  2151. 'start' => dol_mktime(GETPOST($dateparamname_start . 'hour', 'int'), GETPOST($dateparamname_start . 'min', 'int'), GETPOST($dateparamname_start . 'sec', 'int'), GETPOST($dateparamname_start . 'month', 'int'), GETPOST($dateparamname_start . 'day', 'int'), GETPOST($dateparamname_start . 'year', 'int'), 'tzuserrel'),
  2152. 'end' => dol_mktime($dateparamname_end_hour, $dateparamname_end_min, $dateparamname_end_sec, GETPOST($dateparamname_end . 'month', 'int'), GETPOST($dateparamname_end . 'day', 'int'), GETPOST($dateparamname_end . 'year', 'int'), 'tzuserrel')
  2153. );
  2154. }
  2155. } elseif (GETPOST($keysuffix."options_".$key.$keyprefix."year")) {
  2156. // Clean parameters
  2157. if ($key_type == 'datetimegmt') {
  2158. $value_key = dol_mktime(GETPOST($keysuffix."options_".$key.$keyprefix."hour", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."min", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."sec", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."month", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."day", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."year", 'int'), 'gmt');
  2159. } else {
  2160. $value_key = dol_mktime(GETPOST($keysuffix."options_".$key.$keyprefix."hour", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."min", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."sec", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."month", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."day", 'int'), GETPOST($keysuffix."options_".$key.$keyprefix."year", 'int'), 'tzuserrel');
  2161. }
  2162. } else {
  2163. continue; // Value was not provided, we should not set it.
  2164. }
  2165. } elseif ($key_type == 'select') {
  2166. // to detect if we are in search context
  2167. if (GETPOSTISARRAY($keysuffix."options_".$key.$keyprefix)) {
  2168. $value_arr = GETPOST($keysuffix."options_".$key.$keyprefix, 'array:aZ09');
  2169. // Make sure we get an array even if there's only one selected
  2170. $value_arr = (array) $value_arr;
  2171. $value_key = implode(',', $value_arr);
  2172. } else {
  2173. $value_key = GETPOST($keysuffix."options_".$key.$keyprefix);
  2174. }
  2175. } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
  2176. // We test on a hidden field named "..._multiselect" that is always set to 1 if param is in form so
  2177. // when nothing is provided we can make a difference between noparam in the form and param was set to nothing.
  2178. if (!GETPOSTISSET($keysuffix."options_".$key.$keyprefix.'_multiselect')) {
  2179. continue; // Value was not provided, we should not set it.
  2180. }
  2181. $value_arr = GETPOST($keysuffix."options_".$key.$keyprefix);
  2182. // Make sure we get an array even if there's only one checkbox
  2183. $value_arr = (array) $value_arr;
  2184. $value_key = implode(',', $value_arr);
  2185. } elseif (in_array($key_type, array('price', 'double', 'int'))) {
  2186. if (!GETPOSTISSET($keysuffix."options_".$key.$keyprefix)) {
  2187. continue; // Value was not provided, we should not set it.
  2188. }
  2189. $value_arr = GETPOST($keysuffix."options_".$key.$keyprefix);
  2190. if ($keysuffix != 'search_') { // If value is for a search, we must keep complex string like '>100 <=150'
  2191. $value_key = price2num($value_arr);
  2192. } else {
  2193. $value_key = $value_arr;
  2194. }
  2195. } elseif (in_array($key_type, array('boolean'))) {
  2196. // We test on a hidden field named "..._boolean" that is always set to 1 if param is in form so
  2197. // when nothing is provided we can make a difference between noparam in the form and param was set to nothing.
  2198. if (!GETPOSTISSET($keysuffix."options_".$key.$keyprefix."_boolean")) {
  2199. $value_key = '';
  2200. } else {
  2201. $value_arr = GETPOST($keysuffix."options_".$key.$keyprefix);
  2202. $value_key = $value_arr;
  2203. }
  2204. } elseif (in_array($key_type, array('html'))) {
  2205. if (!GETPOSTISSET($keysuffix."options_".$key.$keyprefix)) {
  2206. continue; // Value was not provided, we should not set it.
  2207. }
  2208. $value_key = dol_htmlcleanlastbr(GETPOST($keysuffix."options_".$key.$keyprefix, 'restricthtml'));
  2209. } else {
  2210. if (!GETPOST($keysuffix."options_".$key.$keyprefix)) {
  2211. continue; // Value was not provided, we should not set it.
  2212. }
  2213. $value_key = GETPOST($keysuffix."options_".$key.$keyprefix);
  2214. }
  2215. $array_options[$keysuffix."options_".$key] = $value_key; // No keyprefix here. keyprefix is used only for read.
  2216. }
  2217. return $array_options;
  2218. }
  2219. return 0;
  2220. }
  2221. /**
  2222. * Return if a value is "empty" for a mandatory vision.
  2223. *
  2224. * @param mixed $v Value to test
  2225. * @param string $type Type of extrafield 'sellist', 'link', 'select', ...
  2226. * @return boolean True is value is an empty value, not allowed for a mandatory field.
  2227. */
  2228. public static function isEmptyValue($v, string $type)
  2229. {
  2230. if ($v === null || $v === '') {
  2231. return true;
  2232. }
  2233. if (is_array($v) || $type == 'select') {
  2234. return empty($v);
  2235. }
  2236. if ($type == 'link') {
  2237. return ($v == '-1');
  2238. }
  2239. if ($type == 'sellist') {
  2240. return ($v == '0');
  2241. }
  2242. return (empty($v) && $v != '0');
  2243. }
  2244. }