diff --git a/ChangeLog b/ChangeLog index 4525d48049d..285efc6f481 100644 --- a/ChangeLog +++ b/ChangeLog @@ -199,8 +199,7 @@ NEW: add option "If the feature to manage kits of module Stock is used, show det For developers: --------------- -NEW: add substitution key __MEMBER_TYPE__ -NEW: add substitution key __TYPE__ +NEW: add __MEMBER_TYPE__ and __TYPE__ substitution key NEW: add function dolButtonToOpenUrlInDialogPopup() to be able to open page into a popup NEW: show line number on intervention card (via MAIN_VIEW_LINE_NUMBER) NEW: add some fields to link website page to an other object @@ -212,12 +211,9 @@ NEW: can add event to log into blockedlog module with a constant NEW: add property cssview when declaring fields of an object NEW: can use dynamic code into the 'enabled' property of DAO fields NEW: field ref_ext in llx_commandedet -NEW: field ref_ext for Attributes and Combinations +NEW: fields ref_ext for Attributes and Combinations NEW: OAuth SCOPE for Admin SDK -NEW: retrieve discount from invoice from API NEW: standardizes API thirdparties by email like other object -NEW: Thirdparty REST API: endpoint to set price level -NEW: use new category API for project list view NEW: Triggers Attributes and Attributes values NEW: add incoterms data into the substitution array NEW: add send context for ticket @@ -234,11 +230,12 @@ NEW: API can update a payment NEW: API get member by thirdparty NEW: API get thirdparty by barcode NEW: API get users by email / login -NEW: allow to edit field "demand reason" through API NEW: fetch contact by email with REST API -NEW: get state by REST API -NEW: get state dictionary by REST API -NEW: improve Product API for product variants +NEW: get state dictionnary by REST API +NEW: improve Product API for variant products +NEW: retrieve discount from invoice from API +NEW: Thirdparty REST API: endpoint to set price level +NEW: use new category API for project list view HOOKs NEW: add hook on propal card @@ -256,7 +253,7 @@ Following changes may create regressions for some external modules, but were nec * All properties ->titre have been renamed into ->title * Property $paiementid in API 'api_supplier_invoices.php' has been renamed into into $payment_mode_id * Property 'num_paiement' has been renamed 'num_payment' everywhere for better code consistency. -* The deprecated subsitution key __SIGNATURE__ has been removed. Use __USER_SIGNATURE__ if you used the old syntax in your email templates. +* The deprecated subsitution key __SIGNATURE__ has been removed. Replace it with __USER_SIGNATURE__ if you used the old syntax in your email templates. * The hidden option HOLIDAY_MORE_PUBLIC_HOLIDAYS has been removed. Use instead the dictionary table if you need to define custom days of holiday. * If you build a class that implement CommonObject to use the incoterm properties or methods (->fk_incoterm, ->label_incoterm, ->location_incoterm), you must now also include declaration of the Trait 'CommonIncoterm' in your class. All incoterm functions were moved into this Trait. @@ -269,6 +266,7 @@ Following changes may create regressions for some external modules, but were nec * Context for hook showSocinfoOnPrint has been moved from "showsocinfoonprint" to "main" * Library htdocs/includes/phpoffice/phpexcel as been removed (replaced with htdocs/includes/phpoffice/PhpSpreadsheet) * Databse transaction in your triggers must be correctly balanced (one close for one open). If not, an error will be returned by the trigger, even if trigger did return error code. +* Dolibarr v13 is still compatible with any PHP version between 5.6.0 and 7.4.*; Unit tests are OK with PHP 8.0 but some warnings or troubles may appears with PHP 8.0. ***** ChangeLog for 12.0.4 compared to 12.0.3 ***** diff --git a/htdocs/admin/mails.php b/htdocs/admin/mails.php index 5e32a637c8f..88ae8964c1b 100644 --- a/htdocs/admin/mails.php +++ b/htdocs/admin/mails.php @@ -532,6 +532,7 @@ if ($action == 'edit') print ''.$langs->trans("EMailsDesc")."
\n"; print "
\n"; + print '
'; // You can use div-table-responsive-no-min if you dont need reserved height for your table print ''; print ''; @@ -552,10 +553,12 @@ if ($action == 'edit') } print '
'.$langs->trans("Parameter").''.$langs->trans("Value").'
'; + print '
'; if (empty($conf->global->MAIN_DISABLE_ALL_MAILS)) { print '
'; + print '
'; // You can use div-table-responsive-no-min if you dont need reserved height for your table print ''; print ''; @@ -663,6 +666,7 @@ if ($action == 'edit') } print '
'.$langs->trans("MAIN_MAIL_SENDMODE").'
'; + print '
'; if ($conf->global->MAIN_MAIL_SENDMODE == 'mail' && empty($conf->global->MAIN_HIDE_WARNING_TO_ENCOURAGE_SMTP_SETUP)) { print info_admin($langs->trans("WarningPHPMail").'
'.$langs->trans("WarningPHPMailA").'
'.$langs->trans("WarningPHPMailB").'
'.$langs->trans("WarningPHPMailC").'

'.$langs->trans("WarningPHPMailD"), 0, 0, 'warning'); @@ -670,11 +674,12 @@ if ($action == 'edit') print '
'; + print '
'; // You can use div-table-responsive-no-min if you dont need reserved height for your table print ''; print ''; // From - print ''; + print ''; print ''; @@ -746,6 +751,7 @@ if ($action == 'edit') print ''; print '
'.$langs->trans("OtherOptions").'
'.$langs->trans("MAIN_MAIL_EMAIL_FROM", ini_get('sendmail_from') ?ini_get('sendmail_from') : $langs->transnoentities("Undefined")).'
'.$langs->trans("MAIN_MAIL_EMAIL_FROM", ini_get('sendmail_from') ?ini_get('sendmail_from') : $langs->transnoentities("Undefined")).''.$conf->global->MAIN_MAIL_EMAIL_FROM; if (!empty($conf->global->MAIN_MAIL_EMAIL_FROM) && !isValidEmail($conf->global->MAIN_MAIL_EMAIL_FROM)) print img_warning($langs->trans("ErrorBadEMail")); print '
'.$langs->trans("MAIN_MAIL_ENABLED_USER_DEST_SELECT").''.yn($conf->global->MAIN_MAIL_ENABLED_USER_DEST_SELECT).'
'; + print '
'; } print dol_get_fiche_end(); diff --git a/htdocs/core/lib/admin.lib.php b/htdocs/core/lib/admin.lib.php index f3faeda4b3a..ce9ffb31697 100644 --- a/htdocs/core/lib/admin.lib.php +++ b/htdocs/core/lib/admin.lib.php @@ -1870,11 +1870,6 @@ function email_admin_prepare_head() } } - $head[$h][0] = DOL_URL_ROOT."/admin/mails_templates.php"; - $head[$h][1] = $langs->trans("EMailTemplates"); - $head[$h][2] = 'templates'; - $h++; - if (!empty($user->admin) && (empty($_SESSION['leftmenu']) || $_SESSION['leftmenu'] != 'email_templates')) { $head[$h][0] = DOL_URL_ROOT."/admin/mails_senderprofile_list.php"; $head[$h][1] = $langs->trans("EmailSenderProfiles"); @@ -1882,6 +1877,11 @@ function email_admin_prepare_head() $h++; } + $head[$h][0] = DOL_URL_ROOT."/admin/mails_templates.php"; + $head[$h][1] = $langs->trans("EMailTemplates"); + $head[$h][2] = 'templates'; + $h++; + complete_head_from_modules($conf, $langs, null, $head, $h, 'email_admin', 'remove'); return $head; diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index 68c8775fd81..b21b4c3e839 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -686,7 +686,7 @@ function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = $out = dol_string_nohtmltag($out, 0); } break; - case 'alphawithlgt': // No " and no ../ but we keep < > tags. Can be used for email string like "Name " + case 'alphawithlgt': // No " and no ../ but we keep balanced < > tags with no special chars inside. Can be used for email string like "Name " if (!is_array($out)) { // '"' is dangerous because param in url can close the href= or src= and add javascript functions. // '../' is dangerous because it allows dir transversals @@ -5762,7 +5762,7 @@ function picto_required() * @param string $stringtoclean String to clean * @param integer $removelinefeed 1=Replace all new lines by 1 space, 0=Only ending new lines are removed others are replaced with \n, 2=Ending new lines are removed but others are kept with a same number of \n than nb of
when there is both "...
\n..." * @param string $pagecodeto Encoding of input/output string - * @param integer $strip_tags 0=Use internal strip, 1=Use strip_tags() php function (bugged when text contains a < char that is not for a html tag) + * @param integer $strip_tags 0=Use internal strip, 1=Use strip_tags() php function (bugged when text contains a < char that is not for a html tag or when tags is not closed like '0000-021 - $temp = preg_replace($pattern, "", $temp); // pass 1 - // $temp after pass 1: 0000-021 - $temp = preg_replace($pattern, "", $temp); // pass 2 - // $temp after pass 2: 0000-021 + $temp = preg_replace($pattern, "", $temp); // pass 1 - $temp after pass 1: 0000-021 + $temp = preg_replace($pattern, "", $temp); // pass 2 - $temp after pass 2: 0000-021 + // removed '<' into non closing html tags like 'ref = $lines[$i]->projectref; $projectstatic->title = $lines[$i]->projectlabel; $projectstatic->public = $lines[$i]->public; + $projectstatic->status = $lines[$i]->projectstatus; $taskstatic->id = $lines[$i]->id; $taskstatic->ref = ($lines[$i]->ref ? $lines[$i]->ref : $lines[$i]->id); @@ -1491,6 +1492,7 @@ function projectLinesPerWeek(&$inc, $firstdaytoshow, $fuser, $parent, $lines, &$ $projectstatic->title = $lines[$i]->projectlabel; $projectstatic->public = $lines[$i]->public; $projectstatic->thirdparty_name = $lines[$i]->thirdparty_name; + $projectstatic->status = $lines[$i]->projectstatus; $taskstatic->id = $lines[$i]->id; $taskstatic->ref = ($lines[$i]->ref ? $lines[$i]->ref : $lines[$i]->id); diff --git a/htdocs/core/modules/modSociete.class.php b/htdocs/core/modules/modSociete.class.php index 1bb0c61b83e..e5a3c584adb 100644 --- a/htdocs/core/modules/modSociete.class.php +++ b/htdocs/core/modules/modSociete.class.php @@ -197,15 +197,25 @@ class modSociete extends DolibarrModules $this->rights[$r][3] = 0; // La permission est-elle une permission par defaut $this->rights[$r][4] = 'export'; - // 262 : Resteindre l'acces des commerciaux + // 262 : Restrict access to sales representative $r++; $this->rights[$r][0] = 262; - $this->rights[$r][1] = 'Read all third parties by internal users (otherwise only if commercial contact). Not effective for external users (limited to themselves).'; + $this->rights[$r][1] = 'Read all third parties (and their objects) by internal users (otherwise only if commercial contact). Not effective for external users (limited to themselves).'; $this->rights[$r][2] = 'r'; $this->rights[$r][3] = 0; $this->rights[$r][4] = 'client'; $this->rights[$r][5] = 'voir'; + /* + $r++; + $this->rights[$r][0] = 263; + $this->rights[$r][1] = 'Read all third parties (without their objects) by internal users (otherwise only if commercial contact). Not effective for external users (limited to themselves).'; + $this->rights[$r][2] = 'r'; + $this->rights[$r][3] = 0; + $this->rights[$r][4] = 'client'; + $this->rights[$r][5] = 'readallthirdparties_advance'; + */ + $r++; $this->rights[$r][0] = 281; // id de la permission $this->rights[$r][1] = 'Read contacts'; // libelle de la permission diff --git a/htdocs/includes/odtphp/odf.php b/htdocs/includes/odtphp/odf.php index 88fa70c5646..0f5861caf68 100644 --- a/htdocs/includes/odtphp/odf.php +++ b/htdocs/includes/odtphp/odf.php @@ -820,11 +820,17 @@ IMG; // Export to PDF using LibreOffice if ($conf->global->MAIN_ODT_AS_PDF == 'libreoffice') { + dol_mkdir($conf->user->dir_temp); // We must be sure the directory exists and is writable + + // We delete and recreate a subdir because the soffice may have change pemrissions on it + dol_delete_dir_recursive($conf->user->dir_temp.'/odtaspdf'); + dol_mkdir($conf->user->dir_temp.'/odtaspdf'); + // Install prerequisites: apt install soffice libreoffice-common libreoffice-writer // using windows libreoffice that must be in path // using linux/mac libreoffice that must be in path // Note PHP Config "fastcgi.impersonate=0" must set to 0 - Default is 1 - $command ='soffice --headless -env:UserInstallation=file:"//'.$conf->user->dir_temp.'" --convert-to pdf --outdir '. escapeshellarg(dirname($name)). " ".escapeshellarg($name); + $command ='soffice --headless -env:UserInstallation=file:\''.$conf->user->dir_temp.'/odtaspdf\' --convert-to pdf --outdir '. escapeshellarg(dirname($name)). " ".escapeshellarg($name); } elseif (preg_match('/unoconv/', $conf->global->MAIN_ODT_AS_PDF)) { diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 2cf34ace170..ee7b0052b7c 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -349,7 +349,7 @@ UpdateServerOffline=Update server offline WithCounter=Manage a counter GenericMaskCodes=You may enter any numbering mask. In this mask, the following tags could be used:
{000000} corresponds to a number which will be incremented on each %s. Enter as many zeros as the desired length of the counter. The counter will be completed by zeros from the left in order to have as many zeros as the mask.
{000000+000} same as previous but an offset corresponding to the number to the right of the + sign is applied starting on first %s.
{000000@x} same as previous but the counter is reset to zero when month x is reached (x between 1 and 12, or 0 to use the early months of fiscal year defined in your configuration, or 99 to reset to zero every month). If this option is used and x is 2 or higher, then sequence {yy}{mm} or {yyyy}{mm} is also required.
{dd} day (01 to 31).
{mm} month (01 to 12).
{yy}, {yyyy} or {y} year over 2, 4 or 1 numbers.
GenericMaskCodes2={cccc} the client code on n characters
{cccc000} the client code on n characters is followed by a counter dedicated for customer. This counter dedicated to customer is reset at same time than global counter.
{tttt} The code of third party type on n characters (see menu Home - Setup - Dictionary - Types of third parties). If you add this tag, the counter will be different for each type of third party.
-GenericMaskCodes3=All other characters in the mask will remain intact (except * or ? in 13th position in EAN13).
Spaces are not allowed.
In EAN13 the last character after the last } in 13th position
should be * or ? it will be replaced by the calculated key.
+GenericMaskCodes3=All other characters in the mask will remain intact (except * or ? in 13th position in EAN13).
Spaces are not allowed.
In EAN13, the last character after the last } in 13th position should be * or ? . It will be replaced by the calculated key.
GenericMaskCodes4a=Example on the 99th %s of the third party TheCompany, with date 2007-01-31:
GenericMaskCodes4b=Example on third party created on 2007-03-01:
GenericMaskCodes4c=Example on product created on 2007-03-01:
@@ -805,7 +805,8 @@ PermissionAdvanced253=Create/modify internal/external users and permissions Permission254=Create/modify external users only Permission255=Modify other users password Permission256=Delete or disable other users -Permission262=Extend access to all third parties (not only third parties for which that user is a sale representative).
Not effective for external users (always limited to themselves for proposals, orders, invoices, contracts, etc.).
Not effective for projects (only rules on project permissions, visibility and assignment matters). +Permission262=Extend access to all third parties AND their objects (not only third parties for which the user is a sale representative).
Not effective for external users (always limited to themselves for proposals, orders, invoices, contracts, etc.).
Not effective for projects (only rules on project permissions, visibility and assignment matters). +Permission263=Extend access to all third parties WITHOUT their objects (not only third parties for which the user is a sale representative).
Not effective for external users (always limited to themselves for proposals, orders, invoices, contracts, etc.).
Not effective for projects (only rules on project permissions, visibility and assignment matters). Permission271=Read CA Permission272=Read invoices Permission273=Issue invoices diff --git a/htdocs/langs/en_US/mrp.lang b/htdocs/langs/en_US/mrp.lang index 9f0ddf152db..5ca0dbc6981 100644 --- a/htdocs/langs/en_US/mrp.lang +++ b/htdocs/langs/en_US/mrp.lang @@ -83,8 +83,6 @@ Workstations=Workstations WorkstationsDescription=Workstations management WorkstationSetup = Workstations setup WorkstationSetupPage = Workstations setup page -WorkstationAbout = About Workstation -WorkstationAboutPage = Workstations about page WorkstationList=Workstation list WorkstationCreate=Add new workstation ConfirmEnableWorkstation=Are you sure you want to enable workstation %s ? diff --git a/htdocs/langs/en_US/stocks.lang b/htdocs/langs/en_US/stocks.lang index ace97c85c3f..ad4946ee9f9 100644 --- a/htdocs/langs/en_US/stocks.lang +++ b/htdocs/langs/en_US/stocks.lang @@ -62,7 +62,7 @@ EnhancedValueOfWarehouses=Warehouses value UserWarehouseAutoCreate=Create a user warehouse automatically when creating a user AllowAddLimitStockByWarehouse=Manage also value for minimum and desired stock per pairing (product-warehouse) in addition to the value for minimum and desired stock per product RuleForWarehouse=Rule for warehouses -WarehouseAskWarehouseDuringPropal=Set a warehouse on Sale propal +WarehouseAskWarehouseDuringPropal=Set a warehouse on Commercial proposals WarehouseAskWarehouseDuringOrder=Set a warehouse on Sale orders UserDefaultWarehouse=Set a warehouse on Users MainDefaultWarehouse=Default warehouse diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index 85f5d775929..e0055890152 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -51,6 +51,8 @@ if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO'])) /** * Security: WAF layer for SQL Injection and XSS Injection (scripts) protection (Filters on GET, POST, PHP_SELF). + * Warning: Such a protection can't be enough. It is not reliable as it will alwyas be possible to bypass this. Good protection can + * only be guaranted by escaping data during output. * * @param string $val Value brut found int $_GET, $_POST or PHP_SELF * @param string $type 1=GET, 0=POST, 2=PHP_SELF, 3=GET without sql reserved keywords (the less tolerant test) @@ -120,6 +122,19 @@ function testSqlAndScriptInject($val, $type) $inj += preg_match('/on(keydown|keypress|keyup|load|loadeddata|loadedmetadata|loadstart|offline|online|pagehide|pageshow)\s*=/i', $val); $inj += preg_match('/on(paste|pause|play|playing|progress|ratechange|resize|reset|scroll|search|seeking|select|show|stalled|start|submit|suspend)\s*=/i', $val); $inj += preg_match('/on(timeupdate|toggle|unload|volumechange|waiting)\s*=/i', $val); + + // We refuse html into html because some hacks try to obfuscate evil strings by inserting HTML into HTML. Example: error=alert(1) to bypass test on onerror + $tmpval = preg_replace('/<[^<]+>/', '', $val); + // List of dom events is on https://www.w3schools.com/jsref/dom_obj_event.asp + $inj += preg_match('/onmouse([a-z]*)\s*=/i', $tmpval); // onmousexxx can be set on img or any html tag like + $inj += preg_match('/ondrag([a-z]*)\s*=/i', $tmpval); // + $inj += preg_match('/ontouch([a-z]*)\s*=/i', $tmpval); // + $inj += preg_match('/on(abort|afterprint|beforeprint|beforeunload|blur|canplay|canplaythrough|change|click|contextmenu|copy|cut)\s*=/i', $tmpval); + $inj += preg_match('/on(dblclick|drop|durationchange|ended|error|focus|focusin|focusout|hashchange|input|invalid)\s*=/i', $tmpval); + $inj += preg_match('/on(keydown|keypress|keyup|load|loadeddata|loadedmetadata|loadstart|offline|online|pagehide|pageshow)\s*=/i', $tmpval); + $inj += preg_match('/on(paste|pause|play|playing|progress|ratechange|resize|reset|scroll|search|seeking|select|show|stalled|start|submit|suspend)\s*=/i', $tmpval); + $inj += preg_match('/on(timeupdate|toggle|unload|volumechange|waiting)\s*=/i', $tmpval); + //$inj += preg_match('/on[A-Z][a-z]+\*=/', $val); // To lock event handlers onAbort(), ... $inj += preg_match('/:|:|:/i', $val); // refused string ':' encoded (no reason to have it encoded) to lock 'javascript:...' $inj += preg_match('/javascript\s*:/i', $val); diff --git a/htdocs/projet/activity/perday.php b/htdocs/projet/activity/perday.php index a4a37f22a31..c6b38390ac9 100644 --- a/htdocs/projet/activity/perday.php +++ b/htdocs/projet/activity/perday.php @@ -3,7 +3,7 @@ * Copyright (C) 2004-2016 Laurent Destailleur * Copyright (C) 2005-2010 Regis Houssin * Copyright (C) 2010 François Legastelois - * Copyright (C) 2018 Frédéric France + * Copyright (C) 2018 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 @@ -504,19 +504,19 @@ $moreforfilter .= '
'; $moreforfilter .= '
'; $includeonly = 'hierarchyme'; if (empty($user->rights->user->user->lire)) $includeonly = array($user->id); -$moreforfilter .= img_picto($langs->trans('User'), 'user').$form->select_dolusers($search_usertoprocessid ? $search_usertoprocessid : $usertoprocess->id, 'search_usertoprocessid', $user->rights->user->user->lire ? 0 : 0, null, 0, $includeonly, null, 0, 0, 0, '', 0, '', 'maxwidth200 marginleftonly'); +$moreforfilter .= img_picto($langs->trans('Filter').' '.$langs->trans('User'), 'user').$form->select_dolusers($search_usertoprocessid ? $search_usertoprocessid : $usertoprocess->id, 'search_usertoprocessid', $user->rights->user->user->lire ? 0 : 0, null, 0, $includeonly, null, 0, 0, 0, '', 0, '', 'maxwidth200 marginleftonly'); $moreforfilter .= '
'; if (empty($conf->global->PROJECT_TIMESHEET_DISABLEBREAK_ON_PROJECT)) { $moreforfilter .= '
'; $moreforfilter .= '
'; - $moreforfilter .= img_picto($langs->trans('Project'), 'project').''; + $moreforfilter .= img_picto($langs->trans('Filter').' '.$langs->trans('Project'), 'project').''; $moreforfilter .= '
'; $moreforfilter .= '
'; $moreforfilter .= '
'; - $moreforfilter .= img_picto($langs->trans('ThirdParty'), 'company').''; + $moreforfilter .= img_picto($langs->trans('Filter').' '.$langs->trans('ThirdParty'), 'company').''; $moreforfilter .= '
'; } @@ -594,7 +594,7 @@ if (!empty($arrayfields['t.progress']['checked'])) if ($usertoprocess->id == $user->id) print ''.$langs->trans("TimeSpentByYou").''; else print ''.$langs->trans("TimeSpentByUser").'';*/ print ''.$langs->trans("TimeSpent").'
'.$langs->trans("Everybody").''; -print ''.$langs->trans("TimeSpent").($usertoprocess->firstname ? '
'.dol_trunc($usertoprocess->firstname, 10).'' : '').''; +print ''.$langs->trans("TimeSpent").($usertoprocess->firstname ? '
'.$usertoprocess->getNomUrl(-2).''.dol_trunc($usertoprocess->firstname, 10).'' : '').''; print ''.$langs->trans("HourStart").''; // By default, we can edit only tasks we are assigned to diff --git a/htdocs/projet/activity/permonth.php b/htdocs/projet/activity/permonth.php index 9a95f8abf79..626634b13cd 100644 --- a/htdocs/projet/activity/permonth.php +++ b/htdocs/projet/activity/permonth.php @@ -429,19 +429,19 @@ $moreforfilter .= '
'; $moreforfilter .= '
'; $includeonly = 'hierachyme'; if (empty($user->rights->user->user->lire)) $includeonly = array($user->id); -$moreforfilter .= img_picto($langs->trans('User'), 'user').$form->select_dolusers($search_usertoprocessid ? $search_usertoprocessid : $usertoprocess->id, 'search_usertoprocessid', $user->rights->user->user->lire ? 0 : 0, null, 0, $includeonly, null, 0, 0, 0, '', 0, '', 'maxwidth200'); +$moreforfilter .= img_picto($langs->trans('Filter').' '.$langs->trans('User'), 'user').$form->select_dolusers($search_usertoprocessid ? $search_usertoprocessid : $usertoprocess->id, 'search_usertoprocessid', $user->rights->user->user->lire ? 0 : 0, null, 0, $includeonly, null, 0, 0, 0, '', 0, '', 'maxwidth200'); $moreforfilter .= '
'; if (empty($conf->global->PROJECT_TIMESHEET_DISABLEBREAK_ON_PROJECT)) { $moreforfilter .= '
'; $moreforfilter .= '
'; - $moreforfilter .= img_picto($langs->trans('Project'), 'project').''; + $moreforfilter .= img_picto($langs->trans('Filter').' '.$langs->trans('Project'), 'project').''; $moreforfilter .= '
'; $moreforfilter .= '
'; $moreforfilter .= '
'; - $moreforfilter .= img_picto($langs->trans('ThirdParty'), 'company').''; + $moreforfilter .= img_picto($langs->trans('Filter').' '.$langs->trans('ThirdParty'), 'company').''; $moreforfilter .= '
'; } @@ -488,7 +488,7 @@ print ''.$langs->trans("ProgressDeclared"). if ($usertoprocess->id == $user->id) print ''.$langs->trans("TimeSpentByYou").''; else print ''.$langs->trans("TimeSpentByUser").'';*/ print ''.$langs->trans("TimeSpent").'
'.$langs->trans("Everybody").''; -print ''.$langs->trans("TimeSpent").($usertoprocess->firstname ? '
'.dol_trunc($usertoprocess->firstname, 10).'' : '').''; +print ''.$langs->trans("TimeSpent").($usertoprocess->firstname ? '
'.$usertoprocess->getNomUrl(-2).''.dol_trunc($usertoprocess->firstname, 10).'' : '').''; foreach ($TWeek as $week_number) { diff --git a/htdocs/projet/activity/perweek.php b/htdocs/projet/activity/perweek.php index c29486302af..f001a73d0f4 100644 --- a/htdocs/projet/activity/perweek.php +++ b/htdocs/projet/activity/perweek.php @@ -3,7 +3,7 @@ * Copyright (C) 2004-2015 Laurent Destailleur * Copyright (C) 2005-2010 Regis Houssin * Copyright (C) 2010 François Legastelois - * Copyright (C) 2018 Frédéric France + * Copyright (C) 2018 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 @@ -559,19 +559,19 @@ $moreforfilter .= '
'; $moreforfilter .= '
'; $includeonly = 'hierarchyme'; if (empty($user->rights->user->user->lire)) $includeonly = array($user->id); -$moreforfilter .= img_picto($langs->trans('User'), 'user').$form->select_dolusers($search_usertoprocessid ? $search_usertoprocessid : $usertoprocess->id, 'search_usertoprocessid', $user->rights->user->user->lire ? 0 : 0, null, 0, $includeonly, null, 0, 0, 0, '', 0, '', 'maxwidth200'); +$moreforfilter .= img_picto($langs->trans('Filter').' '.$langs->trans('User'), 'user').$form->select_dolusers($search_usertoprocessid ? $search_usertoprocessid : $usertoprocess->id, 'search_usertoprocessid', $user->rights->user->user->lire ? 0 : 0, null, 0, $includeonly, null, 0, 0, 0, '', 0, '', 'maxwidth200'); $moreforfilter .= '
'; if (empty($conf->global->PROJECT_TIMESHEET_DISABLEBREAK_ON_PROJECT)) { $moreforfilter .= '
'; $moreforfilter .= '
'; - $moreforfilter .= img_picto($langs->trans('Project'), 'project').''; + $moreforfilter .= img_picto($langs->trans('Filter').' '.$langs->trans('Project'), 'project').''; $moreforfilter .= '
'; $moreforfilter .= '
'; $moreforfilter .= '
'; - $moreforfilter .= img_picto($langs->trans('ThirdParty'), 'company').''; + $moreforfilter .= img_picto($langs->trans('Filter').' '.$langs->trans('ThirdParty'), 'company').''; $moreforfilter .= '
'; } @@ -652,7 +652,7 @@ if (!empty($arrayfields['t.progress']['checked'])) if ($usertoprocess->id == $user->id) print ''.$langs->trans("TimeSpentByYou").''; else print ''.$langs->trans("TimeSpentByUser").'';*/ print ''.$langs->trans("TimeSpent").'
'.$langs->trans("Everybody").''; -print ''.$langs->trans("TimeSpent").($usertoprocess->firstname ? '
'.dol_trunc($usertoprocess->firstname, 10).'' : '').''; +print ''.$langs->trans("TimeSpent").($usertoprocess->firstname ? '
'.$usertoprocess->getNomUrl(-2).''.dol_trunc($usertoprocess->firstname, 10).'' : '').''; for ($idw = 0; $idw < 7; $idw++) { diff --git a/htdocs/societe/list.php b/htdocs/societe/list.php index 33875fb67da..d3991d06c0c 100644 --- a/htdocs/societe/list.php +++ b/htdocs/societe/list.php @@ -436,9 +436,11 @@ if (!empty($search_categ_sup)) $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX."categorie_f $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX."c_stcomm as st ON s.fk_stcomm = st.id"; // We'll need this table joined to the select in order to filter by sale if ($search_sale == -2) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = s.rowid"; -elseif ($search_sale || (!$user->rights->societe->client->voir && !$socid)) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; +//elseif ($search_sale || (empty($user->rights->societe->client->voir) && (empty($conf->global->MAIN_USE_ADVANCED_PERMS) || empty($user->rights->societe->client->readallthirdparties_advance)) && !$socid)) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; +elseif ($search_sale || (empty($user->rights->societe->client->voir) && !$socid)) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; $sql .= " WHERE s.entity IN (".getEntity('societe').")"; -if (!$user->rights->societe->client->voir && !$socid) $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".$user->id; +//if (empty($user->rights->societe->client->voir) && (empty($conf->global->MAIN_USE_ADVANCED_PERMS) || empty($user->rights->societe->client->readallthirdparties_advance)) && !$socid) $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".$user->id; +if (empty($user->rights->societe->client->voir) && !$socid) $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".$user->id; if ($search_sale && $search_sale != -2) $sql .= " AND s.rowid = sc.fk_soc"; // Join for the needed table to filter by sale if (!$user->rights->fournisseur->lire) $sql .= " AND (s.fournisseur <> 1 OR s.client <> 0)"; // client=0, fournisseur=0 must be visible if ($search_sale == -2) $sql .= " AND sc.fk_user IS NULL"; diff --git a/htdocs/theme/eldy/global.inc.php b/htdocs/theme/eldy/global.inc.php index eaa1bf8c2e2..c4f862863e1 100644 --- a/htdocs/theme/eldy/global.inc.php +++ b/htdocs/theme/eldy/global.inc.php @@ -2577,6 +2577,9 @@ img.userphotosmall { /* size for user photo in lists */ img.userphoto[alt="Gravatar avatar"], img.photouserphoto.dropdown-user-image[alt="Gravatar avatar"] { background: #fff; } +form[name="addtime"] img.userphoto { + border: 1px solid #444; +} .span-icon-user { background-image: url(); background-repeat: no-repeat; diff --git a/htdocs/theme/md/style.css.php b/htdocs/theme/md/style.css.php index f0e4d593f00..a9fbc589a12 100644 --- a/htdocs/theme/md/style.css.php +++ b/htdocs/theme/md/style.css.php @@ -2568,6 +2568,9 @@ img.userphotosmall { /* size for user photo in lists */ img.userphoto[alt="Gravatar avatar"] { background: #fff; } +form[name="addtime"] img.userphoto { + border: 1px solid #444; +} .span-icon-user { background-image: url(); background-repeat: no-repeat; diff --git a/htdocs/user/class/user.class.php b/htdocs/user/class/user.class.php index 1aac46933f2..8f8f8c67ca8 100644 --- a/htdocs/user/class/user.class.php +++ b/htdocs/user/class/user.class.php @@ -2398,53 +2398,54 @@ class User extends CommonObject $label .= '
'; $label .= img_picto('', $this->picto).' '.$langs->trans("User").''; $label .= ' '.$this->getLibStatut(4); - $label .= '
'.$langs->trans('Name').': '.$this->getFullName($langs, ''); + $label .= '
'.$langs->trans('Name').': '.dol_string_nohtmltag($this->getFullName($langs, '')); if (!empty($this->login)) { - $label .= '
'.$langs->trans('Login').': '.$this->login; + $label .= '
'.$langs->trans('Login').': '.dol_string_nohtmltag($this->login); } if (!empty($this->job)) { - $label .= '
'.$langs->trans("Job").': '.$this->job; + $label .= '
'.$langs->trans("Job").': '.dol_string_nohtmltag($this->job); } - $label .= '
'.$langs->trans("Email").': '.$this->email; + $label .= '
'.$langs->trans("Email").': '.dol_string_nohtmltag($this->email); if (!empty($this->phone)) { - $label .= '
'.$langs->trans("Phone").': '.$this->phone; + $label .= '
'.$langs->trans("Phone").': '.dol_string_nohtmltag($this->phone); } if (!empty($this->admin)) { $label .= '
'.$langs->trans("Administrator").': '.yn($this->admin); } + $company = ''; if (!empty($this->socid)) { // Add thirdparty for external users $thirdpartystatic = new Societe($db); $thirdpartystatic->fetch($this->socid); if (empty($hidethirdpartylogo)) { $companylink = ' '.$thirdpartystatic->getNomUrl(2, (($option == 'nolink') ? 'nolink' : '')); // picto only of company } - $company = ' ('.$langs->trans("Company").': '.$thirdpartystatic->name.')'; + $company = ' ('.$langs->trans("Company").': '.dol_string_nohtmltag($thirdpartystatic->name).')'; } $type = ($this->socid ? $langs->trans("External").$company : $langs->trans("Internal")); - $label .= '
'.$langs->trans("Type").': '.$type; + $label .= '
'.$langs->trans("Type").': '.dol_string_nohtmltag($type); $label .= '
'; if ($infologin > 0) { $label .= '
'; $label .= '
'.$langs->trans("Session").''; - $label .= '
'.$langs->trans("IPAddress").': '.$_SERVER["REMOTE_ADDR"]; + $label .= '
'.$langs->trans("IPAddress").': '.dol_string_nohtmltag(getUserRemoteIP()); if (!empty($conf->global->MAIN_MODULE_MULTICOMPANY)) { - $label .= '
'.$langs->trans("ConnectedOnMultiCompany").': '.$conf->entity.' (user entity '.$this->entity.')'; + $label .= '
'.$langs->trans("ConnectedOnMultiCompany").': '.$conf->entity.' (User entity '.$this->entity.')'; } - $label .= '
'.$langs->trans("AuthenticationMode").': '.$_SESSION["dol_authmode"].(empty($dolibarr_main_demo) ? '' : ' (demo)'); + $label .= '
'.$langs->trans("AuthenticationMode").': '.dol_string_nohtmltag($_SESSION["dol_authmode"].(empty($dolibarr_main_demo) ? '' : ' (demo)')); $label .= '
'.$langs->trans("ConnectedSince").': '.dol_print_date($this->datelastlogin, "dayhour", 'tzuser'); $label .= '
'.$langs->trans("PreviousConnexion").': '.dol_print_date($this->datepreviouslogin, "dayhour", 'tzuser'); - $label .= '
'.$langs->trans("CurrentTheme").': '.$conf->theme; - $label .= '
'.$langs->trans("CurrentMenuManager").': '.$menumanager->name; + $label .= '
'.$langs->trans("CurrentTheme").': '.dol_string_nohtmltag($conf->theme); + $label .= '
'.$langs->trans("CurrentMenuManager").': '.dol_string_nohtmltag($menumanager->name); $s = picto_from_langcode($langs->getDefaultLang()); - $label .= '
'.$langs->trans("CurrentUserLanguage").': '.($s ? $s.' ' : '').$langs->getDefaultLang(); - $label .= '
'.$langs->trans("Browser").': '.$conf->browser->name.($conf->browser->version ? ' '.$conf->browser->version : '').' ('.$_SERVER['HTTP_USER_AGENT'].')'; - $label .= '
'.$langs->trans("Layout").': '.$conf->browser->layout; - $label .= '
'.$langs->trans("Screen").': '.$_SESSION['dol_screenwidth'].' x '.$_SESSION['dol_screenheight']; + $label .= '
'.$langs->trans("CurrentUserLanguage").': '.dol_string_nohtmltag(($s ? $s.' ' : '').$langs->getDefaultLang()); + $label .= '
'.$langs->trans("Browser").': '.dol_string_nohtmltag($conf->browser->name.($conf->browser->version ? ' '.$conf->browser->version : '').' ('.$_SERVER['HTTP_USER_AGENT'].')'); + $label .= '
'.$langs->trans("Layout").': '.dol_string_nohtmltag($conf->browser->layout); + $label .= '
'.$langs->trans("Screen").': '.dol_string_nohtmltag($_SESSION['dol_screenwidth'].' x '.$_SESSION['dol_screenheight']); if ($conf->browser->layout == 'phone') { $label .= '
'.$langs->trans("Phone").': '.$langs->trans("Yes"); } if (!empty($_SESSION["disablemodules"])) { - $label .= '
'.$langs->trans("DisabledModules").':
'.join(', ', explode(',', $_SESSION["disablemodules"])); + $label .= '
'.$langs->trans("DisabledModules").':
'.dol_string_nohtmltag(join(', ', explode(',', $_SESSION["disablemodules"]))); } } if ($infologin < 0) { @@ -2508,12 +2509,12 @@ class User extends CommonObject } if ($withpictoimg > -2 && $withpictoimg != 2) { if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { - $result .= ''; + $result .= ''; } if ($mode == 'login') { - $result .= dol_trunc($this->login, $maxlen); + $result .= dol_string_nohtmltag(dol_trunc($this->login, $maxlen)); } else { - $result .= $this->getFullName($langs, '', ($mode == 'firstelselast' ? 3 : ($mode == 'firstname' ? 2 : -1)), $maxlen); + $result .= dol_string_nohtmltag($this->getFullName($langs, '', ($mode == 'firstelselast' ? 3 : ($mode == 'firstname' ? 2 : -1)), $maxlen)); } if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { $result .= ''; diff --git a/htdocs/user/home.php b/htdocs/user/home.php index 4d215685f53..e294be6a219 100644 --- a/htdocs/user/home.php +++ b/htdocs/user/home.php @@ -128,7 +128,7 @@ if ($resql) print ''; print ''; print ''; - print ''; + print ''."\n"; $i = 0; while ($i < $num && $i < $max) diff --git a/test/phpunit/SecurityTest.php b/test/phpunit/SecurityTest.php index 8709c66f15d..fb9dbe5100e 100644 --- a/test/phpunit/SecurityTest.php +++ b/test/phpunit/SecurityTest.php @@ -186,7 +186,11 @@ class SecurityTest extends PHPUnit\Framework\TestCase $_SERVER["PHP_SELF"]='/DIR WITH SPACE/htdocs/admin/index.php?mainmenu=home&leftmenu=setup&username=weservices'; $result=testSqlAndScriptInject($_SERVER["PHP_SELF"], 2); - $this->assertEquals($expectedresult, $result, 'Error on testSqlAndScriptInject 1a'); + $this->assertEquals($expectedresult, $result, 'Error on testSqlAndScriptInject expected 0a'); + + $test = 'This is a < inside string with < and > also and tag like before the >'; + $result=testSqlAndScriptInject($test, 0); + $this->assertEquals($expectedresult, $result, 'Error on testSqlAndScriptInject expected 0b'); // Should detect XSS $expectedresult=1; @@ -275,6 +279,10 @@ class SecurityTest extends PHPUnit\Framework\TestCase $test="onerror=alert(1)"; $result=testSqlAndScriptInject($test, 0); $this->assertGreaterThanOrEqual($expectedresult, $result, 'Error on testSqlAndScriptInject jjj'); + + $test="rror=alert(document.location)"; + $result=testSqlAndScriptInject($test, 0); + $this->assertGreaterThanOrEqual($expectedresult, $result, 'Error on testSqlAndScriptInject kkk'); } /** @@ -299,7 +307,9 @@ class SecurityTest extends PHPUnit\Framework\TestCase $_GET["param5"]="a_1-b"; $_POST["param6"]="">objnotdefined\''; $_POST["param11"]=' Name '; @@ -363,10 +373,18 @@ class SecurityTest extends PHPUnit\Framework\TestCase print __METHOD__." result=".$result."\n"; $this->assertEquals('"c:\this is a path~1\aaan" abcdef', $result); - // With alphanohtml, we must convert the html entities like n - $result=GETPOST("param8", 'alphanohtml'); + // With alphanohtml, we must convert the html entities like n and disable all entities + $result=GETPOST("param8a", 'alphanohtml'); print __METHOD__." result=".$result."\n"; - $this->assertEquals("HackerassertEquals("Hackersvg onload='console.log(123)'", $result); + + $result=GETPOST("param8b", 'alphanohtml'); + print __METHOD__." result=".$result."\n"; + $this->assertEquals('img src=x onerror=alert(document.location) t=', $result, 'Test a string with non closing html tag with alphanohtml'); + + $result=GETPOST("param8c", 'alphanohtml'); + print __METHOD__." result=".$result."\n"; + $this->assertEquals($_POST['param8c'], $result, 'Test a string with non closing html tag with alphanohtml'); $result=GETPOST("param9", 'alphanohtml'); print __METHOD__." result=".$result."\n"; @@ -574,10 +592,10 @@ class SecurityTest extends PHPUnit\Framework\TestCase $this->assertEquals(400, $tmp['http_code'], 'GET url to '.$url.' that is a local URL'); // Test we receive an error because localtest.me is not an external URL /*$url = 'localtest.me'; - $tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL - print __METHOD__." url=".$url."\n"; - $this->assertEquals(400, $tmp['http_code'], 'GET url to '.$url.' that resolves to a local URL'); // Test we receive an error because localtest.me is not an external URL - */ + $tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL + print __METHOD__." url=".$url."\n"; + $this->assertEquals(400, $tmp['http_code'], 'GET url to '.$url.' that resolves to a local URL'); // Test we receive an error because localtest.me is not an external URL + */ return 0; }
'.$langs->trans("LastUsersCreated", min($num, $max)).''.$langs->trans("FullList").'