dolibarr  9.0.0
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  *
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 <http://www.gnu.org/licenses/>.
18  * or see http://www.gnu.org/
19  */
20 
46 function ajax_autocompleter($selected, $htmlname, $url, $urloption='', $minLength=2, $autoselect=0, $ajaxoptions=array())
47 {
48  if (empty($minLength)) $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')) $dataforrenderITem=constant('JS_QUERY_AUTOCOMPLETE_RENDERITEM');
54  if (defined('JS_QUERY_AUTOCOMPLETE_ITEM')) $dataforitem=constant('JS_QUERY_AUTOCOMPLETE_ITEM');
55 
56  // Input search_htmlname is original field
57  // Input htmlname is a second input field used when using ajax autocomplete.
58  $script = '<input type="hidden" name="'.$htmlname.'" id="'.$htmlname.'" value="'.$selected.'" />';
59 
60  $script.= '<!-- Javascript code for autocomplete of field '.$htmlname.' -->'."\n";
61  $script.= '<script type="text/javascript">'."\n";
62  $script.= '$(document).ready(function() {
63  var autoselect = '.$autoselect.';
64  var options = '.json_encode($ajaxoptions).';
65 
66  /* 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 */
67  $("input#search_'.$htmlname.'").keydown(function(e) {
68  if (e.keyCode != 9) /* If not "Tab" key */
69  {
70  console.log("Clear id previously selected for field '.$htmlname.'");
71  $("#'.$htmlname.'").val("");
72  }
73  });
74 
75  // Check options for secondary actions when keyup
76  $("input#search_'.$htmlname.'").keyup(function() {
77  if ($(this).val().length == 0)
78  {
79  $("#search_'.$htmlname.'").val("");
80  $("#'.$htmlname.'").val("").trigger("change");
81  if (options.option_disabled) {
82  $("#" + options.option_disabled).removeAttr("disabled");
83  }
84  if (options.disabled) {
85  $.each(options.disabled, function(key, value) {
86  $("#" + value).removeAttr("disabled");
87  });
88  }
89  if (options.update) {
90  $.each(options.update, function(key, value) {
91  $("#" + key).val("").trigger("change");
92  });
93  }
94  if (options.show) {
95  $.each(options.show, function(key, value) {
96  $("#" + value).hide().trigger("hide");
97  });
98  }
99  if (options.update_textarea) {
100  $.each(options.update_textarea, function(key, value) {
101  if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined" && CKEDITOR.instances[key] != "undefined") {
102  CKEDITOR.instances[key].setData("");
103  } else {
104  $("#" + key).html("");
105  }
106  });
107  }
108  }
109  });
110  $("input#search_'.$htmlname.'").autocomplete({
111  source: function( request, response ) {
112  $.get("'.$url.($urloption?'?'.$urloption:'').'", { '.$htmlname.': request.term }, function(data){
113  if (data != null)
114  {
115  response($.map( data, function(item) {
116  if (autoselect == 1 && data.length == 1) {
117  $("#search_'.$htmlname.'").val(item.value);
118  $("#'.$htmlname.'").val(item.key).trigger("change");
119  }
120  var label = item.label.toString();
121  var update = {};
122  if (options.update) {
123  $.each(options.update, function(key, value) {
124  update[key] = item[value];
125  });
126  }
127  var textarea = {};
128  if (options.update_textarea) {
129  $.each(options.update_textarea, function(key, value) {
130  textarea[key] = item[value];
131  });
132  }
133  return { label: label, value: item.value, id: item.key, update: update, textarea: textarea, disabled: item.disabled }
134  }));
135  }
136  else console.error("Error: Ajax url '.$url.($urloption?'?'.$urloption:'').' has returned an empty page. Should be an empty json array.");
137  }, "json");
138  },
139  dataType: "json",
140  minLength: '.$minLength.',
141  select: function( event, ui ) { // Function ran once new value has been selected into javascript combo
142  console.log("Call change on input '.$htmlname.' because of select definition of autocomplete select call on input#search_'.$htmlname.'");
143  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");
144  $("#'.$htmlname.'").val(ui.item.id).trigger("change"); // Select new value
145  // Disable an element
146  if (options.option_disabled) {
147  console.log("Make action option_disabled on #"+options.option_disabled+" with disabled="+ui.item.disabled)
148  if (ui.item.disabled) {
149  $("#" + options.option_disabled).prop("disabled", true);
150  if (options.error) {
151  $.jnotify(options.error, "error", true); // Output with jnotify the error message
152  }
153  if (options.warning) {
154  $.jnotify(options.warning, "warning", false); // Output with jnotify the warning message
155  }
156  } else {
157  $("#" + options.option_disabled).removeAttr("disabled");
158  }
159  }
160  if (options.disabled) {
161  console.log("Make action disabled on each "+options.option_disabled)
162  $.each(options.disabled, function(key, value) {
163  $("#" + value).prop("disabled", true);
164  });
165  }
166  if (options.show) {
167  console.log("Make action show on each "+options.show)
168  $.each(options.show, function(key, value) {
169  $("#" + value).show().trigger("show");
170  });
171  }
172  // Update an input
173  if (ui.item.update) {
174  console.log("Make action update on each ui.item.update")
175  // loop on each "update" fields
176  $.each(ui.item.update, function(key, value) {
177  $("#" + key).val(value).trigger("change");
178  });
179  }
180  if (ui.item.textarea) {
181  console.log("Make action textarea on each ui.item.textarea")
182  $.each(ui.item.textarea, function(key, value) {
183  if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined" && CKEDITOR.instances[key] != "undefined") {
184  CKEDITOR.instances[key].setData(value);
185  CKEDITOR.instances[key].focus();
186  } else {
187  $("#" + key).html(value);
188  $("#" + key).focus();
189  }
190  });
191  }
192  console.log("ajax_autocompleter new value selected, we trigger change on original component so field #search_'.$htmlname.'");
193 
194  $("#search_'.$htmlname.'").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.
195  }
196  ,delay: 500
197  }).data("'.$dataforrenderITem.'")._renderItem = function( ul, item ) {
198  return $("<li>")
199  .data( "'.$dataforitem.'", item ) // jQuery UI > 1.10.0
200  .append( \'<a><span class="tag">\' + item.label + "</span></a>" )
201  .appendTo(ul);
202  };
203 
204  });';
205  $script.= '</script>';
206 
207  return $script;
208 }
209 
224 function ajax_multiautocompleter($htmlname, $fields, $url, $option='', $minLength=2, $autoselect=0)
225 {
226  $script = '<!-- Autocomplete -->'."\n";
227  $script.= '<script type="text/javascript">';
228  $script.= 'jQuery(document).ready(function() {
229  var fields = '.json_encode($fields).';
230  var nboffields = fields.length;
231  var autoselect = '.$autoselect.';
232  //alert(fields + " " + nboffields);
233 
234  jQuery("input#'.$htmlname.'").autocomplete({
235  dataType: "json",
236  minLength: '.$minLength.',
237  source: function( request, response ) {
238  jQuery.getJSON( "'.$url.($option?'?'.$option:'').'", { '.$htmlname.': request.term }, function(data){
239  response( jQuery.map( data, function( item ) {
240  if (autoselect == 1 && data.length == 1) {
241  jQuery("#'.$htmlname.'").val(item.value);
242  // TODO move this to specific request
243  if (item.states) {
244  jQuery("#state_id").html(item.states);
245  }
246  for (i=0;i<nboffields;i++) {
247  if (item[fields[i]]) { // If defined
248  //alert(item[fields[i]]);
249  jQuery("#" + fields[i]).val(item[fields[i]]);
250  }
251  }
252  }
253  return item
254  }));
255  });
256  },
257  select: function( event, ui ) {
258  needtotrigger = "";
259  for (i=0;i<nboffields;i++) {
260  //alert(fields[i] + " = " + ui.item[fields[i]]);
261  if (fields[i]=="selectcountry_id")
262  {
263  if (ui.item[fields[i]] > 0) // Do not erase country if unknown
264  {
265  oldvalue=jQuery("#" + fields[i]).val();
266  newvalue=ui.item[fields[i]];
267  //alert(oldvalue+" "+newvalue);
268  jQuery("#" + fields[i]).val(ui.item[fields[i]]);
269  if (oldvalue != newvalue) // To force select2 to refresh visible content
270  {
271  needtotrigger="#" + fields[i];
272  }
273 
274  // If we set new country and new state, we need to set a new list of state to allow change
275  if (ui.item.states && ui.item["state_id"] != jQuery("#state_id").value) {
276  jQuery("#state_id").html(ui.item.states);
277  }
278  }
279  }
280  else if (fields[i]=="state_id" || fields[i]=="state_id")
281  {
282  if (ui.item[fields[i]] > 0) // Do not erase state if unknown
283  {
284  oldvalue=jQuery("#" + fields[i]).val();
285  newvalue=ui.item[fields[i]];
286  //alert(oldvalue+" "+newvalue);
287  jQuery("#" + fields[i]).val(ui.item[fields[i]]); // This may fails if not correct country
288  if (oldvalue != newvalue) // To force select2 to refresh visible content
289  {
290  needtotrigger="#" + fields[i];
291  }
292  }
293  }
294  else if (ui.item[fields[i]]) { // If defined
295  oldvalue=jQuery("#" + fields[i]).val();
296  newvalue=ui.item[fields[i]];
297  //alert(oldvalue+" "+newvalue);
298  jQuery("#" + fields[i]).val(ui.item[fields[i]]);
299  if (oldvalue != newvalue) // To force select2 to refresh visible content
300  {
301  needtotrigger="#" + fields[i];
302  }
303  }
304 
305  if (needtotrigger != "") // To force select2 to refresh visible content
306  {
307  // 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
308  // This is required for example when changing zip with autocomplete that change the country
309  jQuery(needtotrigger).delay(500).queue(function() {
310  jQuery(this).trigger("change");
311  });
312  }
313  }
314  }
315  });
316  });';
317  $script.= '</script>';
318 
319  return $script;
320 }
321 
331 function ajax_dialog($title,$message,$w=350,$h=150)
332 {
333  global $langs;
334 
335  $newtitle=dol_textishtml($title)?dol_string_nohtmltag($title,1):$title;
336  $msg= '<div id="dialog-info" title="'.dol_escape_htmltag($newtitle).'">';
337  $msg.= $message;
338  $msg.= '</div>'."\n";
339  $msg.= '<script type="text/javascript">
340  jQuery(function() {
341  jQuery("#dialog-info").dialog({
342  resizable: false,
343  height:'.$h.',
344  width:'.$w.',
345  modal: true,
346  buttons: {
347  Ok: function() {
348  jQuery(this).dialog(\'close\');
349  }
350  }
351  });
352  });
353  </script>';
354 
355  $msg.= "\n";
356 
357  return $msg;
358 }
359 
360 
374 function ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve')
375 {
376  global $conf;
377 
378  // select2 disabled for smartphones with standard browser.
379  // TODO With select2 v4, it seems ok, except that responsive style on table become crazy when scrolling at end of array)
380  if (! empty($conf->browser->layout) && $conf->browser->layout == 'phone') return '';
381 
382  if (! empty($conf->global->MAIN_DISABLE_AJAX_COMBOX)) return '';
383  if (empty($conf->use_javascript_ajax)) return '';
384  if (empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) && ! defined('REQUIRE_JQUERY_MULTISELECT')) return '';
385  if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) return '';
386 
387  if (empty($minLengthToAutocomplete)) $minLengthToAutocomplete=0;
388 
389  $tmpplugin='select2';
390  $msg="\n".'<!-- JS CODE TO ENABLE '.$tmpplugin.' for id = '.$htmlname.' -->
391  <script type="text/javascript">
392  $(document).ready(function () {
393  $(\''.(preg_match('/^\./',$htmlname)?$htmlname:'#'.$htmlname).'\').'.$tmpplugin.'({
394  dir: \'ltr\',
395  width: \''.$widthTypeOfAutocomplete.'\', /* off or resolve */
396  minimumInputLength: '.$minLengthToAutocomplete.',
397  language: select2arrayoflanguage,
398  containerCssClass: \':all:\', /* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
399  templateResult: function (data, container) { /* Format visible output into combo list */
400  /* Code to add class of origin OPTION propagated to the new select2 <li> tag */
401  if (data.element) { $(container).addClass($(data.element).attr("class")); }
402  //console.log(data.html);
403  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
404  return data.text;
405  },
406  templateSelection: function (selection) { /* Format visible output of selected value */
407  return selection.text;
408  },
409  escapeMarkup: function(markup) {
410  return markup;
411  },
412  dropdownCssClass: \'ui-dialog\'
413  })';
414  if ($forcefocus) $msg.= '.select2(\'focus\')';
415  $msg.= ';'."\n";
416 
417  if (is_array($events) && count($events)) // If an array of js events to do were provided.
418  {
419  $msg.= '
420  jQuery("#'.$htmlname.'").change(function () {
421  var obj = '.json_encode($events).';
422  $.each(obj, function(key,values) {
423  if (values.method.length) {
424  runJsCodeForEvent'.$htmlname.'(values);
425  }
426  });
427  });
428 
429  function runJsCodeForEvent'.$htmlname.'(obj) {
430  var id = $("#'.$htmlname.'").val();
431  var method = obj.method;
432  var url = obj.url;
433  var htmlname = obj.htmlname;
434  var showempty = obj.showempty;
435  console.log("Run runJsCodeForEvent-'.$htmlname.' from ajax_combobox id="+id+" method="+method+" showempty="+showempty+" url="+url+" htmlname="+htmlname);
436  $.getJSON(url,
437  {
438  action: method,
439  id: id,
440  htmlname: htmlname,
441  showempty: showempty
442  },
443  function(response) {
444  $.each(obj.params, function(key,action) {
445  if (key.length) {
446  var num = response.num;
447  if (num > 0) {
448  $("#" + key).removeAttr(action);
449  } else {
450  $("#" + key).attr(action, action);
451  }
452  }
453  });
454  $("select#" + htmlname).html(response.value);
455  if (response.num) {
456  var selecthtml_str = response.value;
457  var selecthtml_dom=$.parseHTML(selecthtml_str);
458  $("#inputautocomplete"+htmlname).val(selecthtml_dom[0][0].innerHTML);
459  } else {
460  $("#inputautocomplete"+htmlname).val("");
461  }
462  $("select#" + htmlname).change(); /* Trigger event change */
463  }
464  );
465  }';
466  }
467 
468  $msg.= '});'."\n";
469  $msg.= "</script>\n";
470 
471  return $msg;
472 }
473 
484 function ajax_constantonoff($code, $input=array(), $entity=null, $revertonoff=0, $strict=0)
485 {
486  global $conf, $langs;
487 
488  $entity = ((isset($entity) && is_numeric($entity) && $entity >= 0) ? $entity : $conf->entity);
489 
490  if (empty($conf->use_javascript_ajax))
491  {
492  if (empty($conf->global->$code)) print '<a href="'.$_SERVER['PHP_SELF'].'?action=set_'.$code.'&entity='.$entity.'">'.img_picto($langs->trans("Disabled"),'off').'</a>';
493  else print '<a href="'.$_SERVER['PHP_SELF'].'?action=del_'.$code.'&entity='.$entity.'">'.img_picto($langs->trans("Enabled"),'on').'</a>';
494  }
495  else
496  {
497  $out= "\n<!-- Ajax code to switch constant ".$code." -->".'
498  <script type="text/javascript">
499  $(document).ready(function() {
500  var input = '.json_encode($input).';
501  var url = \''.DOL_URL_ROOT.'/core/ajax/constantonoff.php\';
502  var code = \''.$code.'\';
503  var entity = \''.$entity.'\';
504  var strict = \''.$strict.'\';
505  var yesButton = "'.dol_escape_js($langs->transnoentities("Yes")).'";
506  var noButton = "'.dol_escape_js($langs->transnoentities("No")).'";
507 
508  // Set constant
509  $("#set_" + code).click(function() {
510  if (input.alert && input.alert.set) {
511  if (input.alert.set.yesButton) yesButton = input.alert.set.yesButton;
512  if (input.alert.set.noButton) noButton = input.alert.set.noButton;
513  confirmConstantAction("set", url, code, input, input.alert.set, entity, yesButton, noButton, strict);
514  } else {
515  setConstant(url, code, input, entity);
516  }
517  });
518 
519  // Del constant
520  $("#del_" + code).click(function() {
521  if (input.alert && input.alert.del) {
522  if (input.alert.del.yesButton) yesButton = input.alert.del.yesButton;
523  if (input.alert.del.noButton) noButton = input.alert.del.noButton;
524  confirmConstantAction("del", url, code, input, input.alert.del, entity, yesButton, noButton, strict);
525  } else {
526  delConstant(url, code, input, entity);
527  }
528  });
529  });
530  </script>'."\n";
531 
532  $out.= '<div id="confirm_'.$code.'" title="" style="display: none;"></div>';
533  $out.= '<span id="set_'.$code.'" class="linkobject '.(! empty($conf->global->$code)?'hideobject':'').'">'.($revertonoff?img_picto($langs->trans("Enabled"),'switch_on'):img_picto($langs->trans("Disabled"),'switch_off')).'</span>';
534  $out.= '<span id="del_'.$code.'" class="linkobject '.(! empty($conf->global->$code)?'':'hideobject').'">'.($revertonoff?img_picto($langs->trans("Disabled"),'switch_off'):img_picto($langs->trans("Enabled"),'switch_on')).'</span>';
535  $out.="\n";
536  }
537 
538  return $out;
539 }
540 
552 function ajax_object_onoff($object, $code, $field, $text_on, $text_off, $input=array())
553 {
554  global $langs;
555 
556  $out= '<script type="text/javascript">
557  $(function() {
558  var input = '.json_encode($input).';
559 
560  // Set constant
561  $("#set_'.$code.'_'.$object->id.'").click(function() {
562  $.get( "'.DOL_URL_ROOT.'/core/ajax/objectonoff.php", {
563  action: \'set\',
564  field: \''.$field.'\',
565  value: \'1\',
566  element: \''.$object->element.'\',
567  id: \''.$object->id.'\'
568  },
569  function() {
570  $("#set_'.$code.'_'.$object->id.'").hide();
571  $("#del_'.$code.'_'.$object->id.'").show();
572  // Enable another element
573  if (input.disabled && input.disabled.length > 0) {
574  $.each(input.disabled, function(key,value) {
575  $("#" + value).removeAttr("disabled");
576  if ($("#" + value).hasClass("butActionRefused") == true) {
577  $("#" + value).removeClass("butActionRefused");
578  $("#" + value).addClass("butAction");
579  }
580  });
581  // Show another element
582  } else if (input.showhide && input.showhide.length > 0) {
583  $.each(input.showhide, function(key,value) {
584  $("#" + value).show();
585  });
586  }
587  });
588  });
589 
590  // Del constant
591  $("#del_'.$code.'_'.$object->id.'").click(function() {
592  $.get( "'.DOL_URL_ROOT.'/core/ajax/objectonoff.php", {
593  action: \'set\',
594  field: \''.$field.'\',
595  value: \'0\',
596  element: \''.$object->element.'\',
597  id: \''.$object->id.'\'
598  },
599  function() {
600  $("#del_'.$code.'_'.$object->id.'").hide();
601  $("#set_'.$code.'_'.$object->id.'").show();
602  // Disable another element
603  if (input.disabled && input.disabled.length > 0) {
604  $.each(input.disabled, function(key,value) {
605  $("#" + value).prop("disabled", true);
606  if ($("#" + value).hasClass("butAction") == true) {
607  $("#" + value).removeClass("butAction");
608  $("#" + value).addClass("butActionRefused");
609  }
610  });
611  // Hide another element
612  } else if (input.showhide && input.showhide.length > 0) {
613  $.each(input.showhide, function(key,value) {
614  $("#" + value).hide();
615  });
616  }
617  });
618  });
619  });
620  </script>';
621  $out.= '<span id="set_'.$code.'_'.$object->id.'" class="linkobject '.($object->$code==1?'hideobject':'').'">'.img_picto($langs->trans($text_off),'switch_off').'</span>';
622  $out.= '<span id="del_'.$code.'_'.$object->id.'" class="linkobject '.($object->$code==1?'':'hideobject').'">'.img_picto($langs->trans($text_on),'switch_on').'</span>';
623 
624  return $out;
625 }
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve')
Convert a html select field into an ajax combobox.
Definition: ajax.lib.php:374
print
Draft customers invoices.
Definition: index.php:91
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 field into an au...
Definition: ajax.lib.php:224
select2arrayoflanguage
Set array used for select2 translations.
type
Definition: viewcat.php:284
ajax_dialog($title, $message, $w=350, $h=150)
Show an ajax dialog.
Definition: ajax.lib.php:331
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0)
Clean a string from all HTML tags and entities.
ajax_constantonoff($code, $input=array(), $entity=null, $revertonoff=0, $strict=0)
On/off button for constant.
Definition: ajax.lib.php:484
ajax_autocompleter($selected, $htmlname, $url, $urloption='', $minLength=2, $autoselect=0, $ajaxoptions=array())
Generic function that return javascript to add to a page to transform a common input field into an au...
Definition: ajax.lib.php:46
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='')
Show picto whatever it&#39;s its name (generic function)
dol_textishtml($msg, $option=0)
Return if a text is a html content.