diff --git a/ChangeLog b/ChangeLog index fe5dfbf8b82..c246f9528e5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -238,6 +238,109 @@ The following changes may create regressions for some external modules, but were * If you were using the substitution key __MEMBER_CIVILITY__, you must now use __MEMBER_TITLE__ +***** ChangeLog for 20.0.4 compared to 20.0.2 ***** +FIX: $this->origin_object can not be instance of CommandeFournisseur if it is already an instanceof CommonObject +FIX: 17.0 API endpoints "PUT": prevent overwriting all extrafields if only some are supplied in the request cf. PR #29237 +FIX: 17.0 - collisions in cache for dol_getIdFromCode +FIX: #18713 +FIX: 20.0 - PHP8 fatal when creating a reception unless corresponding PDF model is enabled +FIX: #21294 Stock import sql query +FIX: #26250 fatal error on kit +FIX: #28702 +FIX: #29624 - substitution of __DATE_DELIVERY__ +FIX: #32113 +FIX: #32186 +FIX: #32339 Delete a loan settlement is partial +FIX: #32387 +FIX: #32477 Loan - Insurance amount need decimals +FIX: #32611 +FIX: #32736 + avoid php warning +FIX: #32743 +FIX: #32765 JS Error: Uncaught TypeError +FIX: #32801 VAT type is inverted in form VAT selector +FIX: #32840 +FIX: #32843 +FIX: #32880 - Tags are using a special rendering. +FIX: add other fields +FIX: autofill price with multicurrency on supplier doc +FIX: avoid phan error +FIX: avoid php8 warnings +FIX: avoid warning with the new Dolistore website +FIX: backport from develop to avoid php warning +FIX: Bad calculation of the theoretical stock. Did not take into account +FIX: bad dispatched quantities for batches on shipment card +FIX: Brian is in the kitchen +FIX: broken feature, compatibility with "Default search filters" +FIX: broken feature with check $pa_ht_isemptystring +FIX: Bug on select user on time.php (all project list) +FIX: can not delete files in task card +FIX: Check "$search_sale" only if it's an internal user +FIX: clean unique extrafields when create product combination +FIX: code not visible correctly into view of dictionary +FIX: compatibility between next_prev_filter and hook return +FIX: compatibility with multicompany +FIX: Complete path was started in #17243 for pdf_cannelle +FIX: Continue for eagle_proforma +FIX: country id is not saved when we provide country_code only +FIX: #CVE-2024-34051 +FIX: delete supplier order when at least one line linked to customer order line +FIX: display error when loan can't be deleted +FIX: display full tree on shipment card when a kit contains a same component in other sub-kit +FIX: DROP INDEX IF EXISTS is not possible ! +FIX: extrafields lost during creation from rec invoice +FIX: FEC import +FIX: Fiscal year - missing translation on status +FIX: Fix return value of hook sendMail when hook return -1 who must be return false in sendfile() function +FIX: GETPOST('private_message') +FIX: glob is better for search files with wildcard + avoid warning +FIX: if $force_entity = 0 ($force_entity != 'default') = false +FIX: Loan - Insurance amount need decimals +FIX: Many status on invoice linked object block +FIX: merge problem +FIX: missing company name if dontaion is linked to third party +FIX: missing default values if $objsrc or $soc fields are empty +FIX: missing edit extrafields inline for member card +FIX: missing quick edit for extrafields +FIX: more bugs and warnings +FIX: Multilangs : PDF lines description +FIX: Must not have both thirdparty and member. +FIX: ODT substitution when many HTML tags in string +FIX: on the road again +FIX: pdf_cannelle (supplier_invoice) add background - Complete #17243 +FIX: Prices didn't update when clone a propale with update prices +FIX: product variants copy: also copy multiprice variations +FIX: refactorize (maybe broken feature for not received completely) +FIX: remove debug trace +FIX: remove socid when cloning a project without third parties +FIX: removes traces of <<"rpmbuild", "RPM_MANDRIVA"=>"rpmbuild", "RPM_OPENSUSE"=>"rpmbuild", -"DEB"=>"dpkg", +"DEB"=>"dpkg,po2debconf", # need also debhelper "FLATPACK"=>"flatpack", "EXEDOLIWAMP"=>"ISCC.exe", "SNAPSHOT"=>"tar" @@ -85,8 +85,9 @@ if (! $ENV{"DESTIBETARC"} || ! $ENV{"DESTISTABLE"}) print "set DESTIBETARC=c:/tmp\n"; print "set DESTISTABLE=c:/tmp\n"; print "\n"; - print "Example: DESTIBETARC='/media/HDDATA1_LD/Mes Sites/Web/Dolibarr/dolibarr.org/files/lastbuild'\n"; - print "Example: DESTISTABLE='/media/HDDATA1_LD/Mes Sites/Web/Dolibarr/dolibarr.org/files/stable'\n"; + print "Example in .bashrc:\n"; + print "export DESTIBETARC='/mnt/HDDATA1_LD/Mes Archives/Doli/dolibarr/lastbuild'\n"; + print "export DESTISTABLE='/mnt/HDDATA1_LD/Mes Archives/Doli/dolibarr/stable'\n"; sleep 2; exit 1; } diff --git a/dev/setup/pre-commit/README.md b/dev/setup/pre-commit/README.md index 71318703b91..4c32111e5e9 100644 --- a/dev/setup/pre-commit/README.md +++ b/dev/setup/pre-commit/README.md @@ -44,8 +44,22 @@ the project: `pre-commit-config.yaml`. The good file redirects output to the error channel so your IDE will be able to catch the error. +### Troubleshooting + +* If you get error "ModuleNotFoundError: No module named 'platformdirs'" + +Install the python package with +`pip3 install platformdirs` or `pip3 install platformdirs --break-system-packages` + +* If you get error "ERROR: PHP_CodeSniffer requires the tokenizer, xmlwriter and SimpleXML extensions to be enabled. Please enable xmlwriter and SimpleXML." + +Install the PHP package xml +`sudo apt install php-simplexml` + + ### Tips + After installing `pre-commit` onto your local git clone, pre-commit will run on every commit. The first time, all tools required by pre-commit will be installed into ~/.cache/pre-commit diff --git a/htdocs/accountancy/bookkeeping/balance.php b/htdocs/accountancy/bookkeeping/balance.php index ab17d51de09..5daa02a7364 100644 --- a/htdocs/accountancy/bookkeeping/balance.php +++ b/htdocs/accountancy/bookkeeping/balance.php @@ -100,14 +100,14 @@ $formother = new FormOther($db); $form = new Form($db); if (empty($search_date_start) && !GETPOSTISSET('formfilteraction')) { - $sql = "SELECT date_start, date_end"; - $sql .=" FROM ".MAIN_DB_PREFIX."accounting_fiscalyear "; - if (getDolGlobalInt('ACCOUNTANCY_FISCALYEAR_DEFAULT')) { - $sql .= " WHERE rowid = " . getDolGlobalInt('ACCOUNTANCY_FISCALYEAR_DEFAULT'); - } else { - $sql .= " WHERE date_start < '" . $db->idate(dol_now()) . "' and date_end > '" . $db->idate(dol_now()) . "'"; - } - $sql .= $db->plimit(1); + $sql = "SELECT date_start, date_end"; + $sql .=" FROM ".MAIN_DB_PREFIX."accounting_fiscalyear "; + if (getDolGlobalInt('ACCOUNTANCY_FISCALYEAR_DEFAULT')) { + $sql .= " WHERE rowid = " . getDolGlobalInt('ACCOUNTANCY_FISCALYEAR_DEFAULT'); + } else { + $sql .= " WHERE date_start < '" . $db->idate(dol_now()) . "' and date_end > '" . $db->idate(dol_now()) . "'"; + } + $sql .= $db->plimit(1); $res = $db->query($sql); if ($db->num_rows($res) > 0) { diff --git a/htdocs/accountancy/bookkeeping/list.php b/htdocs/accountancy/bookkeeping/list.php index 67d62117ba2..9821c434329 100644 --- a/htdocs/accountancy/bookkeeping/list.php +++ b/htdocs/accountancy/bookkeeping/list.php @@ -32,7 +32,6 @@ require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'; -require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfiscalyear.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; @@ -881,14 +880,6 @@ if ($massactionbutton && $contextpage != 'poslist') { $selectedfields .= $form->showCheckAddButtons('checkforselect', 1); } -if (getDolGlobalInt('ACCOUNTANCY_FISCALYEAR_DEFAULT')) { - $fiscalYear = getDolGlobalInt('ACCOUNTANCY_FISCALYEAR_DEFAULT'); - $useempty = 0; -} else { - $fiscalYear = 0; - $useempty = 1; -} - $moreforfilter = ''; $moreforfilter .= '
'; $moreforfilter .= $langs->trans('AccountingCategory').': '; diff --git a/htdocs/ai/admin/custom_prompt.php b/htdocs/ai/admin/custom_prompt.php index c127674c744..83753c01a7a 100644 --- a/htdocs/ai/admin/custom_prompt.php +++ b/htdocs/ai/admin/custom_prompt.php @@ -27,7 +27,7 @@ // Load Dolibarr environment require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php"; -require_once '../lib/ai.lib.php'; +require_once DOL_DOCUMENT_ROOT."/ai/lib/ai.lib.php"; /** * @var Conf $conf @@ -39,6 +39,8 @@ require_once '../lib/ai.lib.php'; $langs->loadLangs(array("admin", "website", "other")); +$arrayofaifeatures = getLitOfAIFeatures(); + // Parameters $action = GETPOST('action', 'aZ09'); $backtopage = GETPOST('backtopage', 'alpha'); @@ -81,18 +83,6 @@ $setupnotempty += count($formSetup->items); $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); -// List of AI features -$arrayofaifeatures = array( - 'textgenerationemail' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("EmailContent").')', 'picto'=>'', 'status'=>'development'), - 'textgenerationwebpage' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("WebsitePage").')', 'picto'=>'', 'status'=>'development'), - 'textgeneration' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("Other").')', 'picto'=>'', 'status'=>'notused'), - 'imagegeneration' => array('label' => 'ImageGeneration', 'picto'=>'', 'status'=>'notused'), - 'videogeneration' => array('label' => 'VideoGeneration', 'picto'=>'', 'status'=>'notused'), - 'audiogeneration' => array('label' => 'AudioGeneration', 'picto'=>'', 'status'=>'notused'), - 'transcription' => array('label' => 'Transcription', 'picto'=>'', 'status'=>'notused'), - 'translation' => array('label' => 'Translation', 'picto'=>'', 'status'=>'notused') -); - /* * Actions @@ -213,7 +203,7 @@ print load_fiche_titre($langs->trans($title), $linkback, 'title_setup'); // Configuration header $head = aiAdminPrepareHead(); -print dol_get_fiche_head($head, 'custom', $langs->trans($title), -1, "fa-microchip"); +print dol_get_fiche_head($head, 'custom', $langs->trans($title), -1, "ai"); //$newbutton = ''.$langs->trans("New").''; $newbutton = ''; @@ -279,6 +269,7 @@ if ($action == 'edit' || $action == 'deleteproperty') { $out .= ''; $out .= ''; + $out .= ''; $out .= ''; $out .= ''; @@ -314,6 +305,7 @@ if ($action == 'edit' || $action == 'deleteproperty') { $out .= $form->buttonsSaveCancel("Add", ""); $out .= ''; + $out .= '


'; print $out; @@ -374,7 +366,7 @@ if ($action == 'edit' || $action == 'create' || $action == 'deleteproperty') { $out .= ''; $out .= ''; $out .= ''; - $out .= ''; + $out .= ''; $out .= '   '; include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; @@ -383,6 +375,8 @@ if ($action == 'edit' || $action == 'create' || $action == 'deleteproperty') { $formmail = new FormMail($db); $htmlname = $key; + $out .= '

'; + // Fill $out include DOL_DOCUMENT_ROOT.'/core/tpl/formlayoutai.tpl.php'; diff --git a/htdocs/ai/admin/setup.php b/htdocs/ai/admin/setup.php index 8df3b7bd9e2..2e04f1d2f2d 100644 --- a/htdocs/ai/admin/setup.php +++ b/htdocs/ai/admin/setup.php @@ -28,7 +28,8 @@ // Load Dolibarr environment require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php"; -require_once '../lib/ai.lib.php'; +require_once DOL_DOCUMENT_ROOT."/core/class/doleditor.class.php"; +require_once DOL_DOCUMENT_ROOT."/ai/lib/ai.lib.php"; /** * @var Conf $conf @@ -38,7 +39,9 @@ require_once '../lib/ai.lib.php'; * @var User $user */ -$langs->loadLangs(array("admin")); +$langs->loadLangs(array("admin", "website", "other")); + +$arrayofaifeatures = getLitOfAIFeatures(); // Parameters $action = GETPOST('action', 'aZ09'); @@ -49,10 +52,7 @@ if (empty($action)) { $action = 'edit'; } -$value = GETPOST('value', 'alpha'); -$label = GETPOST('label', 'alpha'); -$scandir = GETPOST('scan_dir', 'alpha'); -$type = 'myobject'; +$content = GETPOST('content'); $error = 0; $setupnotempty = 0; @@ -69,6 +69,7 @@ $formSetup = new FormSetup($db); // List all available IA $arrayofia = array( + '-1' => $langs->trans('SelectAService'), 'chatgpt' => 'ChatGPT', 'groq' => 'Groq', 'custom' => 'Custom' @@ -89,14 +90,14 @@ foreach ($arrayofia as $ia => $ialabel) { $item->nameText = $langs->trans("AI_API_KEY").' ('.$ialabel.')'; $item->defaultFieldValue = ''; $item->fieldParams['hideGenerateButton'] = 1; - $item->fieldParams['trClass'] = $ia; - $item->cssClass = 'minwidth500 text-security'; + $item->fieldParams['trClass'] = 'iaservice '.$ia; + $item->cssClass = 'minwidth500 text-security input'.$ia; $item = $formSetup->newItem('AI_API_'.strtoupper($ia).'_URL'); // Name of constant must end with _KEY so it is encrypted when saved into database. $item->nameText = $langs->trans("AI_API_URL").' ('.$ialabel.')'; $item->defaultFieldValue = ''; - $item->fieldParams['trClass'] = $ia; - $item->cssClass = 'minwidth500'; + $item->fieldParams['trClass'] = 'iaservice '.$ia; + $item->cssClass = 'minwidth500 input'.$ia; } $setupnotempty = + count($formSetup->items); @@ -140,7 +141,7 @@ print load_fiche_titre($langs->trans($title), $linkback, 'title_setup'); // Configuration header $head = aiAdminPrepareHead(); -print dol_get_fiche_head($head, 'settings', $langs->trans($title), -1, "fa-microchip"); +print dol_get_fiche_head($head, 'settings', $langs->trans($title), -1, "ai"); if ($action == 'edit') { @@ -160,8 +161,86 @@ if (empty($setupnotempty)) { print '
'.$langs->trans("NothingToSetup"); } +print ''; + // Page end print dol_get_fiche_end(); + +if (getDolGlobalString("AI_API_SERVICE")) { + // Section to test + print '
'; + print ''; + print ''; + print ''; + + $functioncode = GETPOST('functioncode'); + $out = ''; + + if ($functioncode) { + $labeloffeature = empty($arrayofaifeatures[GETPOST('functioncode')]['label']) ? 'Undefined' : $arrayofaifeatures[GETPOST('functioncode')]['label']; + + //$out .= $langs->trans("Test").' '.$labeloffeature.'...

'; + + if (GETPOST('functioncode') == 'textgenerationemail') { + $key = 'textgenerationemail'; // The HTML ID of field to fill + + include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; + $showlinktoai = $key; // 'textgeneration', 'imagegeneration', ... + $showlinktoailabel = $langs->trans("Test").' '.$labeloffeature; + $showlinktolayout = 0; + $formmail = new FormMail($db); + $htmlname = $key; + + // Fill $out + include DOL_DOCUMENT_ROOT.'/core/tpl/formlayoutai.tpl.php'; + + $out .= '
'; + } else { + $out .= $langs->trans("FeatureNotYetAvailable").'

'; + $functioncode = ''; + } + } + + if (!$functioncode) { + // Combo list of AI features + $out .= ''; + $out .= ajax_combobox("functioncode"); + + $out .= ''; + } + print $out; + + print '
'; +} + llxFooter(); $db->close(); diff --git a/htdocs/ai/ajax/generate_content.php b/htdocs/ai/ajax/generate_content.php index 0f1e1f76a92..0cd6a125970 100644 --- a/htdocs/ai/ajax/generate_content.php +++ b/htdocs/ai/ajax/generate_content.php @@ -81,15 +81,17 @@ $format = empty($jsonData['format']) ? '' : $jsonData['format']; $generatedContent = $ai->generateContent($instructions, 'auto', $function, $format); -if (is_array($generatedContent) && $generatedContent['error']) { +if (is_null($generatedContent) || (is_array($generatedContent) && $generatedContent['error'])) { // Output error if (!empty($generatedContent['code']) && $generatedContent['code'] == 429) { print "Quota or allowed period exceeded. Retry Later !"; - } elseif ($generatedContent['code'] >= 400) { + } elseif (!empty($generatedContent['code']) && $generatedContent['code'] >= 400) { print "Error : " . $generatedContent['message']; print '
'.$langs->trans('ErrorGoToModuleSetup').''; - } else { + } elseif (!empty($generatedContent['message'])) { print "Error returned by API call: " . $generatedContent['message']; + } else { + print "Error API returned no answer"; } } else { if ($function == 'textgenerationemail' || $function == 'textgenerationwebpage') { diff --git a/htdocs/ai/class/ai.class.php b/htdocs/ai/class/ai.class.php index 922df84dcbb..8eb4ae4d642 100644 --- a/htdocs/ai/class/ai.class.php +++ b/htdocs/ai/class/ai.class.php @@ -80,6 +80,8 @@ class Ai */ public function generateContent($instructions, $model = 'auto', $function = 'textgeneration', $format = '') { + global $dolibarr_main_data_root; + if (empty($this->apiKey)) { return array('error' => true, 'message' => 'API key is not defined for the AI enabled service ('.$this->apiService.')'); } @@ -192,14 +194,14 @@ class Ai if (isset($configurations[$function])) { if (isset($configurations[$function]['prePrompt'])) { - $prePrompt = $configurations[$function]['prePrompt']; // TODO We can send prePrompt into a separated message with role system. + $prePrompt = $configurations[$function]['prePrompt']; } if (isset($configurations[$function]['postPrompt'])) { $postPrompt = $configurations[$function]['postPrompt']; } } - $fullInstructions = ($prePrompt ? $prePrompt.' ' : '').$instructions.($postPrompt ? '. '.$postPrompt : ''); + $fullInstructions = $instructions.($postPrompt ? (preg_match('/[\.\!\?]$/', $instructions) ? '' : '.').' '.$postPrompt : ''); // Set payload string /*{ @@ -224,18 +226,48 @@ class Ai "temperature": 0.7, "top_p": 0.95 }*/ - $payload = json_encode([ - 'messages' => [ - ['role' => 'user', 'content' => $fullInstructions] - ], - 'model' => $model, - //'stream' => false - ]); - $headers = ([ + $arrayforpayload = array( + 'messages' => array(array('role' => 'user', 'content' => $fullInstructions)), + 'model' => $model, + ); + + // Add a system message + $addDateTimeContext = false; + if ($addDateTimeContext) { // @phpstan-ignore-line + $prePrompt = ($prePrompt ? $prePrompt.(preg_match('/[\.\!\?]$/', $prePrompt) ? '' : '.').' ' : '').'Today we are '.dol_print_date(dol_now(), 'dayhourtext'); + } + if ($prePrompt) { + $arrayforpayload['messages'][] = array('role' => 'system', 'content' => $prePrompt); + } + + /* + $arrayforpayload['temperature'] = 0.7; + $arrayforpayload['max_tokens'] = -1; + $arrayforpayload['stream'] = false; + */ + + $payload = json_encode($arrayforpayload); + + $headers = array( 'Authorization: Bearer ' . $this->apiKey, 'Content-Type: application/json' - ]); + ); + + if (getDolGlobalString("AI_DEBUG")) { + if (@is_writable($dolibarr_main_data_root)) { // Avoid fatal error on fopen with open_basedir + $outputfile = $dolibarr_main_data_root."/dolibarr_ai.log"; + $fp = fopen($outputfile, "w"); // overwrite + + if ($fp) { + fwrite($fp, var_export($headers, true)."\n"); + fwrite($fp, var_export($payload, true)."\n"); + + fclose($fp); + dolChmod($outputfile); + } + } + } $localurl = 2; // Accept both local and external endpoints $response = getURLContent($this->apiEndpoint, 'POST', $payload, 1, $headers, array('http', 'https'), $localurl); @@ -248,7 +280,17 @@ class Ai } if (getDolGlobalString("AI_DEBUG")) { - dol_syslog("response content = ".var_export($response['content'], true)); + if (@is_writable($dolibarr_main_data_root)) { // Avoid fatal error on fopen with open_basedir + $outputfile = $dolibarr_main_data_root."/dolibarr_ai.log"; + $fp = fopen($outputfile, "a"); + + if ($fp) { + fwrite($fp, var_export((empty($response['content']) ? 'No content result' : $response['content']), true)."\n"); + + fclose($fp); + dolChmod($outputfile); + } + } } // Decode JSON response diff --git a/htdocs/ai/lib/ai.lib.php b/htdocs/ai/lib/ai.lib.php index fe8c6232db0..8705f1e961f 100644 --- a/htdocs/ai/lib/ai.lib.php +++ b/htdocs/ai/lib/ai.lib.php @@ -23,6 +23,30 @@ * \brief Library files with common functions for Ai */ + +/** + * Prepare admin pages header + * + * @return array> + */ +function getLitOfAIFeatures() +{ + global $langs; + + $arrayofaifeatures = array( + 'textgenerationemail' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("EmailContent").')', 'picto'=>'', 'status'=>'dolibarr'), + 'textgenerationwebpage' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("WebsitePage").')', 'picto'=>'', 'status'=>'dolibarr'), + 'textgeneration' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("Other").')', 'picto'=>'', 'status'=>'notused'), + 'imagegeneration' => array('label' => 'ImageGeneration', 'picto'=>'', 'status'=>'notused'), + 'videogeneration' => array('label' => 'VideoGeneration', 'picto'=>'', 'status'=>'notused'), + 'audiogeneration' => array('label' => 'AudioGeneration', 'picto'=>'', 'status'=>'notused'), + 'transcription' => array('label' => 'Transcription', 'picto'=>'', 'status'=>'notused'), + 'translation' => array('label' => 'Translation', 'picto'=>'', 'status'=>'notused') + ); + + return $arrayofaifeatures; +} + /** * Prepare admin pages header * diff --git a/htdocs/core/ajax/box.php b/htdocs/core/ajax/box.php index 775f1465def..d1e2e1a4990 100644 --- a/htdocs/core/ajax/box.php +++ b/htdocs/core/ajax/box.php @@ -53,10 +53,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/infobox.class.php'; $boxid = GETPOSTINT('boxid'); $boxorder = GETPOST('boxorder'); -$zone = GETPOST('zone'); // Can be key for zone -if ($zone !== '') { - $zone = (int) $zone; -} +$zone = GETPOST('zone'); // Can be '0' or '1' or 'pagename'... $userid = GETPOSTINT('userid'); // Security check @@ -91,7 +88,7 @@ if ($boxorder && $zone != '' && $userid > 0) { // boxorder value is the target order: "A:idboxA1,idboxA2,A-B:idboxB1,idboxB2,B" dol_syslog("AjaxBox boxorder=".$boxorder." zone=".$zone." userid=".$userid, LOG_DEBUG); - $result = InfoBox::saveboxorder($db, (int) $zone, $boxorder, $userid); + $result = InfoBox::saveboxorder($db, $zone, $boxorder, $userid); if ($result > 0) { $langs->load("boxes"); if (!GETPOST('closing')) { diff --git a/htdocs/core/class/CMailFile.class.php b/htdocs/core/class/CMailFile.class.php index 81efbf470a7..ce57b90a4c7 100644 --- a/htdocs/core/class/CMailFile.class.php +++ b/htdocs/core/class/CMailFile.class.php @@ -1483,24 +1483,26 @@ class CMailFile $outputfile = $dolibarr_main_data_root."/dolibarr_mail.log"; $fp = fopen($outputfile, "w"); // overwrite - if ($this->sendmode == 'mail') { - fwrite($fp, $this->headers); - fwrite($fp, $this->eol); // This eol is added by the mail function, so we add it in log - fwrite($fp, $this->message); - } elseif ($this->sendmode == 'smtps') { - fwrite($fp, $this->smtps->log); // this->smtps->log is filled only if MAIN_MAIL_DEBUG was set to on - } elseif ($this->sendmode == 'swiftmailer') { - fwrite($fp, "smtpheader=\n".$this->message->getHeaders()->toString()."\n"); - fwrite($fp, $this->logger->dump()); // this->logger is filled only if MAIN_MAIL_DEBUG was set to on - } + if ($fp) { + if ($this->sendmode == 'mail') { + fwrite($fp, $this->headers); + fwrite($fp, $this->eol); // This eol is added by the mail function, so we add it in log + fwrite($fp, $this->message); + } elseif ($this->sendmode == 'smtps') { + fwrite($fp, $this->smtps->log); // this->smtps->log is filled only if MAIN_MAIL_DEBUG was set to on + } elseif ($this->sendmode == 'swiftmailer') { + fwrite($fp, "smtpheader=\n".$this->message->getHeaders()->toString()."\n"); + fwrite($fp, $this->logger->dump()); // this->logger is filled only if MAIN_MAIL_DEBUG was set to on + } - fclose($fp); - dolChmod($outputfile); + fclose($fp); + dolChmod($outputfile); - // Move dolibarr_mail.log into a dolibarr_mail.log.v123456789 - if (getDolGlobalInt('MAIN_MAIL_DEBUG_LOG_WITH_DATE')) { - require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; - archiveOrBackupFile($outputfile, getDolGlobalInt('MAIN_MAIL_DEBUG_LOG_WITH_DATE')); + // Move dolibarr_mail.log into a dolibarr_mail.log.v123456789 + if (getDolGlobalInt('MAIN_MAIL_DEBUG_LOG_WITH_DATE')) { + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + archiveOrBackupFile($outputfile, getDolGlobalInt('MAIN_MAIL_DEBUG_LOG_WITH_DATE')); + } } } } diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index a4f954cbaff..6975ad29155 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -10360,9 +10360,9 @@ abstract class CommonObject /** * Create object in the database * - * @param User $user User that creates - * @param int<0,1> $notrigger 0=launch triggers after, 1=disable triggers - * @return int<-1,max> Return integer <0 if KO, Id of created object if OK + * @param User $user User that creates + * @param int<0,1> $notrigger 0=launch triggers after, 1=disable triggers + * @return int<-1,max> Return integer <0 if KO, Id of created object if OK */ public function createCommon(User $user, $notrigger = 0) { @@ -10382,6 +10382,7 @@ abstract class CommonObject $fieldvalues['date_creation'] = $this->db->idate($now); $this->date_creation = $this->db->idate($now); } + // For backward compatibility, if a property ->fk_user_creat exists and not filled. if (array_key_exists('fk_user_creat', $fieldvalues) && !($fieldvalues['fk_user_creat'] > 0)) { $fieldvalues['fk_user_creat'] = $user->id; $this->fk_user_creat = $user->id; diff --git a/htdocs/core/class/html.formmail.class.php b/htdocs/core/class/html.formmail.class.php index 1fdd72ab371..c3904969a64 100644 --- a/htdocs/core/class/html.formmail.class.php +++ b/htdocs/core/class/html.formmail.class.php @@ -1493,6 +1493,7 @@ class FormMail extends Form * @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 + * TODO Move into a file html.formai.class.php */ public function getSectionForAIPrompt($function = 'textgeneration', $format = '', $htmlContent = 'message') { @@ -1502,7 +1503,7 @@ class FormMail extends Form $htmlContent = preg_replace('/[^a-z0-9_]/', '', $htmlContent); - $out = '