Merge branch 'develop' into dev_new_default_email_template_ticket

This commit is contained in:
Laurent Destailleur
2024-12-19 18:33:34 +01:00
committed by GitHub
86 changed files with 1386 additions and 1216 deletions

View File

@@ -1,22 +1,22 @@
<?php
/* Copyright (C) 2006-2015 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2005-2013 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2012-2013 Christophe Battarel <christophe.battarel@altairis.fr>
* Copyright (C) 2011-2022 Philippe Grand <philippe.grand@atoo-net.com>
* Copyright (C) 2012-2015 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2012-2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
* Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
* Copyright (C) 2015-2022 Alexandre Spangaro <aspangaro@open-dsi.fr>
* Copyright (C) 2016 Bahfir abbes <bafbes@gmail.com>
* Copyright (C) 2017 ATM Consulting <support@atm-consulting.fr>
* Copyright (C) 2017-2019 Nicolas ZABOURI <info@inovea-conseil.com>
* Copyright (C) 2017 Rui Strecht <rui.strecht@aliartalentos.com>
/* Copyright (C) 2006-2015 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2005-2013 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2012-2013 Christophe Battarel <christophe.battarel@altairis.fr>
* Copyright (C) 2011-2022 Philippe Grand <philippe.grand@atoo-net.com>
* Copyright (C) 2012-2015 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2012-2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
* Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
* Copyright (C) 2015-2022 Alexandre Spangaro <aspangaro@open-dsi.fr>
* Copyright (C) 2016 Bahfir abbes <bafbes@gmail.com>
* Copyright (C) 2017 ATM Consulting <support@atm-consulting.fr>
* Copyright (C) 2017-2019 Nicolas ZABOURI <info@inovea-conseil.com>
* Copyright (C) 2017 Rui Strecht <rui.strecht@aliartalentos.com>
* Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2018 Josep Lluís Amador <joseplluis@lliuretic.cat>
* Copyright (C) 2023 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
* Copyright (C) 2021 Grégory Blémand <gregory.blemand@atm-consulting.fr>
* Copyright (C) 2023 Lenin Rivas <lenin.rivas777@gmail.com>
* Copyright (C) 2018 Josep Lluís Amador <joseplluis@lliuretic.cat>
* Copyright (C) 2023 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
* Copyright (C) 2021 Grégory Blémand <gregory.blemand@atm-consulting.fr>
* Copyright (C) 2023 Lenin Rivas <lenin.rivas777@gmail.com>
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
*
@@ -4925,6 +4925,15 @@ abstract class CommonObject
$haschild = 0;
foreach ($arraytoscan as $table => $element) {
//print $id.'-'.$table.'-'.$elementname.'<br>';
// Check if module is enabled (to avoid error if tables of module not created)
if (isset($element['enabled']) && !empty($element['enabled'])) {
$enabled = (int) dol_eval($element['enabled'], 1);
if (empty($enabled)) {
continue;
}
}
// Check if element can be deleted
$sql = "SELECT COUNT(*) as nb";
$sql .= " FROM ".$this->db->prefix().$table." as c";
@@ -10333,6 +10342,7 @@ abstract class CommonObject
if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) {
$fieldvalues['date_creation'] = $this->db->idate($now);
$this->date_creation = $this->db->idate($now);
}
if (array_key_exists('fk_user_creat', $fieldvalues) && !($fieldvalues['fk_user_creat'] > 0)) {
$fieldvalues['fk_user_creat'] = $user->id;

View File

@@ -1149,15 +1149,16 @@ class Form
* Return list of types of lines (product or service)
* Example: 0=product, 1=service, 9=other (for external module)
*
* @param string $selected Preselected type
* @param string $htmlname Name of field in html form
* @param int<0,1>|string $showempty Add an empty field
* @param int $hidetext Do not show label 'Type' before combo box (used only if there is at least 2 choices to select)
* @param integer $forceall 1=Force to show products and services in combo list, whatever are activated modules, 0=No force, 2=Force to show only Products, 3=Force to show only services, -1=Force none (and set hidden field to 'service')
* @param string $morecss More css
* @param string $selected Preselected type
* @param string $htmlname Name of field in html form
* @param int<0,1>|string $showempty Add an empty field
* @param int $hidetext Do not show label 'Type' before combo box (used only if there is at least 2 choices to select)
* @param integer $forceall 1=Force to show products and services in combo list, whatever are activated modules, 0=No force, 2=Force to show only Products, 3=Force to show only services, -1=Force none (and set hidden field to 'service')
* @param string $morecss More css
* @param int $useajaxcombo 1=Use ajaxcombo
* @return void
*/
public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0, $morecss = "")
public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0, $morecss = "", $useajaxcombo = 1)
{
// phpcs:enable
global $langs;
@@ -1166,11 +1167,11 @@ class Form
if ($forceall == 1 || (empty($forceall) && isModEnabled("product") && isModEnabled("service"))
|| (empty($forceall) && !isModEnabled('product') && !isModEnabled('service'))) {
if (empty($hidetext)) {
print $langs->trans("Type") . ': ';
print $langs->trans("Type").'...';
}
print '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $htmlname . '" name="' . $htmlname . '">';
if ($showempty) {
print '<option value="-1"';
print '<option value="-1" class="opacitymedium"'.($useajaxcombo ? '' : ' disabled="disabled"');
if ($selected == -1) {
print ' selected';
}
@@ -1196,7 +1197,10 @@ class Form
print '>' . $langs->trans("Service");
print '</select>';
print ajax_combobox('select_' . $htmlname);
if ($useajaxcombo) {
print ajax_combobox('select_' . $htmlname);
}
//if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
}
if ((empty($forceall) && !isModEnabled('product') && isModEnabled("service")) || $forceall == 3) {
@@ -2228,7 +2232,17 @@ class Form
$sql .= " AND u.fk_soc IS NULL";
}
if (!empty($morefilter)) {
$sql .= " " . $morefilter;
$errormessage = '';
$sql .= forgeSQLFromUniversalSearchCriteria($morefilter, $errormessage);
if ($errormessage) {
$this->errors[] = $errormessage;
dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
if ($outputmode == 0) {
return 'Error bad param $morefilter';
} else {
return array();
}
}
}
//Add hook to filter on user (for example on usergroup define in custom modules)
@@ -2425,28 +2439,29 @@ class Form
* Return select list of users. Selected users are stored into session.
* List of users are provided into $_SESSION['assignedtouser'].
*
* @param string $action Value for $action
* @param string $htmlname Field name in form
* @param int<0,1> $show_empty 0=list without the empty value, 1=add empty value
* @param int[] $exclude Array list of users id to exclude
* @param int<0,1> $disabled If select list must be disabled
* @param int[]|string $include Array list of users id to include or 'hierarchy' to have only supervised users
* @param int[]|int $enableonly Array list of users id to be enabled. All other must be disabled
* @param string $force_entity '0' or Ids of environment to force
* @param int $maxlength Maximum length of string into list (0=no limit)
* @param int<0,1> $showstatus 0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status
* @param string $morefilter Add more filters into sql request
* @param int $showproperties Show properties of each attendees
* @param int[] $listofuserid Array with properties of each user
* @param int[] $listofcontactid Array with properties of each contact
* @param int[] $listofotherid Array with properties of each other contact
* @return string HTML select string
* @param string $action Value for $action
* @param string $htmlname Field name in form
* @param int<0,1> $show_empty 0=list without the empty value, 1=add empty value
* @param int[] $exclude Array list of users id to exclude
* @param int<0,1> $disabled If select list must be disabled
* @param int[]|''|'hierarchy'|'hierarchyme' $include Array list of users id to include or 'hierarchy' to have only supervised users
* @param int[]|int $enableonly Array list of users id to be enabled. All other must be disabled
* @param string $force_entity '0' or Ids of environment to force
* @param int $maxlength Maximum length of string into list (0=no limit)
* @param int<0,1> $showstatus 0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status
* @param string $morefilter Add more filters into sql request (Example: '(employee:=:1)'). This value must not come from user input.
* @param int $showproperties Show properties of each attendees
* @param int[] $listofuserid Array with properties of each user
* @param int[] $listofcontactid Array with properties of each contact
* @param int[] $listofotherid Array with properties of each other contact
* @param int $canremoveowner 1 if we can remove owner, 0=no way
* @return string HTML select string
* @see select_dolgroups()
*/
public function select_dolusers_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = array(), $enableonly = array(), $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofuserid = array(), $listofcontactid = array(), $listofotherid = array())
public function select_dolusers_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = array(), $enableonly = array(), $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofuserid = array(), $listofcontactid = array(), $listofotherid = array(), $canremoveowner = 1)
{
// phpcs:enable
global $langs;
global $langs, $user;
$userstatic = new User($this->db);
$out = '';
@@ -2479,8 +2494,25 @@ class Form
$ownerid = $value['id'];
$out .= ' (' . $langs->trans("Owner") . ')';
}
// Add picto to delete owner/assignee
if ($nbassignetouser > 1 && $action != 'view') {
$out .= ' <input type="image" style="border: 0px;" src="' . img_picto($langs->trans("Remove"), 'delete', '', 0, 1) . '" value="' . $userstatic->id . '" class="removedassigned reposition" id="removedassigned_' . $userstatic->id . '" name="removedassigned_' . $userstatic->id . '">';
$canremoveassignee = 1;
if ($i == 0) {
// We are on the owner of the event
if (!$canremoveowner) {
$canremoveassignee = 0;
}
if (!$user->hasRight('agenda', 'allactions', 'create')) {
$canremoveassignee = 0; // Can't remove the owner
}
} else {
// We are not on the owner of the event but on a secondary assignee
}
if ($canremoveassignee) {
// If user has all permission, he should be ableto remove a assignee.
// If user has not all permission, he can onlyremove assignee of other (he can't remove itself)
$out .= ' <input type="image" style="border: 0px;" src="' . img_picto($langs->trans("Remove"), 'delete', '', 0, 1) . '" value="' . $userstatic->id . '" class="removedassigned reposition" id="removedassigned_' . $userstatic->id . '" name="removedassigned_' . $userstatic->id . '">';
}
}
// Show my availability
if ($showproperties) {
@@ -7404,6 +7436,7 @@ class Form
$hourend = $hourstart;
}
}
// Show hour
$retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth50 ' . ($fullday ? $fullday . 'hour' : '') . '" id="' . $prefix . 'hour" name="' . $prefix . 'hour">';
if ($emptyhours) {
@@ -7426,7 +7459,7 @@ class Form
if ($m) {
// Show minutes
$retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth50 ' . ($fullday ? $fullday . 'min' : '') . '" id="' . $prefix . 'min" name="' . $prefix . 'min">';
$retstring .= '<select ' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth50 ' . ($fullday ? $fullday . 'min' : '') . '" id="' . $prefix . 'min" name="' . $prefix . 'min">';
if ($emptyhours) {
$retstring .= '<option value="-1">&nbsp;</option>';
}
@@ -7444,7 +7477,7 @@ class Form
}
// Add a "Now" link
if (!empty($conf->use_javascript_ajax) && $addnowlink) {
if (!empty($conf->use_javascript_ajax) && $addnowlink && !$disabled) {
// Script which will be inserted in the onClick of the "Now" link
$reset_scripts = "";
if ($addnowlink == 2) { // local computer time
@@ -7531,7 +7564,7 @@ class Form
}
// Add a "Plus one hour" link
if ($conf->use_javascript_ajax && $addplusone) {
if ($conf->use_javascript_ajax && $addplusone && !$disabled) {
// Script which will be inserted in the onClick of the "Add plusone" link
$reset_scripts = "";
@@ -7569,7 +7602,7 @@ class Form
}
// Add a link to set data
if ($conf->use_javascript_ajax && !empty($adddateof)) {
if ($conf->use_javascript_ajax && !empty($adddateof) && !$disabled) {
if (!is_array($adddateof)) {
$arrayofdateof = array(array('adddateof' => $adddateof, 'labeladddateof' => $labeladddateof));
} else {

View File

@@ -282,7 +282,7 @@ if (!class_exists('MenuManager')) {
$menufound = 0;
$dirmenus = array_merge(array("/core/menus/"), (array) $conf->modules_parts['menus']);
foreach ($dirmenus as $dirmenu) {
$menufound = dol_include_once($dirmenu."standard/".$file_menu);
$menufound = dol_include_once($dirmenu."standard/".dol_sanitizeFileName($file_menu));
if ($menufound) {
break;
}
@@ -290,7 +290,7 @@ if (!class_exists('MenuManager')) {
if (!$menufound) { // If failed to include, we try with standard
dol_syslog("You define a menu manager '".$file_menu."' that can not be loaded.", LOG_WARNING);
$file_menu = 'eldy_menu.php';
include_once DOL_DOCUMENT_ROOT."/core/menus/standard/".$file_menu;
include_once DOL_DOCUMENT_ROOT."/core/menus/standard/".dol_sanitizeFileName($file_menu);
}
}
// @phan-suppress-next-line PhanRedefinedClassReference

View File

@@ -1557,11 +1557,13 @@ if (!getDolGlobalString('MAIN_DISABLE_SELECT2_FOCUS_PROTECTION') && !defined('DI
* TODO: Recheck with the select2 GH issue and remove once this is fixed on their side
*/
$(document).on('select2:open', (e) => {
console.log("Execute the focus (click on combo or use space when on component");
console.log("Execute the focus (click on combo or use space when on component)");
const target = $(e.target);
if (target && target.length) {
let id = target[0].id || target[0].name;
if (id.substr(-2) == "[]") id = id.substr(0,id.length-2);
if (id.substr(-2) == "[]") {
id = id.substr(0,id.length-2);
}
document.querySelector('input[aria-controls*='+id+']').focus();
}
});

View File

@@ -2490,7 +2490,7 @@ function dol_compress_file($inputfile, $outputfile, $mode = "gz", &$errorstring
if (defined('ODTPHP_PATHTOPCLZIP')) {
$foundhandler = 1;
include_once ODTPHP_PATHTOPCLZIP.'/pclzip.lib.php';
include_once ODTPHP_PATHTOPCLZIP.'pclzip.lib.php';
$archive = new PclZip($outputfile);
$result = $archive->add($inputfile, PCLZIP_OPT_REMOVE_PATH, dirname($inputfile));
@@ -2557,7 +2557,7 @@ function dol_uncompress($inputfile, $outputdir)
if ($fileinfo["extension"] == "zip") {
if (defined('ODTPHP_PATHTOPCLZIP') && !getDolGlobalString('MAIN_USE_ZIPARCHIVE_FOR_ZIP_UNCOMPRESS')) {
dol_syslog("Constant ODTPHP_PATHTOPCLZIP for pclzip library is set to ".ODTPHP_PATHTOPCLZIP.", so we use Pclzip to unzip into ".$outputdir);
include_once ODTPHP_PATHTOPCLZIP.'/pclzip.lib.php';
include_once ODTPHP_PATHTOPCLZIP.'pclzip.lib.php';
$archive = new PclZip($inputfile);
// We create output dir manually, so it uses the correct permission (When created by the archive->extract, dir is rwx for everybody).

View File

@@ -10586,6 +10586,7 @@ function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1'
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('/^!?[a-zA-Z0-9_]+\(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
@@ -10646,22 +10647,26 @@ function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1'
$forbiddenphpstrings = array('$$', '$_', '}[');
$forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
// We list all forbidden function as keywords we don't want to see (we don't mind it if is "kewyord(" or just "keyword", we don't want "keyword" at all)
$forbiddenphpfunctions = array();
// @phpcs:ignore
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("base64"."_"."decode", "rawurl"."decode", "url"."decode", "str"."_rot13", "hex"."2bin")); // name of forbidden functions are split to avoid false positive
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "require", "include", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func", "call_user_func_array"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "executeCLI", "verifCond")); // native dolibarr functions
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_compress_dir", "dol_decode", "dol_delete_file", "dol_delete_dir", "dol_delete_dir_recursive", "dol_copy", "archiveOrBackupFile")); // more dolibarr functions
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
$forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
$forbiddenphpregex = 'global\s+\$|\b('.implode('|', $forbiddenphpfunctions).')\b';
$forbiddenphpregex = 'global\s*\$';
$forbiddenphpregex .= '|';
$forbiddenphpregex .= '\b('.implode('|', $forbiddenphpfunctions).')\b';
$forbiddenphpmethodsregex = '->('.implode('|', $forbiddenphpmethods).')';

View File

@@ -2629,8 +2629,11 @@ function pdf_getLinkedObjects(&$object, $outputlangs)
if (is_object($hookmanager)) {
$parameters = array('linkedobjects' => $linkedobjects, 'outputlangs' => $outputlangs);
$action = '';
$hookmanager->executeHooks('pdf_getLinkedObjects', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
if (!empty($hookmanager->resArray)) {
$reshook = $hookmanager->executeHooks('pdf_getLinkedObjects', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
if (empty($reshook)) {
$linkedobjects = array_replace($linkedobjects, $hookmanager->resArray); // array_replace is used to preserve keys
} elseif ($reshook>0) {
// The array must be reinserted even if it is empty because clearing the array could be one of the actions performed by the hook.
$linkedobjects = $hookmanager->resArray;
}
}

View File

@@ -694,8 +694,8 @@ function showWebsiteTemplates(Website $website)
/**
* Check a new string containing only php code (including <php tag)
* - Block if bad code in the new string.
* - Block also if user has no permission to change PHP code.
* - Block if user has no permission to change PHP code.
* - Block also if bad code found in the new string.
*
* @param string $phpfullcodestringold PHP old string (before the change). For example "<?php echo 'a' ?><php echo 'b' ?>"
* @param string $phpfullcodestring PHP new string. For example "<?php echo 'a' ?><php echo 'c' ?>"
@@ -722,13 +722,15 @@ function checkPHPCode(&$phpfullcodestringold, &$phpfullcodestring)
// Then check forbidden commands
if (!$error) {
$forbiddenphpstrings = array('$$', '}[');
$forbiddenphpstrings = array_merge($forbiddenphpstrings, array('ReflectionFunction'));
$forbiddenphpstrings = array('$$', '$_', '}[');
//$forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
$forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', 'ReflectionFunction'));
$forbiddenphpfunctions = array();
//$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("base64"."_"."decode", "rawurl"."decode", "url"."decode", "str"."_rot13", "hex"."2bin")); // name of forbidden functions are split to avoid false positive
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("call_user_func"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("call_user_func", "call_user_func_array"));
//$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
if (!getDolGlobalString('WEBSITE_PHP_ALLOW_EXEC')) { // If option is not on, we disallow functions to execute commands
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
@@ -736,27 +738,38 @@ function checkPHPCode(&$phpfullcodestringold, &$phpfullcodestring)
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
}
if (!getDolGlobalString('WEBSITE_PHP_ALLOW_WRITE')) { // If option is not on, we disallow functions to write files
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_compress_dir", "dol_decode", "dol_delete_file", "dol_delete_dir", "dol_delete_dir_recursive", "dol_copy", "archiveOrBackupFile")); // more dolibarr functions
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
}
//$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
$forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
foreach ($forbiddenphpstrings as $forbiddenphpstring) {
if (preg_match('/'.preg_quote($forbiddenphpstring, '/').'/ms', $phpfullcodestring)) {
if (preg_match('/'.preg_quote($forbiddenphpstring, '/').'/ims', $phpfullcodestring)) {
$error++;
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpstring), null, 'errors');
break;
}
}
foreach ($forbiddenphpfunctions as $forbiddenphpcommand) {
if (preg_match('/'.$forbiddenphpcommand.'\s*\(/ms', $phpfullcodestring)) {
/* replaced with next block
foreach ($forbiddenphpfunctions as $forbiddenphpfunction) { // Check "function(" but also "'function'(" and "function ("
if (preg_match('/'.$forbiddenphpfunction.'[\'\s]*\(/ims', $phpfullcodestring)) {
$error++;
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpcommand), null, 'errors');
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpfunction), null, 'errors');
break;
}
}*/
foreach ($forbiddenphpfunctions as $forbiddenphpfunction) { // Check "function" whatever is "function(" or "function'(" or "function (" or "function"
if (preg_match('/\b'.$forbiddenphpfunction.'\b/ims', $phpfullcodestring)) {
$error++;
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpfunction), null, 'errors');
break;
}
}
foreach ($forbiddenphpmethods as $forbiddenphpmethod) {
if (preg_match('/->'.$forbiddenphpmethod.'/ms', $phpfullcodestring)) {
if (preg_match('/->'.$forbiddenphpmethod.'/ims', $phpfullcodestring)) {
$error++;
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpmethod), null, 'errors');
break;
@@ -764,14 +777,14 @@ function checkPHPCode(&$phpfullcodestringold, &$phpfullcodestring)
}
}
// This char can be used to execute RCE for example using with echo `ls`
// This char can be used to execute RCE for example by using echo `ls`
if (!$error) {
$forbiddenphpchars = array();
if (!getDolGlobalString('WEBSITE_PHP_ALLOW_DANGEROUS_CHARS')) { // If option is not on, we disallow functions to execute commands
$forbiddenphpchars = array("`");
}
foreach ($forbiddenphpchars as $forbiddenphpchar) {
if (preg_match('/'.$forbiddenphpchar.'/ms', $phpfullcodestring)) {
if (preg_match('/'.$forbiddenphpchar.'/ims', $phpfullcodestring)) {
$error++;
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpchar), null, 'errors');
break;
@@ -779,23 +792,31 @@ function checkPHPCode(&$phpfullcodestringold, &$phpfullcodestring)
}
}
// Deny dynamic functions '${a}(' or '$a[b](' => So we refuse '}(' and ']('
// Deny code to call a function obfuscated with comment, like "exec/*...*/ ('ls')";
if (!$error) {
if (preg_match('/[}\]]\(/ims', $phpfullcodestring)) {
if (preg_match('/\*\/\s*\(/ims', $phpfullcodestring)) {
$error++;
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", "exec/*...*/ ('ls')"), null, 'errors');
}
}
// Deny dynamic functions '${a}(' or '$a[b](' => So we refuse '}(' and ']('
if (!$error) {
if (preg_match('/[}\]]\s*\(/ims', $phpfullcodestring)) {
$error++;
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", ']('), null, 'errors');
}
}
// Deny dynamic functions '$xxx('
// Deny dynamic functions '$xxx(' or '$xxx (' or '$xxx" ('
if (!$error) {
if (preg_match('/\$[a-z0-9_\-\/\*]+\(/ims', $phpfullcodestring)) {
if (preg_match('/\$[a-z0-9_\-\/\*\"]+\s*\(/ims', $phpfullcodestring)) {
$error++;
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", '$...('), null, 'errors');
}
}
// No need to block $conf->global->aaa() because PHP try to run method aaa an not function into $conf->global->aaa.
// 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) {

View File

@@ -173,8 +173,9 @@ class pdf_standard_asset extends ModelePDFAsset
// Load translation files required by the page
$outputlangs->loadLangs(array("main", "bills", "products", "dict", "companies"));
global $outputlangsbis;
$outputlangsbis = null;
if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE') && $outputlangs->defaultlang != getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE')) {
global $outputlangsbis;
$outputlangsbis = new Translate('', $conf);
$outputlangsbis->setDefaultLang(getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE'));
$outputlangsbis->loadLangs(array("main", "bills", "products", "dict", "companies"));

View File

@@ -95,7 +95,7 @@ class mod_barcode_product_standard extends ModeleNumRefBarCode
// Mask parameter
//$texte.= '<tr><td>'.$langs->trans("Mask").' ('.$langs->trans("BarCodeModel").'):</td>';
$texte .= '<tr><td>'.$langs->trans("Mask").':</td>';
$texte .= '<td class="right">'.$form->textwithpicto('<input type="text" class="flat minwidth175" name="value1" value="'.(getDolGlobalString('BARCODE_STANDARD_PRODUCT_MASK') ? $conf->global->BARCODE_STANDARD_PRODUCT_MASK : '').'"'.$disabled.'>', $tooltip, 1, 'help', 'valignmiddle', 0, 3, $this->name).'</td>';
$texte .= '<td class="right">'.$form->textwithpicto('<input type="text" class="flat minwidth175" name="value1" value="'.getDolGlobalString('BARCODE_STANDARD_PRODUCT_MASK').'"'.$disabled.'>', $tooltip, 1, 'help', 'valignmiddle', 0, 3, $this->name).'</td>';
$texte .= '<td class="left" rowspan="2">&nbsp; <input type="submit" class="button button-edit reposition smallpaddingimp" name="modify" value="'.$langs->trans("Modify").'"'.$disabled.'></td>';
$texte .= '</tr>';

View File

@@ -131,7 +131,7 @@ class mod_barcode_thirdparty_standard extends ModeleNumRefBarCode
// Mask parameter
//$texte.= '<tr><td>'.$langs->trans("Mask").' ('.$langs->trans("BarCodeModel").'):</td>';
$texte .= '<tr><td>'.$langs->trans("Mask").':</td>';
$texte .= '<td class="right">'.$form->textwithpicto('<input type="text" class="flat minwidth175" name="value1" value="'.(getDolGlobalString('BARCODE_STANDARD_THIRDPARTY_MASK') ? $conf->global->BARCODE_STANDARD_THIRDPARTY_MASK : '').'"'.$disabled.'>', $tooltip, 1, 'help', 'valignmiddle', 0, 3, $this->name).'</td>';
$texte .= '<td class="right">'.$form->textwithpicto('<input type="text" class="flat minwidth175" name="value1" value="'.getDolGlobalString('BARCODE_STANDARD_THIRDPARTY_MASK').'"'.$disabled.'>', $tooltip, 1, 'help', 'valignmiddle', 0, 3, $this->name).'</td>';
$texte .= '<td class="left" rowspan="2">&nbsp; <input type="submit" class="button button-edit reposition smallpaddingimp" name="modify" value="'.$langs->trans("Modify").'"'.$disabled.'></td>';
$texte .= '</tr>';

View File

@@ -291,7 +291,7 @@ class pdf_standard_member extends CommonStickerGenerator
// List of values to scan for a replacement
$substitutionarray = array(
'__ID__' => $object->id,
'__ID__' => (string) $object->id,
'__REF__' => $object->ref,
'__LOGIN__' => empty($object->login) ? '' : $object->login,
'__FIRSTNAME__' => empty($object->firstname) ? '' : $object->firstname,
@@ -309,7 +309,7 @@ class pdf_standard_member extends CommonStickerGenerator
'__YEAR__' => $year,
'__MONTH__' => $month,
'__DAY__' => $day,
'__DOL_MAIN_URL_ROOT__' => DOL_MAIN_URL_ROOT,
'__DOL_MAIN_URL_ROOT__' => (string) DOL_MAIN_URL_ROOT,
'__SERVER__' => "https://".$_SERVER["SERVER_NAME"]."/"
);
complete_substitutions_array($substitutionarray, $langs);

View File

@@ -3,6 +3,7 @@
* Copyright (C) 2011-2022 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024 Charlene Benke <charlene@patas-monkey.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -281,11 +282,12 @@ if ($disablenofollow) {
<div class="tagtable left centpercent" title="<?php echo $langs->trans("EnterLoginDetail"); ?>">
<!-- Login -->
<?php if (!isset($conf->file->main_authentication) || $conf->file->main_authentication != 'googleoauth') { ?>
<div class="trinputlogin">
<div class="tagtd nowraponall center valignmiddle tdinputlogin">
<?php if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
?><label for="username" class="hidden"><?php echo $langs->trans("Login"); ?></label><?php
} ?>
<?php if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
?><label for="username" class="hidden"><?php echo $langs->trans("Login"); ?></label><?php
} ?>
<!-- <span class="span-icon-user">-->
<span class="fa fa-user"></span>
<input type="text" id="username" maxlength="255" placeholder="<?php echo $langs->trans("Login"); ?>" name="username" class="flat input-icon-user minwidth150" value="<?php echo dol_escape_htmltag($login); ?>" tabindex="1" autofocus="autofocus" autocapitalize="off" autocomplete="on" spellcheck="false" autocorrect="off" />
@@ -293,7 +295,6 @@ if ($disablenofollow) {
</div>
<!-- Password -->
<?php if (!isset($conf->file->main_authentication) || $conf->file->main_authentication != 'googleoauth') { ?>
<div class="trinputlogin">
<div class="tagtd nowraponall center valignmiddle tdinputlogin">
<?php if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
@@ -317,7 +318,7 @@ if (!empty($captcha)) {
}
// List of directories where we can find captcha handlers
$dirModCaptcha = array_merge(array('main' => '/core/modules/security/captcha/'), is_array($conf->modules_parts['captcha']) ? $conf->modules_parts['captcha'] : array());
$dirModCaptcha = array_merge(array('main' => '/core/modules/security/captcha/'), (isset($conf->modules_parts['captcha']) && is_array($conf->modules_parts['captcha'])) ? $conf->modules_parts['captcha'] : array());
$fullpathclassfile = '';
foreach ($dirModCaptcha as $dir) {
$fullpathclassfile = dol_buildpath($dir."modCaptcha".ucfirst($captcha).'.class.php', 0, 2);
@@ -334,7 +335,7 @@ if (!empty($captcha)) {
$classname = "modCaptcha".ucfirst($captcha);
if (class_exists($classname)) {
/** @var ModeleCaptcha $captchaobj */
$captchaobj = new $classname($db, $conf, $langs, $user);
$captchaobj = new $classname($db, $conf, $langs, null);
'@phan-var-force ModeleCaptcha $captchaobj';
if (is_object($captchaobj) && method_exists($captchaobj, 'getCaptchaCodeForForm')) {

File diff suppressed because it is too large Load Diff

View File

@@ -224,7 +224,7 @@ class InterfaceZapierTriggers extends DolibarrTriggers
case 'ORDER_CLASSIFY_BILLED':
case 'ORDER_SETDRAFT':
case 'LINEORDER_INSERT':
case 'LINEORDER_UPDATE':
case 'LINEORDER_MODIFY':
case 'LINEORDER_DELETE':
break;
// Supplier orders
@@ -239,7 +239,7 @@ class InterfaceZapierTriggers extends DolibarrTriggers
// case 'ORDER_SUPPLIER_RECEIVE':
// case 'LINEORDER_SUPPLIER_DISPATCH':
// case 'LINEORDER_SUPPLIER_CREATE':
// case 'LINEORDER_SUPPLIER_UPDATE':
// case 'LINEORDER_SUPPLIER_MODIFY':
// Proposals
// case 'PROPAL_CREATE':
@@ -251,7 +251,7 @@ class InterfaceZapierTriggers extends DolibarrTriggers
// case 'PROPAL_CLOSE_REFUSED':
// case 'PROPAL_DELETE':
// case 'LINEPROPAL_INSERT':
// case 'LINEPROPAL_UPDATE':
// case 'LINEPROPAL_MODIFY':
// case 'LINEPROPAL_DELETE':
// SupplierProposal
@@ -264,7 +264,7 @@ class InterfaceZapierTriggers extends DolibarrTriggers
// case 'SUPPLIER_PROPOSAL_CLOSE_REFUSED':
// case 'SUPPLIER_PROPOSAL_DELETE':
// case 'LINESUPPLIER_PROPOSAL_INSERT':
// case 'LINESUPPLIER_PROPOSAL_UPDATE':
// case 'LINESUPPLIER_PROPOSAL_MODIFY':
// case 'LINESUPPLIER_PROPOSAL_DELETE':
// Contracts
@@ -274,7 +274,7 @@ class InterfaceZapierTriggers extends DolibarrTriggers
// case 'CONTRACT_CLOSE':
// case 'CONTRACT_DELETE':
// case 'LINECONTRACT_INSERT':
// case 'LINECONTRACT_UPDATE':
// case 'LINECONTRACT_MODIFY':
// case 'LINECONTRACT_DELETE':
// Bills
@@ -288,19 +288,19 @@ class InterfaceZapierTriggers extends DolibarrTriggers
// case 'BILL_DELETE':
// case 'BILL_PAYED':
// case 'LINEBILL_INSERT':
// case 'LINEBILL_UPDATE':
// case 'LINEBILL_MODIFY':
// case 'LINEBILL_DELETE':
//Supplier Bill
// case 'BILL_SUPPLIER_CREATE':
// case 'BILL_SUPPLIER_UPDATE':
// case 'BILL_SUPPLIER_MODIFY':
// case 'BILL_SUPPLIER_DELETE':
// case 'BILL_SUPPLIER_PAYED':
// case 'BILL_SUPPLIER_UNPAYED':
// case 'BILL_SUPPLIER_VALIDATE':
// case 'BILL_SUPPLIER_UNVALIDATE':
// case 'LINEBILL_SUPPLIER_CREATE':
// case 'LINEBILL_SUPPLIER_UPDATE':
// case 'LINEBILL_SUPPLIER_MODIFY':
// case 'LINEBILL_SUPPLIER_DELETE':
// Payments
@@ -316,7 +316,7 @@ class InterfaceZapierTriggers extends DolibarrTriggers
// Donation
// case 'DON_CREATE':
// case 'DON_UPDATE':
// case 'DON_MODIFY':
// case 'DON_DELETE':
// Interventions
@@ -325,7 +325,7 @@ class InterfaceZapierTriggers extends DolibarrTriggers
// case 'FICHINTER_VALIDATE':
// case 'FICHINTER_DELETE':
// case 'LINEFICHINTER_CREATE':
// case 'LINEFICHINTER_UPDATE':
// case 'LINEFICHINTER_MODIFY':
// case 'LINEFICHINTER_DELETE':
// Members