dolibarr 25.0.0-alpha
ajax.lib.php
Go to the documentation of this file.
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 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
6 * Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
7 * Copyright (C) 2026 Open-Dsi <support@open-dsi.fr>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 * or see https://www.gnu.org/
22 */
23
50function ajax_autocompleter($selected, $htmlname, $url, $urloption = '', $minLength = 2, $autoselect = 0, $ajaxoptions = array(), $moreparams = '')
51{
52 if (empty($minLength)) {
53 $minLength = 1;
54 }
55
56 $dataforrenderITem = 'ui-autocomplete';
57 $dataforitem = 'ui-autocomplete-item';
58 // Allow two constant to use other values for backward compatibility
59 if (defined('JS_QUERY_AUTOCOMPLETE_RENDERITEM')) {
60 $dataforrenderITem = constant('JS_QUERY_AUTOCOMPLETE_RENDERITEM');
61 }
62 if (defined('JS_QUERY_AUTOCOMPLETE_ITEM')) {
63 $dataforitem = constant('JS_QUERY_AUTOCOMPLETE_ITEM');
64 }
65
66 $htmlnamejquery = str_replace('.', '\\\\.', $htmlname);
67
68 // Input search_htmlname is original field
69 // Input htmlname is a second input field used when using ajax autocomplete.
70 $script = '<input type="hidden" name="'.$htmlname.'" id="'.$htmlname.'" value="'.$selected.'" '.($moreparams ? $moreparams : '').' />';
71
72 $script .= '<!-- Javascript code for autocomplete of field '.$htmlname.' -->'."\n";
73 $script .= '<script>'."\n";
74 $script .= '$(document).ready(function() {
75 var autoselect = '.((int) $autoselect).';
76 var options = '.json_encode($ajaxoptions).'; /* Option of actions to do after keyup, or after select */
77
78 /* 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 losing the product id. This is needed only for select of predefined product */
79 $("input#search_'.$htmlnamejquery.'").keydown(function(e) {
80 if (e.keyCode != 9) /* If not "Tab" key */
81 {
82 if (e.keyCode == 13) { return false; } /* disable "ENTER" key useful for barcode readers */
83 console.log("Clear id previously selected for field '.$htmlname.'");
84 $("#'.$htmlnamejquery.'").val("");
85 }
86 });
87
88 // Check options for secondary actions when keyup
89 $("input#search_'.$htmlnamejquery.'").keyup(function() {
90 if ($(this).val().length == 0)
91 {
92 $("#search_'.$htmlnamejquery.'").val("");
93 $("#'.$htmlnamejquery.'").val("").trigger("change");
94 if (options.option_disabled) {
95 $("#" + options.option_disabled).removeAttr("disabled");
96 }
97 if (options.disabled) {
98 $.each(options.disabled, function(key, value) {
99 $("#" + value).removeAttr("disabled");
100 });
101 }
102 if (options.update) {
103 $.each(options.update, function(key, value) {
104 $("#" + key).val("").trigger("change");
105 });
106 }
107 if (options.show) {
108 $.each(options.show, function(key, value) {
109 $("#" + value).hide().trigger("hide");
110 });
111 }
112 if (options.update_textarea) {
113 $.each(options.update_textarea, function(key, value) {
114 if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined" && CKEDITOR.instances[key] != "undefined") {
115 CKEDITOR.instances[key].setData("");
116 } else {
117 $("#" + key).html("");
118 }
119 });
120 }
121 }
122 });
123
124 // Activate the autocomplete to execute the GET
125 $("input#search_'.$htmlnamejquery.'").autocomplete({
126 source: function( request, response ) {
127 $.get("'.$url.($urloption ? '?'.$urloption : '').'", { "'.str_replace('.', '_', $htmlname).'": request.term }, function(data){
128 if (data != null)
129 {
130 response($.map( data, function(item) {
131 console.log("Received answer from ajax GET, we populate array to return to the jquery autocomplete");
132 if (autoselect == 1 && data.length == 1) {
133 $("#search_'.$htmlnamejquery.'").val(item.value);
134 ';
135 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
136 $script .= '
137 // When product has only one price by qty and 1 result, must set data attributes before triggering change
138 $("#'.$htmlname.'").attr("data-pbq", item.pbq);
139 $("#'.$htmlname.'").attr("data-pbqup", item.price_ht);
140 $("#'.$htmlname.'").attr("data-pbqbase", item.pricebasetype);
141 $("#'.$htmlname.'").attr("data-pbqqty", item.qty);
142 $("#'.$htmlname.'").attr("data-pbqpercent", item.discount);
143 ';
144 }
145 $script .= '
146 $("#'.$htmlnamejquery.'").val(item.key).trigger("change");
147 }
148 var label = "";
149 if (item.labelhtml != null) {
150 label = item.labelhtml.toString();
151 } else if (item.label != null) {
152 label = item.label.toString();
153 }
154 var update = {};
155 if (options.update) {
156 $.each(options.update, function(key, value) {
157 update[key] = item[value];
158 });
159 }
160 var textarea = {};
161 if (options.update_textarea) {
162 $.each(options.update_textarea, function(key, value) {
163 textarea[key] = item[value];
164 });
165 }
166
167 return { label: label,
168 value: item.value,
169 id: item.key,
170 disabled: item.disabled,
171 update: update,
172 textarea: textarea,
173 pbq: item.pbq,
174 type: item.type,
175 qty: item.qty,
176 discount: item.discount,
177 pricebasetype: item.pricebasetype,
178 price_ht: item.price_ht,
179 price_ttc: item.price_ttc,
180 price_unit_ht: item.price_unit_ht,
181 price_unit_ht_locale: item.price_unit_ht_locale,
182 multicurrency_code: item.multicurrency_code,
183 multicurrency_unitprice: item.multicurrency_unitprice,
184 description : item.description,
185 ref_customer: item.ref_customer,
186 tva_tx: item.tva_tx,
187 default_vat_code: item.default_vat_code,
188 supplier_ref: item.supplier_ref
189 }
190 }));
191 } else {
192 console.error("Error: Ajax url '.$url.($urloption ? '?'.$urloption : '').' has returned an empty page. Should be an empty json array.");
193 }
194 }, "json");
195 },
196 dataType: "json",
197 minLength: '.((int) $minLength).',
198 select: function( event, ui ) { // Function ran once a new value has been selected into the javascript combo
199 console.log("We will trigger change on input '.$htmlname.' because of the select definition of autocomplete code for input#search_'.$htmlname.'");
200 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");
201
202 console.log("Before, we propagate some properties, retrieved by the ajax of the get, into the data-xxx properties of the component #'.$htmlnamejquery.'");
203 //console.log(ui.item);
204
205 // For supplier price and customer when price by quantity is off
206 $("#'.$htmlnamejquery.'").attr("data-up", ui.item.price_ht);
207 $("#'.$htmlnamejquery.'").attr("data-up-locale", ui.item.price_unit_ht_locale);
208 $("#'.$htmlnamejquery.'").attr("data-base", ui.item.pricebasetype);
209 $("#'.$htmlnamejquery.'").attr("data-qty", ui.item.qty);
210 $("#'.$htmlnamejquery.'").attr("data-discount", ui.item.discount);
211 $("#'.$htmlnamejquery.'").attr("data-description", ui.item.description);
212 $("#'.$htmlnamejquery.'").attr("data-ref-customer", ui.item.ref_customer);
213 $("#'.$htmlnamejquery.'").attr("data-tvatx", ui.item.tva_tx);
214 $("#'.$htmlnamejquery.'").attr("data-default-vat-code", ui.item.default_vat_code);
215 $("#'.$htmlnamejquery.'").attr("data-supplier-ref", ui.item.supplier_ref); // supplier_ref of price
216
217 // For multi-currency values
218 $("#'.$htmlnamejquery.'").attr("data-multicurrency-code", ui.item.multicurrency_code);
219 $("#'.$htmlnamejquery.'").attr("data-multicurrency-unitprice", ui.item.multicurrency_unitprice);
220 ';
221 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
222 $script .= '
223 // For customer price when PRODUIT_CUSTOMER_PRICES_BY_QTY is on
224 console.log("PRODUIT_CUSTOMER_PRICES_BY_QTY is on, so we propagate also prices by quantity into data-pbqxxx properties");
225 $("#'.$htmlnamejquery.'").attr("data-pbq", ui.item.pbq);
226 $("#'.$htmlnamejquery.'").attr("data-pbqup", ui.item.price_ht);
227 $("#'.$htmlnamejquery.'").attr("data-pbqbase", ui.item.pricebasetype);
228 $("#'.$htmlnamejquery.'").attr("data-pbqqty", ui.item.qty);
229 $("#'.$htmlnamejquery.'").attr("data-pbqpercent", ui.item.discount);
230 ';
231 }
232 $script .= '
233 // A new value has been selected, we trigger the handlers on #htmlnamejquery
234 console.log("Now, we trigger changes on #'.$htmlnamejquery.'");
235 $("#'.$htmlnamejquery.'").val(ui.item.id).trigger("change"); // Select new value
236
237 // Complementary actions
238
239 // Disable an element
240 if (options.option_disabled) {
241 console.log("Make action option_disabled on #"+options.option_disabled+" with disabled="+ui.item.disabled)
242 if (ui.item.disabled) {
243 $("#" + options.option_disabled).prop("disabled", true);
244 if (options.error) {
245 $.jnotify(options.error, "error", true); // Output with jnotify the error message
246 }
247 if (options.warning) {
248 $.jnotify(options.warning, "warning", false); // Output with jnotify the warning message
249 }
250 } else {
251 $("#" + options.option_disabled).removeAttr("disabled");
252 }
253 }
254
255 if (options.disabled) {
256 console.log("Make action \'disabled\' on each "+options.option_disabled)
257 $.each(options.disabled, function(key, value) {
258 $("#" + value).prop("disabled", true);
259 });
260 }
261 if (options.show) {
262 console.log("Make action \'show\' on each "+options.show)
263 $.each(options.show, function(key, value) {
264 $("#" + value).show().trigger("show");
265 });
266 }
267
268 // Update an input
269 if (ui.item.update) {
270 console.log("Make action \'update\' on each ui.item.update (if there is)")
271 // loop on each "update" fields
272 $.each(ui.item.update, function(key, value) {
273 console.log("Set value "+value+" into #"+key);
274 $("#" + key).val(value).trigger("change");
275 });
276 }
277 if (ui.item.textarea) {
278 console.log("Make action \'textarea\' on each ui.item.textarea (if there is)")
279 $.each(ui.item.textarea, function(key, value) {
280 if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined" && CKEDITOR.instances[key] != "undefined") {
281 CKEDITOR.instances[key].setData(value);
282 CKEDITOR.instances[key].focus();
283 } else {
284 $("#" + key).html(value);
285 $("#" + key).focus();
286 }
287 });
288 }
289 console.log("ajax_autocompleter new value selected, we trigger change also on original component so on field #search_'.$htmlname.'");
290
291 $("#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.
292 }
293 ,delay: 500
294 });
295 const widgetData = $("input#search_'.$htmlnamejquery.'").data("'.$dataforrenderITem.'");
296 if (widgetData) {
297 widgetData._renderItem = function( ul, item ) {
298 return $("<li>")
299 .data( "'.$dataforitem.'", item ) // jQuery UI > 1.10.0
300 .append( \'<a><span class="tag">\' + item.label + "</span></a>" )
301 .appendTo(ul);
302 };
303 }
304
305 });';
306 $script .= '</script>';
307
308 return $script;
309}
310
325function ajax_multiautocompleter($htmlname, $fields, $url, $option = '', $minLength = 2, $autoselect = 0)
326{
327 $script = '<!-- Autocomplete -->'."\n";
328 $script .= '<script>';
329 $script .= 'jQuery(document).ready(function() {
330 var fields = '.json_encode($fields).';
331 var nboffields = fields.length;
332 var autoselect = '.$autoselect.';
333 //alert(fields + " " + nboffields);
334
335 // Activate the autocomplete to execute the GET
336 jQuery("input#'.$htmlname.'").autocomplete({
337 dataType: "json",
338 minLength: '.$minLength.',
339 source: function( request, response ) {
340 jQuery.getJSON( "'.$url.($option ? '?'.$option : '').'", { '.$htmlname.': request.term }, function(data){
341 response( jQuery.map( data, function( item ) {
342 if (autoselect == 1 && data.length == 1) {
343 jQuery("#'.$htmlname.'").val(item.value);
344 // TODO move this to specific request
345 if (item.states) {
346 jQuery("#state_id").html(item.states);
347 }
348 for (i=0;i<nboffields;i++) {
349 if (item[fields[i]]) { // If defined
350 //alert(item[fields[i]]);
351 jQuery("#" + fields[i]).val(item[fields[i]]);
352 }
353 }
354 }
355 return item
356 }));
357 });
358 },
359 select: function( event, ui ) {
360 needtotrigger = "";
361 for (i=0;i<nboffields;i++) {
362 //alert(fields[i] + " = " + ui.item[fields[i]]);
363 if (fields[i]=="selectcountry_id")
364 {
365 if (ui.item[fields[i]] > 0) // Do not erase country if unknown
366 {
367 oldvalue=jQuery("#" + fields[i]).val();
368 newvalue=ui.item[fields[i]];
369 //alert(oldvalue+" "+newvalue);
370 jQuery("#" + fields[i]).val(ui.item[fields[i]]);
371 if (oldvalue != newvalue) // To force select2 to refresh visible content
372 {
373 needtotrigger="#" + fields[i];
374 }
375
376 // If we set new country and new state, we need to set a new list of state to allow change
377 if (ui.item.states && ui.item["state_id"] != jQuery("#state_id").value) {
378 jQuery("#state_id").html(ui.item.states);
379 }
380 }
381 }
382 else if (fields[i]=="state_id" || fields[i]=="state_id")
383 {
384 if (ui.item[fields[i]] > 0) // Do not erase state if unknown
385 {
386 oldvalue=jQuery("#" + fields[i]).val();
387 newvalue=ui.item[fields[i]];
388 //alert(oldvalue+" "+newvalue);
389 jQuery("#" + fields[i]).val(ui.item[fields[i]]); // This may fails if not correct country
390 if (oldvalue != newvalue) // To force select2 to refresh visible content
391 {
392 needtotrigger="#" + fields[i];
393 }
394 }
395 }
396 else if (ui.item[fields[i]]) { // If defined
397 oldvalue=jQuery("#" + fields[i]).val();
398 newvalue=ui.item[fields[i]];
399 //alert(oldvalue+" "+newvalue);
400 jQuery("#" + fields[i]).val(ui.item[fields[i]]);
401 if (oldvalue != newvalue) // To force select2 to refresh visible content
402 {
403 needtotrigger="#" + fields[i];
404 }
405 }
406
407 if (needtotrigger != "") // To force select2 to refresh visible content
408 {
409 // 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
410 // This is required for example when changing zip with autocomplete that change the country
411 jQuery(needtotrigger).delay(500).queue(function() {
412 jQuery(this).trigger("change");
413 });
414 }
415 }
416 }
417 });
418 });';
419 $script .= '</script>';
420
421 return $script;
422}
423
433function ajax_dialog($title, $message, $w = 350, $h = 150)
434{
435 $newtitle = dol_textishtml($title) ? dol_string_nohtmltag($title, 1) : $title;
436 $msg = '<div id="dialog-info" title="'.dol_escape_htmltag($newtitle).'">';
437 $msg .= $message;
438 $msg .= '</div>'."\n";
439 $msg .= '<script>
440 jQuery(function() {
441 jQuery("#dialog-info").dialog({
442 resizable: false,
443 height:'.$h.',
444 width:'.$w.',
445 modal: true,
446 buttons: {
447 Ok: function() {
448 jQuery(this).dialog(\'close\');
449 }
450 }
451 });
452 });
453 </script>';
454
455 $msg .= "\n";
456
457 return $msg;
458}
459
460
476function ajax_combobox($htmlname, $events = array(), $minLengthToAutocomplete = 0, $forcefocus = 0, $widthTypeOfAutocomplete = 'resolve', $idforemptyvalue = '-1', $morecss = '')
477{
478 global $conf;
479
480 // select2 can be disabled for smartphones
481 if (!empty($conf->browser->layout) && $conf->browser->layout == 'phone' && getDolGlobalString('MAIN_DISALLOW_SELECT2_WITH_SMARTPHONE')) {
482 return '';
483 }
484
485 if (getDolGlobalString('MAIN_DISABLE_AJAX_COMBOX')) {
486 return '';
487 }
488 if (empty($conf->use_javascript_ajax)) {
489 return '';
490 }
491 if (!getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') && !defined('REQUIRE_JQUERY_MULTISELECT')) {
492 return '';
493 }
494 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
495 return '';
496 }
497
498 if (empty($minLengthToAutocomplete)) {
499 $minLengthToAutocomplete = 0;
500 }
501
502 $moreselect2theme = ($morecss ? dol_escape_js(' '.$morecss) : '');
503 $moreselect2theme = preg_replace('/widthcentpercentminus[^\s]*/', '', $moreselect2theme);
504
505 $tmpplugin = 'select2';
506 $componentname = (preg_match('/^\./', $htmlname) ? $htmlname : '#'.$htmlname); // To support when htmlname is not an ID but a class
507 $msg = "\n";
508 $msg .= '<!-- JS CODE TO ENABLE '.$tmpplugin.' for id = '.$htmlname.' -->'."\n";
509 $msg .= "<script>\n";
510 $msg .= '$(document).ready(function () {
511 $(\''.dol_escape_js($componentname).'\').'.$tmpplugin.'({';
512 // when $morecss contains 'onrightofpage', the select2 component must also be inside a parent with class="parentonrightofpage"
513 if (preg_match('/onrightofpage/', $morecss)) { // In this cas, htmlname must be an ID not a class.
514 $msg .= ' dropdownAutoWidth: true, ';
515 $msg .= ' dropdownParent: $(\'#'.$htmlname.'\').parent(), ';
516 }
517 $msg .= '
518 dir: \'ltr\',
519 width: \''.dol_escape_js($widthTypeOfAutocomplete).'\', /* off or resolve */
520 minimumInputLength: '.((int) $minLengthToAutocomplete).',
521 language: (typeof select2arrayoflanguage === \'undefined\') ? \'en\' : select2arrayoflanguage,
522 matcher: function (params, data) {
523 if ($.trim(params.term) === "") { return data; }';
524 if (getDolGlobalString('MAIN_ENABLE_ACCENT_INSENSITIVE_SEARCH')) { // lowercase + remove accents
525 $msg .= '
526 var term = params.term.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase()
527 var searchText = data.element ? $(data.element).attr("data-search") : undefined;
528 var text = (searchText !== undefined ? searchText : (data.text || "")).normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase() || "";';
529 } else { // lowercase only (accent kept)
530 $msg .= '
531 var term = params.term.toLowerCase();
532 var searchText = data.element ? $(data.element).attr("data-search") : undefined;
533 var text = (searchText !== undefined ? searchText : (data.text || "")).toLowerCase();';
534 }
535 $msg .= '
536 var keywords = term.split(" ");
537 for (var i = 0; i < keywords.length; i++) {
538 if (text.indexOf(keywords[i]) === -1) {
539 return null;
540 }
541 }
542 return data;
543 },
544 theme: \'default'.dol_escape_js($moreselect2theme).'\', /* to add css on generated html components */
545 containerCssClass: \':all:\', /* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
546 selectionCssClass: \':all:\', /* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
547 dropdownCssClass: \'ui-dialog\',
548 templateResult: function (data, container) { /* Format visible output into combo list */
549 /* Code to add class of origin OPTION propagated to the new select2 <li> tag */
550 if (data.element) { $(container).addClass($(data.element).attr("class")); }
551 /* console.log("data html is "+$(data.element).attr("data-html")); */
552 if (data.id == \''.(dol_escape_js($idforemptyvalue)).'\' && $(data.element).attr("data-html") == undefined) {
553 return \'&nbsp;\';
554 }
555 if ($(data.element).attr("data-html") != undefined) {
556 /* If property html set, we decode html entities and use this. */
557 /* Note that HTML content must have been sanitized against js injection with dol_escape_htmltag(xxx, 0, 0, \'\', 0, 1) when building the select option. */
558 if (typeof htmlEntityDecodeJs === "function") {
559 return htmlEntityDecodeJs($(data.element).attr("data-html"));
560 }
561 }
562 return data.text;
563 },
564 templateSelection: function (selection) { /* Format visible output of selected value */
565 if (selection.id == \''.(dol_escape_js($idforemptyvalue)).'\') return \'<span class="placeholder">\'+selection.text+\'</span>\';
566 if (selection.element && $(selection.element).attr("data-select-html") != undefined) {
567 if (typeof htmlEntityDecodeJs === "function") {
568 return htmlEntityDecodeJs($(selection.element).attr("data-select-html"));
569 }
570 }
571 return selection.text;
572 },
573 escapeMarkup: function(markup) {
574 return markup;
575 }
576 })';
577 if ($forcefocus) {
578 $msg .= '.select2(\'focus\')';
579 }
580 $msg .= ';'."\n";
581
582 $msg .= '});'."\n";
583 $msg .= "</script>\n";
584
585 $msg .= ajax_event($htmlname, $events);
586
587 return $msg;
588}
589
590
599function ajax_event($htmlname, $events)
600{
601 $out = '';
602
603 if (is_array($events) && count($events)) { // If an array of js events to do were provided.
604 $out = '<!-- JS code to manage event for id = ' . $htmlname . ' -->
605 <script>
606 $(document).ready(function () {
607 jQuery("#'.$htmlname.'").change(function () {
608 var obj = '.json_encode($events) . ';
609 $.each(obj, function(key,values) {
610 if (values.method.length) {
611 runJsCodeForEvent'.$htmlname.'(values);
612 }
613 });
614 });
615 function runJsCodeForEvent'.$htmlname.'(obj) {
616 var id = $("#'.$htmlname.'").val();
617 var method = obj.method;
618 var url = obj.url;
619 var htmlname = obj.htmlname;
620 var showempty = obj.showempty;
621 console.log("Run runJsCodeForEvent-'.$htmlname.' from ajax_combobox id="+id+" method="+method+" showempty="+showempty+" url="+url+" htmlname="+htmlname);
622 $.getJSON(url,
623 {
624 action: method,
625 id: id,
626 htmlname: htmlname,
627 showempty: showempty
628 },
629 function(response) {
630 $.each(obj.params, function(key,action) {
631 if (key.length) {
632 var num = response.num;
633 if (num > 0) {
634 $("#" + key).removeAttr(action);
635 } else {
636 $("#" + key).attr(action, action);
637 }
638 }
639 });
640
641 console.log("Replace HTML content of select#"+htmlname);
642 $("select#" + htmlname).html(response.value);
643 if (response.num) {
644 var selecthtml_str = response.value; /* response.value is the HTML string with list of options */
645 var selecthtml_dom=$.parseHTML(selecthtml_str);
646 if (typeof(selecthtml_dom[0][0]) !== \'undefined\') {
647 $("#inputautocomplete"+htmlname).val(selecthtml_dom[0][0].innerHTML);
648 }
649 } else {
650 $("#inputautocomplete"+htmlname).val("");
651 }
652 $("select#" + htmlname).change(); /* Trigger event change */
653 }
654 );
655 }
656 });
657 </script>';
658 }
659
660 return $out;
661}
662
663
685function ajax_constantonoff($code, $input = array(), $entity = null, $revertonoff = 0, $strict = 0, $forcereload = 0, $marginleftonlyshort = 2, $forcenoajax = 0, $setzeroinsteadofdel = 0, $suffix = '', $mode = '', $morecss = 'inline-block', $userconst = 0, $showwarning = '', $disabled = 0)
686{
687 global $conf, $langs, $user, $db;
688
689 $entity = ((isset($entity) && is_numeric($entity) && $entity >= 0) ? $entity : $conf->entity);
690 if (!isset($input)) {
691 $input = array();
692 }
693
694 $out = '';
695
696 if (empty($conf->use_javascript_ajax) || $forcenoajax) {
697 if (!getDolGlobalString($code)) {
698 $out = '<a '.($morecss ? 'class="'.$morecss.'" ' : '').'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>';
699 } else {
700 $out = '<a '.($morecss ? 'class="'.$morecss.'" ' : '').' 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>';
701 }
702 } else {
703 $userconstid = 0;
704 if (is_object($userconst)) {
705 $userconstid = $userconst->id;
706 } elseif (is_numeric($userconst) && $userconst > 0) {
707 $userconstid = $userconst;
708 $userconst = new User($db);
709 $userconst->fetch($userconstid);
710 }
711
712 if ($disabled) {
713 $morecss .= ' disabled opacitymedium';
714 } else {
715 $out = "\n<!-- Ajax code to switch constant ".$code." -->".'
716 <script>
717 $(document).ready(function() {
718 var input = '.json_encode($input).';
719 var url = \''.DOL_URL_ROOT.'/core/ajax/constantonoff.php\';
720 var code = \''.dol_escape_js($code).'\';
721 var entity = \''.dol_escape_js((string) $entity).'\';
722 var strict = \''.dol_escape_js((string) $strict).'\';
723 var userid = \''.dol_escape_js((string) $user->id).'\';
724 var userconst = '.((int) $userconstid).';
725 var yesButton = \''.dol_escape_js($langs->transnoentities("Yes")).'\';
726 var noButton = \''.dol_escape_js($langs->transnoentities("No")).'\';
727 var token = \''.currentToken().'\';
728 var warning = \''.dol_escape_js($showwarning).'\';
729
730 // Set constant
731 $("#set_" + code).click(function() {
732 if (warning) {
733 alert(warning);
734 }
735
736 if (input.alert && input.alert.set) {
737 if (input.alert.set.yesButton) yesButton = input.alert.set.yesButton;
738 if (input.alert.set.noButton) noButton = input.alert.set.noButton;
739 confirmConstantAction("set", url, code, input, input.alert.set, entity, yesButton, noButton, strict, userid, token);
740 } else {
741 setConstant(url, code, input, entity, 0, '.((int) $forcereload).', userid, token, 1, userconst);
742 }
743 });
744
745 // Del constant
746 $("#del_" + code).click(function() {
747 if (input.alert && input.alert.del) {
748 if (input.alert.del.yesButton) yesButton = input.alert.del.yesButton;
749 if (input.alert.del.noButton) noButton = input.alert.del.noButton;
750 confirmConstantAction("del", url, code, input, input.alert.del, entity, yesButton, noButton, strict, userid, token);
751 } else {';
752 if (empty($setzeroinsteadofdel)) {
753 $out .= ' delConstant(url, code, input, entity, 0, '.((int) $forcereload).', userid, token, userconst);';
754 } else {
755 $out .= ' setConstant(url, code, input, entity, 0, '.((int) $forcereload).', userid, token, 0, userconst);';
756 }
757 $out .= ' }
758 });
759 });
760 </script>'."\n";
761 }
762
763 if (!empty($userconst) && $userconst instanceof User) {
764 $value = getDolUserString($code, '', $userconst);
765 } else {
766 $value = getDolGlobalString($code);
767 }
768
769 if (is_array($suffix)) {
770 $suffixon = $suffix['ifon'];
771 $suffixoff = $suffix['ifoff'];
772 } else { // old mode deprecated
773 $suffixon = (string) $suffix;
774 $suffixoff = '';
775 }
776
777 $out .= '<div id="confirm_'.$code.'" title="" style="display: none;"></div>';
778 $out .= '<span id="set_'.$code.'" class="valignmiddle inline-block linkobject '.($value ? 'hideobject' : '').($morecss ? ' '.$morecss : '').'">'.($revertonoff ? img_picto($langs->trans("Enabled"), 'switch_on'.$suffixoff, '', 0, 0, 0, '', '', $marginleftonlyshort) : img_picto($langs->trans("Disabled"), 'switch_off'.$suffixoff, '', 0, 0, 0, '', '', $marginleftonlyshort)).'</span>';
779 $out .= '<span id="del_'.$code.'" class="valignmiddle inline-block linkobject '.($value ? '' : 'hideobject').($morecss ? ' '.$morecss : '').'">'.($revertonoff ? img_picto($langs->trans("Disabled"), 'switch_off'.$suffixon, '', 0, 0, 0, '', '', $marginleftonlyshort) : img_picto($langs->trans("Enabled"), 'switch_on'.$suffixon, '', 0, 0, 0, '', '', $marginleftonlyshort)).'</span>';
780 $out .= "\n";
781 }
782
783 return $out;
784}
785
804function ajax_object_onoff($object, $code, $field, $text_on, $text_off, $input = array(), $morecss = '', $htmlname = '', $forcenojs = 0, $moreparam = '', $readonly = 0)
805{
806 global $conf, $langs;
807
808 if (empty($htmlname)) {
809 $htmlname = $code;
810 }
811
812 $out = '';
813
814 if (!empty($conf->use_javascript_ajax) && empty($forcenojs) && empty($readonly)) {
815 $out .= '<script>
816 $(function() {
817 var input = '.json_encode($input).';
818
819 // Set constant
820 $("#set_'.$htmlname.'_'.$object->id.'").click(function() {
821 console.log("Click managed by ajax_object_onoff");
822 $.get( "'.DOL_URL_ROOT.'/core/ajax/objectonoff.php", {
823 action: \'set\',
824 field: \''.dol_escape_js($field).'\',
825 value: \'1\',
826 element: \''.dol_escape_js((empty($object->module) || $object->module == $object->element) ? $object->element : $object->element.'@'.$object->module).'\',
827 id: \''.((int) $object->id).'\',
828 token: \''.currentToken().'\'
829 }).done(
830 function(response) {
831 try {
832 var data = JSON.parse(response);
833 console.log(data);
834 } catch (e) {
835 console.log(response);
836 }
837
838 $("#set_'.$htmlname.'_'.$object->id.'").hide();
839 $("#del_'.$htmlname.'_'.$object->id.'").show();
840 // Enable another element
841 if (input.disabled && input.disabled.length > 0) {
842 $.each(input.disabled, function(key,value) {
843 $("#" + value).removeAttr("disabled");
844 if ($("#" + value).hasClass("butActionRefused") == true) {
845 $("#" + value).removeClass("butActionRefused");
846 $("#" + value).addClass("butAction");
847 }
848 });
849 // Show another element
850 } else if (input.showhide && input.showhide.length > 0) {
851 $.each(input.showhide, function(key,value) {
852 $("#" + value).show();
853 });
854 }
855 }).fail(
856 function(response) {
857 //alert(response.responseText);
858 console.warn(response.responseText);
859 Dolibarr.tools.setEventMessage(response.responseText, "errors");
860 }
861 );
862 });
863
864 // Del constant
865 $("#del_'.$htmlname.'_'.$object->id.'").click(function() {
866 console.log("Click managed by ajax_object_onoff");
867 $.get( "'.DOL_URL_ROOT.'/core/ajax/objectonoff.php", {
868 action: \'set\',
869 field: \''.dol_escape_js($field).'\',
870 value: \'0\',
871 element: \''.dol_escape_js((empty($object->module) || $object->module == $object->element) ? $object->element : $object->element.'@'.$object->module).'\',
872 id: \''.((int) $object->id).'\',
873 token: \''.currentToken().'\'
874 }).done(
875 function(response) {
876 try {
877 var data = JSON.parse(response);
878 console.log(data);
879 } catch (e) {
880 console.log(response);
881 }
882
883 $("#del_'.$htmlname.'_'.$object->id.'").hide();
884 $("#set_'.$htmlname.'_'.$object->id.'").show();
885 // Disable another element
886 if (input.disabled && input.disabled.length > 0) {
887 $.each(input.disabled, function(key,value) {
888 $("#" + value).prop("disabled", true);
889 if ($("#" + value).hasClass("butAction") == true) {
890 $("#" + value).removeClass("butAction");
891 $("#" + value).addClass("butActionRefused");
892 }
893 });
894 // Hide another element
895 } else if (input.showhide && input.showhide.length > 0) {
896 $.each(input.showhide, function(key,value) {
897 $("#" + value).hide();
898 });
899 }
900 }).fail(
901 function(response) {
902 //alert(response.responseText);
903 console.warn(response.responseText);
904 Dolibarr.tools.setEventMessage(response.responseText, "errors");
905 }
906 );
907 });
908 });
909 </script>';
910 }
911
912 $switchon = 'switch_on';
913 $switchoff = 'switch_off';
914 $cssswitchon = '';
915 $cssswitchoff = '';
916 $tmparray = explode(':', $text_on);
917 if (!empty($tmparray[1])) {
918 $text_on = $tmparray[0];
919 $switchon = $tmparray[1];
920 if (!empty($tmparray[2])) {
921 $cssswitchon = $tmparray[2];
922 }
923 }
924 $tmparray = explode(':', $text_off);
925 if (!empty($tmparray[1])) {
926 $text_off = $tmparray[0];
927 $switchoff = $tmparray[1];
928 if (!empty($tmparray[2])) {
929 $cssswitchoff = $tmparray[2];
930 }
931 }
932
933 if (empty($conf->use_javascript_ajax) || $forcenojs) {
934 $url = DOL_URL_ROOT.'/core/ajax/objectonoff.php?action=set&token='.newToken().'&id='.((int) $object->id).'&element='.urlencode($object->element).'&field='.urlencode($field).'&value=1&backtopage='.urlencode($_SERVER["PHP_SELF"].'?id='.$object->id.($moreparam ? '&'.$moreparam : ''));
935 if ($readonly) {
936 $url = '#';
937 }
938 $out .= '<a id="set_'.$htmlname.'_'.$object->id.'" class="linkobject '.($object->$code == 1 ? 'hideobject' : '').($morecss ? ' '.$morecss : '').'" href="'.$url.'">'.img_picto($langs->trans($text_off), $switchoff, '', 0, 0, 0, '', $cssswitchoff).'</a>';
939 $url = DOL_URL_ROOT.'/core/ajax/objectonoff.php?action=set&token='.newToken().'&id='.((int) $object->id).'&element='.urlencode($object->element).'&field='.urlencode($field).'&value=0&backtopage='.urlencode($_SERVER["PHP_SELF"].'?id='.$object->id.($moreparam ? '&'.$moreparam : ''));
940 if ($readonly) {
941 $url = '#';
942 }
943 $out .= '<a id="del_'.$htmlname.'_'.$object->id.'" class="linkobject '.($object->$code == 1 ? '' : 'hideobject').($morecss ? ' '.$morecss : '').'" href="'.$url.'">'.img_picto($langs->trans($text_on), $switchon, '', 0, 0, 0, '', $cssswitchon).'</a>';
944 } else {
945 $out .= '<span id="set_'.$htmlname.'_'.$object->id.'" class="linkobject '.($object->$code == 1 ? 'hideobject' : '').($morecss ? ' '.$morecss : '').'">'.img_picto($langs->trans($text_off), $switchoff, '', 0, 0, 0, '', $cssswitchoff).'</span>';
946 $out .= '<span id="del_'.$htmlname.'_'.$object->id.'" class="linkobject '.($object->$code == 1 ? '' : 'hideobject').($morecss ? ' '.$morecss : '').'">'.img_picto($langs->trans($text_on), $switchon, '', 0, 0, 0, '', $cssswitchon).'</span>';
947 }
948
949 return $out;
950}
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
ajax_autocompleter($selected, $htmlname, $url, $urloption='', $minLength=2, $autoselect=0, $ajaxoptions=array(), $moreparams='')
Generic function that return javascript to add to transform a common input text or select field into ...
Definition ajax.lib.php:50
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition ajax.lib.php:476
ajax_dialog($title, $message, $w=350, $h=150)
Show an ajax dialog.
Definition ajax.lib.php:433
ajax_multiautocompleter($htmlname, $fields, $url, $option='', $minLength=2, $autoselect=0)
Generic function that return javascript to add to a page to transform a common input text field into ...
Definition ajax.lib.php:325
ajax_event($htmlname, $events)
Add event management script.
Definition ajax.lib.php:599
Class to manage Dolibarr users.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
currentToken()
Return the value of token currently saved into session with name 'token'.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into JavaScript code.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dol_textishtml($msg, $option=0)
Return if a text is a html content.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
if(empty( $takeposterminal)) fail($message)
Abort invoice creation with a given error message.
Definition invoice.php:124