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 ?
'<br>' :
'');
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 ?
'<br>' :
'');
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,
'minwidth250 ai_summarize'.$htmlContent.
'_select', 1);
146 if (empty($onlyenhancements) || in_array($onlyenhancements, array(
'textrephrase'))) {
148 $out .= ($out ?
'<br>' :
'');
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,
'minwidth250 ai_rephraser'.$htmlContent.
'_select', 1);
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 lang '+$(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 lang '+$(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 */
244 console.log('htmlname='+htmlname+' functionai='+functionai);
245 if ($('#'+htmlname).is('div')) {
246 texttomodify = $('#'+htmlname).html(); /* for div */
248 texttomodify = $('#'+htmlname).val(); /* for input or textarea */
250 if (functionai == 'texttranslation') {
252 if (CKEDITOR.instances) {
253 editorInstance = CKEDITOR.instances[htmlname];
254 if (editorInstance) {
255 texttomodify = editorInstance.getData();
262 lang = $('#ai_translation'+htmlname+'_select').val();
263 instructions = 'Translate only the following text to ' + lang + ': ' + texttomodify;
265 } else if (functionai == 'textsummarize') {
266 width = $('#ai_summarize'+htmlname+'_select').val();
267 arr = width.split('_');
270 if (width == undefined || unit == undefined){
271 console.log('Bad value so we choose 20 words')
286 console.log('unit not found so we choose words');
290 instructions = 'Summarize the following text '+ (unit == 'percent' ? 'by ' : 'in') + width + ' ' + unit + ': ' + texttomodify;
291 } else if (functionai == 'textrephraser') {
292 style = $('#ai_rephraser'+htmlname+'_select').val();
293 instructions = 'Rephrase the following text in a '+style+' style: ' + texttomodify;
294 } else if (functionai == 'textgenerationextrafield'){
295 instructions = $(element).val();
297 instructions = userprompt;
300 /* Show message API running */
301 $('#ai_status_message".$htmlContent.
"').show();
302 $('#ai_status_message".$htmlContent.
"').html('".
dol_escape_js($messageaiwait).
"');
303 $('.icon-container .loader').show();
305 setTimeout(function() {
307 $('#ai_status_message".$htmlContent.
"').hide();
310 console.log('Instruction forged by javascript = '+instructions);
312 callAIGenerator(functionai, instructions, format, htmlname);
315 CKEDITOR.on( 'instanceReady', function(e) {
316 if (CKEDITOR.instances) {
317 var htmlname = '".$htmlContent.
"';
318 /* if a ckeditor handler exist for this div, we add a handler to have the main html component updated */
319 console.log('Add handler on CKEDITOR.instances[".$htmlContent.
"]');
320 if (CKEDITOR.instances[htmlname] != undefined) {
321 CKEDITOR.instances[htmlname].on('change', function() {
322 data = CKEDITOR.instances[htmlname].getData();
323 $('#'+htmlname).val(data); /* for input or textarea */
324 $('#'+htmlname).html(data); /* for div */
343 if ($this->aicallfunctioncalled) {
349 function callAIGenerator(aifunction, instructions, format, htmlname){
350 if (aifunction === 'imagegeneration') {
351 // Handle image generation request
353 url: '". DOL_URL_ROOT.
"/ai/ajax/generate_content.php?token=".
currentToken().
"',
355 contentType: 'application/json',
356 data: JSON.stringify({
357 'format': format, /* the format for output */
358 'function': aifunction, /* the AI feature to call */
359 'instructions': instructions, /* the prompt string */
361 success: function(response) {
362 console.log('Received image URL: '+response);
364 // make substitutions
365 let substit = ". json_encode($this->substit).
";
366 for (let key in substit) {
367 if (substit.hasOwnProperty(key)) {
368 // Replace the placeholder with its corresponding value
369 response = response.replace(key, substit[key]);
373 // Assuming response is the URL of the generated image
374 var imageUrl = response;
375 $('#ai_image_result').html('<img src=\"' + imageUrl + '\" alt=\"Generated Image\" />');
377 // Clear the input field
378 $('#ai_instructions').val('');
381 if (timeoutfinished) {
382 $('#ai_status_message').hide();
385 error: function(xhr, status, error) {
387 console.log('error ajax', status, error);
388 /*$('#ai_status_message'+htmlname).hide();*/
389 $('#ai_status_message'+htmlname).val(error);
390 $('#ai_status_message'+htmlname).html(error);
395 // set editor in readonly
396 if (CKEDITOR.instances[htmlname]) {
397 CKEDITOR.instances[htmlname].setReadOnly(1);
400 console.log('Call generate_content.php');
402 url: '". DOL_URL_ROOT.
"/ai/ajax/generate_content.php?token=".
currentToken().
"',
404 contentType: 'application/json',
405 data: JSON.stringify({
406 'format': format, /* the format for output */
407 'function': aifunction, /* the AI feature to call */
408 'instructions': instructions, /* the prompt string */
410 success: function(response) {
411 console.log('Add response into field \'#'+htmlname+'\': '+response);
413 jQuery('#'+htmlname).val(response); // If #htmlcontent is a input name or textarea
414 jQuery('#'+htmlname).html(response).trigger('change'); // If #htmlContent is a div and trigger event change for extrafield update
415 //jQuery('#'+htmlname+'preview').val(response);
417 if (CKEDITOR.instances) {
418 var editorInstance = CKEDITOR.instances[htmlname];
419 if (editorInstance) {
420 editorInstance.setReadOnly(0);
421 editorInstance.setData(response);
423 //var editorInstancepreview = CKEDITOR.instances[htmlname+'preview'];
424 //if (editorInstancepreview) {
425 // editorInstancepreview.setData(response);
429 // Remove all value from Ai Section select
430 $('#ai_instructions'+htmlname).val('');
431 $('#ai_translation'+htmlname+'_select').val('-1');
432 $('#ai_translation'+htmlname+'_select').trigger('change');
433 $('#ai_summarize'+htmlname+'_select').val('-1');
434 $('#ai_summarize'+htmlname+'_select').trigger('change');
435 $('#ai_rephraser'+htmlname+'_select').val('-1');
436 $('#ai_rephraser'+htmlname+'_select').trigger('change');
437 $('#ai_status_message'+htmlname).hide();
438 $('#ai_dropdown'+htmlname).hide();
440 error: function(xhr, status, error) {
442 console.log('error ajax ', status, error);
443 /* $('#ai_status_message'+htmlname).hide(); */
444 if (xhr.responseText) {
445 $('#ai_status_message'+htmlname).val(xhr.responseText);
446 $('#ai_status_message'+htmlname).html(xhr.responseText);
448 $('#ai_status_message'+htmlname).val(error);
449 $('#ai_status_message'+htmlname).html(error);
457 $this->aicallfunctioncalled =
true;
473 $parameters = array();
474 $tmparray = getCommonSubstitutionArray($outputlangs, 0,
null,
$object);
477 $this->substit = $tmparray;
480 if (is_array(
$object->lines)) {
481 foreach (
$object->lines as $line) {
482 $substit_line = array(
483 '__PRODUCT_REF__' => isset($line->product_ref) ? $line->product_ref :
'',
484 '__PRODUCT_LABEL__' => isset($line->product_label) ? $line->product_label :
'',
485 '__PRODUCT_DESCRIPTION__' => isset($line->product_desc) ? $line->product_desc :
'',
486 '__LABEL__' => isset($line->label) ? $line->label :
'',
487 '__DESCRIPTION__' => isset($line->desc) ? $line->desc :
'',
488 '__DATE_START_YMD__' =>
dol_print_date($line->date_start,
'day',
false, $outputlangs),
489 '__DATE_END_YMD__' =>
dol_print_date($line->date_end,
'day',
false, $outputlangs),
490 '__QUANTITY__' => $line->qty,
491 '__SUBPRICE__' =>
price($line->subprice),
492 '__AMOUNT__' =>
price($line->total_ttc),
493 '__AMOUNT_EXCL_TAX__' =>
price($line->total_ht)
497 if (!empty($line->fk_product)) {
498 if (!is_object($extrafields)) {
501 $product =
new Product($this->db);
502 $product->fetch($line->fk_product);
503 $product->fetch_optionals();
505 $extrafields->fetch_name_optionals_label($product->table_element,
true);
507 if (!empty($extrafields->attributes[$product->table_element][
'label']) && is_array($extrafields->attributes[$product->table_element][
'label']) && count($extrafields->attributes[$product->table_element][
'label']) > 0) {
508 foreach ($extrafields->attributes[$product->table_element][
'label'] as $key => $label) {
509 $substit_line[
'__PRODUCT_EXTRAFIELD_'.strtoupper($key).
'__'] = isset($product->array_options[
'options_'.$key]) ? $product->array_options[
'options_'.$key] :
'';
514 $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.
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)
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.