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);
399 // set editor in readonly
400 if (CKEDITOR.instances[htmlname]) {
401 CKEDITOR.instances[htmlname].setReadOnly(1);
404 console.log('Call generate_content.php');
406 url: '". DOL_URL_ROOT.
"/ai/ajax/generate_content.php?token=".
currentToken().
"',
408 contentType: 'application/json',
409 data: JSON.stringify({
410 'format': format, /* the format for output */
411 'function': aifunction, /* the AI feature to call */
412 'instructions': instructions, /* the prompt string */
414 success: function(response) {
415 console.log('Add response into field \'#'+htmlname+'\': '+response);
417 jQuery('#'+htmlname).val(response); // If #htmlcontent is a input name or textarea
418 jQuery('#'+htmlname).html(response).trigger('change'); // If #htmlContent is a div and trigger event change for extrafield update
419 //jQuery('#'+htmlname+'preview').val(response);
421 if (CKEDITOR.instances) {
422 var editorInstance = CKEDITOR.instances[htmlname];
423 if (editorInstance) {
424 editorInstance.setReadOnly(0);
425 editorInstance.setData(response);
427 //var editorInstancepreview = CKEDITOR.instances[htmlname+'preview'];
428 //if (editorInstancepreview) {
429 // editorInstancepreview.setData(response);
433 // Remove all value from Ai Section select
434 $('#ai_instructions'+htmlname).val('');
435 $('#ai_translation'+htmlname+'_select').val('-1');
436 $('#ai_translation'+htmlname+'_select').trigger('change');
437 $('#ai_summarize'+htmlname+'_select').val('-1');
438 $('#ai_summarize'+htmlname+'_select').trigger('change');
439 $('#ai_rephraser'+htmlname+'_select').val('-1');
440 $('#ai_rephraser'+htmlname+'_select').trigger('change');
441 $('#ai_status_message'+htmlname).hide();
442 $('#ai_dropdown'+htmlname).hide();
444 error: function(xhr, status, error) {
446 console.log('error ajax ', status, error);
447 /* $('#ai_status_message'+htmlname).hide(); */
448 if (xhr.responseText) {
449 $('#ai_status_message'+htmlname).val(xhr.responseText);
450 $('#ai_status_message'+htmlname).html(xhr.responseText);
452 $('#ai_status_message'+htmlname).val(error);
453 $('#ai_status_message'+htmlname).html(error);
461 $this->aicallfunctioncalled =
true;
477 $parameters = array();
478 $tmparray = getCommonSubstitutionArray($outputlangs, 0,
null,
$object);
481 $this->substit = $tmparray;
484 if (is_array(
$object->lines)) {
485 foreach (
$object->lines as $line) {
486 $substit_line = array(
487 '__PRODUCT_REF__' => isset($line->product_ref) ? $line->product_ref :
'',
488 '__PRODUCT_LABEL__' => isset($line->product_label) ? $line->product_label :
'',
489 '__PRODUCT_DESCRIPTION__' => isset($line->product_desc) ? $line->product_desc :
'',
490 '__LABEL__' => isset($line->label) ? $line->label :
'',
491 '__DESCRIPTION__' => isset($line->desc) ? $line->desc :
'',
492 '__DATE_START_YMD__' =>
dol_print_date($line->date_start,
'day',
false, $outputlangs),
493 '__DATE_END_YMD__' =>
dol_print_date($line->date_end,
'day',
false, $outputlangs),
494 '__QUANTITY__' => $line->qty,
495 '__SUBPRICE__' =>
price($line->subprice),
496 '__AMOUNT__' =>
price($line->total_ttc),
497 '__AMOUNT_EXCL_TAX__' =>
price($line->total_ht)
501 if (!empty($line->fk_product)) {
502 if (!is_object($extrafields)) {
505 $product =
new Product($this->db);
506 $product->fetch($line->fk_product);
507 $product->fetch_optionals();
509 $extrafields->fetch_name_optionals_label($product->table_element,
true);
511 if (!empty($extrafields->attributes[$product->table_element][
'label']) && is_array($extrafields->attributes[$product->table_element][
'label']) && count($extrafields->attributes[$product->table_element][
'label']) > 0) {
512 foreach ($extrafields->attributes[$product->table_element][
'label'] as $key => $label) {
513 $substit_line[
'__PRODUCT_EXTRAFIELD_'.strtoupper($key).
'__'] = isset($product->array_options[
'options_'.$key]) ? $product->array_options[
'options_'.$key] :
'';
518 $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.