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.
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)
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.