32require_once DOL_DOCUMENT_ROOT.
'/core/class/html.form.class.php';
61 public $substit = array();
66 public $substit_lines = array();
71 public $param = array();
76 public $withoptiononeemailperrecipient;
81 public $aicallfunctioncalled =
false;
103 public function getSectionForAIEnhancement($function =
'textgeneration', $format =
'', $htmlContent =
'message', $onlyenhancements =
'', $aiprompt =
"")
105 global $langs, $form;
106 require_once DOL_DOCUMENT_ROOT.
"/ai/lib/ai.lib.php";
107 require_once DOL_DOCUMENT_ROOT.
'/core/class/html.formadmin.class.php';
110 if (!is_object($form)) {
111 $form =
new Form($this->db);
114 $langs->load(
"other");
116 $messageaiwait =
'<i class="fa fa-spinner fa-spin fa-2x fa-fw valignmiddle marginrightonly"></i>'.$langs->trans(
"AIProcessingPleaseWait",
getDolGlobalString(
'AI_API_SERVICE',
'chatgpt'));
118 $htmlContent = preg_replace(
'/[^a-z0-9_]/',
'', $htmlContent);
121 if (empty($onlyenhancements) || in_array($onlyenhancements, array(
'textgenerationemail',
'textgenerationwebpage'))) {
122 $out .=
'<div id="ai_textgeneration'.$htmlContent.
'" class="ai_textgeneration'.$htmlContent.
' paddingtop paddingbottom ai_feature">';
124 $out .=
'<textarea class="centpercent textarea-ai_feature" data-functionai="textgeneration" id="ai_instructions'.$htmlContent.
'" name="instruction" placeholder="'.$langs->trans(
"EnterYourAIPromptHere").
'..." /></textarea>';
125 $out .=
'<input id="generate_button'.$htmlContent.
'" type="button" class="button smallpaddingimp" disabled data-functionai="'.$function.
'" value="'.$langs->trans(
'Generate').
'"/>';
129 if (empty($onlyenhancements) || in_array($onlyenhancements, array(
'texttranslation'))) {
130 $out .= ($out ?
'<div style="height: 10px;"></div>' :
'');
131 $out .=
'<div id="ai_translation'.$htmlContent.
'" class="ai_translation'.$htmlContent.
' paddingtop paddingbottom ai_feature">';
132 $out .=
img_picto(
'',
'language',
'class="pictofixedwidth paddingrightonly"');
133 $out .= $formadmin->select_language(
"",
"ai_translation".$htmlContent.
"_select", 0, array(), $langs->trans(
"TranslateByAI").
'...', 0, 0,
'minwidth250 ai_translation'.$htmlContent.
'_select');
137 if (empty($onlyenhancements) || in_array($onlyenhancements, array(
'textsummarize'))) {
139 $out .= ($out ?
'<div style="height: 10px;"></div>' :
'');
140 $out .=
'<div id="ai_summarize'.$htmlContent.
'" class="ai_summarize'.$htmlContent.
' paddingtop paddingbottom ai_feature">';
141 $out .=
img_picto(
'',
'edit',
'class="pictofixedwidth paddingrightonly"');
142 $out .= $form->selectarray(
"ai_summarize".$htmlContent.
"_select", $summarizearray, 0, $langs->trans(
"SummarizeByAI").
'...', 0, 0,
'', 1, 0, 0,
'',
'minwidth250 ai_summarize'.$htmlContent.
'_select');
146 if (empty($onlyenhancements) || in_array($onlyenhancements, array(
'textrephrase'))) {
148 $out .= ($out ?
'<div style="height: 10px;"></div>' :
'');
149 $out .=
'<div id="ai_rephraser'.$htmlContent.
'" class="ai_rephraser'.$htmlContent.
' paddingtop paddingbottom ai_feature">';
150 $out .=
img_picto(
'',
'edit',
'class="pictofixedwidth paddingrightonly"');
151 $out .= $form->selectarray(
"ai_rephraser".$htmlContent.
"_select", $stylearray, 0, $langs->trans(
"RephraserByAI").
'...', 0, 0,
'', 1, 0, 0,
'',
'minwidth250 ai_rephraser'.$htmlContent.
'_select');
155 if (in_array($onlyenhancements, array(
'textgenerationextrafield'))) {
156 $out .=
'<div id="ai_textgenerationextrafield'.$htmlContent.
'" class="ai_textgenerationextrafield'.$htmlContent.
' paddingtop paddingbottom ai_feature">';
157 $out .=
'<input id="input_ai_textgenerationextrafield'.$htmlContent.
'" type="hidden" class="button smallpaddingimp" data-functionai="textgenerationextrafield" value="'.$aiprompt.
'"/>';
161 $out =
'<!-- getSectionForAIEnhancement -->'.$out;
162 $out =
'<div id="ai_dropdown'.$htmlContent.
'" class="dropdown-menu ai_dropdown ai_dropdown'.$htmlContent.
' paddingtop paddingbottom">'.$out;
164 $out .=
'<div id="ai_status_message'.$htmlContent.
'" class="fieldrequired hideobject marginrightonly margintoponly">';
165 $out .= $messageaiwait;
168 if ($function ==
'imagegeneration') {
169 $out .=
'<div id="ai_image_result" class="margintoponly"></div>';
173 $out .=
"<script type='text/javascript'>
174 $(document).ready(function() {
175 $('#ai_translation".$htmlContent.
"_select').data('functionai', 'texttranslation')
176 $('#ai_summarize".$htmlContent.
"_select').data('functionai', 'textsummarize')
177 $('#ai_rephraser".$htmlContent.
"_select').data('functionai', 'textrephraser')
179 $('#ai_instructions".$htmlContent.
"').keyup(function(){
180 console.log('We type a key up on #ai_instructions".$htmlContent.
"');
181 if ($(this).val() != '') {
182 $('#generate_button".$htmlContent.
"').prop('disabled', false);
184 $('#generate_button".$htmlContent.
"').prop('disabled', true);
189 $('#ai_instructions".$htmlContent.
"').keydown(function(event) {
190 if (event.keyCode === 13 && $(this).val() != '') {
191 console.log('We type enter on #ai_instructions".$htmlContent.
"');
192 event.preventDefault();
193 $('#generate_button".$htmlContent.
"').click();
197 $('#generate_button".$htmlContent.
"').click(function() {
198 console.log('We click on #generate_button".$htmlContent.
"');
199 prepareCallAIGenerator($(this));
202 $('#ai_translation".$htmlContent.
"_select').on('change', function() {
203 console.log('We change #ai_translation".$htmlContent.
"_select with lang '+$(this).val());
204 if ($(this).val() != null && $(this).val() != '' && $(this).val() != '-1') {
205 prepareCallAIGenerator($(this));
209 $('#ai_summarize".$htmlContent.
"_select').on('change', function() {
210 console.log('We change #ai_summarize".$htmlContent.
"_select with length '+$(this).val());
211 if ($(this).val() != null && $(this).val() != '' && $(this).val() != '-1') {
212 prepareCallAIGenerator($(this));
216 $('#ai_rephraser".$htmlContent.
"_select').on('change', function() {
217 console.log('We change #ai_summarize".$htmlContent.
"_select with mode '+$(this).val());
218 if ($(this).val() != null && $(this).val() != '' && $(this).val() != '-1') {
219 prepareCallAIGenerator($(this));
222 $('#linkforaiprompt".$function.
"').on('click', function() {
223 //Get value aiprompt + prepare ai generator
224 elementforprompt = $('#input_ai_textgenerationextrafield".$htmlContent.
"');
225 aiprompt = elementforprompt.val();
226 if (aiprompt != null && aiprompt != '' && aiprompt != '-1'){
227 prepareCallAIGenerator(elementforprompt);
231 function prepareCallAIGenerator(element) {
232 console.log('We prepare ajax call to AI to url /ai/ajax/generate_content.php function=".
dol_escape_js($function).
" format=".
dol_escape_js($format).
"');
234 var userprompt = $('#ai_instructions".$htmlContent.
"').val();
235 var timeoutfinished = 0;
236 var apicallfinished = 0;
241 functionai = $(element).data('functionai'); /* element is the html element we have manipulated in the ai tool */
242 style = $('#ai_rephraser'+htmlname+'_select').val();
245 console.log('htmlname='+htmlname+' functionai='+functionai+' style='+style);
246 if ($('#'+htmlname).is('div')) {
247 texttomodify = $('#'+htmlname).html(); /* for div */
249 texttomodify = $('#'+htmlname).val(); /* for input or textarea */
251 if (functionai == 'texttranslation') {
253 if (CKEDITOR.instances) {
254 editorInstance = CKEDITOR.instances[htmlname];
255 if (editorInstance) {
256 texttomodify = editorInstance.getData();
263 lang = $('#ai_translation'+htmlname+'_select').val();
264 instructions = 'Translate only the following text to ' + lang + ': ' + texttomodify;
266 } else if (functionai == 'textsummarize') {
267 width = $('#ai_summarize'+htmlname+'_select').val();
268 arr = width.split('_');
271 if (width == undefined || unit == undefined){
272 console.log('Bad value so we choose 20 words')
287 console.log('unit not found so we choose words');
291 instructions = 'Summarize the following text '+ (unit == 'percent' ? 'by ' : 'in') + width + ' ' + unit + ': ' + texttomodify;
292 } else if (functionai == 'textrephraser') {
293 if (style == 'spellchecker') {
294 instructions = 'Fix spelling and grammar errors in the following text : ' + texttomodify;
295 functionai = 'textspellchecker';
297 instructions = 'Rephrase the following text in a '+style+' style: ' + texttomodify;
299 } else if (functionai == 'textgenerationextrafield'){
300 instructions = $(element).val();
302 instructions = userprompt;
305 /* Show message API running */
306 $('#ai_status_message".$htmlContent.
"').show();
307 $('#ai_status_message".$htmlContent.
"').html('".
dol_escape_js($messageaiwait).
"');
308 $('.icon-container .loader').show();
310 setTimeout(function() {
312 $('#ai_status_message".$htmlContent.
"').hide();
315 console.log('Instruction forged by javascript = '+instructions);
317 callAIGenerator(functionai, instructions, format, htmlname);
320 CKEDITOR.on( 'instanceReady', function(e) {
321 if (CKEDITOR.instances) {
322 var htmlname = '".$htmlContent.
"';
323 /* if a ckeditor handler exist for this div, we add a handler to have the main html component updated */
324 console.log('Add handler on CKEDITOR.instances[".$htmlContent.
"]');
325 if (CKEDITOR.instances[htmlname] != undefined) {
326 CKEDITOR.instances[htmlname].on('change', function() {
327 data = CKEDITOR.instances[htmlname].getData();
328 $('#'+htmlname).val(data); /* for input or textarea */
329 $('#'+htmlname).html(data); /* for div */
348 if ($this->aicallfunctioncalled) {
354 function callAIGenerator(aifunction, instructions, format, htmlname){
355 if (aifunction === 'imagegeneration') {
356 // Handle image generation request
358 url: '". DOL_URL_ROOT.
"/ai/ajax/generate_content.php?token=".
currentToken().
"',
360 contentType: 'application/json',
361 data: JSON.stringify({
362 'format': format, /* the format for output */
363 'function': aifunction, /* the AI feature to call */
364 'instructions': instructions, /* the prompt string */
366 success: function(response) {
367 console.log('Received image URL: '+response);
369 // make substitutions
370 let substit = ". json_encode($this->substit).
";
371 for (let key in substit) {
372 if (substit.hasOwnProperty(key)) {
373 // Replace the placeholder with its corresponding value
374 response = response.replace(key, substit[key]);
378 // Assuming response is the URL of the generated image
379 var imageUrl = response;
380 $('#ai_image_result').html('<img src=\"' + imageUrl + '\" alt=\"Generated Image\" />');
382 // Clear the input field
383 $('#ai_instructions').val('');
386 if (timeoutfinished) {
387 $('#ai_status_message').hide();
390 error: function(xhr, status, error) {
392 console.log('error ajax', status, error);
393 /*$('#ai_status_message'+htmlname).hide();*/
394 $('#ai_status_message'+htmlname).val(error);
395 $('#ai_status_message'+htmlname).html(error);
400 // set editor in readonly
401 if (CKEDITOR.instances[htmlname]) {
402 CKEDITOR.instances[htmlname].setReadOnly(1);
405 console.log('Call generate_content.php');
407 url: '". DOL_URL_ROOT.
"/ai/ajax/generate_content.php?token=".
currentToken().
"',
409 contentType: 'application/json',
410 data: JSON.stringify({
411 'format': format, /* the format for output */
412 'function': aifunction, /* the AI feature to call */
413 'instructions': instructions, /* the prompt string */
415 success: function(response) {
416 console.log('Add response into field \'#'+htmlname+'\': '+response);
418 jQuery('#'+htmlname).val(response); // If #htmlcontent is a input name or textarea
419 jQuery('#'+htmlname).html(response).trigger('change'); // If #htmlContent is a div and trigger event change for extrafield update
420 //jQuery('#'+htmlname+'preview').val(response);
422 if (CKEDITOR.instances) {
423 var editorInstance = CKEDITOR.instances[htmlname];
424 if (editorInstance) {
425 editorInstance.setReadOnly(0);
426 editorInstance.setData(response);
428 //var editorInstancepreview = CKEDITOR.instances[htmlname+'preview'];
429 //if (editorInstancepreview) {
430 // editorInstancepreview.setData(response);
434 // Remove all value from Ai Section select
435 $('#ai_instructions'+htmlname).val('');
436 $('#ai_translation'+htmlname+'_select').val('-1');
437 $('#ai_translation'+htmlname+'_select').trigger('change');
438 $('#ai_summarize'+htmlname+'_select').val('-1');
439 $('#ai_summarize'+htmlname+'_select').trigger('change');
440 $('#ai_rephraser'+htmlname+'_select').val('-1');
441 $('#ai_rephraser'+htmlname+'_select').trigger('change');
442 $('#ai_status_message'+htmlname).hide();
443 $('#ai_dropdown'+htmlname).hide();
445 error: function(xhr, status, error) {
447 console.log('error ajax ', status, error);
448 /* $('#ai_status_message'+htmlname).hide(); */
449 if (xhr.responseText) {
450 $('#ai_status_message'+htmlname).val(xhr.responseText);
451 $('#ai_status_message'+htmlname).html(xhr.responseText);
453 $('#ai_status_message'+htmlname).val(error);
454 $('#ai_status_message'+htmlname).html(error);
462 $this->aicallfunctioncalled =
true;
478 $parameters = array();
479 $tmparray = getCommonSubstitutionArray($outputlangs, 0,
null,
$object);
482 $this->substit = $tmparray;
485 if (is_array(
$object->lines)) {
486 foreach (
$object->lines as $line) {
487 $substit_line = array(
488 '__PRODUCT_REF__' => isset($line->product_ref) ? $line->product_ref :
'',
489 '__PRODUCT_LABEL__' => isset($line->product_label) ? $line->product_label :
'',
490 '__PRODUCT_DESCRIPTION__' => isset($line->product_desc) ? $line->product_desc :
'',
491 '__LABEL__' => isset($line->label) ? $line->label :
'',
492 '__DESCRIPTION__' => isset($line->desc) ? $line->desc :
'',
493 '__DATE_START_YMD__' =>
dol_print_date($line->date_start,
'day',
false, $outputlangs),
494 '__DATE_END_YMD__' =>
dol_print_date($line->date_end,
'day',
false, $outputlangs),
495 '__QUANTITY__' => $line->qty,
496 '__SUBPRICE__' =>
price($line->subprice),
497 '__AMOUNT__' =>
price($line->total_ttc),
498 '__AMOUNT_EXCL_TAX__' =>
price($line->total_ht)
502 if (!empty($line->fk_product)) {
503 if (!is_object($extrafields)) {
506 $product =
new Product($this->db);
507 $product->fetch($line->fk_product);
508 $product->fetch_optionals();
510 $extrafields->fetch_name_optionals_label($product->table_element,
true);
512 if (!empty($extrafields->attributes[$product->table_element][
'label']) && is_array($extrafields->attributes[$product->table_element][
'label']) && count($extrafields->attributes[$product->table_element][
'label']) > 0) {
513 foreach ($extrafields->attributes[$product->table_element][
'label'] as $key => $label) {
514 $substit_line[
'__PRODUCT_EXTRAFIELD_'.strtoupper($key).
'__'] = isset($product->array_options[
'options_'.$key]) ? $product->array_options[
'options_'.$key] :
'';
519 $this->substit_lines[$line->id] = $substit_line;
if(! $sortfield) if(! $sortorder) $object
getListForAIRephraseStyle()
Get list for AI style of writing.
getListForAISummarize()
Get list for AI summarize.
Class to manage products or services.
currentToken()
Return the value of token currently saved into session with name 'token'.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into JavaScript code.
complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
Complete the $substitutionarray with more entries coming from external module that had set the "subst...
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
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)