diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index d898628013e48..a992c0a13f635 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -36,7 +36,7 @@ Definition: As the Developer: -1. Check you agree with the terms of the [DCO - Developer's Certificate of Origin](https://github.com/Dolibarr/dolibarr/DCO) +1. Check you agree with the terms of the [DCO - Developer's Certificate of Origin](https://github.com/Dolibarr/dolibarr/blob/develop/DCO) 2. [Fork](https://help.github.com/articles/fork-a-repo) the [GitHub repository](https://github.com/Dolibarr/dolibarr). 3. Clone your fork. 4. Choose a branch(See the [Branches](#branches) section below). diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 49f32592868c3..5f75f64fc1600 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,6 @@ repos: exclude: | (?x)^(htdocs/includes/.*)$ # This checks that yaml files are correct - args: [--branch, develop, --pattern, \d+.0$] - id: check-yaml args: [--unsafe] # This checks that json files are correct diff --git a/build/phpstan/phpstan-baseline.neon b/build/phpstan/phpstan-baseline.neon index 1c7672653e5b6..f301d5c4b4a45 100644 --- a/build/phpstan/phpstan-baseline.neon +++ b/build/phpstan/phpstan-baseline.neon @@ -16908,18 +16908,6 @@ parameters: count: 1 path: ../../htdocs/core/tpl/objectline_view.tpl.php - - - message: '#^Variable \$action might not be defined\.$#' - identifier: variable.undefined - count: 2 - path: ../../htdocs/core/tpl/objectline_view.tpl.php - - - - message: '#^Variable \$i might not be defined\.$#' - identifier: variable.undefined - count: 5 - path: ../../htdocs/core/tpl/objectline_view.tpl.php - - message: '#^Variable \$objp might not be defined\.$#' identifier: variable.undefined @@ -24864,18 +24852,6 @@ parameters: count: 1 path: ../../htdocs/product/canvas/product/actions_card_product.class.php - - - message: '#^Variable \$canvas might not be defined\.$#' - identifier: variable.undefined - count: 1 - path: ../../htdocs/product/canvas/product/tpl/card_create.tpl.php - - - - message: '#^Variable \$refalreadyexists might not be defined\.$#' - identifier: variable.undefined - count: 1 - path: ../../htdocs/product/canvas/product/tpl/card_create.tpl.php - - message: '#^Property ActionsCardService\:\:\$field_list has no type specified\.$#' identifier: missingType.property @@ -24888,30 +24864,6 @@ parameters: count: 1 path: ../../htdocs/product/canvas/service/actions_card_service.class.php - - - message: '#^Cannot access property \$control on mixed\.$#' - identifier: property.nonObject - count: 2 - path: ../../htdocs/product/canvas/service/tpl/card_create.tpl.php - - - - message: '#^Variable \$canvas might not be defined\.$#' - identifier: variable.undefined - count: 1 - path: ../../htdocs/product/canvas/service/tpl/card_create.tpl.php - - - - message: '#^Variable \$refalreadyexists might not be defined\.$#' - identifier: variable.undefined - count: 1 - path: ../../htdocs/product/canvas/service/tpl/card_create.tpl.php - - - - message: '#^Variable \$this might not be defined\.$#' - identifier: variable.undefined - count: 2 - path: ../../htdocs/product/canvas/service/tpl/card_create.tpl.php - - message: '#^Negated boolean expression is always true\.$#' identifier: booleanNot.alwaysTrue @@ -30450,24 +30402,6 @@ parameters: count: 3 path: ../../htdocs/societe/canvas/company/tpl/card_view.tpl.php - - - message: '#^Variable \$canvas might not be defined\.$#' - identifier: variable.undefined - count: 1 - path: ../../htdocs/societe/canvas/individual/tpl/card_create.tpl.php - - - - message: '#^Variable \$canvas might not be defined\.$#' - identifier: variable.undefined - count: 1 - path: ../../htdocs/societe/canvas/individual/tpl/card_edit.tpl.php - - - - message: '#^Variable \$canvas might not be defined\.$#' - identifier: variable.undefined - count: 2 - path: ../../htdocs/societe/canvas/individual/tpl/card_view.tpl.php - - message: '#^Variable \$objcanvas might not be defined\.$#' identifier: variable.undefined diff --git a/htdocs/adherents/card.php b/htdocs/adherents/card.php index 0910e85b9d311..c4ea3c8fbce51 100644 --- a/htdocs/adherents/card.php +++ b/htdocs/adherents/card.php @@ -1351,7 +1351,7 @@ function initfieldrequired() { // EMail print ''.(getDolGlobalString("ADHERENT_MAIL_REQUIRED") ? '' : '').$langs->trans("EMail").(getDolGlobalString("ADHERENT_MAIL_REQUIRED") ? '' : '').''; - print ''.img_picto('', 'object_email', 'class="pictofixedwidth"').'email).'">'; + print ''.img_picto('', 'object_email', 'class="pictofixedwidth"').'email).'">'; // Website print ''.$form->editfieldkey('Web', 'member_url', GETPOST('member_url', 'alpha'), $object, 0).''; diff --git a/htdocs/admin/dict.php b/htdocs/admin/dict.php index ffabe10d68d83..360488a241015 100644 --- a/htdocs/admin/dict.php +++ b/htdocs/admin/dict.php @@ -8,7 +8,7 @@ * Copyright (C) 2011 Remy Younes * Copyright (C) 2012-2015 Marcos García * Copyright (C) 2012 Christophe Battarel - * Copyright (C) 2011-2023 Alexandre Spangaro + * Copyright (C) 2011-2024 Alexandre Spangaro * Copyright (C) 2015 Ferran Marcet * Copyright (C) 2016 Raphaël Doursenaud * Copyright (C) 2019-2024 Frédéric France @@ -723,10 +723,8 @@ 'supplier_proposal' => img_picto('', 'supplier_proposal', 'class="pictofixedwidth"').$langs->trans('SupplierProposal'), 'order_supplier' => img_picto('', 'supplier_order', 'class="pictofixedwidth"').$langs->trans('SupplierOrder'), 'invoice_supplier' => img_picto('', 'supplier_invoice', 'class="pictofixedwidth"').$langs->trans('SupplierBill'), + 'conferenceorbooth' => img_picto('', 'eventorganization', 'class="pictofixedwidth"').$langs->trans('ConferenceOrBooth'), ); - if (getDolGlobalString('MAIN_FEATURES_LEVEL') && getDolGlobalInt('MAIN_FEATURES_LEVEL') >= 2) { - $elementList['conferenceorbooth'] = img_picto('', 'eventorganization', 'class="pictofixedwidth"').$langs->trans('ConferenceOrBooth'); - } complete_elementList_with_modules($elementList); diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index 555778e7124b0..c8965d32c6ae0 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -5128,7 +5128,7 @@ public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, $resql = $this->db->query($sql); if ($resql && $this->db->num_rows($resql) > 0) { if ($showempty) { - $return .= ''; + $return .= ''; } while ($res = $this->db->fetch_object($resql)) { @@ -5144,6 +5144,8 @@ public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, } } $return .= ''; + + $return .= ajax_combobox($htmlname); } return $return; } diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index 6f47d17176133..0fd073885de84 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -1567,6 +1567,7 @@ function dol_get_object_properties($obj, $properties = []) * @param T $object Object to clone * @param int $native 0=Full isolation method, 1=Native PHP method, 2=Full isolation method keeping only scalar and array properties (recommended) * @return T Clone object + * * @see https://php.net/manual/language.oop5.cloning.php * @phan-suppress PhanTypeExpectedObjectPropAccess */ @@ -10523,7 +10524,7 @@ function verifCond($strToEvaluate, $onlysimplestring = '1') * @param int<0,1> $hideerrors 1=Hide errors * @param string $onlysimplestring '0' (deprecated, do not use it anymore)=Accept all chars, * '1' (most common use)=Accept only simple string with char 'a-z0-9\s^$_+-.*>&|=!?():"\',/@';', - * '2' (used for example for the compute property of extrafields)=Accept also '[]' + * '2' (used for example for the compute property of extrafields)=Accept also '<[]' * @return void|string Nothing or return result of eval (even if type can be int, it is safer to assume string and find all potential typing issues as abs(dol_eval(...)). * @see verifCond(), checkPHPCode() to see sanitizing rules that should be very close. * @phan-suppress PhanPluginUnsafeEval @@ -10551,21 +10552,31 @@ function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1' if ($onlysimplestring == '1' || $onlysimplestring == '2') { // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")' // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$object->canvas=="patient@cabinetmed"' - // We must accept with 2: (($reloadedobj = new Task($db)) && ($reloadedobj->fetchNoCompute($object->id) > 0) && ($secondloadedobj = new Project($db)) && ($secondloadedobj->fetchNoCompute($reloadedobj->fk_project) > 0)) ? $secondloadedobj->ref : "Parent project not found" + // We must accept with 2: (($reloadedobj = new Task($db)) && ($reloadedobj->fetchNoCompute($object->id) <= 99) && ($secondloadedobj = new Project($db)) && ($secondloadedobj->fetchNoCompute($reloadedobj->fk_project) > 0)) ? $secondloadedobj->ref : "Parent project not found" - // Check if there is dynamic call (first we check chars are all into use a whitelist chars) + // Check if there is dynamic call (first we check chars are all into a whitelist chars) $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@'; if ($onlysimplestring == '2') { - $specialcharsallowed .= '[]'; + $specialcharsallowed .= '<[]'; } if (getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL')) { $specialcharsallowed .= getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL'); } if (preg_match('/[^a-z0-9\s'.preg_quote($specialcharsallowed, '/').']/i', $s)) { if ($returnvalue) { - return 'Bad string syntax to evaluate (found chars that are not chars for a simple clean eval string): '.$s; + return 'Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): '.$s; + } else { + dol_syslog('Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): '.$s, LOG_WARNING); + return ''; + } + } + + // Check if there is a < or <= without spaces before/after + if (preg_match('/<=?[^\s]/', $s)) { + if ($returnvalue) { + return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found a < or <= without space before and after): '.$s; } else { - dol_syslog('Bad string syntax to evaluate (found chars that are not chars for a simple clean eval string): '.$s, LOG_WARNING); + dol_syslog('Bad string syntax to evaluate (mode '.$onlysimplestring.', found a < or <= without space before and after): '.$s, LOG_WARNING); return ''; } } @@ -10580,15 +10591,17 @@ function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1' } } - // Now we check if we try dynamic call (by removing white list pattern of using parenthesis then testing if a parenthesis exists) + // Now we check if we try dynamic call + // First we remove white list pattern of using parenthesis then testing if one open parenthesis exists $savescheck = ''; $scheck = $s; while ($scheck && $savescheck != $scheck) { $savescheck = $scheck; $scheck = preg_replace('/->[a-zA-Z0-9_]+\(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...' $scheck = preg_replace('/::[a-zA-Z0-9_]+\(/', '->__METHOD__', $scheck); // accept parenthesis in '...::method(...' - $scheck = preg_replace('/^\(/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with __PARENTHESIS__ with a space after to allow following substitutions - $scheck = preg_replace('/\s\(/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '... (' like in 'if ($a == 1)'. Must replace with __PARENTHESIS__ with a space after to allow following substitutions + $scheck = preg_replace('/^\(+/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with "__PARENTHESIS__ with a space after "to allow following substitutions + $scheck = preg_replace('/\&\&\s+\(/', '__ANDPARENTHESIS__ ', $scheck); // accept parenthesis in '... (' like in '&& (...'. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions + $scheck = preg_replace('/\|\|\s+\(/', '__ORPARENTHESIS__ ', $scheck); // accept parenthesis in '... (' like in '|| (...'. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function(' $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function(' $scheck = preg_replace('/^!\(/', '__NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '!(' @@ -10597,6 +10610,7 @@ function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1' } //print 'scheck='.$scheck." : ".strpos($scheck, '(')."
\n"; + // Now test if it remains 1 one parenthesis. if (strpos($scheck, '(') !== false) { if ($returnvalue) { return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found call of a function or method without using the direct name of the function): '.$s; diff --git a/htdocs/core/lib/invoice2.lib.php b/htdocs/core/lib/invoice2.lib.php index bf2e51a47fdf0..72703a72fc4db 100644 --- a/htdocs/core/lib/invoice2.lib.php +++ b/htdocs/core/lib/invoice2.lib.php @@ -47,12 +47,63 @@ * @param string $paymentbankid Only if payment on this bank account id * @param int[] $thirdpartiesid List of thirdparties id when using filter=excludethirdpartiesid or filter=onlythirdpartiesid * @param string $fileprefix Prefix to add into filename of generated PDF + * @param int $donotmerge 0=Default, 1=Disable the merge so do only the regeneration of PDFs. + * @param string $mode 'invoice' for invoices, 'proposal' for proposal, ... * @return int Error code */ -function rebuild_merge_pdf($db, $langs, $conf, $diroutputpdf, $newlangid, $filter, $dateafterdate, $datebeforedate, $paymentdateafter, $paymentdatebefore, $usestdout, $regenerate = '', $filesuffix = '', $paymentbankid = '', $thirdpartiesid = [], $fileprefix = 'mergedpdf') +function rebuild_merge_pdf($db, $langs, $conf, $diroutputpdf, $newlangid, $filter, $dateafterdate, $datebeforedate, $paymentdateafter, $paymentdatebefore, $usestdout, $regenerate = '', $filesuffix = '', $paymentbankid = '', $thirdpartiesid = [], $fileprefix = 'mergedpdf', $donotmerge = 0, $mode = 'invoice') { + if ($mode == 'invoice') { + require_once DOL_DOCUMENT_ROOT."/compta/facture/class/facture.class.php"; + require_once DOL_DOCUMENT_ROOT."/core/modules/facture/modules_facture.php"; + + $table = "facture"; + $dir_output = $conf->facture->dir_output; + $date = "datef"; + + if ($diroutputpdf == 'auto') { + $diroutputpdf = $conf->invoice->dir_output.'/temp'; + } + } elseif ($mode == 'order') { + require_once DOL_DOCUMENT_ROOT."/commande/class/commande.class.php"; + require_once DOL_DOCUMENT_ROOT."/core/modules/commande/modules_commande.php"; + + $table = "commande"; + $dir_output = $conf->order->dir_output; + $date = "date"; + + if ($diroutputpdf == 'auto') { + $diroutputpdf = $conf->order->dir_output.'/temp'; + } + } elseif ($mode == 'proposal') { + require_once DOL_DOCUMENT_ROOT."/comm/propal/class/propal.class.php"; + require_once DOL_DOCUMENT_ROOT."/core/modules/propale/modules_propale.php"; + + $table = "propal"; + $dir_output = $conf->propal->dir_output; + $date = "datep"; + + if ($diroutputpdf == 'auto') { + $diroutputpdf = $conf->propal->dir_output.'/temp'; + } + } elseif ($mode == 'shipment') { + require_once DOL_DOCUMENT_ROOT."/expedition/class/expedition.class.php"; + require_once DOL_DOCUMENT_ROOT."/core/modules/expedition/modules_expedition.php"; + + $table = "propal"; + $dir_output = $conf->shipment->dir_output; + $date = "date"; + + if ($diroutputpdf == 'auto') { + $diroutputpdf = $conf->shipment->dir_output.'/temp'; + } + } else { + print "Bad value for mode"; + return -1; + } + $sql = "SELECT DISTINCT f.rowid, f.ref"; - $sql .= " FROM ".MAIN_DB_PREFIX."facture as f"; + $sql .= " FROM ".MAIN_DB_PREFIX.$table." as f"; $sqlwhere = ''; $sqlorder = ''; if (in_array('all', $filter)) { @@ -65,10 +116,11 @@ function rebuild_merge_pdf($db, $langs, $conf, $diroutputpdf, $newlangid, $filte $sqlwhere .= " AND"; } $sqlwhere .= " f.fk_statut > 0"; - $sqlwhere .= " AND f.datef >= '".$db->idate($dateafterdate)."'"; - $sqlwhere .= " AND f.datef <= '".$db->idate($datebeforedate)."'"; - $sqlorder = " ORDER BY f.datef ASC"; + $sqlwhere .= " AND f.".$db->sanitize($date)." >= '".$db->idate($dateafterdate)."'"; + $sqlwhere .= " AND f.".$db->sanitize($date)." <= '".$db->idate($datebeforedate)."'"; + $sqlorder = " ORDER BY ".$db->sanitize($date)." ASC"; } + // Filter for invoices only if (in_array('nopayment', $filter)) { $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiement_facture as pf ON f.rowid = pf.fk_facture"; if (empty($sqlwhere)) { @@ -79,6 +131,7 @@ function rebuild_merge_pdf($db, $langs, $conf, $diroutputpdf, $newlangid, $filte $sqlwhere .= " f.fk_statut > 0"; $sqlwhere .= " AND pf.fk_paiement IS NULL"; } + // Filter for invoices only if (in_array('payments', $filter) || in_array('bank', $filter)) { $sql .= ", ".MAIN_DB_PREFIX."paiement_facture as pf, ".MAIN_DB_PREFIX."paiement as p"; if (in_array('bank', $filter)) { @@ -102,6 +155,7 @@ function rebuild_merge_pdf($db, $langs, $conf, $diroutputpdf, $newlangid, $filte } $sqlorder = " ORDER BY p.datep ASC"; } + // Filter for invoices only if (in_array('nodeposit', $filter)) { if (empty($sqlwhere)) { $sqlwhere = ' WHERE '; @@ -110,6 +164,7 @@ function rebuild_merge_pdf($db, $langs, $conf, $diroutputpdf, $newlangid, $filte } $sqlwhere .= ' type <> 3'; } + // Filter for invoices only if (in_array('noreplacement', $filter)) { if (empty($sqlwhere)) { $sqlwhere = ' WHERE '; @@ -118,6 +173,7 @@ function rebuild_merge_pdf($db, $langs, $conf, $diroutputpdf, $newlangid, $filte } $sqlwhere .= ' type <> 1'; } + // Filter for invoices only if (in_array('nocreditnote', $filter)) { if (empty($sqlwhere)) { $sqlwhere = ' WHERE '; @@ -167,9 +223,6 @@ function rebuild_merge_pdf($db, $langs, $conf, $diroutputpdf, $newlangid, $filte if ($resql = $db->query($sql)) { $num = $db->num_rows($resql); $cpt = 0; - $oldemail = ''; - $message = ''; - $total = ''; if ($num) { // First loop on each resultset to build PDF @@ -178,7 +231,17 @@ function rebuild_merge_pdf($db, $langs, $conf, $diroutputpdf, $newlangid, $filte while ($cpt < $num) { $obj = $db->fetch_object($resql); - $fac = new Facture($db); + $fac = null; + if ($mode == 'invoice') { + $fac = new Facture($db); + } elseif ($mode == 'order') { + $fac = new Commande($db); + } elseif ($mode == 'proposal') { + $fac = new Propal($db); + } elseif ($mode == 'shipment') { + $fac = new Expedition($db); + } + $result = $fac->fetch($obj->rowid); if ($result > 0) { $outputlangs = $langs; @@ -188,15 +251,15 @@ function rebuild_merge_pdf($db, $langs, $conf, $diroutputpdf, $newlangid, $filte $outputlangs->setDefaultLang($newlangid); } } - $filename = $conf->facture->dir_output.'/'.$fac->ref.'/'.$fac->ref.'.pdf'; + $filename = $dir_output.'/'.$fac->ref.'/'.$fac->ref.'.pdf'; if ($regenerate || !dol_is_file($filename)) { if ($usestdout) { - print "Build PDF for invoice ".$obj->ref." - Lang = ".$outputlangs->defaultlang."\n"; + print "Build PDF for document ".$obj->ref." - Lang = ".$outputlangs->defaultlang."\n"; } $result = $fac->generateDocument($regenerate ? $regenerate : $fac->model_pdf, $outputlangs); } else { if ($usestdout) { - print "PDF for invoice ".$obj->ref." already exists\n"; + print "PDF for document ".$obj->ref." already exists\n"; } } @@ -207,15 +270,18 @@ function rebuild_merge_pdf($db, $langs, $conf, $diroutputpdf, $newlangid, $filte if ($result <= 0) { $error++; if ($usestdout) { - print "Error: Failed to build PDF for invoice ".($fac->ref ? $fac->ref : ' id '.$obj->rowid)."\n"; + print "Error: Failed to build PDF for document ".($fac->ref ? $fac->ref : ' id '.$obj->rowid)."\n"; } else { - dol_syslog("Failed to build PDF for invoice ".($fac->ref ? $fac->ref : ' id '.$obj->rowid), LOG_ERR); + dol_syslog("Failed to build PDF for document ".($fac->ref ? $fac->ref : ' id '.$obj->rowid), LOG_ERR); } } $cpt++; } + if ($donotmerge) { + return 1; + } // Define format of output PDF $formatarray = pdf_getFormat($langs); diff --git a/htdocs/core/lib/memory.lib.php b/htdocs/core/lib/memory.lib.php index 239b9f26f1db7..78523378ec317 100644 --- a/htdocs/core/lib/memory.lib.php +++ b/htdocs/core/lib/memory.lib.php @@ -63,10 +63,11 @@ * @param string $memoryid Memory id of shared area * @param mixed $data Data to save. It must not be a null value. * @param int $expire ttl in seconds, 0 never expire + * @param int $filecache 1 Enable file cache if no other session cache available, 0 Disabled (default) * @return int Return integer <0 if KO, 0 if nothing is done, Nb of bytes written if OK * @see dol_getcache() */ -function dol_setcache($memoryid, $data, $expire = 0) +function dol_setcache($memoryid, $data, $expire = 0, $filecache = 0) { global $conf; @@ -124,6 +125,31 @@ function dol_setcache($memoryid, $data, $expire = 0) } elseif (getDolGlobalInt('MAIN_OPTIMIZE_SPEED') & 0x02) { // This is a really not reliable cache ! Use Memcached instead. // Using shmop $result = dol_setshmop($memoryid, $data, $expire); + } elseif ($filecache > 0) { + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + require_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php'; + require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; + $now = dol_now(); + $memoryid = session_name().'_'.$memoryid; + $dircache = 'dolcache'; + $pathcache = DOL_DATA_ROOT.'/'.$dircache; + if (!dol_is_dir($pathcache)) { + $result = dol_mkdir($pathcache); + if ($result < 0) { + return $result; + } + } + if ($expire != 0) { + $expire = dol_time_plus_duree($now, $expire, 's'); + } + + $cachedata = array("expire" => $expire, "data" => $data); + $cachejson = dolEncrypt(json_encode($cachedata)); + if (!dol_is_file($pathcache.'/'.$memoryid.'.cache')) { + $result = file_put_contents($pathcache.'/'.$memoryid.'.cache', $cachejson); + } else { + return 0; + } } else { // No intersession cache system available, we use at least the perpage cache $conf->cache['cachememory_'.$memoryid] = $data; @@ -137,10 +163,11 @@ function dol_setcache($memoryid, $data, $expire = 0) * Read a memory area shared by all users, all sessions on server * * @param string $memoryid Memory id of shared area + * @param int $filecache 1 Enable file cache if no other session cache available, 0 Disabled (default) * @return int|mixed Return integer <0 if KO, data if OK, null if not found into cache or no caching feature enabled * @see dol_setcache() */ -function dol_getcache($memoryid) +function dol_getcache($memoryid, $filecache = 0) { global $conf; @@ -203,6 +230,31 @@ function dol_getcache($memoryid) // Using shmop $data = dol_getshmop($memoryid); return $data; + } elseif ($filecache > 0) { + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + require_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php'; + require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; + $now = dol_now(); + $memoryid = session_name().'_'.$memoryid; + $dircache = 'dolcache'; + $pathcache = DOL_DATA_ROOT.'/'.$dircache; + if (!dol_is_file($pathcache.'/'.$memoryid.'.cache')) { + return null; + } + $data = file_get_contents($pathcache.'/'.$memoryid.'.cache'); + if (!$data) { + return -1; + } + $json = json_decode(dolDecrypt($data)); + if ($json->expire > $now) { + return $json->data; + } else { + $result = dol_delete_file($pathcache.'/'.$memoryid.'.cache'); + if (!$result) { + return -2; + } + } + return null; } else { // No intersession cache system available, we use at least the perpage cache if (isset($conf->cache['cachememory_'.$memoryid])) { diff --git a/htdocs/core/lib/modulebuilder.lib.php b/htdocs/core/lib/modulebuilder.lib.php index eff2df330a2c7..67d57193729b9 100644 --- a/htdocs/core/lib/modulebuilder.lib.php +++ b/htdocs/core/lib/modulebuilder.lib.php @@ -411,7 +411,7 @@ function rebuildObjectSql($destdir, $module, $objectname, $newmask, $readdir = ' foreach ($object->fields as $key => $val) { $i++; if (!empty($val['index'])) { - $texttoinsert .= "ALTER TABLE llx_".strtolower($module).'_'.strtolower($objectname)." ADD INDEX idx_".strtolower($module).'_'.strtolower($objectname)."_".$key." (".$key.");"; + $texttoinsert .= "ALTER TABLE llx_".strtolower($module).'_'.strtolower($objectname)." ADD ".($key == 'ref' ? "UNIQUE INDEX uk_" : "INDEX idx_").strtolower($module).'_'.strtolower($objectname)."_".$key." (".$key.($key == 'ref' && array_key_exists('entity', $object->fields) ? ", entity" : "").");"; $texttoinsert .= "\n"; } if (!empty($val['foreignkey'])) { diff --git a/htdocs/core/lib/website2.lib.php b/htdocs/core/lib/website2.lib.php index 0323e67265ada..c82484fc0566f 100644 --- a/htdocs/core/lib/website2.lib.php +++ b/htdocs/core/lib/website2.lib.php @@ -720,10 +720,19 @@ function checkPHPCode(&$phpfullcodestringold, &$phpfullcodestring) } } + $phpfullcodestringnew = $phpfullcodestring; + // Then check forbidden commands if (!$error) { - $forbiddenphpstrings = array('$$', '$_', '}['); - //$forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction')); + if (getDolGlobalString("WEBSITE_DISALLOW_DOLLAR_UNDERSCORE")) { + $phpfullcodestring = preg_replace('/\$_COOKIE\[/', '__DOLLARCOOKIE__', $phpfullcodestring); + $phpfullcodestring = preg_replace('/\$_FILES\[/', '__DOLLARFILES__', $phpfullcodestring); + $phpfullcodestring = preg_replace('/\$_SESSION\[/', '__DOLLARSESSION__', $phpfullcodestring); + $forbiddenphpstrings = array('$$', '$_', '}['); + } else { + $forbiddenphpstrings = array('$$', '}['); + } + //$forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_FILES', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction')); $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', 'ReflectionFunction')); $forbiddenphpfunctions = array(); @@ -818,8 +827,8 @@ function checkPHPCode(&$phpfullcodestringold, &$phpfullcodestring) // No need to block $conf->global->aaa() because PHP try to run the method aaa of $conf->global and not the function into $conf->global->aaa. - // Then check if installmodules does not block dynamic PHP code change. - if ($phpfullcodestringold != $phpfullcodestring) { + // Then check if installmodules.lock does not block dynamic PHP code change. + if ($phpfullcodestringold != $phpfullcodestringnew) { if (!$error) { $dolibarrdataroot = preg_replace('/([\\/]+)$/i', '', DOL_DATA_ROOT); $allowimportsite = true; diff --git a/htdocs/core/tpl/objectline_create.tpl.php b/htdocs/core/tpl/objectline_create.tpl.php index 4cf2258fded39..96cfc189c13c2 100644 --- a/htdocs/core/tpl/objectline_create.tpl.php +++ b/htdocs/core/tpl/objectline_create.tpl.php @@ -1317,7 +1317,7 @@ function setforpredef() { jQuery("#select_type").val(-1); jQuery("#select_type").addClass("placeholder"); - jQuery("#select_type").trigger("change"); + /* jQuery("#select_type").trigger("change"); // Disabled. This create troubles. Never mind if the rester of combo is not done when using an ajax select_type combo. We don't use it because we are not able to call a focus on a change event of this combo. */ jQuery("#prod_entry_mode_free").prop('checked',false).change(); jQuery("#prod_entry_mode_predef").prop('checked',true).change(); diff --git a/htdocs/core/tpl/objectline_view.tpl.php b/htdocs/core/tpl/objectline_view.tpl.php index 9e34f4ba39fb5..93bc95c0a1291 100644 --- a/htdocs/core/tpl/objectline_view.tpl.php +++ b/htdocs/core/tpl/objectline_view.tpl.php @@ -52,6 +52,8 @@ * @var Translate $langs * @var User $user * + * @var string $action + * @var int $i * @var 0|1 $forceall * @var int $num * @var 0|1 $senderissupplier diff --git a/htdocs/document.php b/htdocs/document.php index 6ed099120436d..067eac0d7bbed 100644 --- a/htdocs/document.php +++ b/htdocs/document.php @@ -129,7 +129,7 @@ function llxFooter($comment = '', $zone = 'private', $disabledoutputofmessages = $hashp = GETPOST('hashp', 'aZ09'); $modulepart = GETPOST('modulepart', 'alpha'); $urlsource = GETPOST('urlsource', 'alpha'); -$entity = GETPOSTINT('entity') ? GETPOSTINT('entity') : $conf->entity; +$entity = GETPOSTINT('entity'); // Security check if (empty($modulepart) && empty($hashp)) { diff --git a/htdocs/install/mysql/data/llx_c_type_contact.sql b/htdocs/install/mysql/data/llx_c_type_contact.sql index 50a952bbea129..39813dfb70fff 100644 --- a/htdocs/install/mysql/data/llx_c_type_contact.sql +++ b/htdocs/install/mysql/data/llx_c_type_contact.sql @@ -125,4 +125,4 @@ insert into llx_c_type_contact (element, source, code, libelle, active ) values insert into llx_c_type_contact (element, source, code, libelle, active ) values ('conferenceorbooth', 'external', 'RESPONSIBLE', 'Booth responsible', 1); -- Thirdparty -insert into llx_c_type_contact (element, source, code, libelle, active ) values ('societe', 'internal', 'SALESREPTHIRD', 'Sales Representative', 1); +insert into llx_c_type_contact (element, source, code, libelle, active ) values ('societe', 'external', 'SALESREPTHIRD', 'Sales Representative', 1); diff --git a/htdocs/install/pgsql/functions/functions-bom.sql b/htdocs/install/pgsql/functions/functions-bom.sql new file mode 100644 index 0000000000000..e31c64e170ec2 --- /dev/null +++ b/htdocs/install/pgsql/functions/functions-bom.sql @@ -0,0 +1,23 @@ +-- ============================================================================ +-- Copyright (C) 2024 Laurent Destailleur +-- +-- 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 . +-- +-- ============================================================================ + + +CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_bom_bom FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); +CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_bom_bom_extrafields FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); +CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_bom_bomline FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); +CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_bom_bomline_extrafields FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); diff --git a/htdocs/install/pgsql/functions/functions-mo.sql b/htdocs/install/pgsql/functions/functions-mo.sql new file mode 100644 index 0000000000000..37b92ae526919 --- /dev/null +++ b/htdocs/install/pgsql/functions/functions-mo.sql @@ -0,0 +1,23 @@ +-- ============================================================================ +-- Copyright (C) 2024 Laurent Destailleur +-- +-- 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 . +-- +-- ============================================================================ + + +CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_mrp_mo FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); +CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_mrp_mo_extrafields FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); +CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_mrp_production FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); +CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_mrp_production_extrafields FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); diff --git a/htdocs/install/pgsql/functions/functions.sql b/htdocs/install/pgsql/functions/functions.sql index 1d3587c8e7cc4..5260a44a8c3ce 100644 --- a/htdocs/install/pgsql/functions/functions.sql +++ b/htdocs/install/pgsql/functions/functions.sql @@ -75,10 +75,6 @@ CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_bank_account FOR EAC CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_bank_account_extrafields FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_blockedlog FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_blockedlog_authority FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); -CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_bom_bom FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); -CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_bom_bom_extrafields FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); -CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_bom_bomline FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); -CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_bom_bomline_extrafields FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_bordereau_cheque FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_boxes_def FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_c_email_templates FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); @@ -127,10 +123,6 @@ CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_fichinterdet_extrafi CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_delivery FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_localtax FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_menu FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); -CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_mrp_mo FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); -CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_mrp_mo_extrafields FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); -CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_mrp_production FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); -CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_mrp_production_extrafields FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_notify FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_notify_def FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON llx_paiement FOR EACH ROW EXECUTE PROCEDURE update_modified_column_tms(); diff --git a/htdocs/langs/en_US/companies.lang b/htdocs/langs/en_US/companies.lang index bff22a33e9fe6..4bb38cda648eb 100644 --- a/htdocs/langs/en_US/companies.lang +++ b/htdocs/langs/en_US/companies.lang @@ -33,8 +33,8 @@ CountryIsInEEC=Country is inside the European Economic Community PriceFormatInCurrentLanguage=Price display format in the current language and currency ThirdPartyName=Third-party name ThirdPartyEmail=Third-party email -ThirdParty=Third-party -ThirdParties=Third-parties +ThirdParty=Third party +ThirdParties=Third parties ThirdPartyProspects=Prospects ThirdPartyProspectsStats=Prospects ThirdPartyCustomers=Customers diff --git a/htdocs/langs/en_US/main.lang b/htdocs/langs/en_US/main.lang index 4099d76274578..73e7e6fd306b0 100644 --- a/htdocs/langs/en_US/main.lang +++ b/htdocs/langs/en_US/main.lang @@ -1155,6 +1155,7 @@ ContactDefault_project_task=Task ContactDefault_propal=Proposal ContactDefault_supplier_proposal=Supplier Proposal ContactDefault_ticket=Ticket +ContactDefault_societe=Third party ContactAddedAutomatically=Contact added from third-party contact roles More=More ShowDetails=Show details diff --git a/htdocs/langs/en_US/products.lang b/htdocs/langs/en_US/products.lang index 2d8d05e9761fc..ededc52cbfc5c 100644 --- a/htdocs/langs/en_US/products.lang +++ b/htdocs/langs/en_US/products.lang @@ -209,6 +209,7 @@ unitL=Liter unitT=ton unitKG=kg unitG=Gram +unitGAL=gallon unitMG=mg unitLB=pound unitOZ=ounce diff --git a/htdocs/product/canvas/product/tpl/card_create.tpl.php b/htdocs/product/canvas/product/tpl/card_create.tpl.php index 5e4621b24c1a7..1f750f1108a48 100644 --- a/htdocs/product/canvas/product/tpl/card_create.tpl.php +++ b/htdocs/product/canvas/product/tpl/card_create.tpl.php @@ -1,6 +1,6 @@ - * Copyright (C) 2024 Frédéric France +/* Copyright (C) 2010-2018 Regis Houssin + * Copyright (C) 2024 Frédéric France * * 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 @@ -16,10 +16,14 @@ * along with this program. If not, see . */ /** + * @var Canvas $this * @var Conf $conf * @var Form $form * @var Translate $langs * @var User $user + * + * @var string $canvas + * @var int $refalreadyexists */ // Protection to avoid direct call of template if (empty($conf) || !is_object($conf)) { diff --git a/htdocs/product/canvas/product/tpl/card_edit.tpl.php b/htdocs/product/canvas/product/tpl/card_edit.tpl.php index b6a95262e0dd0..1248221093495 100644 --- a/htdocs/product/canvas/product/tpl/card_edit.tpl.php +++ b/htdocs/product/canvas/product/tpl/card_edit.tpl.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2010-2018 Regis Houssin * Copyright (C) 2024 Frédéric France * * This program is free software; you can redistribute it and/or modify diff --git a/htdocs/product/canvas/product/tpl/card_view.tpl.php b/htdocs/product/canvas/product/tpl/card_view.tpl.php index 3cf61f55d2fc1..e7b26f58762ec 100644 --- a/htdocs/product/canvas/product/tpl/card_view.tpl.php +++ b/htdocs/product/canvas/product/tpl/card_view.tpl.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2010-2018 Regis Houssin * Copyright (C) 2024 Frédéric France * * This program is free software; you can redistribute it and/or modify diff --git a/htdocs/product/canvas/service/tpl/card_create.tpl.php b/htdocs/product/canvas/service/tpl/card_create.tpl.php index 875b790e68841..6266cdbda478e 100644 --- a/htdocs/product/canvas/service/tpl/card_create.tpl.php +++ b/htdocs/product/canvas/service/tpl/card_create.tpl.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2010-2018 Regis Houssin * Copyright (C) 2024 Frédéric France * * This program is free software; you can redistribute it and/or modify @@ -16,10 +16,14 @@ * along with this program. If not, see . */ /** + * @var Canvas $this * @var Conf $conf * @var Form $form * @var Translate $langs * @var User $user + * + * @var string $canvas + * @var int $refalreadyexists */ // Protection to avoid direct call of template if (empty($conf) || !is_object($conf)) { diff --git a/htdocs/product/canvas/service/tpl/card_edit.tpl.php b/htdocs/product/canvas/service/tpl/card_edit.tpl.php index eb40955a2009e..a95415aac722d 100644 --- a/htdocs/product/canvas/service/tpl/card_edit.tpl.php +++ b/htdocs/product/canvas/service/tpl/card_edit.tpl.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2010-2018 Regis Houssin * Copyright (C) 2024 Frédéric France * * This program is free software; you can redistribute it and/or modify diff --git a/htdocs/product/canvas/service/tpl/card_view.tpl.php b/htdocs/product/canvas/service/tpl/card_view.tpl.php index 2b48f913b0e88..b5fd7372b3989 100644 --- a/htdocs/product/canvas/service/tpl/card_view.tpl.php +++ b/htdocs/product/canvas/service/tpl/card_view.tpl.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2010-2018 Regis Houssin * Copyright (C) 2024 Frédéric France * * This program is free software; you can redistribute it and/or modify diff --git a/htdocs/product/list.php b/htdocs/product/list.php index 0f7d57db40c8d..287e00cd4c96c 100644 --- a/htdocs/product/list.php +++ b/htdocs/product/list.php @@ -119,7 +119,7 @@ $search_accountancy_code_buy_export = GETPOST("search_accountancy_code_buy_export", 'alpha'); $search_import_key = GETPOST("search_import_key", 'alpha'); $search_finished = GETPOST("search_finished"); -$search_units = GETPOST('search_units', 'alpha'); +$search_units = GETPOST('search_units', 'int'); $type = GETPOST("type", 'alpha'); // Show/hide child product variants @@ -583,6 +583,7 @@ if (dol_strlen($canvas) > 0) { $sql .= " AND p.canvas = '".$db->escape($canvas)."'"; } + // Search for tag/category ($searchCategoryProductList is an array of ID) if (!empty($searchCategoryProductList)) { $searchCategoryProductSqlList = array(); @@ -641,9 +642,10 @@ if ($search_accountancy_code_buy_export) { $sql .= natural_search($alias_product_perentity . '.accountancy_code_buy_export', clean_account($search_accountancy_code_buy_export)); } -if (getDolGlobalString('PRODUCT_USE_UNITS') && $search_units) { +if (getDolGlobalString('PRODUCT_USE_UNITS') && $search_units && $search_units != '-1') { $sql .= natural_search('cu.rowid', $search_units); } + // Add where from extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php'; // Add where from hooks diff --git a/htdocs/projet/element.php b/htdocs/projet/element.php index 6f3e138c5875a..f4ffea34d182b 100644 --- a/htdocs/projet/element.php +++ b/htdocs/projet/element.php @@ -1324,10 +1324,11 @@ print "\n"; // Ref - print ''; + print ''; if ($tablename == 'expensereport_det') { print $expensereport->getNomUrl(1); } else { + print ''; + + print '
'; // Show ref with link if ($element instanceof Task) { print $element->getNomUrl(1, 'withproject', 'time'); @@ -1363,6 +1364,10 @@ } print ''; + print ''; + // Show supplier ref if (!empty($element->ref_supplier)) { print ' - '.$element->ref_supplier; @@ -1375,6 +1380,8 @@ if (empty($element->ref_customer) && !empty($element->ref_client)) { print ' - '.$element->ref_client; } + + print '
'; } print "\n"; // Product and qty on stock movement diff --git a/htdocs/projet/list.php b/htdocs/projet/list.php index 9170ba31ed3bc..77eda021f11a9 100644 --- a/htdocs/projet/list.php +++ b/htdocs/projet/list.php @@ -256,12 +256,12 @@ include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_array_fields.tpl.php'; // Add non object fields to fields for list -$arrayfields['s.nom'] = array('label' => $langs->trans("ThirdParty"), 'checked' => 1, 'position' => 21, 'enabled' => (!isModEnabled('societe') ? 0 : 1)); +$arrayfields['s.nom'] = array('label' => "ThirdParty", 'checked' => 1, 'position' => 21, 'enabled' => (!isModEnabled('societe') ? 0 : 1)); $arrayfields['s.name_alias'] = array('label' => "AliasNameShort", 'checked' => 0, 'position' => 22); $arrayfields['co.country_code'] = array('label' => "Country", 'checked' => -1, 'position' => 23); -$arrayfields['commercial'] = array('label' => $langs->trans("SaleRepresentativesOfThirdParty"), 'checked' => 0, 'position' => 25); -$arrayfields['c.assigned'] = array('label' => $langs->trans("AssignedTo"), 'checked' => 1, 'position' => 120); -$arrayfields['opp_weighted_amount'] = array('label' => $langs->trans('OpportunityWeightedAmountShort'), 'checked' => 0, 'enabled' => (!getDolGlobalString('PROJECT_USE_OPPORTUNITIES') ? 0 : 1), 'position' => 106); +$arrayfields['commercial'] = array('label' => "SaleRepresentativesOfThirdParty", 'checked' => 0, 'position' => 25); +$arrayfields['c.assigned'] = array('label' => "AssignedTo", 'checked' => 1, 'position' => 120); +$arrayfields['opp_weighted_amount'] = array('label' => 'OpportunityWeightedAmountShort', 'checked' => 0, 'enabled' => (!getDolGlobalString('PROJECT_USE_OPPORTUNITIES') ? 0 : 1), 'position' => 106); $arrayfields['u.login'] = array('label' => "Author", 'checked' => -1, 'position' => 165); // Force some fields according to search_usage filter... if (GETPOST('search_usage_opportunity')) { diff --git a/htdocs/reception/class/reception.class.php b/htdocs/reception/class/reception.class.php index 22c1ad326ba7f..9cc8d48b56bc7 100644 --- a/htdocs/reception/class/reception.class.php +++ b/htdocs/reception/class/reception.class.php @@ -1112,7 +1112,9 @@ public function delete(User $user) $this->db->begin(); // Stock control - if (isModEnabled('stock') && !getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION') && $this->statut > 0) { + if (isModEnabled('stock') && ((getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION') && $this->status > Reception::STATUS_DRAFT) + || (getDolGlobalInt('STOCK_CALCULATE_ON_RECEPTION_CLOSE') && $this->status == Reception::STATUS_CLOSED)) + ) { require_once DOL_DOCUMENT_ROOT."/product/stock/class/mouvementstock.class.php"; $langs->load("agenda"); @@ -1136,7 +1138,7 @@ public function delete(User $user) // we do not log origin because it will be deleted $mouvS->origin = null; - $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $obj->qty, 0, $langs->trans("ReceptionDeletedInDolibarr", $this->ref), '', $obj->eatby, $obj->sellby, $obj->batch); // Price is set to 0, because we don't want to see WAP changed + $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $obj->qty, 0, $langs->trans("ReceptionDeletedInDolibarr", $this->ref), '', $obj->eatby ? $this->db->jdate($obj->eatby) : null, $obj->sellby ? $this->db->jdate($obj->sellby) : null, $obj->batch); // Price is set to 0, because we don't want to see WAP changed if ($result < 0) { $error++; $this->error = $mouvS->error; diff --git a/htdocs/societe/canvas/individual/tpl/card_create.tpl.php b/htdocs/societe/canvas/individual/tpl/card_create.tpl.php index ebd3dcb6e01c7..0620678fc026c 100644 --- a/htdocs/societe/canvas/individual/tpl/card_create.tpl.php +++ b/htdocs/societe/canvas/individual/tpl/card_create.tpl.php @@ -18,12 +18,15 @@ */ /** + * @var Canvas $this * @var Conf $conf * @var CommonObject $this * @var DoliDB $db * @var FormFile $formfile * @var Translate $langs * @var User $user + * + * @var string $canvas */ // Protection to avoid direct call of template if (empty($conf) || !is_object($conf)) { diff --git a/htdocs/societe/canvas/individual/tpl/card_edit.tpl.php b/htdocs/societe/canvas/individual/tpl/card_edit.tpl.php index d21880eb30385..3964a8a7491dc 100644 --- a/htdocs/societe/canvas/individual/tpl/card_edit.tpl.php +++ b/htdocs/societe/canvas/individual/tpl/card_edit.tpl.php @@ -18,12 +18,15 @@ */ /** + * @var Canvas $this * @var Conf $conf * @var CommonObject $this * @var DoliDB $db * @var FormFile $formfile * @var Translate $langs * @var User $user + * + * @var string $canvas */ // Protection to avoid direct call of template if (empty($conf) || !is_object($conf)) { diff --git a/htdocs/societe/canvas/individual/tpl/card_view.tpl.php b/htdocs/societe/canvas/individual/tpl/card_view.tpl.php index 2c0a6277ac4da..07e2afc2ae0e8 100644 --- a/htdocs/societe/canvas/individual/tpl/card_view.tpl.php +++ b/htdocs/societe/canvas/individual/tpl/card_view.tpl.php @@ -17,12 +17,15 @@ */ /** + * @var Canvas $this * @var Conf $conf * @var CommonObject $this * @var DoliDB $db * @var FormFile $formfile * @var Translate $langs * @var User $user + * + * @var string $canvas */ // Protection to avoid direct call of template if (empty($conf) || !is_object($conf)) { diff --git a/htdocs/ticket/class/ticket.class.php b/htdocs/ticket/class/ticket.class.php index 9121d0ec0325e..e0da84aa3d97a 100644 --- a/htdocs/ticket/class/ticket.class.php +++ b/htdocs/ticket/class/ticket.class.php @@ -3192,7 +3192,7 @@ public function load_board($user, $mode) while ($obj = $this->db->fetch_object($resql)) { $response->nbtodo++; if ($mode == 'opened') { - $datelimit = $this->db->jdate($obj->datec) + $delay_warning; + $datelimit = (int) $this->db->jdate($obj->datec) + (int) $delay_warning; if ($datelimit < $now) { //$response->nbtodolate++; } diff --git a/scripts/invoices/rebuild_merge_pdf.php b/scripts/invoices/rebuild_merge_pdf.php index d7ee2915281d3..c636f9e129a61 100755 --- a/scripts/invoices/rebuild_merge_pdf.php +++ b/scripts/invoices/rebuild_merge_pdf.php @@ -43,10 +43,9 @@ require_once $path."../../htdocs/master.inc.php"; require_once DOL_DOCUMENT_ROOT.'/core/lib/functionscli.lib.php'; // After this $db is an opened handler to database. We close it at end of file. -require_once DOL_DOCUMENT_ROOT."/compta/facture/class/facture.class.php"; -require_once DOL_DOCUMENT_ROOT."/core/modules/facture/modules_facture.php"; require_once DOL_DOCUMENT_ROOT."/core/lib/date.lib.php"; require_once DOL_DOCUMENT_ROOT.'/core/lib/invoice2.lib.php'; + /** * @var Conf $conf * @var DoliDB $db @@ -83,12 +82,15 @@ exit(1); } -$diroutputpdf = $conf->invoice->dir_output.'/temp'; + $newlangid = 'en_EN'; // To force a new lang id $filter = array(); $regenerate = ''; // Ask regenerate (contains name of model to use) $option = ''; $fileprefix = 'mergedpdf'; +$nomerge = 0; +$mode = 'invoice'; + $dateafterdate = ''; $datebeforedate = ''; $paymentdateafter = ''; @@ -113,6 +115,14 @@ print 'Use prefix for filename '.$fileprefix.".\n"; } + if (preg_match('/^mode=/i', $value)) { + $found = true; + $valarray = explode('=', $value); + $mode = $valarray[1]; + print 'Use mode '.$fileprefix.".\n"; + } + + $reg = array(); if (preg_match('/^regenerate=(.*)/i', $value, $reg)) { if (!in_array($reg[1], array('', '0', 'no'))) { $found = true; @@ -120,6 +130,14 @@ print 'Regeneration of PDF is requested with template '.$regenerate."\n"; } } + if (preg_match('/^regeneratenomerge=(.*)/i', $value, $reg)) { + if (!in_array($reg[1], array('', '0', 'no'))) { + $found = true; + $regenerate = $reg[1]; + $nomerge = 1; + print 'Regeneration (without merge) of PDF is requested with template '.$regenerate."\n"; + } + } if ($value == 'filter=all') { $found = true; @@ -136,7 +154,7 @@ $dateafterdate = dol_stringtotime($argv[$key + 1]); $datebeforedate = dol_stringtotime($argv[$key + 2]); - print 'Rebuild PDF for invoices validated between '.dol_print_date($dateafterdate, 'day', 'gmt')." and ".dol_print_date($datebeforedate, 'day', 'gmt').".\n"; + print 'Rebuild PDF for documents validated between '.dol_print_date($dateafterdate, 'day', 'gmt')." and ".dol_print_date($datebeforedate, 'day', 'gmt').".\n"; } if ($value == 'filter=payments') { @@ -150,7 +168,7 @@ print 'Error: Bad date format or value'."\n"; exit(1); } - print 'Rebuild PDF for invoices with at least one payment between '.dol_print_date($paymentdateafter, 'day', 'gmt')." and ".dol_print_date($paymentdatebefore, 'day', 'gmt').".\n"; + print 'Rebuild PDF for ivoices with at least one payment between '.dol_print_date($paymentdateafter, 'day', 'gmt')." and ".dol_print_date($paymentdatebefore, 'day', 'gmt').".\n"; } if ($value == 'filter=nopayment') { @@ -158,7 +176,7 @@ $option .= (empty($option) ? '' : '_').'nopayment'; $filter[] = 'nopayment'; - print 'Rebuild PDF for invoices with no payment done yet.'."\n"; + print 'Rebuild PDF for ivoices with no payment done yet.'."\n"; } if ($value == 'filter=bank') { @@ -242,9 +260,11 @@ exit(1); } +$diroutputpdf = 'auto'; + // Define SQL and SQL request to select invoices // Use $filter, $dateafterdate, datebeforedate, $paymentdateafter, $paymentdatebefore -$result = rebuild_merge_pdf($db, $langs, $conf, $diroutputpdf, $newlangid, $filter, $dateafterdate, $datebeforedate, $paymentdateafter, $paymentdatebefore, 1, $regenerate, $option, (string) $paymentonbankid, $thirdpartiesid, $fileprefix); +$result = rebuild_merge_pdf($db, $langs, $conf, $diroutputpdf, $newlangid, $filter, $dateafterdate, $datebeforedate, $paymentdateafter, $paymentdatebefore, 1, $regenerate, $option, (string) $paymentonbankid, $thirdpartiesid, $fileprefix, $nomerge, $mode); // -------------------- END OF YOUR CODE -------------------- @@ -269,29 +289,29 @@ function rebuild_merge_pdf_usage() { global $script_file; - print "Rebuild PDF files for some invoices and merge PDF files into one.\n"; + print "Rebuild PDF files for some invoices/orders/proposals and/or merge PDF files into one.\n"; print "\n"; - print "To build/merge PDF for invoices in a date range:\n"; - print "Usage: ".$script_file." filter=date dateafter datebefore\n"; + print "To build/merge PDF for all documents, use filter=all\n"; + print "Usage: ".$script_file." mode=invoice filter=all\n"; + print "To build/merge PDF for documents in a date range:\n"; + print "Usage: ".$script_file." mode=invoice filter=date dateafter datebefore\n"; print "To build/merge PDF for invoices with at least one payment in a date range:\n"; - print "Usage: ".$script_file." filter=payments dateafter datebefore\n"; + print "Usage: ".$script_file." mode=invoice filter=payments dateafter datebefore\n"; print "To build/merge PDF for invoices with at least one payment onto a bank account:\n"; - print "Usage: ".$script_file." filter=bank bankref\n"; - print "To build/merge PDF for all invoices, use filter=all\n"; - print "Usage: ".$script_file." filter=all\n"; + print "Usage: ".$script_file." mode=invoice filter=bank bankref\n"; print "To build/merge PDF for invoices with no payments, use filter=nopayment\n"; - print "Usage: ".$script_file." filter=nopayment\n"; + print "Usage: ".$script_file." mode=invoice filter=nopayment\n"; print "To exclude credit notes, use filter=nocreditnote\n"; print "To exclude replacement invoices, use filter=noreplacement\n"; print "To exclude deposit invoices, use filter=nodeposit\n"; print "To exclude some thirdparties, use filter=excludethirdparties id1,id2...\n"; print "To limit to some thirdparties, use filter=onlythirdparties id1,id2...\n"; - print "To regenerate existing PDF, use regenerate=templatename\n"; - print "To generate invoices in a language, use lang=xx_XX\n"; + print "To regenerate existing PDF, use regenerate=templatename or regeneratenomerge=templatename\n"; + print "To generate documents in a given language, use lang=xx_XX\n"; print "To set prefix of generated file name, use prefix=myfileprefix\n"; print "\n"; - print "Example: ".$script_file." filter=payments 20080101 20081231 lang=fr_FR regenerate=crabe\n"; - print "Example: ".$script_file." filter=all lang=en_US\n"; + print "Example: ".$script_file." mode=invoice filter=payments 20080101 20081231 lang=fr_FR regenerate=crabe\n"; + print "Example: ".$script_file." mode=invoice filter=all lang=en_US\n"; print "\n"; print "Note that some filters can be cumulated.\n"; } diff --git a/test/phpunit/SecurityTest.php b/test/phpunit/SecurityTest.php index 9b5db827bc3b5..743b6d33c8e59 100644 --- a/test/phpunit/SecurityTest.php +++ b/test/phpunit/SecurityTest.php @@ -623,54 +623,83 @@ public function testDolEval() $s = '(($reloadedobj = new Task($db)) && ($reloadedobj->fetchNoCompute($object->id) > 0) && ($secondloadedobj = new Project($db)) && ($secondloadedobj->fetchNoCompute($reloadedobj->fk_project) > 0)) ? $secondloadedobj->ref : \'Parent project not found\''; $result = (string) dol_eval($s, 1, 1, '2'); print "result4 = ".$result."\n"; - $this->assertEquals('Parent project not found', $result); + $this->assertEquals('Parent project not found', $result, 'Test 4'); + + $s = '4 < 5'; + $result = (string) dol_eval($s, 1, 1, '2'); + print "result5 = ".$result."\n"; + $this->assertEquals('1', $result, 'Test 5'); + + + /* not allowed. Not a one line eval string + $result = (string) dol_eval('if ($a == 1) { }', 1, 1); + print "result4b = ".$result."\n"; + $this->assertEquals('aaa', $result); + */ + + // Now string not allowed + + $s = '4 <5'; + $result = (string) dol_eval($s, 1, 1, '2'); // in mode 2, char < is allowed only if followed by a space + print "result = ".$result."\n"; + $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'Test 4 <5 - The string was not detected as evil'); + + $s = '4 < 5'; + $result = (string) dol_eval($s, 1, 1, '1'); // in mode 1, char < is always forbidden + print "result = ".$result."\n"; + $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'Test 4 < 5 - The string was not detected as evil'); $s = 'new abc->invoke(\'whoami\')'; $result = (string) dol_eval($s, 1, 1, '2'); print "result = ".$result."\n"; - $this->assertEquals('Bad string syntax to evaluate: new abc__forbiddenstring__(\'whoami\')', $result); + $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'The string was not detected as evil'); $s = 'new ReflectionFunction(\'abc\')'; $result = (string) dol_eval($s, 1, 1, '2'); print "result = ".$result."\n"; - $this->assertEquals('Bad string syntax to evaluate: new __forbiddenstring__(\'abc\')', $result); - + $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'The string was not detected as evil'); $result = dol_eval('$a=function() { }; $a', 1, 1, '0'); // result of dol_eval may be an object Closure print "result5 = ".json_encode($result)."\n"; - $this->assertStringContainsString('Bad string syntax to evaluate', json_encode($result)); + $this->assertStringContainsString('Bad string syntax to evaluate', json_encode($result), 'The string was not detected as evil'); $result = dol_eval('$a=function() { }; $a();', 1, 1, '1'); print "result6 = ".json_encode($result)."\n"; - $this->assertStringContainsString('Bad string syntax to evaluate', json_encode($result)); + $this->assertStringContainsString('Bad string syntax to evaluate', json_encode($result), 'The string was not detected as evil'); $result = (string) dol_eval('$a=exec("ls");', 1, 1); print "result7 = ".$result."\n"; - $this->assertStringContainsString('Bad string syntax to evaluate', $result); + $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'The string was not detected as evil'); $result = (string) dol_eval('$a=exec ("ls")', 1, 1); print "result8 = ".$result."\n"; - $this->assertStringContainsString('Bad string syntax to evaluate', $result); + $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'The string was not detected as evil'); + + $result = (string) dol_eval("strrev('metsys') ('whoami')", 1, 1); + print "result8b = ".$result."\n"; + $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'The string was not detected as evil'); $result = (string) dol_eval('$a="test"; $$a;', 1, 0); print "result9 = ".$result."\n"; - $this->assertStringContainsString('Bad string syntax to evaluate', $result); + $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'The string was not detected as evil'); $result = (string) dol_eval('`ls`', 1, 0); print "result10 = ".$result."\n"; - $this->assertStringContainsString('Bad string syntax to evaluate', $result); + $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'The string was not detected as evil'); $result = (string) dol_eval("('ex'.'ec')('echo abc')", 1, 0); print "result11 = ".$result."\n"; - $this->assertStringContainsString('Bad string syntax to evaluate', $result); + $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'The string was not detected as evil'); $result = (string) dol_eval("sprintf(\"%s%s\", \"ex\", \"ec\")('echo abc')", 1, 0); print "result12 = ".$result."\n"; - $this->assertStringContainsString('Bad string syntax to evaluate', $result); + $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'The string was not detected as evil'); $result = dol_eval("90402.38+267678+0", 1, 1, 1); print "result13 = ".$result."\n"; - $this->assertEquals('358080.38', $result); + $this->assertEquals('358080.38', $result, 'The string was not detected as evil'); + + // Must be allowed global $leftmenu; // Used into strings to eval @@ -706,28 +735,29 @@ public function testDolEval() print "result18 = ".$result."\n"; $this->assertFalse($result); + // Not allowed + $a = 'ab'; $result = (string) dol_eval("(\$a.'s')", 1, 0); print "result19 = ".$result."\n"; - $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'Test 19'); + $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'Test 19 - The string was not detected as evil'); $leftmenu = 'abs'; $result = (string) dol_eval('$leftmenu(-5)', 1, 0); print "result20 = ".$result."\n"; - $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'Test 20'); + $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'Test 20 - The string was not detected as evil'); $result = (string) dol_eval('str_replace("z","e","zxzc")("whoami");', 1, 0); print "result21 = ".$result."\n"; - $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'Test 21'); + $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'Test 21 - The string was not detected as evil'); $result = (string) dol_eval('($a = "ex") && ($b = "ec") && ($cmd = "$a$b") && $cmd ("curl localhost:5555")', 1, 0); print "result22 = ".$result."\n"; - $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'Test 22'); - + $this->assertStringContainsString('Bad string syntax to evaluate', $result, 'Test 22 - The string was not detected as evil'); $result = (string) dol_eval('\'exec\'("aaa")', 1, 0); - print "result1 = ".$result."\n"; - $this->assertStringContainsString('Bad string syntax to evaluate', json_encode($result), 'Cant find the string Bad string syntaxwhen i should'); + print "result23 = ".$result."\n"; + $this->assertStringContainsString('Bad string syntax to evaluate', json_encode($result), 'Test 23 - The string was not detected as evil - Can\'t find the string Bad string syntax when i should'); } /** diff --git a/test/phpunit/WebsiteTest.php b/test/phpunit/WebsiteTest.php index bbf80cad4c59e..0bdfdb5ca449b 100644 --- a/test/phpunit/WebsiteTest.php +++ b/test/phpunit/WebsiteTest.php @@ -145,6 +145,12 @@ public function testCheckPHPCode() print __METHOD__." result checkPHPCode=".$result."\n"; $this->assertEquals($result, 0, 'checkPHPCode detect string as dangerous when it is legitimate'); + $t = ''; + $s = ''; + $result = checkPHPCode($t, $s); + print __METHOD__." result checkPHPCode=".$result."\n"; + $this->assertEquals($result, 0, 'checkPHPCode detect string as dangerous when it is legitimate'); + // Dangerous