From bec5ff474979d37bcaa28fbcdd4ca518be72a184 Mon Sep 17 00:00:00 2001 From: MDW Date: Tue, 9 Apr 2024 23:21:28 +0200 Subject: [PATCH 1/9] Fix: Enforce/use int nature of BookKeeping(Line)::$piece_num, fix hooks typings (#29298) * Fix: Enforce/use int nature of BookKeeping(Line)::$piece_num * Fix typing in hookmanager class * Improve DolHooksCollector phpdoc * Fix PhanTypePossiblyInvalidDimOffset by testing index's existence * Ignore PhanTypePossiblyInvalidDimOffset that phan can not detect * Allow to be null, initialise instead of unset --- htdocs/accountancy/bookkeeping/card.php | 27 +++++----- .../accountancy/class/bookkeeping.class.php | 3 ++ htdocs/core/class/commonhookactions.class.php | 3 +- htdocs/core/class/hookmanager.class.php | 49 +++++++++++++------ .../modules/mrp/doc/pdf_vinci.modules.php | 2 +- .../class/DataCollector/DolHooksCollector.php | 8 +-- htdocs/index.php | 14 ++++-- .../template/class/actions_mymodule.class.php | 3 +- 8 files changed, 71 insertions(+), 38 deletions(-) diff --git a/htdocs/accountancy/bookkeeping/card.php b/htdocs/accountancy/bookkeeping/card.php index d775e6ae61e..74ae94c98ff 100644 --- a/htdocs/accountancy/bookkeeping/card.php +++ b/htdocs/accountancy/bookkeeping/card.php @@ -4,6 +4,7 @@ * Copyright (C) 2013-2024 Alexandre Spangaro * Copyright (C) 2017 Laurent Destailleur * Copyright (C) 2018-2020 Frédéric France + * Copyright (C) 2024 MDW * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -227,7 +228,7 @@ if (empty($reshook)) { $object = new BookKeeping($db); $result = $object->fetch($id, null, $mode); - $piece_num = $object->piece_num; + $piece_num = (int) $object->piece_num; if ($result < 0) { setEventMessages($object->error, $object->errors, 'errors'); @@ -284,7 +285,7 @@ if (empty($reshook)) { } $action = ''; $id = $object->id; - $piece_num = $object->piece_num; + $piece_num = (int) $object->piece_num; } } } @@ -438,7 +439,7 @@ if ($action == 'create') { $head = array(); $h = 0; - $head[$h][0] = $_SERVER['PHP_SELF'].'?piece_num='.$object->piece_num.($mode ? '&mode='.$mode : ''); + $head[$h][0] = $_SERVER['PHP_SELF'].'?piece_num='.((int) $object->piece_num).($mode ? '&mode='.$mode : ''); $head[$h][1] = $langs->trans("Transaction"); $head[$h][2] = 'transaction'; $h++; @@ -467,14 +468,14 @@ if ($action == 'create') { if ($action != 'editdate') { print ''; if ($permissiontoadd) { - print 'piece_num).'&mode='.urlencode((string) $mode).'">'.img_edit($langs->transnoentitiesnoconv('SetDate'), 1).''; + print 'piece_num).'&mode='.urlencode((string) $mode).'">'.img_edit($langs->transnoentitiesnoconv('SetDate'), 1).''; } print ''; } print ''; print ''; if ($action == 'editdate') { - print '
'; + print ''; if ($optioncss != '') { print ''; } @@ -498,14 +499,14 @@ if ($action == 'create') { if ($action != 'editjournal') { print ''; if ($permissiontoadd) { - print 'piece_num).'&mode='.urlencode((string) $mode).'">'.img_edit($langs->transnoentitiesnoconv('Edit'), 1).''; + print 'piece_num).'&mode='.urlencode((string) $mode).'">'.img_edit($langs->transnoentitiesnoconv('Edit'), 1).''; } print ''; } print ''; print ''; if ($action == 'editjournal') { - print ''; + print ''; if ($optioncss != '') { print ''; } @@ -529,14 +530,14 @@ if ($action == 'create') { if ($action != 'editdocref') { print ''; if ($permissiontoadd) { - print 'piece_num).'&mode='.urlencode((string) $mode).'">'.img_edit($langs->transnoentitiesnoconv('Edit'), 1).''; + print 'piece_num).'&mode='.urlencode((string) $mode).'">'.img_edit($langs->transnoentitiesnoconv('Edit'), 1).''; } print ''; } print ''; print ''; if ($action == 'editdocref') { - print ''; + print ''; if ($optioncss != '') { print ''; } @@ -664,7 +665,7 @@ if ($action == 'create') { // List of movements print load_fiche_titre($langs->trans("ListeMvts"), '', ''); - print ''; + print ''; if ($optioncss != '') { print ''; } @@ -787,7 +788,7 @@ if ($action == 'create') { print ''; if ($permissiontoadd) { if (empty($line->date_export) && empty($line->date_validation)) { - print 'id . '&piece_num=' . urlencode($line->piece_num) . '&mode=' . urlencode((string) $mode) . '&token=' . urlencode(newToken()) . '">'; + print 'id . '&piece_num=' . ((int) $line->piece_num) . '&mode=' . urlencode((string) $mode) . '&token=' . urlencode(newToken()) . '">'; print img_edit('', 0, 'class="marginrightonly"'); print '  '; } else { @@ -802,7 +803,7 @@ if ($action == 'create') { $actiontodelete = 'confirm_delete'; } - print ''; + print ''; print img_delete(); print ''; } else { @@ -830,7 +831,7 @@ if ($action == 'create') { print '
'; print '
'; if ($total_debit == $total_credit) { - print ''.$langs->trans("ValidTransaction").''; + print ''.$langs->trans("ValidTransaction").''; } else { print ''; } diff --git a/htdocs/accountancy/class/bookkeeping.class.php b/htdocs/accountancy/class/bookkeeping.class.php index e38bcd94993..fd8b6696117 100644 --- a/htdocs/accountancy/class/bookkeeping.class.php +++ b/htdocs/accountancy/class/bookkeeping.class.php @@ -3151,6 +3151,9 @@ class BookKeepingLine extends CommonObjectLine public $import_key; public $code_journal; public $journal_label; + /** + * @var int accounting transaction id + */ public $piece_num; /** diff --git a/htdocs/core/class/commonhookactions.class.php b/htdocs/core/class/commonhookactions.class.php index d3282360fbb..a6fd27afe7c 100644 --- a/htdocs/core/class/commonhookactions.class.php +++ b/htdocs/core/class/commonhookactions.class.php @@ -1,5 +1,6 @@ + * Copyright (C) 2024 MDW * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,7 +29,7 @@ abstract class CommonHookActions { /** - * @var string String of results. + * @var ?string String of results. */ public $resprints; diff --git a/htdocs/core/class/hookmanager.class.php b/htdocs/core/class/hookmanager.class.php index a7a4b6e0900..e43e13610ab 100644 --- a/htdocs/core/class/hookmanager.class.php +++ b/htdocs/core/class/hookmanager.class.php @@ -2,6 +2,7 @@ /* Copyright (C) 2010-2016 Laurent Destailleur * Copyright (C) 2010-2014 Regis Houssin * Copyright (C) 2010-2011 Juanjo Menent + * Copyright (C) 2024 MDW * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,28 +45,41 @@ class HookManager */ public $errors = array(); - // Context hookmanager was created for ('thirdpartycard', 'thirdpartydao', ...) + /** + * @var string[] Context hookmanager was created for ('thirdpartycard', 'thirdpartydao', ...) + */ public $contextarray = array(); - // Array with instantiated classes + /** + * array> Array with instantiated classes + */ public $hooks = array(); /** - * @var array List of hooks called during this request + * @var array List of hooks called during this request (key = hash) */ public $hooksHistory = []; - // Array result + /** + * @var mixed[] Result + */ public $resArray = array(); - // Printable result + + /** + * @var string Printable result + */ public $resPrint = ''; - // Nb of qualified hook ran + + /** + * @var int Nb of qualified hook ran + */ public $resNbOfHooks = 0; /** * Constructor * * @param DoliDB $db Database handler + * @return void */ public function __construct($db) { @@ -82,7 +96,7 @@ class HookManager * Then when a hook executeHooks('aMethod'...) is called, the method aMethod found into class will be executed. * * @param string[] $arraycontext Array list of searched hooks tab/features. For example: 'thirdpartycard' (for hook methods into page card thirdparty), 'thirdpartydao' (for hook methods into Societe), ... - * @return int 0 or 1 + * @return int<0,1> 0 or 1 */ public function initHooks($arraycontext) { @@ -125,6 +139,7 @@ class HookManager if ($resaction) { $controlclassname = 'Actions'.ucfirst($module); $actionInstance = new $controlclassname($this->db); + '@phan-var-force CommonHookActions $actionInstance'; $priority = empty($actionInstance->priority) ? 50 : $actionInstance->priority; $this->hooks[$context][$priority.':'.$module] = $actionInstance; } @@ -150,10 +165,10 @@ class HookManager * Execute hooks (if they were initialized) for the given method * * @param string $method Name of method hooked ('doActions', 'printSearchForm', 'showInputField', ...) - * @param array $parameters Array of parameters - * @param Object $object Object to use hooks on + * @param array $parameters Array of parameters + * @param object $object Object to use hooks on * @param string $action Action code on calling page ('create', 'edit', 'view', 'add', 'update', 'delete'...) - * @return int For 'addreplace' hooks (doActions, formConfirm, formObjectOptions, pdf_xxx,...): Return 0 if we want to keep standard actions, >0 if we want to stop/replace standard actions, <0 if KO. Things to print are returned into ->resprints and set into ->resPrint. Things to return are returned into ->results by hook and set into ->resArray for caller. + * @return int<-1,1> For 'addreplace' hooks (doActions, formConfirm, formObjectOptions, pdf_xxx,...): Return 0 if we want to keep standard actions, >0 if we want to stop/replace standard actions, <0 if KO. Things to print are returned into ->resprints and set into ->resPrint. Things to return are returned into ->results by hook and set into ->resArray for caller. * For 'output' hooks (printLeftBlock, formAddObjectLine, formBuilddocOptions, ...): Return 0 if we want to keep standard actions, >0 uf we want to stop/replace standard actions (at least one > 0 and replacement will be done), <0 if KO. Things to print are returned into ->resprints and set into ->resPrint. Things to return are returned into ->results by hook and set into ->resArray for caller. * All types can also return some values into an array ->results that will be merged into this->resArray for caller. * $this->error or this->errors are also defined by class called by this function if error. @@ -170,6 +185,7 @@ class HookManager 'contexts' => $this->contextarray, 'file' => $trace[0]['file'], 'line' => $trace[0]['line'], + 'count' => 0, ]; $hash = md5(json_encode($hookInformations)); if (!empty($this->hooksHistory[$hash])) { @@ -245,6 +261,7 @@ class HookManager $error = 0; foreach ($this->hooks as $context => $modules) { // $this->hooks is an array with context as key and value is an array of modules that handle this context if (!empty($modules)) { + '@phan-var-force array $modules'; // Loop on each active hooks of module for this context foreach ($modules as $module => $actionclassinstance) { $module = preg_replace('/^\d+:/', '', $module); @@ -278,6 +295,7 @@ class HookManager $parameters['currentcontext'] = $context; // Hooks that must return int (hooks with type 'addreplace') if ($hooktype == 'addreplace') { + // @phan-suppress-next-line PhanUndeclaredMethod The method's existence is tested above. $resactiontmp = $actionclassinstance->$method($parameters, $object, $action, $this); // $object and $action can be changed by method ($object->id during creation for example or $action to go back to other action for example) $resaction += $resactiontmp; @@ -298,9 +316,9 @@ class HookManager if (!empty($actionclassinstance->resprints)) { if ($resactiontmp > 0) { - $localResPrint = $actionclassinstance->resprints; + $localResPrint = (string) $actionclassinstance->resprints; } else { - $localResPrint .= $actionclassinstance->resprints; + $localResPrint .= (string) $actionclassinstance->resprints; } } } else { @@ -315,6 +333,7 @@ class HookManager dol_syslog("Call method ".$method." of class ".get_class($actionclassinstance).", module=".$module.", hooktype=".$hooktype, LOG_DEBUG); } + // @phan-suppress-next-line PhanUndeclaredMethod The method's existence is tested above. $resactiontmp = $actionclassinstance->$method($parameters, $object, $action, $this); // $object and $action can be changed by method ($object->id during creation for example or $action to go back to other action for example) $resaction += $resactiontmp; @@ -322,7 +341,7 @@ class HookManager $localResArray = array_merge_recursive($localResArray, $actionclassinstance->results); } if (!empty($actionclassinstance->resprints)) { - $localResPrint .= $actionclassinstance->resprints; + $localResPrint .= (string) $actionclassinstance->resprints; } if (is_numeric($resactiontmp) && $resactiontmp < 0) { $error++; @@ -342,8 +361,8 @@ class HookManager //print "After hook context=".$context." ".get_class($actionclassinstance)." method=".$method." hooktype=".$hooktype." results=".count($actionclassinstance->results)." resprints=".count($actionclassinstance->resprints)." resaction=".$resaction."
\n"; - unset($actionclassinstance->results); - unset($actionclassinstance->resprints); + $actionclassinstance->results = array(); + $actionclassinstance->resprints = null; } } } diff --git a/htdocs/core/modules/mrp/doc/pdf_vinci.modules.php b/htdocs/core/modules/mrp/doc/pdf_vinci.modules.php index ee0b0cf3ff9..70fdb1edb89 100644 --- a/htdocs/core/modules/mrp/doc/pdf_vinci.modules.php +++ b/htdocs/core/modules/mrp/doc/pdf_vinci.modules.php @@ -999,7 +999,7 @@ class pdf_vinci extends ModelePDFMo $pdf->line($colDef['xStartPos'], $tab_top, $colDef['xStartPos'], $tab_top + $tab_height); } - if (empty($hidetop)) { + if (empty($hidetop) && array_key_exists('title', $colDef) && array_key_exists('width', $colDef)) { $pdf->SetXY($colDef['xStartPos'] + $colDef['title']['padding'][3], $tab_top + $colDef['title']['padding'][0]); $textWidth = $colDef['width'] - $colDef['title']['padding'][3] - $colDef['title']['padding'][1]; diff --git a/htdocs/debugbar/class/DataCollector/DolHooksCollector.php b/htdocs/debugbar/class/DataCollector/DolHooksCollector.php index 52fb3a9ae38..09bba301c3f 100644 --- a/htdocs/debugbar/class/DataCollector/DolHooksCollector.php +++ b/htdocs/debugbar/class/DataCollector/DolHooksCollector.php @@ -1,5 +1,6 @@ + * Copyright (C) 2024 MDW * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,7 +24,6 @@ use DebugBar\DataCollector\RequestDataCollector; - /** * DolRequestDataCollector class */ @@ -32,7 +32,7 @@ class DolHooksCollector extends RequestDataCollector /** * Collects the data from the collectors * - * @return array Array of collected data + * @return array{nb_of_hooks:int,hooks:array} Array of collected data */ public function collect() { @@ -41,7 +41,7 @@ class DolHooksCollector extends RequestDataCollector */ global $hookmanager; - $data = ['hooks' => []]; + $data = ['hooks' => [], 'nb_of_hooks' => 0]; if (empty($hookmanager->hooksHistory)) { return $data; } @@ -62,7 +62,7 @@ class DolHooksCollector extends RequestDataCollector /** * Return widget settings * - * @return array Array + * @return array Array */ public function getWidgets() { diff --git a/htdocs/index.php b/htdocs/index.php index 1d44347356c..d6af48f77fb 100644 --- a/htdocs/index.php +++ b/htdocs/index.php @@ -5,6 +5,7 @@ * Copyright (C) 2011-2012 Juanjo Menent * Copyright (C) 2015 Marcos García * Copyright (C) 2021 Frédéric France + * Copyright (C) 2024 MDW * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,9 +44,15 @@ $hookmanager->initHooks(array('index')); */ $nbmodulesnotautoenabled = count($conf->modules); -if (in_array('fckeditor', $conf->modules)) $nbmodulesnotautoenabled--; -if (in_array('export', $conf->modules)) $nbmodulesnotautoenabled--; -if (in_array('import', $conf->modules)) $nbmodulesnotautoenabled--; +if (in_array('fckeditor', $conf->modules)) { + $nbmodulesnotautoenabled--; +} +if (in_array('export', $conf->modules)) { + $nbmodulesnotautoenabled--; +} +if (in_array('import', $conf->modules)) { + $nbmodulesnotautoenabled--; +} // Check if company name is defined (first install) if (!getDolGlobalString('MAIN_INFO_SOCIETE_NOM') || !getDolGlobalString('MAIN_INFO_SOCIETE_COUNTRY')) { @@ -522,6 +529,7 @@ if (!getDolGlobalString('MAIN_DISABLE_GLOBAL_WORKBOARD') && getDolGlobalInt('MAI if (!empty($groupElement['lang'])) { $langs->load($groupElement['lang']); } + // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset $groupName = $langs->trans($groupElement['groupName']); $groupKeyLowerCase = strtolower($groupKey); diff --git a/htdocs/modulebuilder/template/class/actions_mymodule.class.php b/htdocs/modulebuilder/template/class/actions_mymodule.class.php index 3b9d04b0e38..d76e193d30e 100644 --- a/htdocs/modulebuilder/template/class/actions_mymodule.class.php +++ b/htdocs/modulebuilder/template/class/actions_mymodule.class.php @@ -1,5 +1,6 @@ * * 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 @@ -52,7 +53,7 @@ class ActionsMyModule extends CommonHookActions public $results = array(); /** - * @var string String displayed by executeHook() immediately after return + * @var ?string String displayed by executeHook() immediately after return */ public $resprints; From 9fa6a77723bd9f88cebae84d2bbd36fa56d7427b Mon Sep 17 00:00:00 2001 From: Regis Houssin Date: Tue, 9 Apr 2024 23:55:07 +0200 Subject: [PATCH 2/9] FIX multiple problems when MULTICOMPANY_TRANSVERSE_MODE (#29287) * FIX can not show group when MULTICOMPANY_TRANSVERSE_MODE * FIX wrong permissions with multicompany MULTICOMPANY_TRANSVERSE_MODE * FIX better check for the "read" permission * FIX only a superadmin can manage users in transverse mode * FIX this is not "create" but "write" * FIX use the new function hasRight * FIX wrong check --------- Co-authored-by: Laurent Destailleur --- htdocs/core/menus/standard/eldy.lib.php | 34 ++++++++++++------------- htdocs/user/group/list.php | 21 ++++++--------- htdocs/user/home.php | 6 ++--- htdocs/user/list.php | 20 +++++++-------- 4 files changed, 38 insertions(+), 43 deletions(-) diff --git a/htdocs/core/menus/standard/eldy.lib.php b/htdocs/core/menus/standard/eldy.lib.php index 80593ee6d0d..4eb8a525620 100644 --- a/htdocs/core/menus/standard/eldy.lib.php +++ b/htdocs/core/menus/standard/eldy.lib.php @@ -1,13 +1,13 @@ - * Copyright (C) 2010 Regis Houssin - * Copyright (C) 2012-2015 Juanjo Menent - * Copyright (C) 2013 Cédric Salvador - * Copyright (C) 2015 Marcos García - * Copyright (C) 2018 Ferran Marcet - * Copyright (C) 2018-2022 Frédéric France - * Copyright (C) 2021 Gauthier VERDOL - * Copyright (C) 2024 MDW +/* Copyright (C) 2010-2022 Laurent Destailleur + * Copyright (C) 2010-2024 Regis Houssin + * Copyright (C) 2012-2015 Juanjo Menent + * Copyright (C) 2013 Cédric Salvador + * Copyright (C) 2015 Marcos García + * Copyright (C) 2018 Ferran Marcet + * Copyright (C) 2018-2022 Frédéric France + * Copyright (C) 2021 Gauthier VERDOL + * Copyright (C) 2024 MDW * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -1213,17 +1213,17 @@ function get_left_menu_home($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = $newmenu->add("/user/home.php?leftmenu=users", $langs->trans("MenuUsersAndGroups"), 0, $user->hasRight('user', 'user', 'read'), '', $mainmenu, 'users', 0, '', '', '', img_picto('', 'user', 'class="paddingright pictofixedwidth"')); if ($user->hasRight('user', 'user', 'read')) { if ($usemenuhider || empty($leftmenu) || $leftmenu == "users") { - $newmenu->add("", $langs->trans("Users"), 1, $user->hasRight('user', 'user', 'lire') || $user->admin); - $newmenu->add("/user/card.php?leftmenu=users&action=create", $langs->trans("NewUser"), 2, ($user->hasRight("user", "user", "write") || $user->admin) && !(isModEnabled('multicompany') && $conf->entity > 1 && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')), '', 'home'); - $newmenu->add("/user/list.php?leftmenu=users", $langs->trans("ListOfUsers"), 2, $user->hasRight('user', 'user', 'lire') || $user->admin); - $newmenu->add("/user/hierarchy.php?leftmenu=users", $langs->trans("HierarchicView"), 2, $user->hasRight('user', 'user', 'lire') || $user->admin); + $newmenu->add("", $langs->trans("Users"), 1, $user->hasRight('user', 'user', 'read') || $user->admin); + $newmenu->add("/user/card.php?leftmenu=users&action=create", $langs->trans("NewUser"), 2, ($user->hasRight("user", "user", "write") || $user->admin) && !(isModEnabled('multicompany') && !empty($user->entity) && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')), '', 'home'); + $newmenu->add("/user/list.php?leftmenu=users", $langs->trans("ListOfUsers"), 2, $user->hasRight('user', 'user', 'read') || $user->admin); + $newmenu->add("/user/hierarchy.php?leftmenu=users", $langs->trans("HierarchicView"), 2, $user->hasRight('user', 'user', 'read') || $user->admin); if (isModEnabled('category')) { $langs->load("categories"); - $newmenu->add("/categories/index.php?leftmenu=users&type=7", $langs->trans("UsersCategoriesShort"), 2, $user->hasRight('categorie', 'lire'), '', $mainmenu, 'cat'); + $newmenu->add("/categories/index.php?leftmenu=users&type=7", $langs->trans("UsersCategoriesShort"), 2, $user->hasRight('categorie', 'read'), '', $mainmenu, 'cat'); } - $newmenu->add("", $langs->trans("Groups"), 1, ($user->hasRight('user', 'user', 'lire') || $user->admin) && !(isModEnabled('multicompany') && $conf->entity > 1 && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE'))); - $newmenu->add("/user/group/card.php?leftmenu=users&action=create", $langs->trans("NewGroup"), 2, ((getDolGlobalString('MAIN_USE_ADVANCED_PERMS') ? $user->hasRight("user", "group_advance", "create") : $user->hasRight("user", "user", "creer")) || $user->admin) && !(isModEnabled('multicompany') && $conf->entity > 1 && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE'))); - $newmenu->add("/user/group/list.php?leftmenu=users", $langs->trans("ListOfGroups"), 2, ((getDolGlobalString('MAIN_USE_ADVANCED_PERMS') ? $user->hasRight('user', 'group_advance', 'read') : $user->hasRight('user', 'user', 'lire')) || $user->admin) && !(isModEnabled('multicompany') && $conf->entity > 1 && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE'))); + $newmenu->add("", $langs->trans("Groups"), 1, ($user->hasRight('user', 'user', 'read') || $user->admin) && !(isModEnabled('multicompany') && !empty($user->entity) && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE'))); + $newmenu->add("/user/group/card.php?leftmenu=users&action=create", $langs->trans("NewGroup"), 2, ((getDolGlobalString('MAIN_USE_ADVANCED_PERMS') ? $user->hasRight("user", "group_advance", "write") : $user->hasRight("user", "user", "write")) || $user->admin) && !(isModEnabled('multicompany') && !empty($user->entity) && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE'))); + $newmenu->add("/user/group/list.php?leftmenu=users", $langs->trans("ListOfGroups"), 2, ((getDolGlobalString('MAIN_USE_ADVANCED_PERMS') ? $user->hasRight('user', 'group_advance', 'read') : $user->hasRight('user', 'user', 'read')) || $user->admin)); } } } diff --git a/htdocs/user/group/list.php b/htdocs/user/group/list.php index f491bdad587..f8d5da2cc8b 100644 --- a/htdocs/user/group/list.php +++ b/htdocs/user/group/list.php @@ -1,9 +1,9 @@ - * Copyright (C) 2004-2018 Laurent Destailleur - * Copyright (C) 2005-2018 Regis Houssin - * Copyright (C) 2011 Herve Prot - * Copyright (C) 2019-2021 Frédéric France + * Copyright (C) 2004-2018 Laurent Destailleur + * Copyright (C) 2005-2024 Regis Houssin + * Copyright (C) 2011 Herve Prot + * Copyright (C) 2019-2021 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 @@ -93,21 +93,16 @@ if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) { } } -// Users/Groups management only in master entity if transverse mode -if (isModEnabled('multicompany') && $conf->entity > 1 && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) { - accessforbidden(); -} - if (!$user->hasRight("user", "user", "read") && !$user->admin) { accessforbidden(); } // Defini si peux lire/modifier utilisateurs et permissions -$caneditperms = ($user->admin || $user->hasRight("user", "user", "write")); -$permissiontodelete = ($user->admin || $user->hasRight("user", "user", "write")); +$caneditperms = (isModEnabled('multicompany') && !empty($user->entity) && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE') ? false : (!empty($user->admin) || $user->hasRight("user", "user", "write"))); +$permissiontodelete = $caneditperms; // Advanced permissions if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) { - $caneditperms = ($user->admin || $user->hasRight("user", "group_advance", "write")); + $caneditperms = (isModEnabled('multicompany') && !empty($user->entity) && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE') ? false : ($user->admin || $user->hasRight("user", "group_advance", "write"))); } @@ -287,7 +282,7 @@ $newcardbutton .= dolGetButtonTitle($langs->trans('ViewKanban'), '', 'fa fa-th-l if ($caneditperms) { $newcardbutton .= dolGetButtonTitleSeparator(); - $newcardbutton .= dolGetButtonTitle($langs->trans('NewGroup'), '', 'fa fa-plus-circle', DOL_URL_ROOT.'/user/group/card.php?action=create&leftmenu='); + $newcardbutton .= dolGetButtonTitle($langs->trans('NewGroup'), '', 'fa fa-plus-circle', DOL_URL_ROOT.'/user/group/card.php?action=create&leftmenu=', '', $caneditperms); } print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'object_'.$object->picto, 0, $newcardbutton, '', $limit, 0, 0, 1); diff --git a/htdocs/user/home.php b/htdocs/user/home.php index f9aef49c949..c6af5a06144 100644 --- a/htdocs/user/home.php +++ b/htdocs/user/home.php @@ -1,7 +1,7 @@ - * Copyright (C) 2005-2018 Regis Houssin - * Copyright (C) 2019 Nicolas ZABOURI + * Copyright (C) 2005-2024 Regis Houssin + * Copyright (C) 2019 Nicolas ZABOURI * * 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 @@ -40,7 +40,7 @@ $langs->load("users"); $canreadperms = true; if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) { - $canreadperms = ($user->admin || $user->rights->user->group_advance->read); + $canreadperms = (!empty($user->admin) || $user->hasRight("user", "group_advance", "read")); } // Security check (for external users) diff --git a/htdocs/user/list.php b/htdocs/user/list.php index 65955a4c39f..b9234625794 100644 --- a/htdocs/user/list.php +++ b/htdocs/user/list.php @@ -1,10 +1,10 @@ - * Copyright (C) 2004-2021 Laurent Destailleur - * Copyright (C) 2005-2017 Regis Houssin - * Copyright (C) 2015 Alexandre Spangaro - * Copyright (C) 2016 Marcos García - * Copyright (C) 2024 MDW +/* Copyright (C) 2002-2005 Rodolphe Quiedeville + * Copyright (C) 2004-2021 Laurent Destailleur + * Copyright (C) 2005-2024 Regis Houssin + * Copyright (C) 2015 Alexandre Spangaro + * Copyright (C) 2016 Marcos García + * Copyright (C) 2024 MDW * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -198,15 +198,15 @@ if ($contextpage == 'employeelist' && !GETPOSTISSET('search_employee')) { } // Define value to know what current user can do on users -$permissiontoadd = (!empty($user->admin) || $user->hasRight("user", "user", "write")); +$permissiontoadd = (isModEnabled('multicompany') && !empty($user->entity) && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE') ? false : (!empty($user->admin) || $user->hasRight("user", "user", "write"))); $canreaduser = (!empty($user->admin) || $user->hasRight("user", "user", "read")); -$canedituser = (!empty($user->admin) || $user->hasRight("user", "user", "write")); -$candisableuser = (!empty($user->admin) || $user->hasRight("user", "user", "delete")); +$canedituser = $permissiontoadd; +$candisableuser = (isModEnabled('multicompany') && !empty($user->entity) && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE') ? false : (!empty($user->admin) || $user->hasRight("user", "user", "delete"))); $canreadgroup = $canreaduser; $caneditgroup = $canedituser; if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) { $canreadgroup = (!empty($user->admin) || $user->hasRight("user", "group_advance", "read")); - $caneditgroup = (!empty($user->admin) || $user->hasRight("user", "group_advance", "write")); + $caneditgroup = (isModEnabled('multicompany') && !empty($user->entity) && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE') ? false : (!empty($user->admin) || $user->hasRight("user", "group_advance", "write"))); } $error = 0; From 132595f10f532be2f5704d07e88ddb9d43109b87 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 10 Apr 2024 01:38:36 +0200 Subject: [PATCH 3/9] CSS customreport --- htdocs/core/class/html.form.class.php | 4 ++-- htdocs/core/customreports.php | 2 +- htdocs/core/db/Database.interface.php | 2 +- htdocs/core/db/DoliDB.class.php | 2 +- htdocs/core/lib/customreports.lib.php | 18 ++++++++++++++++++ htdocs/theme/eldy/global.inc.php | 3 ++- htdocs/theme/md/style.css.php | 3 ++- 7 files changed, 27 insertions(+), 7 deletions(-) diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index c946683b8de..79895728c3d 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -10712,9 +10712,9 @@ class Form $ret = ''; - $ret .= '
'; + $ret .= '
'; $ret .= ''; - $ret .= ''; + $ret .= ''; $ret .= ''; $ret .= '
'; diff --git a/htdocs/core/customreports.php b/htdocs/core/customreports.php index 49b9dbc56ad..75ee1126897 100644 --- a/htdocs/core/customreports.php +++ b/htdocs/core/customreports.php @@ -606,7 +606,7 @@ if (!defined('MAIN_CUSTOM_REPORT_KEEP_GRAPH_ONLY')) { // Filter (you can use param &show_search_component_params_hidden=1 for debug) if (!empty($object)) { - print '
'; + print '
'; print $form->searchComponent(array($object->element => $object->fields), $search_component_params, array(), $search_component_params_hidden); print '
'; } diff --git a/htdocs/core/db/Database.interface.php b/htdocs/core/db/Database.interface.php index 216865b39a3..57f87ae6ad3 100644 --- a/htdocs/core/db/Database.interface.php +++ b/htdocs/core/db/Database.interface.php @@ -42,7 +42,7 @@ interface Database * @param string $nameoffield Name of field * @return string SQL string */ - public function stddevPop($nameoffield); + public function stddevpop($nameoffield); // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** diff --git a/htdocs/core/db/DoliDB.class.php b/htdocs/core/db/DoliDB.class.php index 75379297b1c..16bac68eedd 100644 --- a/htdocs/core/db/DoliDB.class.php +++ b/htdocs/core/db/DoliDB.class.php @@ -112,7 +112,7 @@ abstract class DoliDB implements Database * @param string $nameoffield Name of field * @return string SQL string */ - public function stddevPop($nameoffield) + public function stddevpop($nameoffield) { return 'STDDEV_POP('.$nameoffield.')'; } diff --git a/htdocs/core/lib/customreports.lib.php b/htdocs/core/lib/customreports.lib.php index b4011bf0c0e..e3d274a7cf6 100644 --- a/htdocs/core/lib/customreports.lib.php +++ b/htdocs/core/lib/customreports.lib.php @@ -95,6 +95,15 @@ function fillArrayOfMeasures($object, $tablealias, $labelofobject, &$arrayofmesu 'table' => $object->table_element, 'tablefromt' => $tablepath ); + if (getDolGlobalInt('MAIN_FEATURES_LEVEL') >= 2) { + $arrayofmesures[$tablealias.'.'.$key.'-stddevpop'] = array( + 'label' => img_picto('', (empty($object->picto) ? 'generic' : $object->picto), 'class="pictofixedwidth"').$labelofobject.': '.$langs->trans($val['label']).' ('.$langs->trans("StandardDeviationPop").')', + 'labelnohtml' => $labelofobject.': '.$langs->trans($val['label']), + 'position' => ($position + ($count * 100000)).'.5', + 'table' => $object->table_element, + 'tablefromt' => $tablepath + ); + } } } // Add extrafields to Measures @@ -131,6 +140,15 @@ function fillArrayOfMeasures($object, $tablealias, $labelofobject, &$arrayofmesu 'table' => $object->table_element, 'tablefromt' => $tablepath ); + if (getDolGlobalInt('MAIN_FEATURES_LEVEL') >= 2) { + $arrayofmesures[preg_replace('/^t/', 'te', $tablealias).'.'.$key.'-stddevpop'] = array( + 'label' => img_picto('', (empty($object->picto) ? 'generic' : $object->picto), 'class="pictofixedwidth"').$labelofobject.': '.$langs->trans($extrafields->attributes[$object->table_element]['label'][$key]).' ('.$langs->trans("StandardDeviationPop").')', + 'labelnohtml' => $labelofobject.': '.$langs->trans($val), + 'position' => ($position + ($count * 100000)).'.5', + 'table' => $object->table_element, + 'tablefromt' => $tablepath + ); + } } } } diff --git a/htdocs/theme/eldy/global.inc.php b/htdocs/theme/eldy/global.inc.php index 0ebe61efdb9..413e98f5803 100644 --- a/htdocs/theme/eldy/global.inc.php +++ b/htdocs/theme/eldy/global.inc.php @@ -1243,7 +1243,7 @@ div.divsearchfield { padding-: 0; padding-: 0; border-bottom: solid 1px var(--inputbordercolor); - height: 24px; + line-height: 24px; border-radius: 3px; } .search_component_searchtext { @@ -1261,6 +1261,7 @@ div.divsearchfield { padding-bottom: 3px; background: #ddd; border-radius: 4px; + display: inline-block; } .tagsearchdelete { color: #999; diff --git a/htdocs/theme/md/style.css.php b/htdocs/theme/md/style.css.php index b5c9f43ee9c..d206a3b643e 100644 --- a/htdocs/theme/md/style.css.php +++ b/htdocs/theme/md/style.css.php @@ -1415,7 +1415,7 @@ div.divsearchfield { padding-: 0; padding-: 0; border-bottom: solid 1px var(--inputbordercolor); - height: 24px; + line-height: 24px; } .search_component_searchtext { padding-top: 2px; @@ -1432,6 +1432,7 @@ div.divsearchfield { padding-bottom: 3px; background: #ddd; border-radius: 4px; + display: inline-block; } .tagsearchdelete { color: #999; From efaccbc51950c720ce47cda653a21c69255d6453 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 10 Apr 2024 01:50:29 +0200 Subject: [PATCH 4/9] Remove duplicate graph --- dev/tools/apstats.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tools/apstats.php b/dev/tools/apstats.php index 3fc890137d6..388e26cf4a2 100755 --- a/dev/tools/apstats.php +++ b/dev/tools/apstats.php @@ -619,6 +619,7 @@ $html .= '
'; $html .= '
'; // OSSINSIGHT graph +/* $html .= << @@ -632,6 +633,7 @@ $html .= << END; +*/ $html .= ''."\n"; From 31e09f134b42bd1592210877c054c458f31b4032 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 10 Apr 2024 02:52:39 +0200 Subject: [PATCH 5/9] Fix warnings --- htdocs/accountancy/admin/productaccount.php | 9 ++++++--- htdocs/accountancy/bookkeeping/export.php | 1 + htdocs/accountancy/bookkeeping/list.php | 2 +- htdocs/accountancy/bookkeeping/listbyaccount.php | 2 +- htdocs/langs/en_US/main.lang | 1 + 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/htdocs/accountancy/admin/productaccount.php b/htdocs/accountancy/admin/productaccount.php index 5805a376fc8..6a6dfa21d61 100644 --- a/htdocs/accountancy/admin/productaccount.php +++ b/htdocs/accountancy/admin/productaccount.php @@ -54,6 +54,7 @@ if (!$user->hasRight('accounting', 'bind', 'write')) { $action = GETPOST('action', 'aZ09'); $massaction = GETPOST('massaction', 'alpha'); $confirm = GETPOST('confirm', 'alpha'); +$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : str_replace('_', '', basename(dirname(__FILE__)).basename(__FILE__, '.php')); // To manage different context of search $optioncss = GETPOST('optioncss', 'alpha'); $codeventil_buy = GETPOST('codeventil_buy', 'array'); @@ -69,7 +70,7 @@ $changeaccount_buy = GETPOST('changeaccount_buy', 'array'); '@phan-var-force string[] $changeaccount_buy'; $changeaccount_sell = GETPOST('changeaccount_sell', 'array'); '@phan-var-force string[] $changeaccount_sell'; -$searchCategoryProductOperator = (GETPOSTINT('search_category_product_operator') ? GETPOSTINT('search_category_product_operator') : 0); +$searchCategoryProductOperator = GETPOSTINT('search_category_product_operator'); $searchCategoryProductList = GETPOST('search_category_product_list', 'array'); '@phan-var-force string[] $searchCategoryProductList'; $search_ref = GETPOST('search_ref', 'alpha'); @@ -515,10 +516,12 @@ if ($resql) { $varpage = empty($contextpage) ? $_SERVER["PHP_SELF"] : $contextpage; $selectedfields = $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields + $massactionbutton = ''; + if ($massaction !== 'set_default_account') { $arrayofmassactions = array( - 'changeaccount' => img_picto('', 'check', 'class="pictofixedwidth"').$langs->trans("Save") - ,'set_default_account' => img_picto('', 'check', 'class="pictofixedwidth"').$langs->trans("ConfirmPreselectAccount") + 'changeaccount' => img_picto('', 'check', 'class="pictofixedwidth"').$langs->trans("Save"), + 'set_default_account' => img_picto('', 'check', 'class="pictofixedwidth"').$langs->trans("ConfirmPreselectAccount") ); $massactionbutton = $form->selectMassAction('', $arrayofmassactions, 1); } diff --git a/htdocs/accountancy/bookkeeping/export.php b/htdocs/accountancy/bookkeeping/export.php index f28f2ec2b90..0f215e055f3 100644 --- a/htdocs/accountancy/bookkeeping/export.php +++ b/htdocs/accountancy/bookkeeping/export.php @@ -1291,6 +1291,7 @@ while ($i < min($num, $limit)) { //$modulepart = 'invoice_supplier'; if ($objectstatic->id > 0) { + $modulepart = 'invoice_supplier'; $filename = dol_sanitizeFileName($line->doc_ref); $filedir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($line->fk_doc, 2, 0, 0, $objectstatic, $modulepart).dol_sanitizeFileName($line->doc_ref); $subdir = get_exdir($objectstatic->id, 2, 0, 0, $objectstatic, $modulepart).dol_sanitizeFileName($line->doc_ref); diff --git a/htdocs/accountancy/bookkeeping/list.php b/htdocs/accountancy/bookkeeping/list.php index 0d29209780e..c532b2b3b39 100644 --- a/htdocs/accountancy/bookkeeping/list.php +++ b/htdocs/accountancy/bookkeeping/list.php @@ -1177,8 +1177,8 @@ while ($i < min($num, $limit)) { require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php'; $objectstatic = new FactureFournisseur($db); $objectstatic->fetch($line->fk_doc); - //$modulepart = 'invoice_supplier'; + $modulepart = 'invoice_supplier'; $filename = dol_sanitizeFileName($line->doc_ref); $filedir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($line->fk_doc, 2, 0, 0, $objectstatic, $modulepart).dol_sanitizeFileName($line->doc_ref); $subdir = get_exdir($objectstatic->id, 2, 0, 0, $objectstatic, $modulepart).dol_sanitizeFileName($line->doc_ref); diff --git a/htdocs/accountancy/bookkeeping/listbyaccount.php b/htdocs/accountancy/bookkeeping/listbyaccount.php index 82a26a6534c..272ec55389c 100644 --- a/htdocs/accountancy/bookkeeping/listbyaccount.php +++ b/htdocs/accountancy/bookkeeping/listbyaccount.php @@ -1174,8 +1174,8 @@ while ($i < min($num, $limit)) { require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php'; $objectstatic = new FactureFournisseur($db); $objectstatic->fetch($line->fk_doc); - //$modulepart = 'invoice_supplier'; + $modulepart = 'invoice_supplier'; $filename = dol_sanitizeFileName($line->doc_ref); $filedir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($line->fk_doc, 2, 0, 0, $objectstatic, $modulepart).dol_sanitizeFileName($line->doc_ref); $subdir = get_exdir($objectstatic->id, 2, 0, 0, $objectstatic, $modulepart).dol_sanitizeFileName($line->doc_ref); diff --git a/htdocs/langs/en_US/main.lang b/htdocs/langs/en_US/main.lang index f6bbc42efd0..235f2d7c037 100644 --- a/htdocs/langs/en_US/main.lang +++ b/htdocs/langs/en_US/main.lang @@ -457,6 +457,7 @@ VATNPR=Tax Rate NPR DefaultTaxRate=Default tax rate Average=Average Sum=Sum +StandardDeviationPop=Standard deviation Delta=Delta StatusToPay=To pay RemainToPay=Remain to pay From d591289e5368ec824d37387348fa785ddad46f5a Mon Sep 17 00:00:00 2001 From: Christophe Battarel Date: Wed, 10 Apr 2024 02:59:10 +0200 Subject: [PATCH 6/9] NEW : TakePOS - add buttons to allow keyboard usage for discounts (#24819) * NEW : TakePOS - add buttons to allow keyboard usage for discounts * modern html * remove useless code --------- Co-authored-by: Christophe Battarel --- htdocs/takepos/reduction.php | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/htdocs/takepos/reduction.php b/htdocs/takepos/reduction.php index 2d9b3d96e6e..3b32fe4b16e 100644 --- a/htdocs/takepos/reduction.php +++ b/htdocs/takepos/reduction.php @@ -1,5 +1,6 @@ + * Copyright (C) 2023 Christophe Battarel * * 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 @@ -150,6 +151,8 @@ if (!isset($conf->global->TAKEPOS_NUMPAD_USE_PAYMENT_ICON) || getDolGlobalString jQuery('#reduction_type_percent').html(htmlReductionPercent); jQuery('#reduction_type_amount').html(htmlReductionAmount); } + + $("#reduction_total").focus(); } /** @@ -173,6 +176,8 @@ if (!isset($conf->global->TAKEPOS_NUMPAD_USE_PAYMENT_ICON) || getDolGlobalString console.log('ValidateReduction'); reductionTotal = jQuery('#reduction_total').val(); + reductionTotal = $("#reduction_total").val(); + if (reductionTotal.length <= 0) { console.error('Error no reduction'); return; @@ -181,7 +186,7 @@ if (!isset($conf->global->TAKEPOS_NUMPAD_USE_PAYMENT_ICON) || getDolGlobalString var reductionNumber = parseFloat(reductionTotal); if (isNaN(reductionNumber)) { console.error('Error not a valid number :', reductionNumber); - return; + return false; } if (reductionType === 'percent') { @@ -198,14 +203,28 @@ if (!isset($conf->global->TAKEPOS_NUMPAD_USE_PAYMENT_ICON) || getDolGlobalString }); } else { console.error('Error bad reduction type :', reductionType); + return false } + + return true; + } + + // manual input validation + function formvalid(type) { + reductionType = type; + if (reductionType != "") { + return ValidateReduction(); + } + return false; }
trans('Reduction').'">'; + print ''; + print ''; + print ''; ?>
From 6c26562140f83d04d4a72d59b9adbe551b914c60 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 10 Apr 2024 03:14:59 +0200 Subject: [PATCH 7/9] Debug v20 --- htdocs/takepos/reduction.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/htdocs/takepos/reduction.php b/htdocs/takepos/reduction.php index 3b32fe4b16e..1d38e63977d 100644 --- a/htdocs/takepos/reduction.php +++ b/htdocs/takepos/reduction.php @@ -1,6 +1,6 @@ - * Copyright (C) 2023 Christophe Battarel + * Copyright (C) 2023 Christophe Battarel * * 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 @@ -91,6 +91,7 @@ if (!isset($conf->global->TAKEPOS_NUMPAD_USE_PAYMENT_ICON) || getDolGlobalString ?> +
trans('Reduction').'" autofocus>'; +print ''; +if (getDolGlobalString('TAKEPOS_ADD_BUTTON_TO_ENTER_DISCOUNT_WITH_KEYBOARD')) { print ''; print ''; +} ?>
From f7eb0b9fde328674d860d7e860fe6b9b906d1dda Mon Sep 17 00:00:00 2001 From: Jon Bendtsen Date: Wed, 10 Apr 2024 12:02:57 +0200 Subject: [PATCH 8/9] NEW: API setup update extrafields from name, elementtype and json (#29273) * NEW: API setup update extrafields from name, elementtype and json * default_value * fix SQL injection * more SQL injection prevention * Sanitized --------- Co-authored-by: Jon Bendtsen Co-authored-by: Laurent Destailleur --- htdocs/api/class/api_setup.class.php | 85 +++++++++++++++++++++++++ htdocs/core/class/extrafields.class.php | 84 +++++++++++++++++++++++- 2 files changed, 166 insertions(+), 3 deletions(-) diff --git a/htdocs/api/class/api_setup.class.php b/htdocs/api/class/api_setup.class.php index 42a398ee3b5..ddc38a004bd 100644 --- a/htdocs/api/class/api_setup.class.php +++ b/htdocs/api/class/api_setup.class.php @@ -1232,6 +1232,91 @@ class Setup extends DolibarrApi ); } + /** + * Update Extrafield object + * + * @param string $attrname extrafield attrname + * @param string $elementtype extrafield elementtype + * @param array $request_data Request datas + * @return int ID of extrafield + * + * @url PUT extrafields/{elementtype}/{attrname} + * + * @suppress PhanPluginUnknownArrayMethodParamType Luracast limitation + * + */ + public function updateExtrafields($attrname, $elementtype, $request_data = null) + { + if (!DolibarrApiAccess::$user->admin) { + throw new RestException(403, 'Only an admin user can create an extrafield'); + } + + $extrafields = new ExtraFields($this->db); + + $result = $extrafields->fetch_name_optionals_label($elementtype, false, $attrname); + if (!$result) { + throw new RestException(404, 'Extrafield not found from attrname and elementtype'); + } + + foreach ($request_data as $field => $value) { + $extrafields->$field = $this->_checkValForAPI($field, $value, $extrafields); + } + + // built in validation + $enabled = 1; // hardcoded because it seems to always be 1 in every row in the database + if ($request_data['entity']) { + $entity = $request_data['entity']; + } else { + throw new RestException(400, "Entity field absent"); + } + if ($request_data['label']) { + $label = $request_data['label']; + } else { + throw new RestException(400, "label field absent"); + } + + $alwayseditable = $request_data['alwayseditable']; + $default_value = $request_data['default_value']; + $totalizable = $request_data['totalizable']; + $printable = $request_data['printable']; + $required = $request_data['required']; + $langfile = $request_data['langfile']; + $computed = $request_data['computed']; + $unique = $request_data['unique']; + $param = $request_data['param']; + $perms = $request_data['perms']; + $size = $request_data['size']; + $type = $request_data['type']; + $list = $request_data['list']; + $help = $request_data['help']; + $pos = $request_data['pos']; + $moreparams = array(); + + dol_syslog(get_class($this).'::updateExtraField', LOG_DEBUG); + if ( 0 > $extrafields->updateExtraField($attrname, $label, $type, $pos, $size, $elementtype, $unique, $required, $default_value, $param, $alwayseditable, $perms, $list, $help, $computed, $entity, $langfile, $enabled, $totalizable, $printable, $moreparams)) { + throw new RestException(500, 'Error updating extrafield', array_merge(array($extrafields->errno), $extrafields->errors)); + } + + $sql = "SELECT t.rowid as id"; + $sql .= " FROM ".MAIN_DB_PREFIX."extrafields as t"; + $sql .= " WHERE elementtype = '".$this->db->escape($elementtype)."'"; + $sql .= " AND name = '".$this->db->escape($attrname)."'"; + + $resql = $this->db->query($sql); + if ($resql) { + if ($this->db->num_rows($resql)) { + $tab = $this->db->fetch_object($resql); + $id = (int) $tab->id; + } else { + $id = (int) -1; + } + } else { + $id = (int) -2; + } + + return $id; + } + /** * Get the list of towns. * diff --git a/htdocs/core/class/extrafields.class.php b/htdocs/core/class/extrafields.class.php index 32147ed9122..960f8253006 100644 --- a/htdocs/core/class/extrafields.class.php +++ b/htdocs/core/class/extrafields.class.php @@ -182,6 +182,78 @@ class ExtraFields } } + /** + * Update an existing extra field parameter + * + * @param string $attrname Code of attribute + * @param string $label label of attribute + * @param string $type Type of attribute ('boolean','int','varchar','text','html','date','datetime','price', 'pricecy', 'phone','mail','password','url','select','checkbox','separate',...) + * @param int $pos Position of attribute + * @param string $size Size/length definition of attribute ('5', '24,8', ...). For float, it contains 2 numeric separated with a comma. + * @param string $elementtype Element type. Same value than object->table_element (Example 'member', 'product', 'thirdparty', ...) + * @param int $unique Is field unique or not + * @param int $required Is field required or not + * @param string $default_value Defaulted value (In database. use the default_value feature for default value on screen. Example: '', '0', 'null', 'avalue') + * @param array|string $param Params for field (ex for select list : array('options' => array(value'=>'label of option')) ) + * @param int $alwayseditable Is attribute always editable regardless of the document status + * @param string $perms Permission to check + * @param string $list Visibility ('0'=never visible, '1'=visible on list+forms, '2'=list only, '3'=form only or 'eval string') + * @param string $help Text with help tooltip + * @param string $computed Computed value + * @param string $entity Entity of extrafields (for multicompany modules) + * @param string $langfile Language file + * @param string $enabled Condition to have the field enabled or not + * @param int $totalizable Is a measure. Must show a total on lists + * @param int $printable Is extrafield displayed on PDF + * @param array $moreparams More parameters. Example: array('css'=>, 'csslist'=>Css on list, 'cssview'=>...) + * @return int Return integer <=0 if KO, >0 if OK + */ + public function updateExtraField($attrname, $label, $type, $pos, $size, $elementtype, $unique = 0, $required = 0, $default_value = '', $param = '', $alwayseditable = 0, $perms = '', $list = '-1', $help = '', $computed = '', $entity = '', $langfile = '', $enabled = '1', $totalizable = 0, $printable = 0, $moreparams = array()) + { + if (empty($attrname)) { + return -1; + } + if (empty($label)) { + return -1; + } + + $result = 0; + + if ($type == 'separator' || $type == 'separate') { + $type = 'separate'; + $unique = 0; + $required = 0; + } // Force unique and not required if this is a separator field to avoid troubles. + if ($elementtype == 'thirdparty') { + $elementtype = 'societe'; + } + if ($elementtype == 'contact') { + $elementtype = 'socpeople'; + } + + // Create field into database except for separator type which is not stored in database + if ($type != 'separate') { + dol_syslog(get_class($this).'::thisupdate', LOG_DEBUG); + $result = $this->update($attrname, $label, $type, $size, $elementtype, $unique, $required, $pos, $param, $alwayseditable, $perms, $list, $help, $default_value, $computed, $entity, $langfile, $enabled, $totalizable, $printable, $moreparams); + } + $err1 = $this->errno; + if ($result > 0 || $err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' || $type == 'separate') { + // Add declaration of field into table + dol_syslog(get_class($this).'::thislabel', LOG_DEBUG); + $result2 = $this->update_label($attrname, $label, $type, $size, $elementtype, $unique, $required, $pos, $param, $alwayseditable, $perms, $list, $help, $default_value, $computed, $entity, $langfile, $enabled, $totalizable, $printable, $moreparams); + $err2 = $this->errno; + if ($result2 > 0 || ($err1 == 'DB_ERROR_COLUMN_ALREADY_EXISTS' && $err2 == 'DB_ERROR_RECORD_ALREADY_EXISTS')) { + $this->error = ''; + $this->errno = '0'; + return 1; + } else { + return -2; + } + } else { + return -1; + } + } + /** * Add a new optional attribute. * This is a private method. For public method, use addExtraField. @@ -418,10 +490,11 @@ class ExtraFields $sql .= " ".($cssview ? "'".$this->db->escape($cssview)."'" : "null"); $sql .= ')'; - dol_syslog(get_class($this)."::create_label", LOG_DEBUG); if ($this->db->query($sql)) { + dol_syslog(get_class($this)."::create_label_success", LOG_DEBUG); return 1; } else { + dol_syslog(get_class($this)."::create_label_error", LOG_DEBUG); $this->error = $this->db->lasterror(); $this->errno = $this->db->lasterrno(); return -1; @@ -617,19 +690,23 @@ class ExtraFields } } + dol_syslog(get_class($this).'::DDLUpdateField', LOG_DEBUG); if ($type != 'separate') { // No table update when separate type $result = $this->db->DDLUpdateField($this->db->prefix().$table, $attrname, $field_desc); } if ($result > 0 || $type == 'separate') { if ($label) { + dol_syslog(get_class($this).'::update_label', LOG_DEBUG); $result = $this->update_label($attrname, $label, $type, $length, $elementtype, $unique, $required, $pos, $param, $alwayseditable, $perms, $list, $help, $default, $computed, $entity, $langfile, $enabled, $totalizable, $printable, $moreparams); } if ($result > 0) { $sql = ''; if ($unique) { - $sql = "ALTER TABLE ".$this->db->prefix().$table." ADD UNIQUE INDEX uk_".$table."_".$attrname." (".$attrname.")"; + dol_syslog(get_class($this).'::update_unique', LOG_DEBUG); + $sql = "ALTER TABLE ".$this->db->prefix().$table." ADD UNIQUE INDEX uk_".$table."_".$this->db->sanitize($attrname)." (".$this->db->sanitize($attrname).")"; } else { - $sql = "ALTER TABLE ".$this->db->prefix().$table." DROP INDEX IF EXISTS uk_".$table."_".$attrname; + dol_syslog(get_class($this).'::update_common', LOG_DEBUG); + $sql = "ALTER TABLE ".$this->db->prefix().$table." DROP INDEX IF EXISTS uk_".$table."_".$this->db->sanitize($attrname); } dol_syslog(get_class($this).'::update', LOG_DEBUG); $resql = $this->db->query($sql, 1, 'dml'); @@ -654,6 +731,7 @@ class ExtraFields // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** * Modify description of personalized attribute + * This is a private method. For public method, use updateExtraField. * * @param string $attrname Name of attribute * @param string $label Label of attribute From fed71b035c4f7c99ffd7b59bbce713e624f20124 Mon Sep 17 00:00:00 2001 From: Anthony Berton <34568357+BB2A-Anthony@users.noreply.github.com> Date: Wed, 10 Apr 2024 12:11:11 +0200 Subject: [PATCH 9/9] NEW - Font param Look and Feel (#29302) Co-authored-by: Anthony Berton --- ChangeLog | 1 + htdocs/theme/eldy/style.css.php | 11 +++++++++-- htdocs/theme/md/style.css.php | 4 ++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 68a39b7493b..fa12a8b56e9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,7 @@ English Dolibarr ChangeLog For users: ---------- NEW: Compatibility with PHP 8.2 (with no need to disable warnings) +NEW: Font param Look and Feel THEME_FONT_FAMILY ... For developers or integrators: diff --git a/htdocs/theme/eldy/style.css.php b/htdocs/theme/eldy/style.css.php index cf7b332b201..9e7785eb620 100644 --- a/htdocs/theme/eldy/style.css.php +++ b/htdocs/theme/eldy/style.css.php @@ -108,8 +108,15 @@ if (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) { } // Define image path files and other constants -$fontlist = 'arial,tahoma,verdana,helvetica'; //$fontlist='helvetica, verdana, arial, sans-serif'; -//$fontlist='"open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;'; + +//$fontlist='helvetica, verdana, arial, sans-serif'; +//$fontlist='"open sans", "Helvetica Neue", Helvetica, Arial, sans-serif'; + +$fontlist = 'arial,tahoma,verdana,helvetica'; +if (getDolGlobalString('THEME_FONT_FAMILY')) { + $fontlist = getDolGlobalString('THEME_FONT_FAMILY').', '.$fontlist; +} + $img_head = ''; $img_button = dol_buildpath($path.'/theme/'.$theme.'/img/button_bg.png', 1); $dol_hide_topmenu = $conf->dol_hide_topmenu; diff --git a/htdocs/theme/md/style.css.php b/htdocs/theme/md/style.css.php index d206a3b643e..b87cf455281 100644 --- a/htdocs/theme/md/style.css.php +++ b/htdocs/theme/md/style.css.php @@ -112,6 +112,10 @@ if (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) { // Define image path files and other constants $fontlist = 'roboto,arial,tahoma,verdana,helvetica'; //$fontlist='verdana,helvetica,arial,sans-serif'; +if (getDolGlobalString('THEME_FONT_FAMILY')) { + $fontlist = getDolGlobalString('THEME_FONT_FAMILY').', '.$fontlist; +} + $img_head = ''; $img_button = dol_buildpath($path.'/theme/'.$theme.'/img/button_bg.png', 1); $dol_hide_topmenu = $conf->dol_hide_topmenu;