ajax.lib.php 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. <?php
  2. /* Copyright (C) 2007-2010 Laurent Destailleur <eldy@users.sourceforge.net>
  3. * Copyright (C) 2007-2015 Regis Houssin <regis.houssin@inodbox.com>
  4. * Copyright (C) 2012 Christophe Battarel <christophe.battarel@altairis.fr>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. * or see https://www.gnu.org/
  19. */
  20. /**
  21. * \file htdocs/core/lib/ajax.lib.php
  22. * \brief Page called to enhance interface with Javascript and Ajax features.
  23. */
  24. /**
  25. * Generic function that return javascript to add to a page to transform a common input field into an autocomplete field by calling an Ajax page (ex: /societe/ajax/ajaxcompanies.php).
  26. * The HTML field must be an input text with id=search_$htmlname.
  27. * This use the jQuery "autocomplete" function. If we want to use the select2, we must also convert the input into select on funcntions that call this method.
  28. *
  29. * @param string $selected Preselected value
  30. * @param string $htmlname HTML name of input field
  31. * @param string $url Ajax Url to call for request: /path/page.php. Must return a json array ('key'=>id, 'value'=>String shown into input field once selected, 'label'=>String shown into combo list)
  32. * @param string $urloption More parameters on URL request
  33. * @param int $minLength Minimum number of chars to trigger that Ajax search
  34. * @param int $autoselect Automatic selection if just one value (trigger("change") on field is done if search return only 1 result)
  35. * @param array $ajaxoptions Multiple options array
  36. * - Ex: array('update'=>array('field1','field2'...)) will reset field1 and field2 once select done
  37. * - Ex: array('disabled'=> )
  38. * - Ex: array('show'=> )
  39. * - Ex: array('update_textarea'=> )
  40. * - Ex: array('option_disabled'=> id to disable and warning to show if we select a disabled value (this is possible when using autocomplete ajax)
  41. * @param string $moreparams More params provided to ajax call
  42. * @return string Script
  43. */
  44. function ajax_autocompleter($selected, $htmlname, $url, $urloption = '', $minLength = 2, $autoselect = 0, $ajaxoptions = array(), $moreparams = '')
  45. {
  46. global $conf;
  47. if (empty($minLength)) {
  48. $minLength = 1;
  49. }
  50. $dataforrenderITem = 'ui-autocomplete';
  51. $dataforitem = 'ui-autocomplete-item';
  52. // Allow two constant to use other values for backward compatibility
  53. if (defined('JS_QUERY_AUTOCOMPLETE_RENDERITEM')) {
  54. $dataforrenderITem = constant('JS_QUERY_AUTOCOMPLETE_RENDERITEM');
  55. }
  56. if (defined('JS_QUERY_AUTOCOMPLETE_ITEM')) {
  57. $dataforitem = constant('JS_QUERY_AUTOCOMPLETE_ITEM');
  58. }
  59. $htmlnamejquery = str_replace('.', '\\\\.', $htmlname);
  60. // Input search_htmlname is original field
  61. // Input htmlname is a second input field used when using ajax autocomplete.
  62. $script = '<input type="hidden" name="'.$htmlname.'" id="'.$htmlname.'" value="'.$selected.'" '.($moreparams ? $moreparams : '').' />';
  63. $script .= '<!-- Javascript code for autocomplete of field '.$htmlname.' -->'."\n";
  64. $script .= '<script>'."\n";
  65. $script .= '$(document).ready(function() {
  66. var autoselect = '.((int) $autoselect).';
  67. var options = '.json_encode($ajaxoptions).'; /* Option of actions to do after keyup, or after select */
  68. /* Remove selected id as soon as we type or delete a char (it means old selection is wrong). Use keyup/down instead of change to avoid loosing the product id. This is needed only for select of predefined product */
  69. $("input#search_'.$htmlnamejquery.'").keydown(function(e) {
  70. if (e.keyCode != 9) /* If not "Tab" key */
  71. {
  72. if (e.keyCode == 13) { return false; } /* disable "ENTER" key useful for barcode readers */
  73. console.log("Clear id previously selected for field '.$htmlname.'");
  74. $("#'.$htmlnamejquery.'").val("");
  75. }
  76. });
  77. // Check options for secondary actions when keyup
  78. $("input#search_'.$htmlnamejquery.'").keyup(function() {
  79. if ($(this).val().length == 0)
  80. {
  81. $("#search_'.$htmlnamejquery.'").val("");
  82. $("#'.$htmlnamejquery.'").val("").trigger("change");
  83. if (options.option_disabled) {
  84. $("#" + options.option_disabled).removeAttr("disabled");
  85. }
  86. if (options.disabled) {
  87. $.each(options.disabled, function(key, value) {
  88. $("#" + value).removeAttr("disabled");
  89. });
  90. }
  91. if (options.update) {
  92. $.each(options.update, function(key, value) {
  93. $("#" + key).val("").trigger("change");
  94. });
  95. }
  96. if (options.show) {
  97. $.each(options.show, function(key, value) {
  98. $("#" + value).hide().trigger("hide");
  99. });
  100. }
  101. if (options.update_textarea) {
  102. $.each(options.update_textarea, function(key, value) {
  103. if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined" && CKEDITOR.instances[key] != "undefined") {
  104. CKEDITOR.instances[key].setData("");
  105. } else {
  106. $("#" + key).html("");
  107. }
  108. });
  109. }
  110. }
  111. });
  112. $("input#search_'.$htmlnamejquery.'").autocomplete({
  113. source: function( request, response ) {
  114. $.get("'.$url.($urloption ? '?'.$urloption : '').'", { "'.str_replace('.', '_', $htmlname).'": request.term }, function(data){
  115. if (data != null)
  116. {
  117. response($.map( data, function(item) {
  118. if (autoselect == 1 && data.length == 1) {
  119. $("#search_'.$htmlnamejquery.'").val(item.value);
  120. $("#'.$htmlnamejquery.'").val(item.key).trigger("change");
  121. }
  122. var label = "";
  123. if (item.label != null) {
  124. label = item.label.toString();
  125. }
  126. var update = {};
  127. if (options.update) {
  128. $.each(options.update, function(key, value) {
  129. update[key] = item[value];
  130. });
  131. }
  132. var textarea = {};
  133. if (options.update_textarea) {
  134. $.each(options.update_textarea, function(key, value) {
  135. textarea[key] = item[value];
  136. });
  137. }
  138. console.log("Return value from GET to the rest of code");
  139. return { label: label,
  140. value: item.value,
  141. id: item.key,
  142. disabled: item.disabled,
  143. update: update,
  144. textarea: textarea,
  145. pbq: item.pbq,
  146. type: item.type,
  147. qty: item.qty,
  148. discount: item.discount,
  149. pricebasetype: item.pricebasetype,
  150. price_ht: item.price_ht,
  151. price_ttc: item.price_ttc,
  152. price_unit_ht: item.price_unit_ht,
  153. price_unit_ht_locale: item.price_unit_ht_locale,
  154. description : item.description,
  155. ref_customer: item.ref_customer,
  156. tva_tx: item.tva_tx,
  157. default_vat_code: item.default_vat_code
  158. }
  159. }));
  160. } else {
  161. console.error("Error: Ajax url '.$url.($urloption ? '?'.$urloption : '').' has returned an empty page. Should be an empty json array.");
  162. }
  163. }, "json");
  164. },
  165. dataType: "json",
  166. minLength: '.((int) $minLength).',
  167. select: function( event, ui ) { // Function ran once new value has been selected into javascript combo
  168. console.log("We will trigger change on input '.$htmlname.' because of the select definition of autocomplete code for input#search_'.$htmlname.'");
  169. console.log("Selected id = "+ui.item.id+" - If this value is null, it means you select a record with key that is null so selection is not effective");
  170. console.log("Propagate before some properties retrieved by ajax into data-xxx properties of #'.$htmlnamejquery.' component");
  171. //console.log(ui.item);
  172. // For supplier price and customer when price by quantity is off
  173. $("#'.$htmlnamejquery.'").attr("data-up", ui.item.price_ht);
  174. $("#'.$htmlnamejquery.'").attr("data-up-locale", ui.item.price_unit_ht_locale);
  175. $("#'.$htmlnamejquery.'").attr("data-base", ui.item.pricebasetype);
  176. $("#'.$htmlnamejquery.'").attr("data-qty", ui.item.qty);
  177. $("#'.$htmlnamejquery.'").attr("data-discount", ui.item.discount);
  178. $("#'.$htmlnamejquery.'").attr("data-description", ui.item.description);
  179. $("#'.$htmlnamejquery.'").attr("data-ref-customer", ui.item.ref_customer);
  180. $("#'.$htmlnamejquery.'").attr("data-tvatx", ui.item.tva_tx);
  181. $("#'.$htmlnamejquery.'").attr("data-default-vat-code", ui.item.default_vat_code);
  182. ';
  183. if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
  184. $script .= '
  185. // For customer price when PRODUIT_CUSTOMER_PRICES_BY_QTY is on
  186. console.log("PRODUIT_CUSTOMER_PRICES_BY_QTY is on, propagate also prices by quantity into data-pbqxxx properties");
  187. $("#'.$htmlnamejquery.'").attr("data-pbq", ui.item.pbq);
  188. $("#'.$htmlnamejquery.'").attr("data-pbqup", ui.item.price_ht);
  189. $("#'.$htmlnamejquery.'").attr("data-pbqbase", ui.item.pricebasetype);
  190. $("#'.$htmlnamejquery.'").attr("data-pbqqty", ui.item.qty);
  191. $("#'.$htmlnamejquery.'").attr("data-pbqpercent", ui.item.discount);
  192. ';
  193. }
  194. $script .= '
  195. // A new value has been selected, we trigger the handlers on #htmlnamejquery
  196. console.log("Trigger changes on #'.$htmlnamejquery.'");
  197. $("#'.$htmlnamejquery.'").val(ui.item.id).trigger("change"); // Select new value
  198. // Complementary actions
  199. // Disable an element
  200. if (options.option_disabled) {
  201. console.log("Make action option_disabled on #"+options.option_disabled+" with disabled="+ui.item.disabled)
  202. if (ui.item.disabled) {
  203. $("#" + options.option_disabled).prop("disabled", true);
  204. if (options.error) {
  205. $.jnotify(options.error, "error", true); // Output with jnotify the error message
  206. }
  207. if (options.warning) {
  208. $.jnotify(options.warning, "warning", false); // Output with jnotify the warning message
  209. }
  210. } else {
  211. $("#" + options.option_disabled).removeAttr("disabled");
  212. }
  213. }
  214. if (options.disabled) {
  215. console.log("Make action disabled on each "+options.option_disabled)
  216. $.each(options.disabled, function(key, value) {
  217. $("#" + value).prop("disabled", true);
  218. });
  219. }
  220. if (options.show) {
  221. console.log("Make action show on each "+options.show)
  222. $.each(options.show, function(key, value) {
  223. $("#" + value).show().trigger("show");
  224. });
  225. }
  226. // Update an input
  227. if (ui.item.update) {
  228. console.log("Make action update on each ui.item.update (if there is)")
  229. // loop on each "update" fields
  230. $.each(ui.item.update, function(key, value) {
  231. console.log("Set value "+value+" into #"+key);
  232. $("#" + key).val(value).trigger("change");
  233. });
  234. }
  235. if (ui.item.textarea) {
  236. console.log("Make action textarea on each ui.item.textarea (if there is)")
  237. $.each(ui.item.textarea, function(key, value) {
  238. if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined" && CKEDITOR.instances[key] != "undefined") {
  239. CKEDITOR.instances[key].setData(value);
  240. CKEDITOR.instances[key].focus();
  241. } else {
  242. $("#" + key).html(value);
  243. $("#" + key).focus();
  244. }
  245. });
  246. }
  247. console.log("ajax_autocompleter new value selected, we trigger change also on original component so on field #search_'.$htmlname.'");
  248. $("#search_'.$htmlnamejquery.'").trigger("change"); // We have changed value of the combo select, we must be sure to trigger all js hook binded on this event. This is required to trigger other javascript change method binded on original field by other code.
  249. }
  250. ,delay: 500
  251. }).data("'.$dataforrenderITem.'")._renderItem = function( ul, item ) {
  252. return $("<li>")
  253. .data( "'.$dataforitem.'", item ) // jQuery UI > 1.10.0
  254. .append( \'<a><span class="tag">\' + item.label + "</span></a>" )
  255. .appendTo(ul);
  256. };
  257. });';
  258. $script .= '</script>';
  259. return $script;
  260. }
  261. /**
  262. * Generic function that return javascript to add to a page to transform a common input field into an autocomplete field by calling an Ajax page (ex: core/ajax/ziptown.php).
  263. * The Ajax page can also returns several values (json format) to fill several input fields.
  264. * The HTML field must be an input text with id=$htmlname.
  265. * This use the jQuery "autocomplete" function.
  266. *
  267. * @param string $htmlname HTML name of input field
  268. * @param array $fields Array of key of fields to autocomplete
  269. * @param string $url URL for ajax request : /chemin/fichier.php
  270. * @param string $option More parameters on URL request
  271. * @param int $minLength Minimum number of chars to trigger that Ajax search
  272. * @param int $autoselect Automatic selection if just one value
  273. * @return string Script
  274. */
  275. function ajax_multiautocompleter($htmlname, $fields, $url, $option = '', $minLength = 2, $autoselect = 0)
  276. {
  277. $script = '<!-- Autocomplete -->'."\n";
  278. $script .= '<script>';
  279. $script .= 'jQuery(document).ready(function() {
  280. var fields = '.json_encode($fields).';
  281. var nboffields = fields.length;
  282. var autoselect = '.$autoselect.';
  283. //alert(fields + " " + nboffields);
  284. jQuery("input#'.$htmlname.'").autocomplete({
  285. dataType: "json",
  286. minLength: '.$minLength.',
  287. source: function( request, response ) {
  288. jQuery.getJSON( "'.$url.($option ? '?'.$option : '').'", { '.$htmlname.': request.term }, function(data){
  289. response( jQuery.map( data, function( item ) {
  290. if (autoselect == 1 && data.length == 1) {
  291. jQuery("#'.$htmlname.'").val(item.value);
  292. // TODO move this to specific request
  293. if (item.states) {
  294. jQuery("#state_id").html(item.states);
  295. }
  296. for (i=0;i<nboffields;i++) {
  297. if (item[fields[i]]) { // If defined
  298. //alert(item[fields[i]]);
  299. jQuery("#" + fields[i]).val(item[fields[i]]);
  300. }
  301. }
  302. }
  303. return item
  304. }));
  305. });
  306. },
  307. select: function( event, ui ) {
  308. needtotrigger = "";
  309. for (i=0;i<nboffields;i++) {
  310. //alert(fields[i] + " = " + ui.item[fields[i]]);
  311. if (fields[i]=="selectcountry_id")
  312. {
  313. if (ui.item[fields[i]] > 0) // Do not erase country if unknown
  314. {
  315. oldvalue=jQuery("#" + fields[i]).val();
  316. newvalue=ui.item[fields[i]];
  317. //alert(oldvalue+" "+newvalue);
  318. jQuery("#" + fields[i]).val(ui.item[fields[i]]);
  319. if (oldvalue != newvalue) // To force select2 to refresh visible content
  320. {
  321. needtotrigger="#" + fields[i];
  322. }
  323. // If we set new country and new state, we need to set a new list of state to allow change
  324. if (ui.item.states && ui.item["state_id"] != jQuery("#state_id").value) {
  325. jQuery("#state_id").html(ui.item.states);
  326. }
  327. }
  328. }
  329. else if (fields[i]=="state_id" || fields[i]=="state_id")
  330. {
  331. if (ui.item[fields[i]] > 0) // Do not erase state if unknown
  332. {
  333. oldvalue=jQuery("#" + fields[i]).val();
  334. newvalue=ui.item[fields[i]];
  335. //alert(oldvalue+" "+newvalue);
  336. jQuery("#" + fields[i]).val(ui.item[fields[i]]); // This may fails if not correct country
  337. if (oldvalue != newvalue) // To force select2 to refresh visible content
  338. {
  339. needtotrigger="#" + fields[i];
  340. }
  341. }
  342. }
  343. else if (ui.item[fields[i]]) { // If defined
  344. oldvalue=jQuery("#" + fields[i]).val();
  345. newvalue=ui.item[fields[i]];
  346. //alert(oldvalue+" "+newvalue);
  347. jQuery("#" + fields[i]).val(ui.item[fields[i]]);
  348. if (oldvalue != newvalue) // To force select2 to refresh visible content
  349. {
  350. needtotrigger="#" + fields[i];
  351. }
  352. }
  353. if (needtotrigger != "") // To force select2 to refresh visible content
  354. {
  355. // We introduce a delay so hand is back to js and all other js change can be done before the trigger that may execute a submit is done
  356. // This is required for example when changing zip with autocomplete that change the country
  357. jQuery(needtotrigger).delay(500).queue(function() {
  358. jQuery(this).trigger("change");
  359. });
  360. }
  361. }
  362. }
  363. });
  364. });';
  365. $script .= '</script>';
  366. return $script;
  367. }
  368. /**
  369. * Show an ajax dialog
  370. *
  371. * @param string $title Title of dialog box
  372. * @param string $message Message of dialog box
  373. * @param int $w Width of dialog box
  374. * @param int $h height of dialog box
  375. * @return string
  376. */
  377. function ajax_dialog($title, $message, $w = 350, $h = 150)
  378. {
  379. global $langs;
  380. $newtitle = dol_textishtml($title) ?dol_string_nohtmltag($title, 1) : $title;
  381. $msg = '<div id="dialog-info" title="'.dol_escape_htmltag($newtitle).'">';
  382. $msg .= $message;
  383. $msg .= '</div>'."\n";
  384. $msg .= '<script>
  385. jQuery(function() {
  386. jQuery("#dialog-info").dialog({
  387. resizable: false,
  388. height:'.$h.',
  389. width:'.$w.',
  390. modal: true,
  391. buttons: {
  392. Ok: function() {
  393. jQuery(this).dialog(\'close\');
  394. }
  395. }
  396. });
  397. });
  398. </script>';
  399. $msg .= "\n";
  400. return $msg;
  401. }
  402. /**
  403. * Convert a html select field into an ajax combobox.
  404. * Use ajax_combobox() only for small combo list! If not, use instead ajax_autocompleter().
  405. * TODO: It is used when COMPANY_USE_SEARCH_TO_SELECT and CONTACT_USE_SEARCH_TO_SELECT are set by html.formcompany.class.php. Should use ajax_autocompleter instead like done by html.form.class.php for select_produits.
  406. *
  407. * @param string $htmlname Name of html select field ('myid' or '.myclass')
  408. * @param array $events More events option. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled')))
  409. * @param int $minLengthToAutocomplete Minimum length of input string to start autocomplete
  410. * @param int $forcefocus Force focus on field
  411. * @param string $widthTypeOfAutocomplete 'resolve' or 'off'
  412. * @param string $idforemptyvalue '-1'
  413. * @param string $morecss More css
  414. * @return string Return html string to convert a select field into a combo, or '' if feature has been disabled for some reason.
  415. * @see selectArrayAjax() of html.form.class
  416. */
  417. function ajax_combobox($htmlname, $events = array(), $minLengthToAutocomplete = 0, $forcefocus = 0, $widthTypeOfAutocomplete = 'resolve', $idforemptyvalue = '-1', $morecss = '')
  418. {
  419. global $conf;
  420. // select2 can be disabled for smartphones
  421. if (!empty($conf->browser->layout) && $conf->browser->layout == 'phone' && !empty($conf->global->MAIN_DISALLOW_SELECT2_WITH_SMARTPHONE)) {
  422. return '';
  423. }
  424. if (!empty($conf->global->MAIN_DISABLE_AJAX_COMBOX)) {
  425. return '';
  426. }
  427. if (empty($conf->use_javascript_ajax)) {
  428. return '';
  429. }
  430. if (empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) && !defined('REQUIRE_JQUERY_MULTISELECT')) {
  431. return '';
  432. }
  433. if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
  434. return '';
  435. }
  436. if (empty($minLengthToAutocomplete)) {
  437. $minLengthToAutocomplete = 0;
  438. }
  439. $moreselect2theme = ($morecss ? dol_escape_js(' '.$morecss) : '');
  440. $moreselect2theme = preg_replace('/widthcentpercentminus[^\s]*/', '', $moreselect2theme);
  441. $tmpplugin = 'select2';
  442. $msg = "\n".'<!-- JS CODE TO ENABLE '.$tmpplugin.' for id = '.$htmlname.' -->
  443. <script>
  444. $(document).ready(function () {
  445. $(\''.(preg_match('/^\./', $htmlname) ? $htmlname : '#'.$htmlname).'\').'.$tmpplugin.'({
  446. dir: \'ltr\',
  447. width: \''.dol_escape_js($widthTypeOfAutocomplete).'\', /* off or resolve */
  448. minimumInputLength: '.((int) $minLengthToAutocomplete).',
  449. language: select2arrayoflanguage,
  450. matcher: function (params, data) {
  451. if ($.trim(params.term) === "") {
  452. return data;
  453. }
  454. keywords = (params.term).split(" ");
  455. for (var i = 0; i < keywords.length; i++) {
  456. if (((data.text).toUpperCase()).indexOf((keywords[i]).toUpperCase()) == -1) {
  457. return null;
  458. }
  459. }
  460. return data;
  461. },
  462. theme: \'default'.$moreselect2theme.'\', /* to add css on generated html components */
  463. containerCssClass: \':all:\', /* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
  464. selectionCssClass: \':all:\', /* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
  465. templateResult: function (data, container) { /* Format visible output into combo list */
  466. /* Code to add class of origin OPTION propagated to the new select2 <li> tag */
  467. if (data.element) { $(container).addClass($(data.element).attr("class")); }
  468. //console.log($(data.element).attr("data-html"));
  469. if (data.id == '.((int) $idforemptyvalue).' && $(data.element).attr("data-html") == undefined) {
  470. return \'&nbsp;\';
  471. }
  472. if ($(data.element).attr("data-html") != undefined) return htmlEntityDecodeJs($(data.element).attr("data-html")); // If property html set, we decode html entities and use this
  473. return data.text;
  474. },
  475. templateSelection: function (selection) { /* Format visible output of selected value */
  476. if (selection.id == '.((int) $idforemptyvalue).') return \'<span class="placeholder">\'+selection.text+\'</span>\';
  477. return selection.text;
  478. },
  479. escapeMarkup: function(markup) {
  480. return markup;
  481. },
  482. dropdownCssClass: \'ui-dialog\'
  483. })';
  484. if ($forcefocus) {
  485. $msg .= '.select2(\'focus\')';
  486. }
  487. $msg .= ';'."\n";
  488. if (is_array($events) && count($events)) { // If an array of js events to do were provided.
  489. $msg .= '
  490. jQuery("#'.$htmlname.'").change(function () {
  491. var obj = '.json_encode($events).';
  492. $.each(obj, function(key,values) {
  493. if (values.method.length) {
  494. runJsCodeForEvent'.$htmlname.'(values);
  495. }
  496. });
  497. });
  498. function runJsCodeForEvent'.$htmlname.'(obj) {
  499. var id = $("#'.$htmlname.'").val();
  500. var method = obj.method;
  501. var url = obj.url;
  502. var htmlname = obj.htmlname;
  503. var showempty = obj.showempty;
  504. console.log("Run runJsCodeForEvent-'.$htmlname.' from ajax_combobox id="+id+" method="+method+" showempty="+showempty+" url="+url+" htmlname="+htmlname);
  505. $.getJSON(url,
  506. {
  507. action: method,
  508. id: id,
  509. htmlname: htmlname,
  510. showempty: showempty
  511. },
  512. function(response) {
  513. $.each(obj.params, function(key,action) {
  514. if (key.length) {
  515. var num = response.num;
  516. if (num > 0) {
  517. $("#" + key).removeAttr(action);
  518. } else {
  519. $("#" + key).attr(action, action);
  520. }
  521. }
  522. });
  523. $("select#" + htmlname).html(response.value);
  524. if (response.num) {
  525. var selecthtml_str = response.value;
  526. var selecthtml_dom=$.parseHTML(selecthtml_str);
  527. if (typeof(selecthtml_dom[0][0]) !== \'undefined\') {
  528. $("#inputautocomplete"+htmlname).val(selecthtml_dom[0][0].innerHTML);
  529. }
  530. } else {
  531. $("#inputautocomplete"+htmlname).val("");
  532. }
  533. $("select#" + htmlname).change(); /* Trigger event change */
  534. }
  535. );
  536. }';
  537. }
  538. $msg .= '});'."\n";
  539. $msg .= "</script>\n";
  540. return $msg;
  541. }
  542. /**
  543. * On/off button for constant
  544. *
  545. * @param string $code Name of constant
  546. * @param array $input Array of complementary actions to do if success ("disabled"|"enabled'|'set'|'del') => CSS element to switch, 'alert' => message to show, ... Example: array('disabled'=>array(0=>'cssid'))
  547. * @param int $entity Entity. Current entity is used if null.
  548. * @param int $revertonoff 1=Revert on/off
  549. * @param int $strict Use only "disabled" with delConstant and "enabled" with setConstant
  550. * @param int $forcereload Force to reload page if we click/change value (this is supported only when there is no 'alert' option in input)
  551. * @param string $marginleftonlyshort 1 = Add a short left margin on picto, 2 = Add a larger left margin on picto, 0 = No left margin.
  552. * @param int $forcenoajax 1 = Force to use a ahref link instead of ajax code.
  553. * @param int $setzeroinsteadofdel 1 = Set constantto '0' instead of deleting it
  554. * @param string $suffix Suffix to use on the name of the switch_on picto. Example: '', '_red'
  555. * @param string $mode Add parameter &mode= to the href link (Used for href link)
  556. * @return string
  557. */
  558. function ajax_constantonoff($code, $input = array(), $entity = null, $revertonoff = 0, $strict = 0, $forcereload = 0, $marginleftonlyshort = 2, $forcenoajax = 0, $setzeroinsteadofdel = 0, $suffix = '', $mode = '')
  559. {
  560. global $conf, $langs, $user;
  561. $entity = ((isset($entity) && is_numeric($entity) && $entity >= 0) ? $entity : $conf->entity);
  562. if (!isset($input)) {
  563. $input = array();
  564. }
  565. if (empty($conf->use_javascript_ajax) || $forcenoajax) {
  566. if (empty($conf->global->$code)) {
  567. print '<a href="'.$_SERVER['PHP_SELF'].'?action=set_'.$code.'&token='.newToken().'&entity='.$entity.($mode ? '&mode='.$mode : '').($forcereload ? '&dol_resetcache=1' : '').'">'.img_picto($langs->trans("Disabled"), 'off').'</a>';
  568. } else {
  569. print '<a href="'.$_SERVER['PHP_SELF'].'?action=del_'.$code.'&token='.newToken().'&entity='.$entity.($mode ? '&mode='.$mode : '').($forcereload ? '&dol_resetcache=1' : '').'">'.img_picto($langs->trans("Enabled"), 'on').'</a>';
  570. }
  571. } else {
  572. $out = "\n<!-- Ajax code to switch constant ".$code." -->".'
  573. <script>
  574. $(document).ready(function() {
  575. var input = '.json_encode($input).';
  576. var url = \''.DOL_URL_ROOT.'/core/ajax/constantonoff.php\';
  577. var code = \''.dol_escape_js($code).'\';
  578. var entity = \''.dol_escape_js($entity).'\';
  579. var strict = \''.dol_escape_js($strict).'\';
  580. var userid = \''.dol_escape_js($user->id).'\';
  581. var yesButton = \''.dol_escape_js($langs->transnoentities("Yes")).'\';
  582. var noButton = \''.dol_escape_js($langs->transnoentities("No")).'\';
  583. var token = \''.currentToken().'\';
  584. // Set constant
  585. $("#set_" + code).click(function() {
  586. if (input.alert && input.alert.set) {
  587. if (input.alert.set.yesButton) yesButton = input.alert.set.yesButton;
  588. if (input.alert.set.noButton) noButton = input.alert.set.noButton;
  589. confirmConstantAction("set", url, code, input, input.alert.set, entity, yesButton, noButton, strict, userid, token);
  590. } else {
  591. setConstant(url, code, input, entity, 0, '.((int) $forcereload).', userid, token);
  592. }
  593. });
  594. // Del constant
  595. $("#del_" + code).click(function() {
  596. if (input.alert && input.alert.del) {
  597. if (input.alert.del.yesButton) yesButton = input.alert.del.yesButton;
  598. if (input.alert.del.noButton) noButton = input.alert.del.noButton;
  599. confirmConstantAction("del", url, code, input, input.alert.del, entity, yesButton, noButton, strict, userid, token);
  600. } else {';
  601. if (empty($setzeroinsteadofdel)) {
  602. $out .=' delConstant(url, code, input, entity, 0, '.((int) $forcereload).', userid, token);';
  603. } else {
  604. $out .=' setConstant(url, code, input, entity, 0, '.((int) $forcereload).', userid, token, 0);';
  605. }
  606. $out .= ' }
  607. });
  608. });
  609. </script>'."\n";
  610. $out .= '<div id="confirm_'.$code.'" title="" style="display: none;"></div>';
  611. $out .= '<span id="set_'.$code.'" class="valignmiddle linkobject '.(!empty($conf->global->$code) ? 'hideobject' : '').'">'.($revertonoff ?img_picto($langs->trans("Enabled"), 'switch_on', '', false, 0, 0, '', '', $marginleftonlyshort) : img_picto($langs->trans("Disabled"), 'switch_off', '', false, 0, 0, '', '', $marginleftonlyshort)).'</span>';
  612. $out .= '<span id="del_'.$code.'" class="valignmiddle linkobject '.(!empty($conf->global->$code) ? '' : 'hideobject').'">'.($revertonoff ?img_picto($langs->trans("Disabled"), 'switch_off'.$suffix, '', false, 0, 0, '', '', $marginleftonlyshort) : img_picto($langs->trans("Enabled"), 'switch_on'.$suffix, '', false, 0, 0, '', '', $marginleftonlyshort)).'</span>';
  613. $out .= "\n";
  614. }
  615. return $out;
  616. }
  617. /**
  618. * On/off button to change status of an object
  619. * This is called when MAIN_DIRECT_STATUS_UPDATE is set and it use tha ajax service objectonoff.php
  620. *
  621. * @param Object $object Object to set
  622. * @param string $code Name of property in object : 'status' or 'status_buy' for product by example
  623. * @param string $field Name of database field : 'tosell' or 'tobuy' for product by example
  624. * @param string $text_on Text if on
  625. * @param string $text_off Text if off
  626. * @param array $input Array of type->list of CSS element to switch. Example: array('disabled'=>array(0=>'cssid'))
  627. * @param string $morecss More CSS
  628. * @param string $htmlname Name of HTML component. Keep '' or use a different value if you need to use this component several time on same page for same property.
  629. * @return string html for button on/off
  630. */
  631. function ajax_object_onoff($object, $code, $field, $text_on, $text_off, $input = array(), $morecss = '', $htmlname = '')
  632. {
  633. global $langs;
  634. if (empty($htmlname)) {
  635. $htmlname = $code;
  636. }
  637. $out = '<script>
  638. $(function() {
  639. var input = '.json_encode($input).';
  640. // Set constant
  641. $("#set_'.$htmlname.'_'.$object->id.'").click(function() {
  642. console.log("Click managed by ajax_object_onoff");
  643. $.get( "'.DOL_URL_ROOT.'/core/ajax/objectonoff.php", {
  644. action: \'set\',
  645. field: \''.dol_escape_js($field).'\',
  646. value: \'1\',
  647. element: \''.dol_escape_js($object->element).'\',
  648. id: \''.$object->id.'\',
  649. token: \''.currentToken().'\'
  650. },
  651. function() {
  652. $("#set_'.$htmlname.'_'.$object->id.'").hide();
  653. $("#del_'.$htmlname.'_'.$object->id.'").show();
  654. // Enable another element
  655. if (input.disabled && input.disabled.length > 0) {
  656. $.each(input.disabled, function(key,value) {
  657. $("#" + value).removeAttr("disabled");
  658. if ($("#" + value).hasClass("butActionRefused") == true) {
  659. $("#" + value).removeClass("butActionRefused");
  660. $("#" + value).addClass("butAction");
  661. }
  662. });
  663. // Show another element
  664. } else if (input.showhide && input.showhide.length > 0) {
  665. $.each(input.showhide, function(key,value) {
  666. $("#" + value).show();
  667. });
  668. }
  669. });
  670. });
  671. // Del constant
  672. $("#del_'.$htmlname.'_'.$object->id.'").click(function() {
  673. console.log("Click managed by ajax_object_onoff");
  674. $.get( "'.DOL_URL_ROOT.'/core/ajax/objectonoff.php", {
  675. action: \'set\',
  676. field: \''.dol_escape_js($field).'\',
  677. value: \'0\',
  678. element: \''.dol_escape_js($object->element).'\',
  679. id: \''.$object->id.'\',
  680. token: \''.currentToken().'\'
  681. },
  682. function() {
  683. $("#del_'.$htmlname.'_'.$object->id.'").hide();
  684. $("#set_'.$htmlname.'_'.$object->id.'").show();
  685. // Disable another element
  686. if (input.disabled && input.disabled.length > 0) {
  687. $.each(input.disabled, function(key,value) {
  688. $("#" + value).prop("disabled", true);
  689. if ($("#" + value).hasClass("butAction") == true) {
  690. $("#" + value).removeClass("butAction");
  691. $("#" + value).addClass("butActionRefused");
  692. }
  693. });
  694. // Hide another element
  695. } else if (input.showhide && input.showhide.length > 0) {
  696. $.each(input.showhide, function(key,value) {
  697. $("#" + value).hide();
  698. });
  699. }
  700. });
  701. });
  702. });
  703. </script>';
  704. $out .= '<span id="set_'.$htmlname.'_'.$object->id.'" class="linkobject '.($object->$code == 1 ? 'hideobject' : '').($morecss ? ' '.$morecss : '').'">'.img_picto($langs->trans($text_off), 'switch_off').'</span>';
  705. $out .= '<span id="del_'.$htmlname.'_'.$object->id.'" class="linkobject '.($object->$code == 1 ? '' : 'hideobject').($morecss ? ' '.$morecss : '').'">'.img_picto($langs->trans($text_on), 'switch_on').'</span>';
  706. return $out;
  707. }