From 9e7aeca44744a86e5208ae85a11b9bae6c92f118 Mon Sep 17 00:00:00 2001 From: Kamel Khelifa Date: Wed, 20 Mar 2024 15:26:19 +0100 Subject: [PATCH 01/36] FIX: Fix save directory for invoice ODT and multientities --- .../modules/facture/doc/doc_generic_invoice_odt.modules.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/core/modules/facture/doc/doc_generic_invoice_odt.modules.php b/htdocs/core/modules/facture/doc/doc_generic_invoice_odt.modules.php index 6b28da3e1e1..66182bcebba 100644 --- a/htdocs/core/modules/facture/doc/doc_generic_invoice_odt.modules.php +++ b/htdocs/core/modules/facture/doc/doc_generic_invoice_odt.modules.php @@ -253,7 +253,7 @@ class doc_generic_invoice_odt extends ModelePDFFactures $object->fetch_thirdparty(); - $dir = $conf->facture->dir_output; + $dir = empty($conf->facture->multidir_output[$object->entity]) ? $conf->facture->dir_output : $conf->facture->multidir_output[$object->entity]; $objectref = dol_sanitizeFileName($object->ref); if (!preg_match('/specimen/i', $objectref)) { $dir .= "/".$objectref; From 8ab3333eec2dc009ef294e783b5f83e15d87131a Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 20 Mar 2024 20:59:08 +0100 Subject: [PATCH 02/36] FIX logo of paypal payment on payment page --- htdocs/public/payment/newpayment.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/public/payment/newpayment.php b/htdocs/public/payment/newpayment.php index 9e5d578e01a..9825b331f1b 100644 --- a/htdocs/public/payment/newpayment.php +++ b/htdocs/public/payment/newpayment.php @@ -2151,7 +2151,7 @@ if ($action != 'dopayment') { if (getDolGlobalString('PAYPAL_API_INTEGRAL_OR_PAYPALONLY') != 'integral') { print '
 
'; } - print ' '; + print ' '; if (getDolGlobalString('PAYPAL_API_INTEGRAL_OR_PAYPALONLY') == 'integral') { print '
'; print ''.$langs->trans("CreditOrDebitCard").' - '; From 34bd1f0fd300bfda0c263a5cce60a5122a94c213 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 20 Mar 2024 21:00:37 +0100 Subject: [PATCH 03/36] Doc --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 32270994e3b..9f275d641ad 100644 --- a/ChangeLog +++ b/ChangeLog @@ -72,7 +72,7 @@ FIX: #yogosha21416 For users: ---------- -NEW: Compatibility with PHP 8.2 +NEW: Compatibility with PHP 8.2 (warning must be disabled) NEW: Module Workstation (used to enhance the module BOM and Manufacturing Order) is now stable NEW: Add a confirmation popup when deleting extrafields NEW: Add type 'icon' type for extrafields From 440c7b4493c67cf4e938341fd07096b96fb3e61c Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 20 Mar 2024 21:27:28 +0100 Subject: [PATCH 04/36] FIX #28978 FIX #28976 --- htdocs/takepos/index.php | 3 +-- htdocs/user/logout.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/htdocs/takepos/index.php b/htdocs/takepos/index.php index c58d0f5a81b..dc03d83ff90 100644 --- a/htdocs/takepos/index.php +++ b/htdocs/takepos/index.php @@ -73,7 +73,6 @@ if ($setcurrency != "") { // We will recalculate amount for foreign currency at next call of invoice.php when $_SESSION["takeposcustomercurrency"] differs from invoice->multicurrency_code. } -$_SESSION["urlfrom"] = '/takepos/index.php'; $langs->loadLangs(array("bills", "orders", "commercial", "cashdesk", "receiptprinter", "banks")); @@ -1146,7 +1145,7 @@ if (!getDolGlobalString('TAKEPOS_HIDE_HEAD_BAR')) { ?> diff --git a/htdocs/user/logout.php b/htdocs/user/logout.php index 4713d4e9e97..77fcb90a4b9 100644 --- a/htdocs/user/logout.php +++ b/htdocs/user/logout.php @@ -72,7 +72,7 @@ if ($reshook < 0) { } // Define url to go after disconnect -$urlfrom = empty($_SESSION["urlfrom"]) ? '' : $_SESSION["urlfrom"]; +$urlfrom = empty($_SESSION["urlfrom"]) ? GETPOST('urlfrom') : $_SESSION["urlfrom"]; // Define url to go $url = DOL_URL_ROOT."/index.php"; // By default go to login page From 5bae4ffe4d5cd6cbd323c92ecee6ef6e43f42460 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 20 Mar 2024 21:27:28 +0100 Subject: [PATCH 05/36] FIX #28978 FIX #28976 --- htdocs/takepos/index.php | 3 +-- htdocs/user/logout.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/htdocs/takepos/index.php b/htdocs/takepos/index.php index 97dae7e1a14..99374eebc9f 100644 --- a/htdocs/takepos/index.php +++ b/htdocs/takepos/index.php @@ -73,7 +73,6 @@ if ($setcurrency != "") { // We will recalculate amount for foreign currency at next call of invoice.php when $_SESSION["takeposcustomercurrency"] differs from invoice->multicurrency_code. } -$_SESSION["urlfrom"] = '/takepos/index.php'; $langs->loadLangs(array("bills", "orders", "commercial", "cashdesk", "receiptprinter", "banks")); @@ -1107,7 +1106,7 @@ if (empty($conf->global->TAKEPOS_HIDE_HEAD_BAR)) { }?> diff --git a/htdocs/user/logout.php b/htdocs/user/logout.php index d71ee34d93f..129ae24df06 100644 --- a/htdocs/user/logout.php +++ b/htdocs/user/logout.php @@ -72,7 +72,7 @@ if ($reshook < 0) { } // Define url to go after disconnect -$urlfrom = empty($_SESSION["urlfrom"]) ? '' : $_SESSION["urlfrom"]; +$urlfrom = empty($_SESSION["urlfrom"]) ? GETPOST('urlfrom') : $_SESSION["urlfrom"]; // Define url to go $url = DOL_URL_ROOT."/index.php"; // By default go to login page From dc15b2ee79f3a25722eee498b86b34f121de1ef2 Mon Sep 17 00:00:00 2001 From: MDW Date: Wed, 20 Mar 2024 13:39:11 +0100 Subject: [PATCH 06/36] Qual: Bump logToCheckStyle for better filename handling # Qual: Bump logToCheckStyle for better filename handling Some notifications from the PHPCS step in pre-commit extracted too many characters for the filename in a specific case. The update to logToCheckStyle fixes that. --- .github/workflows/pre-commit.yml | 2 +- .github/workflows/windows-ci.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index b29ae8aa809..671a266c767 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -106,7 +106,7 @@ jobs: ls -l ~/.cache/pre-commit/ - name: Convert Raw Log to Annotations - uses: mdeweerd/logToCheckStyle@2024.3.2 + uses: mdeweerd/logToCheckStyle@v2024.3.4 if: ${{ failure() }} with: in: ${{ env.RAW_LOG }} diff --git a/.github/workflows/windows-ci.yaml b/.github/workflows/windows-ci.yaml index edcb9d39226..cb467055156 100644 --- a/.github/workflows/windows-ci.yaml +++ b/.github/workflows/windows-ci.yaml @@ -150,7 +150,7 @@ jobs: for /f "tokens=2 delims==" %%A in ('doskey /m:err') do EXIT /B %%A - name: Convert Raw Log to Annotations - uses: mdeweerd/logToCheckStyle@2024.3.2 + uses: mdeweerd/logToCheckStyle@v2024.3.4 if: ${{ failure() }} with: in: ${{ env.PHPUNIT_LOG }} From 33486f10c05b9e24796aa29ced078ccf31b13d3f Mon Sep 17 00:00:00 2001 From: MDW Date: Wed, 20 Mar 2024 13:58:43 +0100 Subject: [PATCH 07/36] Fix PhanTypeMismatchReturnNullable by initialising return value --- htdocs/adherents/class/adherent.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/adherents/class/adherent.class.php b/htdocs/adherents/class/adherent.class.php index c6ce7429708..f71fa705c3f 100644 --- a/htdocs/adherents/class/adherent.class.php +++ b/htdocs/adherents/class/adherent.class.php @@ -560,12 +560,13 @@ class Adherent extends CommonObject * Return translated label by the nature of a adherent (physical or moral) * * @param string $morphy Nature of the adherent (physical or moral) - * @param int $addbadge Add badge (1=Full label, 2=First letters only) + * @param int<0,2> $addbadge Add badge (1=Full label, 2=First letters only) * @return string Label */ public function getmorphylib($morphy = '', $addbadge = 0) { global $langs; + $s = ''; // Clean var if (!$morphy) { @@ -573,7 +574,6 @@ class Adherent extends CommonObject } if ($addbadge) { - $s = ''; $labeltoshowm = $langs->trans("Moral"); $labeltoshowp = $langs->trans("Physical"); if ($morphy == 'phy') { From 3b93de27097f3a8926d1d9f750a952472dffde8c Mon Sep 17 00:00:00 2001 From: MDW Date: Wed, 20 Mar 2024 14:10:45 +0100 Subject: [PATCH 08/36] Fix PhanTypeMismatchReturnNullable by initialising return value + Some PHPdoc typing --- .../adherents/class/adherentstats.class.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/htdocs/adherents/class/adherentstats.class.php b/htdocs/adherents/class/adherentstats.class.php index f99b20f86d5..612f408edd9 100644 --- a/htdocs/adherents/class/adherentstats.class.php +++ b/htdocs/adherents/class/adherentstats.class.php @@ -3,6 +3,7 @@ * Copyright (c) 2005-2011 Laurent Destailleur * Copyright (C) 2005-2009 Regis Houssin * Copyright (C) 2023 Waël Almoman + * Copyright (C) 2024 MDW * * 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 @@ -168,15 +169,15 @@ class AdherentStats extends Stats /** * Return count of member by status group by adh type, total and average * - * @param int $numberYears Number of years to scan (0 = all) - * @return array Array with total of draft, pending, uptodate, expired, resiliated for each member type + * @param int $numberYears Number of years to scan (0 = all) + * @return array Array with total of draft, pending, uptodate, expired, resiliated for each member type */ public function countMembersByTypeAndStatus($numberYears = 0) { global $user; $now = dol_now(); - $endYear = date('Y'); + $endYear = (int) date('Y'); $startYear = $endYear - $numberYears; $sql = "SELECT t.rowid as fk_adherent_type, t.libelle as label"; @@ -224,7 +225,7 @@ class AdherentStats extends Stats 'members_resiliated' => (int) $objp->members_resiliated ); $totalrow = 0; - foreach ($MembersCountArray[$objp->fk_adherent_type] as $key=>$nb) { + foreach ($MembersCountArray[$objp->fk_adherent_type] as $key => $nb) { if ($key != 'label') { $totalrow += $nb; $totalstatus[$key] += $nb; @@ -244,16 +245,17 @@ class AdherentStats extends Stats /** * Return count of member by status group by adh type, total and average * - * @param int $numberYears Number of years to scan (0 = all) - * @return array Array with total of draft, pending, uptodate, expired, resiliated for each member tag + * @param int $numberYears Number of years to scan (0 = all) + * @return array Array with total of draft, pending, uptodate, expired, resiliated for each member tag */ public function countMembersByTagAndStatus($numberYears = 0) { global $user; $now = dol_now(); - $endYear = date('Y'); + $endYear = (int) date('Y'); $startYear = $endYear - $numberYears; + $MembersCountArray = []; $sql = "SELECT c.rowid as fk_categorie, c.label as label"; $sql .= ", COUNT(".$this->db->ifsql("d.statut = ".Adherent::STATUS_DRAFT, "'members_draft'", 'NULL').") as members_draft"; @@ -282,7 +284,6 @@ class AdherentStats extends Stats if ($result) { $num = $this->db->num_rows($result); $i = 0; - $MembersCountArray = []; $totalstatus = array( 'label' => 'Total', 'members_draft' => 0, @@ -304,7 +305,7 @@ class AdherentStats extends Stats 'members_resiliated' => (int) $objp->members_resiliated ); $totalrow = 0; - foreach ($MembersCountArray[$objp->fk_categorie] as $key=>$nb) { + foreach ($MembersCountArray[$objp->fk_categorie] as $key => $nb) { if ($key != 'label') { $totalrow += $nb; $totalstatus[$key] += $nb; From 3a27977b46fc942eb664e9f933bbab556051f5d8 Mon Sep 17 00:00:00 2001 From: MDW Date: Wed, 20 Mar 2024 14:18:37 +0100 Subject: [PATCH 09/36] Fix PhanTypeMismatchReturnNullable with typing test --- htdocs/comm/action/class/actioncomm.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/comm/action/class/actioncomm.class.php b/htdocs/comm/action/class/actioncomm.class.php index f4f77bcaa9d..09e73b188f8 100644 --- a/htdocs/comm/action/class/actioncomm.class.php +++ b/htdocs/comm/action/class/actioncomm.class.php @@ -1393,7 +1393,7 @@ class ActionComm extends CommonObject * * @param User $user Object user * @param int $load_state_board Load indicator array this->nb - * @return WorkboardResponse|int Return integer <0 if KO, WorkboardResponse if OK + * @return WorkboardResponse|int<-1,1> Return integer <0 if KO, WorkboardResponse if OK */ public function load_board($user, $load_state_board = 0) { @@ -1462,7 +1462,7 @@ class ActionComm extends CommonObject } $this->db->free($resql); - if (empty($load_state_board)) { + if (empty($load_state_board) && $response instanceof WorkboardResponse) { return $response; } else { return 1; From 0a1a663e3471360ce6a8b79e8df535ad027f6609 Mon Sep 17 00:00:00 2001 From: MDW Date: Wed, 20 Mar 2024 14:21:35 +0100 Subject: [PATCH 10/36] Fix PhanTypeMismatchReturnNullable with initialisation --- htdocs/comm/propal/class/api_proposals.class.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/htdocs/comm/propal/class/api_proposals.class.php b/htdocs/comm/propal/class/api_proposals.class.php index aa1e3d0e980..546d40d052d 100644 --- a/htdocs/comm/propal/class/api_proposals.class.php +++ b/htdocs/comm/propal/class/api_proposals.class.php @@ -4,6 +4,7 @@ * Copyright (C) 2020 Thibault FOUCART * Copyright (C) 2022 ATM Consulting * Copyright (C) 2022 OpenDSI + * Copyright (C) 2024 MDW * * 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 @@ -410,6 +411,7 @@ class Proposals extends DolibarrApi } $errors = []; + $updateRes = 0; $this->db->begin(); foreach ($request_data as $TData) { From d245dab56b56dcfab7454a395b790052a094a9c3 Mon Sep 17 00:00:00 2001 From: MDW Date: Wed, 20 Mar 2024 14:56:14 +0100 Subject: [PATCH 11/36] Fix PhanTypeMismatchReturnNullable by setting variable --- htdocs/projet/class/task.class.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/htdocs/projet/class/task.class.php b/htdocs/projet/class/task.class.php index 45dcad80025..84d78726599 100644 --- a/htdocs/projet/class/task.class.php +++ b/htdocs/projet/class/task.class.php @@ -2051,9 +2051,10 @@ class Task extends CommonObjectLine $this->error = $clone_task->error; $error++; } - // End - if (!$error) { + if ($error) { + $clone_task_id = 0; // For static tool check + } else { $clone_task_id = $clone_task->id; $clone_task_ref = $clone_task->ref; From 7e1bf61c0db34f4deb6308e03472444a8da63249 Mon Sep 17 00:00:00 2001 From: MDW Date: Wed, 20 Mar 2024 17:59:48 +0100 Subject: [PATCH 12/36] Fix PhanTypeMismatchReturnNullable with init --- htdocs/core/class/html.formprojet.class.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/htdocs/core/class/html.formprojet.class.php b/htdocs/core/class/html.formprojet.class.php index 9bbc5e75018..b6f50754c6b 100644 --- a/htdocs/core/class/html.formprojet.class.php +++ b/htdocs/core/class/html.formprojet.class.php @@ -3,6 +3,7 @@ * Copyright (C) 2015 Marcos García * Copyright (C) 2018 Charlene Benke * Copyright (C) 2024 Frédéric France + * Copyright (C) 2024 MDW * * 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 @@ -686,7 +687,7 @@ class FormProjets extends Form * @param string $morecss Add more css * @param int $noadmininfo 0=Add admin info, 1=Disable admin info * @param int $addcombojs 1=Add a js combo - * @return int|string The HTML select list of element or '' if nothing or -1 if KO + * @return int<-1,-1>|string The HTML select list of element or '' if nothing or -1 if KO */ public function selectOpportunityStatus($htmlname, $preselected = '-1', $showempty = 1, $useshortlabel = 0, $showallnone = 0, $showpercent = 0, $morecss = '', $noadmininfo = 0, $addcombojs = 0) { @@ -701,6 +702,7 @@ class FormProjets extends Form if ($resql) { $num = $this->db->num_rows($resql); $i = 0; + $sellist = ''; if ($num > 0) { $sellist = ''; } @@ -7277,7 +7277,7 @@ abstract class CommonObject if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_5, '90%'); - $out = $doleditor->Create(1, '', true, '', '', $moreparam, $morecss); + $out = (string) $doleditor->Create(1, '', true, '', '', $moreparam, $morecss); } else { $out = ''; } From 2496d20d4fe19067463e67fe3a615e52c792d80a Mon Sep 17 00:00:00 2001 From: MDW Date: Wed, 20 Mar 2024 23:38:28 +0100 Subject: [PATCH 29/36] Fix nullable return with if($ok == 1) in stead of if($ok) # Fix nullable return with if($ok == 1) in stead of if($ok) This seems to be a bug in Phan where if($ok) changes the type of $ok. Testing with '$ok == 1' instead fixes the issue. This has been reported to the phan team. --- htdocs/core/modules/DolibarrModules.class.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/htdocs/core/modules/DolibarrModules.class.php b/htdocs/core/modules/DolibarrModules.class.php index 850ec739d06..41d2b20efd1 100644 --- a/htdocs/core/modules/DolibarrModules.class.php +++ b/htdocs/core/modules/DolibarrModules.class.php @@ -1126,7 +1126,7 @@ class DolibarrModules // Can not be abstract, because we need to instantiate it * * @param string $reldir Relative directory where to scan files. Example: '/install/mysql/' or '/module/sql/' * @param string $onlywithsuffix Only with the defined suffix - * @return int Return integer <=0 if KO, >0 if OK + * @return int<0,1> Return integer <=0 if KO, >0 if OK */ protected function _load_tables($reldir, $onlywithsuffix = '') { @@ -1135,6 +1135,7 @@ class DolibarrModules // Can not be abstract, because we need to instantiate it $error = 0; $dirfound = 0; + $ok = 1; if (empty($reldir)) { return 1; @@ -1142,9 +1143,8 @@ class DolibarrModules // Can not be abstract, because we need to instantiate it include_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; - $ok = 1; foreach ($conf->file->dol_document_root as $dirroot) { - if ($ok) { + if ($ok == 1) { $dirsql = $dirroot.$reldir; $ok = 0; From 976686262d5ee858842ffaf93588e51f4b141b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Maldonado?= Date: Thu, 21 Mar 2024 02:36:40 +0100 Subject: [PATCH 30/36] FIX: SQL syntax error in DDLUpdateField --- htdocs/core/db/pgsql.class.php | 4 ++-- test/phpunit/DoliDBTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/htdocs/core/db/pgsql.class.php b/htdocs/core/db/pgsql.class.php index 2923992e345..29989873d3c 100644 --- a/htdocs/core/db/pgsql.class.php +++ b/htdocs/core/db/pgsql.class.php @@ -1273,9 +1273,9 @@ class DoliDBPgsql extends DoliDB if (isset($field_desc['default']) && $field_desc['default'] != '') { if ($field_desc['type'] == 'double' || $field_desc['type'] == 'tinyint' || $field_desc['type'] == 'int') { - $sql .= " DEFAULT ".$this->escape($field_desc['default']); + $sql .= ", ALTER COLUMN ".$this->escape($field_name)." SET DEFAULT ".$this->escape($field_desc['default']); } elseif ($field_desc['type'] != 'text') { - $sql .= " DEFAULT '".$this->escape($field_desc['default'])."'"; // Default not supported on text fields + $sql .= ", ALTER COLUMN ".$this->escape($field_name)." SET DEFAULT '"." DEFAULT '".$this->escape($field_desc['default'])."'"; // Default not supported on text fields } } diff --git a/test/phpunit/DoliDBTest.php b/test/phpunit/DoliDBTest.php index 999c3b3e54a..2a380fa637e 100644 --- a/test/phpunit/DoliDBTest.php +++ b/test/phpunit/DoliDBTest.php @@ -160,7 +160,7 @@ class DoliDBTest extends PHPUnit\Framework\TestCase print __METHOD__." result=".$result."\n"; // TODO Use $savtype and $savnull instead of hard coded - $field_desc = array('type'=>'varchar', 'value'=>'16', 'null'=>'NOT NULL'); + $field_desc = array('type'=>'varchar', 'value'=>'16', 'null'=>'NOT NULL', 'default'=>'aaaabbbbccccdddd'); $result = $db->DDLUpdateField($db->prefix().'c_paper_format', 'code', $field_desc); $this->assertEquals(1, $result); From 27c0192b7ca1657a48b559784f9e6df81437cc74 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 21 Mar 2024 11:14:39 +0100 Subject: [PATCH 31/36] cast smallint --- htdocs/core/db/mysqli.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/core/db/mysqli.class.php b/htdocs/core/db/mysqli.class.php index ff63113e47b..6e11faa9fee 100644 --- a/htdocs/core/db/mysqli.class.php +++ b/htdocs/core/db/mysqli.class.php @@ -1013,8 +1013,8 @@ class DoliDBMysqli extends DoliDB } if (isset($field_desc['default']) && $field_desc['default'] != '') { - if ($field_desc['type'] == 'double' || $field_desc['type'] == 'tinyint' || $field_desc['type'] == 'int') { - $sql .= " DEFAULT ".$this->escape($field_desc['default']); + if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) { + $sql .= " DEFAULT ".((float) $field_desc['default']); } elseif ($field_desc['type'] != 'text') { $sql .= " DEFAULT '".$this->escape($field_desc['default'])."'"; // Default not supported on text fields } From 01a058b2081d2fe031bbdc75d6fa903cd23fca76 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 21 Mar 2024 11:22:12 +0100 Subject: [PATCH 32/36] Fix regression --- htdocs/core/db/pgsql.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/core/db/pgsql.class.php b/htdocs/core/db/pgsql.class.php index 29989873d3c..a5d226804b3 100644 --- a/htdocs/core/db/pgsql.class.php +++ b/htdocs/core/db/pgsql.class.php @@ -1273,9 +1273,9 @@ class DoliDBPgsql extends DoliDB if (isset($field_desc['default']) && $field_desc['default'] != '') { if ($field_desc['type'] == 'double' || $field_desc['type'] == 'tinyint' || $field_desc['type'] == 'int') { - $sql .= ", ALTER COLUMN ".$this->escape($field_name)." SET DEFAULT ".$this->escape($field_desc['default']); + $sql .= ", ALTER COLUMN ".$this->escape($field_name)." SET DEFAULT ".((float) $field_desc['default']); } elseif ($field_desc['type'] != 'text') { - $sql .= ", ALTER COLUMN ".$this->escape($field_name)." SET DEFAULT '"." DEFAULT '".$this->escape($field_desc['default'])."'"; // Default not supported on text fields + $sql .= ", ALTER COLUMN ".$this->escape($field_name)." SET DEFAULT '".$this->escape($field_desc['default'])."'"; // Default not supported on text fields } } From ce2959b022a3cc0e74fe3353c70ee4680d9be1db Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 21 Mar 2024 12:10:09 +0100 Subject: [PATCH 33/36] Clean method DDLCreateTable (used by modulebuilder only) #28996 --- htdocs/core/db/mysqli.class.php | 56 ++++++++++++++--------- htdocs/core/db/pgsql.class.php | 60 +++++++++++++++---------- htdocs/core/db/sqlite3.class.php | 65 ++++++++++++++++----------- htdocs/core/lib/modulebuilder.lib.php | 16 +++---- 4 files changed, 117 insertions(+), 80 deletions(-) diff --git a/htdocs/core/db/mysqli.class.php b/htdocs/core/db/mysqli.class.php index 6e11faa9fee..6f3feb04dcc 100644 --- a/htdocs/core/db/mysqli.class.php +++ b/htdocs/core/db/mysqli.class.php @@ -825,55 +825,66 @@ class DoliDBMysqli extends DoliDB public function DDLCreateTable($table, $fields, $primary_key, $type, $unique_keys = null, $fulltext_keys = null, $keys = null) { // phpcs:enable - // FIXME: $fulltext_keys parameter is unused + // @TODO: $fulltext_keys parameter is unused + + if (empty($type)) { + $type = 'InnoDB'; + } $pk = ''; $sqluq = $sqlk = array(); - // cles recherchees dans le tableau des descriptions (fields) : type,value,attribute,null,default,extra - // ex. : $fields['rowid'] = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment'); - $sql = "CREATE TABLE ".$table."("; + // Keys found into the array $fields: type,value,attribute,null,default,extra + // ex. : $fields['rowid'] = array( + // 'type'=>'int' or 'integer', + // 'value'=>'11', + // 'null'=>'not null', + // 'extra'=> 'auto_increment' + // ); + $sql = "CREATE TABLE ".$this->sanitize($table)."("; $i = 0; $sqlfields = array(); foreach ($fields as $field_name => $field_desc) { - $sqlfields[$i] = $field_name." "; - $sqlfields[$i] .= $field_desc['type']; - if (preg_match("/^[^\s]/i", $field_desc['value'])) { - $sqlfields[$i] .= "(".$field_desc['value'].")"; + $sqlfields[$i] = $this->sanitize($field_name)." "; + $sqlfields[$i] .= $this->sanitize($field_desc['type']); + if (!is_null($field_desc['value']) && $field_desc['value'] !== '') { + $sqlfields[$i] .= "(".$this->sanitize($field_desc['value']).")"; } - if (preg_match("/^[^\s]/i", $field_desc['attribute'])) { - $sqlfields[$i] .= " ".$field_desc['attribute']; + if (!is_null($field_desc['attribute']) && $field_desc['attribute'] !== '') { + $sqlfields[$i] .= " ".$this->sanitize($field_desc['attribute']); } - if (preg_match("/^[^\s]/i", $field_desc['default'])) { - if ((preg_match("/null/i", $field_desc['default'])) || (preg_match("/CURRENT_TIMESTAMP/i", $field_desc['default']))) { - $sqlfields[$i] .= " default ".$field_desc['default']; + if (!is_null($field_desc['default']) && $field_desc['default'] !== '') { + if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) { + $sqlfields[$i] .= " DEFAULT ".((float) $field_desc['default']); + } elseif ($field_desc['default'] == 'null' || $field_desc['default'] == 'CURRENT_TIMESTAMP') { + $sqlfields[$i] .= " DEFAULT ".$this->sanitize($field_desc['default']); } else { - $sqlfields[$i] .= " default '".$this->escape($field_desc['default'])."'"; + $sqlfields[$i] .= " DEFAULT '".$this->escape($field_desc['default'])."'"; } } - if (preg_match("/^[^\s]/i", $field_desc['null'])) { - $sqlfields[$i] .= " ".$field_desc['null']; + if (!is_null($field_desc['null']) && $field_desc['null'] !== '') { + $sqlfields[$i] .= " ".$this->sanitize($field_desc['null'], 0, 0, 1); } - if (preg_match("/^[^\s]/i", $field_desc['extra'])) { - $sqlfields[$i] .= " ".$field_desc['extra']; + if (!is_null($field_desc['extra']) && $field_desc['extra'] !== '') { + $sqlfields[$i] .= " ".$this->sanitize($field_desc['extra']); } $i++; } if ($primary_key != "") { - $pk = "primary key(".$primary_key.")"; + $pk = "PRIMARY KEY(".$this->sanitize($primary_key).")"; } if (is_array($unique_keys)) { $i = 0; foreach ($unique_keys as $key => $value) { - $sqluq[$i] = "UNIQUE KEY '".$key."' ('".$this->escape($value)."')"; + $sqluq[$i] = "UNIQUE KEY '".$this->sanitize($key)."' ('".$this->escape($value)."')"; $i++; } } if (is_array($keys)) { $i = 0; foreach ($keys as $key => $value) { - $sqlk[$i] = "KEY ".$key." (".$value.")"; + $sqlk[$i] = "KEY ".$this->sanitize($key)." (".$value.")"; $i++; } } @@ -887,7 +898,8 @@ class DoliDBMysqli extends DoliDB if (is_array($keys)) { $sql .= ",".implode(',', $sqlk); } - $sql .= ") engine=".$type; + $sql .= ")"; + $sql .= " engine=".$this->sanitize($type); if (!$this->query($sql)) { return -1; diff --git a/htdocs/core/db/pgsql.class.php b/htdocs/core/db/pgsql.class.php index 250a7da0855..3cfe5671085 100644 --- a/htdocs/core/db/pgsql.class.php +++ b/htdocs/core/db/pgsql.class.php @@ -1068,51 +1068,63 @@ class DoliDBPgsql extends DoliDB public function DDLCreateTable($table, $fields, $primary_key, $type, $unique_keys = null, $fulltext_keys = null, $keys = null) { // phpcs:enable - // FIXME: $fulltext_keys parameter is unused + // @TODO: $fulltext_keys parameter is unused $sqlfields = array(); $sqlk = array(); $sqluq = array(); - // cles recherchees dans le tableau des descriptions (fields) : type,value,attribute,null,default,extra - // ex. : $fields['rowid'] = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment'); - $sql = "create table ".$table."("; + // Keys found into the array $fields: type,value,attribute,null,default,extra + // ex. : $fields['rowid'] = array( + // 'type'=>'int' or 'integer', + // 'value'=>'11', + // 'null'=>'not null', + // 'extra'=> 'auto_increment' + // ); + $sql = "CREATE TABLE ".$this->sanitize($table)."("; $i = 0; + $sqlfields = array(); foreach ($fields as $field_name => $field_desc) { - $sqlfields[$i] = $field_name." "; - $sqlfields[$i] .= $field_desc['type']; - if (preg_match("/^[^\s]/i", $field_desc['value'])) { - $sqlfields[$i] .= "(".$field_desc['value'].")"; - } elseif (preg_match("/^[^\s]/i", $field_desc['attribute'])) { - $sqlfields[$i] .= " ".$field_desc['attribute']; - } elseif (preg_match("/^[^\s]/i", $field_desc['default'])) { - if (preg_match("/null/i", $field_desc['default'])) { - $sqlfields[$i] .= " default ".$field_desc['default']; + $sqlfields[$i] = $this->sanitize($field_name)." "; + $sqlfields[$i] .= $this->sanitize($field_desc['type']); + if (!is_null($field_desc['value']) && $field_desc['value'] !== '') { + $sqlfields[$i] .= "(".$this->sanitize($field_desc['value']).")"; + } + if (!is_null($field_desc['attribute']) && $field_desc['attribute'] !== '') { + $sqlfields[$i] .= " ".$this->sanitize($field_desc['attribute']); + } + if (!is_null($field_desc['default']) && $field_desc['default'] !== '') { + if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) { + $sqlfields[$i] .= " DEFAULT ".((float) $field_desc['default']); + } elseif ($field_desc['default'] == 'null' || $field_desc['default'] == 'CURRENT_TIMESTAMP') { + $sqlfields[$i] .= " DEFAULT ".$this->sanitize($field_desc['default']); } else { - $sqlfields[$i] .= " default '".$this->escape($field_desc['default'])."'"; + $sqlfields[$i] .= " DEFAULT '".$this->escape($field_desc['default'])."'"; } - } elseif (preg_match("/^[^\s]/i", $field_desc['null'])) { - $sqlfields[$i] .= " ".$field_desc['null']; - } elseif (preg_match("/^[^\s]/i", $field_desc['extra'])) { - $sqlfields[$i] .= " ".$field_desc['extra']; + } + if (!is_null($field_desc['null']) && $field_desc['null'] !== '') { + $sqlfields[$i] .= " ".$this->sanitize($field_desc['null'], 0, 0, 1); + } + if (!is_null($field_desc['extra']) && $field_desc['extra'] !== '') { + $sqlfields[$i] .= " ".$this->sanitize($field_desc['extra']); } $i++; } if ($primary_key != "") { - $pk = "primary key(".$primary_key.")"; + $pk = "PRIMARY KEY(".$this->sanitize($primary_key).")"; } if (is_array($unique_keys)) { $i = 0; foreach ($unique_keys as $key => $value) { - $sqluq[$i] = "UNIQUE KEY '".$key."' ('".$this->escape($value)."')"; + $sqluq[$i] = "UNIQUE KEY '".$this->sanitize($key)."' ('".$this->escape($value)."')"; $i++; } } if (is_array($keys)) { $i = 0; foreach ($keys as $key => $value) { - $sqlk[$i] = "KEY ".$key." (".$value.")"; + $sqlk[$i] = "KEY ".$this->sanitize($key)." (".$value.")"; $i++; } } @@ -1120,15 +1132,15 @@ class DoliDBPgsql extends DoliDB if ($primary_key != "") { $sql .= ",".$pk; } - if (is_array($unique_keys)) { + if ($unique_keys != "") { $sql .= ",".implode(',', $sqluq); } if (is_array($keys)) { $sql .= ",".implode(',', $sqlk); } - $sql .= ") type=".$type; + $sql .= ")"; + //$sql .= " engine=".$this->sanitize($type); - dol_syslog($sql, LOG_DEBUG); if (!$this->query($sql)) { return -1; } else { diff --git a/htdocs/core/db/sqlite3.class.php b/htdocs/core/db/sqlite3.class.php index 060e7318b65..477d7d33c42 100644 --- a/htdocs/core/db/sqlite3.class.php +++ b/htdocs/core/db/sqlite3.class.php @@ -970,51 +970,63 @@ class DoliDBSqlite3 extends DoliDB public function DDLCreateTable($table, $fields, $primary_key, $type, $unique_keys = null, $fulltext_keys = null, $keys = null) { // phpcs:enable - // FIXME: $fulltext_keys parameter is unused + // @TODO: $fulltext_keys parameter is unused $sqlfields = array(); $sqlk = array(); $sqluq = array(); - // cles recherchees dans le tableau des descriptions (fields) : type,value,attribute,null,default,extra - // ex. : $fields['rowid'] = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment'); - $sql = "create table ".$table."("; + // Keys found into the array $fields: type,value,attribute,null,default,extra + // ex. : $fields['rowid'] = array( + // 'type'=>'int' or 'integer', + // 'value'=>'11', + // 'null'=>'not null', + // 'extra'=> 'auto_increment' + // ); + $sql = "CREATE TABLE ".$this->sanitize($table)."("; $i = 0; + $sqlfields = array(); foreach ($fields as $field_name => $field_desc) { - $sqlfields[$i] = $field_name." "; - $sqlfields[$i] .= $field_desc['type']; - if (preg_match("/^[^\s]/i", $field_desc['value'])) { - $sqlfields[$i] .= "(".$field_desc['value'].")"; - } elseif (preg_match("/^[^\s]/i", $field_desc['attribute'])) { - $sqlfields[$i] .= " ".$field_desc['attribute']; - } elseif (preg_match("/^[^\s]/i", $field_desc['default'])) { - if (preg_match("/null/i", $field_desc['default'])) { - $sqlfields[$i] .= " default ".$field_desc['default']; + $sqlfields[$i] = $this->sanitize($field_name)." "; + $sqlfields[$i] .= $this->sanitize($field_desc['type']); + if (!is_null($field_desc['value']) && $field_desc['value'] !== '') { + $sqlfields[$i] .= "(".$this->sanitize($field_desc['value']).")"; + } + if (!is_null($field_desc['attribute']) && $field_desc['attribute'] !== '') { + $sqlfields[$i] .= " ".$this->sanitize($field_desc['attribute']); + } + if (!is_null($field_desc['default']) && $field_desc['default'] !== '') { + if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) { + $sqlfields[$i] .= " DEFAULT ".((float) $field_desc['default']); + } elseif ($field_desc['default'] == 'null' || $field_desc['default'] == 'CURRENT_TIMESTAMP') { + $sqlfields[$i] .= " DEFAULT ".$this->sanitize($field_desc['default']); } else { - $sqlfields[$i] .= " default '".$this->escape($field_desc['default'])."'"; + $sqlfields[$i] .= " DEFAULT '".$this->escape($field_desc['default'])."'"; } - } elseif (preg_match("/^[^\s]/i", $field_desc['null'])) { - $sqlfields[$i] .= " ".$field_desc['null']; - } elseif (preg_match("/^[^\s]/i", $field_desc['extra'])) { - $sqlfields[$i] .= " ".$field_desc['extra']; + } + if (!is_null($field_desc['null']) && $field_desc['null'] !== '') { + $sqlfields[$i] .= " ".$this->sanitize($field_desc['null'], 0, 0, 1); + } + if (!is_null($field_desc['extra']) && $field_desc['extra'] !== '') { + $sqlfields[$i] .= " ".$this->sanitize($field_desc['extra']); } $i++; } if ($primary_key != "") { - $pk = "primary key(".$primary_key.")"; + $pk = "PRIMARY KEY(".$this->sanitize($primary_key).")"; } if (is_array($unique_keys)) { $i = 0; foreach ($unique_keys as $key => $value) { - $sqluq[$i] = "UNIQUE KEY '".$key."' ('".$this->escape($value)."')"; + $sqluq[$i] = "UNIQUE KEY '".$this->sanitize($key)."' ('".$this->escape($value)."')"; $i++; } } if (is_array($keys)) { $i = 0; foreach ($keys as $key => $value) { - $sqlk[$i] = "KEY ".$key." (".$value.")"; + $sqlk[$i] = "KEY ".$this->sanitize($key)." (".$value.")"; $i++; } } @@ -1022,19 +1034,20 @@ class DoliDBSqlite3 extends DoliDB if ($primary_key != "") { $sql .= ",".$pk; } - if (is_array($unique_keys)) { + if ($unique_keys != "") { $sql .= ",".implode(',', $sqluq); } if (is_array($keys)) { $sql .= ",".implode(',', $sqlk); } - $sql .= ") type=".$type; + $sql .= ")"; + //$sql .= " engine=".$this->sanitize($type); - dol_syslog($sql, LOG_DEBUG); - if (!$this -> query($sql)) { + if (!$this->query($sql)) { return -1; + } else { + return 1; } - return 1; } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps diff --git a/htdocs/core/lib/modulebuilder.lib.php b/htdocs/core/lib/modulebuilder.lib.php index 95e4711b396..159c5712399 100644 --- a/htdocs/core/lib/modulebuilder.lib.php +++ b/htdocs/core/lib/modulebuilder.lib.php @@ -1288,12 +1288,12 @@ function createNewDictionnary($modulename, $file, $namedic, $dictionnaires = nul } $columns = array( - 'rowid' => array('type' => 'integer(11)'), - 'code' => array('type' => 'varchar(255) NOT NULL'), - 'label' => array('type' => 'varchar(255) NOT NULL'), - 'position' => array('type' => 'integer(11) NULL'), - 'use_default' => array('type' => 'varchar(255) DEFAULT 1'), - 'active' => array('type' => 'integer') + 'rowid' => array('type' => 'integer', 'value' => 11), + 'code' => array('type' => 'varchar', 'value' => 255, 'null'=>'NOT NULL'), + 'label' => array('type' => 'varchar', 'value' => 255, 'null'=>'NOT NULL'), + 'position' => array('type' => 'integer', 'value' => 11, 'null'=>'NULL'), + 'use_default' => array('type' => 'varchar', 'value' => 11, 'default'=>'1'), + 'active' => array('type' => 'integer', 'value' => 3) ); @@ -1309,13 +1309,13 @@ function createNewDictionnary($modulename, $file, $namedic, $dictionnaires = nul } } // check if tablename exist in Database and create it if not - $query = "SHOW TABLES LIKE '" . MAIN_DB_PREFIX.strtolower($namedic) . "'"; + $query = "SHOW TABLES LIKE '" . $db->sanitize(MAIN_DB_PREFIX.strtolower($namedic)) . "'"; $checkTable = $db->query($query); if ($checkTable && $db->num_rows($checkTable) > 0) { setEventMessages($langs->trans("ErrorTableExist", $namedic), null, 'errors'); return; } else { - $_results = $db->DDLCreateTable(MAIN_DB_PREFIX.strtolower($namedic), $columns, $primaryKey, "InnoDB"); + $_results = $db->DDLCreateTable(MAIN_DB_PREFIX.strtolower($namedic), $columns, $primaryKey, ""); if ($_results < 0) { dol_print_error($db); $langs->load("errors"); From 171c32be342f08dd1c17a4997eb8b2f9123c424e Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 21 Mar 2024 12:21:38 +0100 Subject: [PATCH 34/36] Add DDLDropTable and DDLCreateTable into phpunit tests --- htdocs/core/db/mysqli.class.php | 2 +- test/phpunit/DoliDBTest.php | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/htdocs/core/db/mysqli.class.php b/htdocs/core/db/mysqli.class.php index 6f3feb04dcc..9c2948cb262 100644 --- a/htdocs/core/db/mysqli.class.php +++ b/htdocs/core/db/mysqli.class.php @@ -920,7 +920,7 @@ class DoliDBMysqli extends DoliDB // phpcs:enable $tmptable = preg_replace('/[^a-z0-9\.\-\_]/i', '', $table); - $sql = "DROP TABLE ".$tmptable; + $sql = "DROP TABLE ".$this->sanitize($tmptable); if (!$this->query($sql)) { return -1; diff --git a/test/phpunit/DoliDBTest.php b/test/phpunit/DoliDBTest.php index c2a0de05a20..34f70e459c9 100644 --- a/test/phpunit/DoliDBTest.php +++ b/test/phpunit/DoliDBTest.php @@ -48,6 +48,41 @@ $conf->global->MAIN_DISABLE_ALL_MAILS = 1; */ class DoliDBTest extends CommonClassTest { + /** + * testDDLUpdateField + * + * @return int + */ + public function testDDLCreateTable() + { + global $conf,$user,$langs,$db; + $conf = $this->savconf; + $user = $this->savuser; + $langs = $this->savlangs; + $db = $this->savdb; + + $namedic = MAIN_DB_PREFIX.'tmptesttabletoremove'; + + $res = $db->DDLDropTable($namedic); + + $columns = array( + 'rowid' => array('type' => 'integer', 'value' => 11), + 'code' => array('type' => 'varchar', 'value' => 255, 'null'=>'NOT NULL'), + 'label' => array('type' => 'varchar', 'value' => 255, 'null'=>'NOT NULL'), + 'position' => array('type' => 'integer', 'value' => 11, 'null'=>'NULL'), + 'use_default' => array('type' => 'varchar', 'value' => 11, 'default'=>'1'), + 'active' => array('type' => 'integer', 'value' => 3) + ); + $primaryKey = 'rowid'; + + print __METHOD__.' db->type = '.$db->type."\n"; + + $res = $db->DDLCreateTable($namedic, $columns, $primaryKey, ""); + + $this->assertEquals(1, $res); + print __METHOD__." result=".$res."\n"; + } + /** * testDDLUpdateField * @@ -84,6 +119,7 @@ class DoliDBTest extends CommonClassTest $field_desc = array('type'=>'varchar', 'value'=>'16', 'null'=>'NOT NULL', 'default'=>'aaaabbbbccccdddd'); $result = $db->DDLUpdateField($db->prefix().'c_paper_format', 'code', $field_desc); + $this->assertEquals(1, $result); print __METHOD__." result=".$result."\n"; From aa77573cd9f6f4b16a39face533bdf78d03e0978 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 21 Mar 2024 12:45:58 +0100 Subject: [PATCH 35/36] Sanitize DDL instructions --- htdocs/core/db/mysqli.class.php | 42 ++++++----- htdocs/core/db/pgsql.class.php | 104 +++++++++++++------------- htdocs/core/db/sqlite3.class.php | 22 +++--- htdocs/core/lib/modulebuilder.lib.php | 2 +- test/phpunit/DoliDBTest.php | 2 +- 5 files changed, 89 insertions(+), 83 deletions(-) diff --git a/htdocs/core/db/mysqli.class.php b/htdocs/core/db/mysqli.class.php index 9c2948cb262..5b43b4e2cbc 100644 --- a/htdocs/core/db/mysqli.class.php +++ b/htdocs/core/db/mysqli.class.php @@ -866,7 +866,7 @@ class DoliDBMysqli extends DoliDB $sqlfields[$i] .= " ".$this->sanitize($field_desc['null'], 0, 0, 1); } if (!is_null($field_desc['extra']) && $field_desc['extra'] !== '') { - $sqlfields[$i] .= " ".$this->sanitize($field_desc['extra']); + $sqlfields[$i] .= " ".$this->sanitize($field_desc['extra'], 0, 0, 1); } $i++; } @@ -940,7 +940,7 @@ class DoliDBMysqli extends DoliDB public function DDLDescTable($table, $field = "") { // phpcs:enable - $sql = "DESC ".$table." ".$field; + $sql = "DESC ".$this->sanitize($table)." ".$this->sanitize($field); dol_syslog(get_class($this)."::DDLDescTable ".$sql, LOG_DEBUG); $this->_results = $this->query($sql); @@ -962,30 +962,32 @@ class DoliDBMysqli extends DoliDB // phpcs:enable // cles recherchees dans le tableau des descriptions (field_desc) : type,value,attribute,null,default,extra // ex. : $field_desc = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment'); - $sql = "ALTER TABLE ".$table." ADD ".$field_name." "; - $sql .= $field_desc['type']; - if (preg_match("/^[^\s]/i", $field_desc['value'])) { - if (!in_array($field_desc['type'], array('date', 'datetime')) && $field_desc['value']) { - $sql .= "(".$field_desc['value'].")"; + $sql = "ALTER TABLE ".$this->sanitize($table)." ADD ".$this->sanitize($field_name)." "; + $sql .= $this->sanitize($field_desc['type']); + if (isset($field_desc['value']) && preg_match("/^[^\s]/i", $field_desc['value'])) { + if (!in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'date', 'datetime')) && $field_desc['value']) { + $sql .= "(".$this->sanitize($field_desc['value']).")"; } } if (isset($field_desc['attribute']) && preg_match("/^[^\s]/i", $field_desc['attribute'])) { - $sql .= " ".$field_desc['attribute']; + $sql .= " ".$this->sanitize($field_desc['attribute']); } if (isset($field_desc['null']) && preg_match("/^[^\s]/i", $field_desc['null'])) { $sql .= " ".$field_desc['null']; } if (isset($field_desc['default']) && preg_match("/^[^\s]/i", $field_desc['default'])) { - if (preg_match("/null/i", $field_desc['default'])) { - $sql .= " default ".$field_desc['default']; + if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) { + $sql .= " DEFAULT ".((float) $field_desc['default']); + } elseif ($field_desc['default'] == 'null' || $field_desc['default'] == 'CURRENT_TIMESTAMP') { + $sql .= " DEFAULT ".$this->sanitize($field_desc['default']); } else { - $sql .= " default '".$this->escape($field_desc['default'])."'"; + $sql .= " DEFAULT '".$this->escape($field_desc['default'])."'"; } } if (isset($field_desc['extra']) && preg_match("/^[^\s]/i", $field_desc['extra'])) { - $sql .= " ".$field_desc['extra']; + $sql .= " ".$this->escape($field_desc['extra'], 0, 0, 1); } - $sql .= " ".$field_position; + $sql .= " ".$this->escape($field_position, 0, 0, 1); dol_syslog(get_class($this)."::DDLAddField ".$sql, LOG_DEBUG); if ($this->query($sql)) { @@ -1006,18 +1008,18 @@ class DoliDBMysqli extends DoliDB public function DDLUpdateField($table, $field_name, $field_desc) { // phpcs:enable - $sql = "ALTER TABLE ".$table; - $sql .= " MODIFY COLUMN ".$field_name." ".$field_desc['type']; + $sql = "ALTER TABLE ".$this->sanitize($table); + $sql .= " MODIFY COLUMN ".$this->sanitize($field_name)." ".$this->sanitize($field_desc['type']); if (in_array($field_desc['type'], array('double', 'tinyint', 'int', 'varchar')) && $field_desc['value']) { - $sql .= "(".$field_desc['value'].")"; + $sql .= "(".$this->sanitize($field_desc['value']).")"; } if ($field_desc['null'] == 'not null' || $field_desc['null'] == 'NOT NULL') { // We will try to change format of column to NOT NULL. To be sure the ALTER works, we try to update fields that are NULL if ($field_desc['type'] == 'varchar' || $field_desc['type'] == 'text') { - $sqlbis = "UPDATE ".$table." SET ".$field_name." = '".$this->escape(isset($field_desc['default']) ? $field_desc['default'] : '')."' WHERE ".$field_name." IS NULL"; + $sqlbis = "UPDATE ".$this->sanitize($table)." SET ".$this->sanitize($field_name)." = '".$this->escape(isset($field_desc['default']) ? $field_desc['default'] : '')."' WHERE ".$this->sanitize($field_name)." IS NULL"; $this->query($sqlbis); - } elseif ($field_desc['type'] == 'tinyint' || $field_desc['type'] == 'int') { - $sqlbis = "UPDATE ".$table." SET ".$field_name." = ".((int) $this->escape(isset($field_desc['default']) ? $field_desc['default'] : 0))." WHERE ".$field_name." IS NULL"; + } elseif (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) { + $sqlbis = "UPDATE ".$this->sanitize($table)." SET ".$this->sanitize($field_name)." = ".((float) $this->escape(isset($field_desc['default']) ? $field_desc['default'] : 0))." WHERE ".$this->sanitize($field_name)." IS NULL"; $this->query($sqlbis); } @@ -1053,7 +1055,7 @@ class DoliDBMysqli extends DoliDB // phpcs:enable $tmp_field_name = preg_replace('/[^a-z0-9\.\-\_]/i', '', $field_name); - $sql = "ALTER TABLE ".$table." DROP COLUMN `".$tmp_field_name."`"; + $sql = "ALTER TABLE ".$this->sanitize($table)." DROP COLUMN `".$this->sanitize($tmp_field_name)."`"; if ($this->query($sql)) { return 1; } diff --git a/htdocs/core/db/pgsql.class.php b/htdocs/core/db/pgsql.class.php index 3cfe5671085..51ea5e84447 100644 --- a/htdocs/core/db/pgsql.class.php +++ b/htdocs/core/db/pgsql.class.php @@ -1160,7 +1160,7 @@ class DoliDBPgsql extends DoliDB // phpcs:enable $tmptable = preg_replace('/[^a-z0-9\.\-\_]/i', '', $table); - $sql = "DROP TABLE ".$tmptable; + $sql = "DROP TABLE ".$this->sanitize($tmptable); if (!$this->query($sql)) { return -1; @@ -1169,31 +1169,6 @@ class DoliDBPgsql extends DoliDB } } - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Create a user to connect to database - * - * @param string $dolibarr_main_db_host Ip server - * @param string $dolibarr_main_db_user Name of user to create - * @param string $dolibarr_main_db_pass Password of user to create - * @param string $dolibarr_main_db_name Database name where user must be granted - * @return int Return integer <0 if KO, >=0 if OK - */ - public function DDLCreateUser($dolibarr_main_db_host, $dolibarr_main_db_user, $dolibarr_main_db_pass, $dolibarr_main_db_name) - { - // phpcs:enable - // Note: using ' on user does not works with pgsql - $sql = "CREATE USER ".$this->escape($dolibarr_main_db_user)." with password '".$this->escape($dolibarr_main_db_pass)."'"; - - dol_syslog(get_class($this)."::DDLCreateUser", LOG_DEBUG); // No sql to avoid password in log - $resql = $this->query($sql); - if (!$resql) { - return -1; - } - - return 1; - } - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** * Return a pointer of line with description of a table or field @@ -1231,30 +1206,32 @@ class DoliDBPgsql extends DoliDB // phpcs:enable // cles recherchees dans le tableau des descriptions (field_desc) : type,value,attribute,null,default,extra // ex. : $field_desc = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment'); - $sql = "ALTER TABLE ".$table." ADD ".$field_name." "; - $sql .= $field_desc['type']; - if (preg_match("/^[^\s]/i", $field_desc['value'])) { - if (!in_array($field_desc['type'], array('smallint', 'int', 'date', 'datetime')) && $field_desc['value']) { - $sql .= "(".$field_desc['value'].")"; + $sql = "ALTER TABLE ".$this->sanitize($table)." ADD ".$this->sanitize($field_name)." "; + $sql .= $this->sanitize($field_desc['type']); + if (isset($field_desc['value']) && preg_match("/^[^\s]/i", $field_desc['value'])) { + if (!in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'date', 'datetime')) && $field_desc['value']) { + $sql .= "(".$this->sanitize($field_desc['value']).")"; } } - if (preg_match("/^[^\s]/i", $field_desc['attribute'])) { - $sql .= " ".$field_desc['attribute']; + if (isset($field_desc['attribute']) && preg_match("/^[^\s]/i", $field_desc['attribute'])) { + $sql .= " ".$this->sanitize($field_desc['attribute']); } - if (preg_match("/^[^\s]/i", $field_desc['null'])) { + if (isset($field_desc['null']) && preg_match("/^[^\s]/i", $field_desc['null'])) { $sql .= " ".$field_desc['null']; } - if (preg_match("/^[^\s]/i", $field_desc['default'])) { - if (preg_match("/null/i", $field_desc['default'])) { - $sql .= " default ".$field_desc['default']; + if (isset($field_desc['default']) && preg_match("/^[^\s]/i", $field_desc['default'])) { + if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) { + $sql .= " DEFAULT ".((float) $field_desc['default']); + } elseif ($field_desc['default'] == 'null' || $field_desc['default'] == 'CURRENT_TIMESTAMP') { + $sql .= " DEFAULT ".$this->sanitize($field_desc['default']); } else { - $sql .= " default '".$this->escape($field_desc['default'])."'"; + $sql .= " DEFAULT '".$this->escape($field_desc['default'])."'"; } } - if (preg_match("/^[^\s]/i", $field_desc['extra'])) { - $sql .= " ".$field_desc['extra']; + if (isset($field_desc['extra']) && preg_match("/^[^\s]/i", $field_desc['extra'])) { + $sql .= " ".$this->escape($field_desc['extra'], 0, 0, 1); } - $sql .= " ".$field_position; + $sql .= " ".$this->escape($field_position, 0, 0, 1); dol_syslog($sql, LOG_DEBUG); if (!$this -> query($sql)) { @@ -1275,30 +1252,30 @@ class DoliDBPgsql extends DoliDB public function DDLUpdateField($table, $field_name, $field_desc) { // phpcs:enable - $sql = "ALTER TABLE ".$table; - $sql .= " ALTER COLUMN ".$this->escape($field_name)." TYPE ".$field_desc['type']; + $sql = "ALTER TABLE ".$this->sanitize($table); + $sql .= " ALTER COLUMN ".$this->sanitize($field_name)." TYPE ".$this->sanitize($field_desc['type']); if (preg_match("/^[^\s]/i", $field_desc['value'])) { if (!in_array($field_desc['type'], array('smallint', 'int', 'date', 'datetime')) && $field_desc['value']) { - $sql .= "(".$field_desc['value'].")"; + $sql .= "(".$this->sanitize($field_desc['value']).")"; } } if ($field_desc['null'] == 'not null' || $field_desc['null'] == 'NOT NULL') { // We will try to change format of column to NOT NULL. To be sure the ALTER works, we try to update fields that are NULL if ($field_desc['type'] == 'varchar' || $field_desc['type'] == 'text') { - $sqlbis = "UPDATE ".$table." SET ".$this->escape($field_name)." = '".$this->escape(isset($field_desc['default']) ? $field_desc['default'] : '')."' WHERE ".$this->escape($field_name)." IS NULL"; + $sqlbis = "UPDATE ".$this->sanitize($table)." SET ".$this->escape($field_name)." = '".$this->escape(isset($field_desc['default']) ? $field_desc['default'] : '')."' WHERE ".$this->escape($field_name)." IS NULL"; $this->query($sqlbis); - } elseif ($field_desc['type'] == 'tinyint' || $field_desc['type'] == 'int') { - $sqlbis = "UPDATE ".$table." SET ".$this->escape($field_name)." = ".((int) $this->escape(isset($field_desc['default']) ? $field_desc['default'] : 0))." WHERE ".$this->escape($field_name)." IS NULL"; + } elseif (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) { + $sqlbis = "UPDATE ".$this->sanitize($table)." SET ".$this->escape($field_name)." = ".((float) $this->escape(isset($field_desc['default']) ? $field_desc['default'] : 0))." WHERE ".$this->escape($field_name)." IS NULL"; $this->query($sqlbis); } } if (isset($field_desc['default']) && $field_desc['default'] != '') { - if ($field_desc['type'] == 'double' || $field_desc['type'] == 'tinyint' || $field_desc['type'] == 'int') { - $sql .= ", ALTER COLUMN ".$this->escape($field_name)." SET DEFAULT ".((float) $field_desc['default']); + if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) { + $sql .= ", ALTER COLUMN ".$this->sanitize($field_name)." SET DEFAULT ".((float) $field_desc['default']); } elseif ($field_desc['type'] != 'text') { // Default not supported on text fields ? - $sql .= ", ALTER COLUMN ".$this->escape($field_name)." SET DEFAULT '".$this->escape($field_desc['default'])."'"; + $sql .= ", ALTER COLUMN ".$this->sanitize($field_name)." SET DEFAULT '".$this->escape($field_desc['default'])."'"; } } @@ -1322,7 +1299,7 @@ class DoliDBPgsql extends DoliDB // phpcs:enable $tmp_field_name = preg_replace('/[^a-z0-9\.\-\_]/i', '', $field_name); - $sql = "ALTER TABLE ".$table." DROP COLUMN ".$tmp_field_name; + $sql = "ALTER TABLE ".$this->sanitize($table)." DROP COLUMN ".$this->sanitize($tmp_field_name); if (!$this->query($sql)) { $this->error = $this->lasterror(); return -1; @@ -1330,6 +1307,31 @@ class DoliDBPgsql extends DoliDB return 1; } + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Create a user to connect to database + * + * @param string $dolibarr_main_db_host Ip server + * @param string $dolibarr_main_db_user Name of user to create + * @param string $dolibarr_main_db_pass Password of user to create + * @param string $dolibarr_main_db_name Database name where user must be granted + * @return int Return integer <0 if KO, >=0 if OK + */ + public function DDLCreateUser($dolibarr_main_db_host, $dolibarr_main_db_user, $dolibarr_main_db_pass, $dolibarr_main_db_name) + { + // phpcs:enable + // Note: using ' on user does not works with pgsql + $sql = "CREATE USER ".$this->sanitize($dolibarr_main_db_user)." with password '".$this->escape($dolibarr_main_db_pass)."'"; + + dol_syslog(get_class($this)."::DDLCreateUser", LOG_DEBUG); // No sql to avoid password in log + $resql = $this->query($sql); + if (!$resql) { + return -1; + } + + return 1; + } + /** * Return charset used to store data in database * diff --git a/htdocs/core/db/sqlite3.class.php b/htdocs/core/db/sqlite3.class.php index 477d7d33c42..392b5addffa 100644 --- a/htdocs/core/db/sqlite3.class.php +++ b/htdocs/core/db/sqlite3.class.php @@ -1118,16 +1118,18 @@ class DoliDBSqlite3 extends DoliDB $sql .= " ".$field_desc['null']; } if (preg_match("/^[^\s]/i", $field_desc['default'])) { - if (preg_match("/null/i", $field_desc['default'])) { - $sql .= " default ".$field_desc['default']; + if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) { + $sql .= " DEFAULT ".((float) $field_desc['default']); + } elseif ($field_desc['default'] == 'null' || $field_desc['default'] == 'CURRENT_TIMESTAMP') { + $sql .= " DEFAULT ".$this->sanitize($field_desc['default']); } else { - $sql .= " default '".$this->escape($field_desc['default'])."'"; + $sql .= " DEFAULT '".$this->escape($field_desc['default'])."'"; } } - if (preg_match("/^[^\s]/i", $field_desc['extra'])) { - $sql .= " ".$field_desc['extra']; + if (isset($field_desc['extra']) && preg_match("/^[^\s]/i", $field_desc['extra'])) { + $sql .= " ".$this->escape($field_desc['extra'], 0, 0, 1); } - $sql .= " ".$field_position; + $sql .= " ".$this->escape($field_position, 0, 0, 1); dol_syslog(get_class($this)."::DDLAddField ".$sql, LOG_DEBUG); if (!$this->query($sql)) { @@ -1148,10 +1150,10 @@ class DoliDBSqlite3 extends DoliDB public function DDLUpdateField($table, $field_name, $field_desc) { // phpcs:enable - $sql = "ALTER TABLE ".$table; - $sql .= " MODIFY COLUMN ".$field_name." ".$field_desc['type']; + $sql = "ALTER TABLE ".$this->sanitize($table); + $sql .= " MODIFY COLUMN ".$this->sanitize(($field_name)." ".$this->sanitize($field_desc['type']); if ($field_desc['type'] == 'tinyint' || $field_desc['type'] == 'int' || $field_desc['type'] == 'varchar') { - $sql .= "(".$field_desc['value'].")"; + $sql .= "(".$this->sanitize($field_desc['value']).")"; } dol_syslog(get_class($this)."::DDLUpdateField ".$sql, LOG_DEBUG); @@ -1174,7 +1176,7 @@ class DoliDBSqlite3 extends DoliDB // phpcs:enable $tmp_field_name = preg_replace('/[^a-z0-9\.\-\_]/i', '', $field_name); - $sql = "ALTER TABLE ".$table." DROP COLUMN `".$tmp_field_name."`"; + $sql = "ALTER TABLE ".$this->sanitize($table)." DROP COLUMN `".$this->sanitize($tmp_field_name)."`"; if (!$this->query($sql)) { $this->error = $this->lasterror(); return -1; diff --git a/htdocs/core/lib/modulebuilder.lib.php b/htdocs/core/lib/modulebuilder.lib.php index 159c5712399..1ae0d52d93a 100644 --- a/htdocs/core/lib/modulebuilder.lib.php +++ b/htdocs/core/lib/modulebuilder.lib.php @@ -1288,7 +1288,7 @@ function createNewDictionnary($modulename, $file, $namedic, $dictionnaires = nul } $columns = array( - 'rowid' => array('type' => 'integer', 'value' => 11), + 'rowid' => array('type' => 'integer', 'value' => 11, 'extra' => 'AUTO_INCREMENT PRIMARY KEY'), 'code' => array('type' => 'varchar', 'value' => 255, 'null'=>'NOT NULL'), 'label' => array('type' => 'varchar', 'value' => 255, 'null'=>'NOT NULL'), 'position' => array('type' => 'integer', 'value' => 11, 'null'=>'NULL'), diff --git a/test/phpunit/DoliDBTest.php b/test/phpunit/DoliDBTest.php index 34f70e459c9..adafc6a676c 100644 --- a/test/phpunit/DoliDBTest.php +++ b/test/phpunit/DoliDBTest.php @@ -66,7 +66,7 @@ class DoliDBTest extends CommonClassTest $res = $db->DDLDropTable($namedic); $columns = array( - 'rowid' => array('type' => 'integer', 'value' => 11), + 'rowid' => array('type' => 'integer', 'value' => 11, 'AUTO_INCREMENT PRIMARY KEY'), 'code' => array('type' => 'varchar', 'value' => 255, 'null'=>'NOT NULL'), 'label' => array('type' => 'varchar', 'value' => 255, 'null'=>'NOT NULL'), 'position' => array('type' => 'integer', 'value' => 11, 'null'=>'NULL'), From ba9a84667225316dee92134d9a5a5178eb8af485 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 21 Mar 2024 12:50:30 +0100 Subject: [PATCH 36/36] Fix trans missing --- dev/translation/dynamic_translation_keys.lst | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/translation/dynamic_translation_keys.lst b/dev/translation/dynamic_translation_keys.lst index 6be815d4eca..47ab2e14a79 100644 --- a/dev/translation/dynamic_translation_keys.lst +++ b/dev/translation/dynamic_translation_keys.lst @@ -40,6 +40,7 @@ Reservation ACCOUNTING_ACCOUNT_CUSTOMER ACCOUNTING_ACCOUNT_CUSTOMER_Desc ACCOUNTING_ACCOUNT_CUSTOMER_RETAINED_WARRANTY +ACCOUNTING_ACCOUNT_EXPENSEREPORT ACCOUNTING_ACCOUNT_SUPPLIER ACCOUNTING_ACCOUNT_SUPPLIER_Desc ACCOUNTING_ACCOUNT_SUSPENSE