mirror of
https://github.com/Dolibarr/dolibarr.git
synced 2025-12-07 01:58:09 +01:00
438 lines
16 KiB
PHP
438 lines
16 KiB
PHP
<?php
|
|
/* Copyright (C) 2005-2012 Laurent Destailleur <eldy@users.sourceforge.net>
|
|
* Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
|
|
* Copyright (C) 2010-2011 Juanjo Menent <jmenent@2byte.es>
|
|
* Copyright (C) 2015-2017 Marcos García <marcosgdf@gmail.com>
|
|
* Copyright (C) 2015-2017 Nicolas ZABOURI <info@inovea-conseil.com>
|
|
* Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
|
|
* Copyright (C) 2022 Charlene Benke <charlene@patas-monkey.com>
|
|
* Copyright (C) 2023 Anthony Berton <anthony.berton@bb2a.fr>
|
|
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* \file htdocs/core/class/html.formai.class.php
|
|
* \ingroup core
|
|
* \brief Fichier de la class permettant la generation du formulaire html d'envoi de mail unitaire
|
|
*/
|
|
require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
|
|
|
|
|
|
/**
|
|
* Class permettant la generation du formulaire html d'envoi de mail unitaire
|
|
* Usage: $formail = new FormAI($db)
|
|
* $formai->proprietes=1 ou chaine ou tableau de valeurs
|
|
* $formai->show_form() affiche le formulaire
|
|
*/
|
|
class FormAI extends Form
|
|
{
|
|
/**
|
|
* @var DoliDB Database handler.
|
|
*/
|
|
public $db;
|
|
|
|
/**
|
|
* @var string Use case string to a button "Fill with layout" for this use case. Example 'wesitepage', 'emailing', 'email', ...
|
|
*/
|
|
public $withlayout;
|
|
|
|
/**
|
|
* @var string 'text' or 'html' to add a button "Fill with AI generation"
|
|
*/
|
|
public $withaiprompt;
|
|
|
|
/**
|
|
* @var array<string,string>
|
|
*/
|
|
public $substit = array();
|
|
|
|
/**
|
|
* @var array<int,array<string,string>>
|
|
*/
|
|
public $substit_lines = array();
|
|
|
|
/**
|
|
* @var array{}|array{models:string,langsmodels?:string,fileinit?:string[],returnurl:string}
|
|
*/
|
|
public $param = array();
|
|
|
|
/**
|
|
* @var int<-1,1> -1 suggests the checkbox 'one email per recipient' not checked, 0 = no suggestion, 1 = suggest and checked
|
|
*/
|
|
public $withoptiononeemailperrecipient;
|
|
|
|
/**
|
|
* @var bool
|
|
*/
|
|
public $aicallfunctioncalled = false;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param DoliDB $db Database handler
|
|
*/
|
|
public function __construct($db)
|
|
{
|
|
$this->db = $db;
|
|
}
|
|
|
|
/**
|
|
* Return Html code for AI instruction of message and autofill result
|
|
*
|
|
* @param string $function Function ('textgenerationmail', 'textgenerationwebpage', ...)
|
|
* @param string $format Format for output ('', 'html', ...)
|
|
* @param string $htmlContent HTML name of WYSIWYG field
|
|
* @return string HTML code to ask AI instruction and autofill result
|
|
*/
|
|
public function getSectionForAIPrompt($function = 'textgeneration', $format = '', $htmlContent = 'message')
|
|
{
|
|
global $langs;
|
|
|
|
$langs->load("other");
|
|
|
|
$htmlContent = preg_replace('/[^a-z0-9_]/', '', $htmlContent);
|
|
|
|
$out = '<div id="ai_input'.$htmlContent.'" class="ai_input'.$htmlContent.' hidden paddingtop paddingbottom">';
|
|
$out .= '<input type="text" class="quatrevingtpercent" id="ai_instructions'.$htmlContent.'" name="instruction" placeholder="'.$langs->trans("EnterYourAIPromptHere").'..." />';
|
|
$out .= '<input id="generate_button'.$htmlContent.'" type="button" class="button smallpaddingimp" value="'.$langs->trans('Generate').'"/>';
|
|
$out .= '<div id="ai_status_message'.$htmlContent.'" class="fieldrequired hideobject marginrightonly margintoponly">';
|
|
$out .= '<i class="fa fa-spinner fa-spin fa-2x fa-fw valignmiddle marginrightonly"></i>'.$langs->trans("AIProcessingPleaseWait", getDolGlobalString('AI_API_SERVICE', 'chatgpt'));
|
|
$out .= '</div>';
|
|
|
|
if ($function == 'imagegeneration') {
|
|
$out .= '<div id="ai_image_result" class="margintoponly"></div>'; // Div for displaying the generated image
|
|
}
|
|
|
|
$out .= "</div>\n";
|
|
$out .= "<script type='text/javascript'>
|
|
$(document).ready(function() {
|
|
// for keydown
|
|
$('#ai_instructions".$htmlContent."').keydown(function(event) {
|
|
if (event.keyCode === 13) {
|
|
event.preventDefault();
|
|
$('#generate_button".$htmlContent."').click();
|
|
}
|
|
});
|
|
|
|
$('#generate_button".$htmlContent."').click(function() {
|
|
console.log('We click on generate_button".$htmlContent." ai button, so we make an ajax on url /ai/ajax/generate_content.php');
|
|
|
|
var instructions = $('#ai_instructions".$htmlContent."').val();
|
|
var timeoutfinished = 0;
|
|
var apicallfinished = 0;
|
|
htmlname = '".$htmlContent."';
|
|
format = '".dol_escape_js($format)."';
|
|
functionai = '".dol_escape_js($function)."';
|
|
|
|
$('#ai_status_message".$htmlContent."').show();
|
|
$('.icon-container .loader').show();
|
|
setTimeout(function() {
|
|
timeoutfinished = 1;
|
|
$('#ai_status_message".$htmlContent."').hide();
|
|
}, 30000);
|
|
callAIGenerator(functionai, instructions, format, htmlname);
|
|
});
|
|
});
|
|
</script>
|
|
";
|
|
return $out;
|
|
}
|
|
|
|
/**
|
|
* Return javascript code for call to AI function callAIGenerator()
|
|
*
|
|
* @return string HTML code to ask AI instruction and autofill result
|
|
*/
|
|
public function getAjaxAICallFunction()
|
|
{
|
|
$out = "";
|
|
if ($this->aicallfunctioncalled) {
|
|
return $out;
|
|
}
|
|
|
|
$out .= "
|
|
<script>
|
|
function callAIGenerator(aifunction, instructions, format, htmlname){
|
|
if (aifunction === 'imagegeneration') {
|
|
// Handle image generation request
|
|
$.ajax({
|
|
url: '". DOL_URL_ROOT."/ai/ajax/generate_content.php?token=".currentToken()."',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify({
|
|
'format': format, /* the format for output */
|
|
'function': aifunction, /* the AI feature to call */
|
|
'instructions': instructions, /* the prompt string */
|
|
}),
|
|
success: function(response) {
|
|
console.log('Received image URL: '+response);
|
|
|
|
// make substitutions
|
|
let substit = ". json_encode($this->substit).";
|
|
for (let key in substit) {
|
|
if (substit.hasOwnProperty(key)) {
|
|
// Replace the placeholder with its corresponding value
|
|
response = response.replace(key, substit[key]);
|
|
}
|
|
}
|
|
|
|
// Assuming response is the URL of the generated image
|
|
var imageUrl = response;
|
|
$('#ai_image_result').html('<img src=\"' + imageUrl + '\" alt=\"Generated Image\" />');
|
|
|
|
// Clear the input field
|
|
$('#ai_instructions').val('');
|
|
|
|
apicallfinished = 1;
|
|
if (timeoutfinished) {
|
|
$('#ai_status_message').hide();
|
|
}
|
|
},
|
|
error: function(xhr, status, error) {
|
|
alert(error);
|
|
console.error('error ajax', status, error);
|
|
$('#ai_status_message').hide();
|
|
}
|
|
});
|
|
} else {
|
|
|
|
// set editor in readonly
|
|
if (CKEDITOR.instances[htmlname]) {
|
|
CKEDITOR.instances[htmlname].setReadOnly(1);
|
|
}
|
|
|
|
$.ajax({
|
|
url: '". DOL_URL_ROOT."/ai/ajax/generate_content.php?token=".currentToken()."',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify({
|
|
'format': format, /* the format for output */
|
|
'function': aifunction, /* the AI feature to call */
|
|
'instructions': instructions, /* the prompt string */
|
|
}),
|
|
success: function(response) {
|
|
console.log('Add response into field \'#'+htmlname+'\': '+response);
|
|
|
|
jQuery('#'+htmlname).val(response); // If #htmlcontent is a input name or textarea
|
|
jQuery('#'+htmlname).html(response); // If #htmlContent is a div
|
|
//jQuery('#'+htmlname+'preview').val(response);
|
|
|
|
if (CKEDITOR.instances) {
|
|
var editorInstance = CKEDITOR.instances[htmlname];
|
|
if (editorInstance) {
|
|
editorInstance.setReadOnly(0);
|
|
editorInstance.setData(response);
|
|
}
|
|
//var editorInstancepreview = CKEDITOR.instances[htmlname+'preview'];
|
|
//if (editorInstancepreview) {
|
|
// editorInstancepreview.setData(response);
|
|
//}
|
|
}
|
|
|
|
// remove readonly
|
|
$('#ai_instructions'+htmlname).val('');
|
|
$('#ai_status_message'+htmlname).hide();
|
|
},
|
|
error: function(xhr, status, error) {
|
|
alert(error);
|
|
console.error('error ajax', status, error);
|
|
$('#ai_status_message'+htmlname).hide();
|
|
}
|
|
|
|
});
|
|
}
|
|
}
|
|
</script>";
|
|
$this->aicallfunctioncalled = true;
|
|
return $out;
|
|
}
|
|
|
|
/**
|
|
* Set ->substit (and ->substit_line) array from object. This is call when suggesting the email template into forms before sending email.
|
|
*
|
|
* @param CommonObject $object Object to use
|
|
* @param Translate $outputlangs Object lang
|
|
* @return void
|
|
* @see getCommonSubstitutionArray()
|
|
*/
|
|
public function setSubstitFromObject($object, $outputlangs)
|
|
{
|
|
global $extrafields;
|
|
|
|
$parameters = array();
|
|
$tmparray = getCommonSubstitutionArray($outputlangs, 0, null, $object);
|
|
complete_substitutions_array($tmparray, $outputlangs, null, $parameters);
|
|
|
|
$this->substit = $tmparray;
|
|
|
|
// Fill substit_lines with each object lines content
|
|
if (is_array($object->lines)) {
|
|
foreach ($object->lines as $line) {
|
|
$substit_line = array(
|
|
'__PRODUCT_REF__' => isset($line->product_ref) ? $line->product_ref : '',
|
|
'__PRODUCT_LABEL__' => isset($line->product_label) ? $line->product_label : '',
|
|
'__PRODUCT_DESCRIPTION__' => isset($line->product_desc) ? $line->product_desc : '',
|
|
'__LABEL__' => isset($line->label) ? $line->label : '',
|
|
'__DESCRIPTION__' => isset($line->desc) ? $line->desc : '',
|
|
'__DATE_START_YMD__' => dol_print_date($line->date_start, 'day', false, $outputlangs),
|
|
'__DATE_END_YMD__' => dol_print_date($line->date_end, 'day', false, $outputlangs),
|
|
'__QUANTITY__' => $line->qty,
|
|
'__SUBPRICE__' => price($line->subprice),
|
|
'__AMOUNT__' => price($line->total_ttc),
|
|
'__AMOUNT_EXCL_TAX__' => price($line->total_ht)
|
|
);
|
|
|
|
// Create dynamic tags for __PRODUCT_EXTRAFIELD_FIELD__
|
|
if (!empty($line->fk_product)) {
|
|
if (!is_object($extrafields)) {
|
|
$extrafields = new ExtraFields($this->db);
|
|
}
|
|
$product = new Product($this->db);
|
|
$product->fetch($line->fk_product);
|
|
$product->fetch_optionals();
|
|
|
|
$extrafields->fetch_name_optionals_label($product->table_element, true);
|
|
|
|
if (!empty($extrafields->attributes[$product->table_element]['label']) && is_array($extrafields->attributes[$product->table_element]['label']) && count($extrafields->attributes[$product->table_element]['label']) > 0) {
|
|
foreach ($extrafields->attributes[$product->table_element]['label'] as $key => $label) {
|
|
$substit_line['__PRODUCT_EXTRAFIELD_'.strtoupper($key).'__'] = isset($product->array_options['options_'.$key]) ? $product->array_options['options_'.$key] : '';
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->substit_lines[$line->id] = $substit_line; // @phan-suppress-current-line PhanTypeMismatchProperty
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get list of substitution keys available for emails. This is used for tooltips help.
|
|
* This include the complete_substitutions_array.
|
|
*
|
|
* @param string $mode 'formai', 'formaiwithlines', 'formaiforlines', 'ai', ...
|
|
* @param ?Object $object Object if applicable
|
|
* @return array<string,string> Array of substitution values for emails.
|
|
*/
|
|
public static function getAvailableSubstitKey($mode = 'formai', $object = null)
|
|
{
|
|
global $langs;
|
|
|
|
$tmparray = array();
|
|
if ($mode == 'formai' || $mode == 'formaiwithlines' || $mode == 'formaiforlines') {
|
|
$parameters = array('mode' => $mode);
|
|
$tmparray = getCommonSubstitutionArray($langs, 2, null, $object); // Note: On email templated edition, this is null because it is related to all type of objects
|
|
complete_substitutions_array($tmparray, $langs, null, $parameters);
|
|
|
|
if ($mode == 'formwithlines') {
|
|
$tmparray['__LINES__'] = '__LINES__'; // Will be set by the get_form function
|
|
}
|
|
if ($mode == 'formforlines') {
|
|
$tmparray['__QUANTITY__'] = '__QUANTITY__'; // Will be set by the get_form function
|
|
}
|
|
}
|
|
|
|
if ($mode == 'emailing') {
|
|
$parameters = array('mode' => $mode);
|
|
$tmparray = getCommonSubstitutionArray($langs, 2, array('object', 'objectamount'), $object); // Note: On email templated edition, this is null because it is related to all type of objects
|
|
complete_substitutions_array($tmparray, $langs, null, $parameters);
|
|
|
|
// For mass emailing, we have different keys specific to the data into tagerts list
|
|
$tmparray['__ID__'] = 'IdRecord';
|
|
$tmparray['__THIRDPARTY_CUSTOMER_CODE__'] = 'CustomerCode';
|
|
$tmparray['__EMAIL__'] = 'EMailRecipient';
|
|
$tmparray['__LASTNAME__'] = 'Lastname';
|
|
$tmparray['__FIRSTNAME__'] = 'Firstname';
|
|
$tmparray['__MAILTOEMAIL__'] = 'TagMailtoEmail';
|
|
$tmparray['__OTHER1__'] = 'Other1';
|
|
$tmparray['__OTHER2__'] = 'Other2';
|
|
$tmparray['__OTHER3__'] = 'Other3';
|
|
$tmparray['__OTHER4__'] = 'Other4';
|
|
$tmparray['__OTHER5__'] = 'Other5';
|
|
$tmparray['__CHECK_READ__'] = $langs->trans('TagCheckMail');
|
|
$tmparray['__UNSUBSCRIBE__'] = $langs->trans('TagUnsubscribe');
|
|
$tmparray['__UNSUBSCRIBE_URL__'] = $langs->trans('TagUnsubscribe').' (URL)';
|
|
|
|
$onlinepaymentenabled = 0;
|
|
if (isModEnabled('paypal')) {
|
|
$onlinepaymentenabled++;
|
|
}
|
|
if (isModEnabled('paybox')) {
|
|
$onlinepaymentenabled++;
|
|
}
|
|
if (isModEnabled('stripe')) {
|
|
$onlinepaymentenabled++;
|
|
}
|
|
if ($onlinepaymentenabled && getDolGlobalString('PAYMENT_SECURITY_TOKEN')) {
|
|
$tmparray['__SECUREKEYPAYMENT__'] = getDolGlobalString('PAYMENT_SECURITY_TOKEN');
|
|
if (getDolGlobalString('PAYMENT_SECURITY_TOKEN_UNIQUE')) {
|
|
if (isModEnabled('member')) {
|
|
$tmparray['__SECUREKEYPAYMENT_MEMBER__'] = 'SecureKeyPAYMENTUniquePerMember';
|
|
}
|
|
if (isModEnabled('don')) {
|
|
$tmparray['__SECUREKEYPAYMENT_DONATION__'] = 'SecureKeyPAYMENTUniquePerDonation';
|
|
}
|
|
if (isModEnabled('invoice')) {
|
|
$tmparray['__SECUREKEYPAYMENT_INVOICE__'] = 'SecureKeyPAYMENTUniquePerInvoice';
|
|
}
|
|
if (isModEnabled('order')) {
|
|
$tmparray['__SECUREKEYPAYMENT_ORDER__'] = 'SecureKeyPAYMENTUniquePerOrder';
|
|
}
|
|
if (isModEnabled('contract')) {
|
|
$tmparray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'SecureKeyPAYMENTUniquePerContractLine';
|
|
}
|
|
|
|
//Online payment link
|
|
if (isModEnabled('member')) {
|
|
$tmparray['__ONLINEPAYMENTLINK_MEMBER__'] = 'OnlinePaymentLinkUniquePerMember';
|
|
}
|
|
if (isModEnabled('don')) {
|
|
$tmparray['__ONLINEPAYMENTLINK_DONATION__'] = 'OnlinePaymentLinkUniquePerDonation';
|
|
}
|
|
if (isModEnabled('invoice')) {
|
|
$tmparray['__ONLINEPAYMENTLINK_INVOICE__'] = 'OnlinePaymentLinkUniquePerInvoice';
|
|
}
|
|
if (isModEnabled('order')) {
|
|
$tmparray['__ONLINEPAYMENTLINK_ORDER__'] = 'OnlinePaymentLinkUniquePerOrder';
|
|
}
|
|
if (isModEnabled('contract')) {
|
|
$tmparray['__ONLINEPAYMENTLINK_CONTRACTLINE__'] = 'OnlinePaymentLinkUniquePerContractLine';
|
|
}
|
|
}
|
|
} else {
|
|
/* No need to show into tooltip help, option is not enabled
|
|
$vars['__SECUREKEYPAYMENT__']='';
|
|
$vars['__SECUREKEYPAYMENT_MEMBER__']='';
|
|
$vars['__SECUREKEYPAYMENT_INVOICE__']='';
|
|
$vars['__SECUREKEYPAYMENT_ORDER__']='';
|
|
$vars['__SECUREKEYPAYMENT_CONTRACTLINE__']='';
|
|
*/
|
|
}
|
|
if (getDolGlobalString('MEMBER_ENABLE_PUBLIC')) {
|
|
$tmparray['__PUBLICLINK_NEWMEMBERFORM__'] = 'BlankSubscriptionForm';
|
|
}
|
|
}
|
|
|
|
foreach ($tmparray as $key => $val) {
|
|
if (empty($val)) {
|
|
$tmparray[$key] = $key;
|
|
}
|
|
}
|
|
|
|
return $tmparray;
|
|
}
|
|
}
|