From 7169cefebb7ebaaaa1c7b622de4736afd589225a Mon Sep 17 00:00:00 2001
From: Pierre Ardoin <32256817+mapiolca@users.noreply.github.com>
Date: Wed, 8 Oct 2025 14:31:06 +0200
Subject: [PATCH 1/5] Refonte de la configuration et documentation
TimesheetWeek
---
ChangeLog.md | 12 +-
README.md | 136 ++++----
admin/setup.php | 394 +++++++++++++++---------
core/modules/modTimesheetWeek.class.php | 2 +-
langs/en_US/timesheetweek.lang | 4 +
langs/fr_FR/timesheetweek.lang | 8 +-
6 files changed, 318 insertions(+), 238 deletions(-)
diff --git a/ChangeLog.md b/ChangeLog.md
index 9159931..172760c 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,7 +1,9 @@
# CHANGELOG MODULE TIMESHEETWEEK FOR [DOLIBARR ERP CRM](https://www.dolibarr.org)
-## Non publié
+## 1.0
+- Ajout du statut "Scellée" / "Sealed" et des permissions associées.
+- Initial version.
- Ajout des compteurs de zones et de paniers dans l'entête des feuilles hebdomadaires.
- Recalcul automatique des compteurs de zones et de paniers à chaque enregistrement d'une feuille hebdomadaire.
- Affichage des compteurs de zones et de paniers dans la liste des feuilles hebdomadaires.
@@ -19,8 +21,6 @@
- Filtre Multicompany de l'environnement aligné sur le multiselect natif dans la liste / Multicompany environment filter aligned with the native multiselect in the list.
- Réorganisation des options de partage Multicompany pour séparer les feuilles et la numérotation avec les pictogrammes adaptés / Reorganised Multicompany sharing options to separate sheets and numbering with suitable pictograms.
- Inversion des couleurs des statuts "Scellée" et "Refusée" pour reprendre les repères Dolibarr / Swapped colors of "Sealed" and "Refused" statuses to match Dolibarr visual cues.
-
-## 1.0
-
-- Ajout du statut "Scellée" / "Sealed" et des permissions associées.
-- Initial version
+- Refonte de la page de configuration pour harmoniser la gestion des masques de numérotation et des modèles PDF avec Dolibarr / Setup page redesigned to align numbering masks and PDF templates with Dolibarr standards.
+- README bilingue (FR/EN) entièrement mis à jour / Fully refreshed bilingual (FR/EN) README.
+- Notification d'approbation en français corrigée pour utiliser l'accent « approuvée » sans entité HTML / French approval notification updated to use the plain "approuvée" accent instead of an HTML entity.
diff --git a/README.md b/README.md
index bc9e0a9..3687cbb 100644
--- a/README.md
+++ b/README.md
@@ -1,108 +1,76 @@
# TIMESHEETWEEK FOR [DOLIBARR ERP & CRM](https://www.dolibarr.org)
-## Features
-
-- Statut "Scellée" / "Sealed" pour verrouiller les feuilles approuvées.
-- Redirection automatique vers la feuille existante en cas de doublon / Automatic redirect to the existing sheet when a duplicate is requested.
-
-Description of the module...
-
-- Suivi des compteurs hebdomadaires de zones et de paniers pour chaque feuille de temps.
-- Recalcul automatique des compteurs de zones et de paniers lors de chaque enregistrement.
-- Affichage des compteurs de zones et de paniers dans la liste des feuilles hebdomadaires.
-- Création rapide d'une feuille d'heures depuis le raccourci "Ajouter" du menu supérieur.
-- Compatibilité Multicompany pour partager les feuilles de temps et leur numérotation / Multicompany compatibility to share weekly timesheets and their numbering.
-- Inscription automatique de la configuration Multicompany lors de l'activation et nettoyage lors de la désactivation / Automatic registration of the Multicompany configuration on activation and cleanup on deactivation.
-- Affichage de l'entité dans la liste et la fiche lorsqu'on utilise Multicompany, avec badge natif sous la référence lorsque l'entité diffère / Display entity information in list and card when using Multicompany, with a native badge under the reference when the entity differs.
-- Sécurisation des requêtes SQL sur les feuilles et lignes par entité pour Multicompany / Secured SQL queries on sheets and lines with entity scoping for Multicompany.
-- Filtre multisélection de l'environnement respectant l'interface native de Dolibarr en Multicompany / Multiselect environment filter following Dolibarr native Multicompany interface.
-- Réorganisation des options Multicompany pour distinguer partage et numérotation avec les pictogrammes natifs / Reorganised Multicompany sharing options to split sharing and numbering with native pictograms.
-- Inversion des couleurs des statuts "Scellée" et "Refusée" pour correspondre au code couleur Dolibarr / Swapped colors of "Sealed" and "Refused" statuses to follow Dolibarr color codes.
-- Harmonisation du filtre de semaine de la liste avec la fiche via le sélecteur ISO / Harmonized week filter between list and card using the ISO selector.
-- Filtre de semaine en multi-sélection pour combiner plusieurs périodes directement depuis la liste / Multi-select week filter to combine several periods directly from the list.
-
-
+## 🇫🇷 Présentation
-Other external modules are available on [Dolistore.com](https://www.dolistore.com).
-
-## Translations
-
-Translations can be completed manually by editing files in the module directories under `langs`.
-
-
+TimesheetWeek ajoute une gestion hebdomadaire des feuilles de temps fidèle à l'expérience Dolibarr. Le module renforce les cycles de validation, propose des compteurs opérationnels (zones, paniers, heures supplémentaires) et respecte les standards graphiques pour les écrans administratifs et les modèles de documents.
+### Fonctionnalités principales
-## Installation
+- Statut « Scellée » pour verrouiller les feuilles approuvées et empêcher toute modification ultérieure, avec les permissions associées.
+- Redirection automatique vers la feuille existante en cas de tentative de doublon afin d'éviter les saisies multiples.
+- Suivi des compteurs hebdomadaires de zones et de paniers directement sur les feuilles et recalcul automatique à chaque enregistrement.
+- Affichage des compteurs dans la liste hebdomadaire et ajout du libellé « Zone » sur chaque sélecteur quotidien pour clarifier la saisie.
+- Création rapide d'une feuille d'heures via le raccourci « Ajouter » du menu supérieur.
+- Compatibilité Multicompany pour partager les feuilles et leur numérotation, avec options de partage dédiées et filtres multi-sélection harmonisés à l'interface native.
+- Affichage de l'entité dans les listes et fiches en environnement Multicompany, accompagné d'un badge visuel sous la référence lorsque l'entité diffère.
+- Sécurisation des requêtes SQL par entité et filtres multi-entités alignés sur les pratiques Dolibarr.
+- Harmonisation du filtre de semaine avec un sélecteur ISO multi-sélection permettant de regrouper plusieurs périodes.
+- Inversion des couleurs des statuts « Scellée » et « Refusée » pour respecter les codes couleur Dolibarr.
+- Refonte complète de la page de configuration pour la gestion des masques de numérotation et des modèles PDF selon les codes graphiques Dolibarr.
+- README bilingue (FR/EN) pour faciliter le déploiement et l'adoption.
-Prerequisites: You must have Dolibarr ERP & CRM software installed. You can download it from [Dolibarr.org](https://www.dolibarr.org).
-You can also get a ready-to-use instance in the cloud from https://saas.dolibarr.org
+### Installation
+1. **Pré-requis** : disposer d'une instance Dolibarr fonctionnelle. Les versions supportées correspondent à celles indiquées dans le fichier `modTimesheetWeek.class.php`.
+2. **Déploiement via l'interface** : depuis `Accueil > Configuration > Modules > Déployer un module externe`, importez l'archive `module_timesheetweek-x.y.z.zip` téléchargée sur [Dolistore](https://www.dolistore.com) ou obtenue via votre circuit de diffusion.
+3. **Déploiement manuel** : copiez le répertoire du module dans `htdocs/custom/timesheetweek`, puis purgez le cache des modules depuis l'administration Dolibarr.
+4. **Activation** : connectez-vous en tant que super administrateur, activez le module dans `Configuration > Modules > Projets/Temps`, puis exécutez le script `sql/update_all.sql` pour ajouter les compteurs aux données existantes.
-### From the ZIP file and GUI interface
+### Configuration
-If the module is a ready-to-deploy zip file, so with a name `module_xxx-version.zip` (e.g., when downloading it from a marketplace like [Dolistore](https://www.dolistore.com)),
-go to menu `Home> Setup> Modules> Deploy external module` and upload the zip file.
+- Rendez-vous dans `Configuration > Modules > TimesheetWeek` pour choisir le masque de numérotation actif et activer les modèles PDF souhaités.
+- Ajustez les options Multicompany via les onglets de configuration dédiés si vous partagez les feuilles de temps entre plusieurs entités.
-
+1. **Prerequisites**: a running Dolibarr instance that matches the compatibility range declared in `modTimesheetWeek.class.php`.
+2. **Deploy from the GUI**: go to `Home > Setup > Modules > Deploy external module` and upload the `module_timesheetweek-x.y.z.zip` archive from [Dolistore](https://www.dolistore.com) or your distribution channel.
+3. **Manual deployment**: copy the module directory into `htdocs/custom/timesheetweek`, then refresh the module cache from Dolibarr's administration area.
+4. **Activation**: log in as a super administrator, enable the module from `Setup > Modules > Projects/Timesheets`, and run the `sql/update_all.sql` script so legacy timesheets gain the new counters.
-
-
-### Final steps
-
-Using your browser:
-
- - Log into Dolibarr as a super-administrator
- - Go to "Setup"> "Modules"
- - You should now be able to find and enable the module
- - EN: Run the `sql/update_all.sql` script to ensure older timesheets receive the new counters
- - FR: Exécutez le script `sql/update_all.sql` pour que les feuilles existantes profitent des nouveaux compteurs
+### Translations
+Translation sources are stored under `langs/en_US` and `langs/fr_FR`. Please keep both locales aligned for every new string to stay compatible with Dolibarr's translation workflow.
+Other external modules are available on [Dolistore.com](https://www.dolistore.com).
## Licenses
@@ -112,4 +80,4 @@ GPLv3 or (at your option) any later version. See file COPYING for more informati
### Documentation
-All texts and readme's are licensed under [GFDL](https://www.gnu.org/licenses/fdl-1.3.en.html).
+All texts and README files are licensed under [GFDL](https://www.gnu.org/licenses/fdl-1.3.en.html).
diff --git a/admin/setup.php b/admin/setup.php
index 637bbc6..1daba13 100644
--- a/admin/setup.php
+++ b/admin/setup.php
@@ -39,6 +39,7 @@
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
dol_include_once('/timesheetweek/lib/timesheetweek.lib.php');
+dol_include_once('/timesheetweek/class/timesheetweek.class.php');
$langs->loadLangs(array('admin', 'other', 'timesheetweek@timesheetweek'));
@@ -50,6 +51,171 @@
// FR: On accepte les caractères natifs des classes Dolibarr (underscore, chiffres...).
// EN: Allow native Dolibarr class names with underscores and digits.
$value = GETPOST('value', 'alphanohtml');
+$receivedToken = GETPOST('token', 'alphanohtml');
+
+// EN: Secure the state-changing actions with the Dolibarr CSRF token when available.
+// FR: Sécurise les actions modifiant l'état avec le jeton CSRF de Dolibarr lorsque disponible.
+if (in_array($action, array('setmodule', 'setdoc', 'setdocmodel', 'delmodel'), true)) {
+ if (function_exists('dol_verify_token')) {
+ if (empty($receivedToken) || dol_verify_token($receivedToken) <= 0) {
+ accessforbidden();
+ }
+ }
+}
+
+// EN: Helper to collect metadata for numbering modules so we can render a native-looking table.
+// FR: Collecte des métadonnées sur les modules de numérotation pour afficher un tableau au format natif.
+function timesheetweek_collect_numbering_modules(array $directories, Translate $langs, TimesheetWeek $sampleObject, $selected)
+{
+ global $db;
+
+ $modules = array();
+
+ foreach ($directories as $reldir) {
+ $dir = dol_buildpath($reldir.'core/modules/timesheetweek/');
+ if (!is_dir($dir)) {
+ continue;
+ }
+
+ $filelist = dol_dir_list($dir, 'files', 0, '^mod_.*\.php$');
+ foreach ($filelist as $fileinfo) {
+ $file = $fileinfo['name'];
+ $classname = preg_replace('/\.php$/', '', $file);
+
+ require_once $dir.$file;
+ if (!class_exists($classname)) {
+ continue;
+ }
+
+ try {
+ $module = new $classname($db);
+ } catch (Throwable $exception) {
+ continue;
+ }
+
+ $label = !empty($module->name) ? $module->name : $classname;
+ if ($label && $langs->transnoentitiesnoconv($label) !== $label) {
+ $label = $langs->trans($label);
+ }
+
+ $description = '';
+ if (method_exists($module, 'info')) {
+ try {
+ $description = $module->info($langs);
+ } catch (Throwable $exception) {
+ $description = '';
+ }
+ } elseif (!empty($module->description)) {
+ $description = $module->description;
+ } elseif (!empty($module->desc)) {
+ $description = $module->desc;
+ }
+
+ $example = '';
+ if (method_exists($module, 'getExample')) {
+ try {
+ $example = $module->getExample();
+ } catch (Throwable $exception) {
+ $example = '';
+ }
+ }
+
+ $canBeActivated = true;
+ $activationError = '';
+ if (method_exists($module, 'canBeActivated')) {
+ try {
+ $canBeActivated = (bool) $module->canBeActivated($sampleObject);
+ if (!$canBeActivated && !empty($module->error)) {
+ $activationError = $module->error;
+ }
+ } catch (Throwable $exception) {
+ $canBeActivated = false;
+ $activationError = $exception->getMessage();
+ }
+ }
+
+ $modules[] = array(
+ 'classname' => $classname,
+ 'label' => $label,
+ 'description' => $description,
+ 'example' => $example,
+ 'active' => ($selected === $classname),
+ 'can_be_activated' => $canBeActivated,
+ 'activation_error' => $activationError,
+ );
+ }
+ }
+
+ usort($modules, function ($a, $b) {
+ return strcasecmp($a['label'], $b['label']);
+ });
+
+ return $modules;
+}
+
+// EN: Helper to collect PDF model metadata while respecting Dolibarr document conventions.
+// FR: Collecte les métadonnées des modèles PDF en respectant les conventions Dolibarr.
+function timesheetweek_collect_document_models(array $directories, Translate $langs, array $enabledModels, $defaultModel)
+{
+ global $db;
+
+ $models = array();
+
+ foreach ($directories as $reldir) {
+ $dir = dol_buildpath($reldir.'core/modules/timesheetweek/doc/');
+ if (!is_dir($dir)) {
+ continue;
+ }
+
+ $filelist = dol_dir_list($dir, 'files', 0, '^[a-z0-9_]+\.php$');
+ foreach ($filelist as $fileinfo) {
+ $file = $fileinfo['name'];
+ $classname = preg_replace('/\.php$/', '', $file);
+
+ require_once $dir.$file;
+ if (!class_exists($classname)) {
+ continue;
+ }
+
+ try {
+ $module = new $classname($db);
+ } catch (Throwable $exception) {
+ continue;
+ }
+
+ if (!property_exists($module, 'type') || $module->type !== 'pdf') {
+ continue;
+ }
+
+ $name = !empty($module->name) ? $module->name : $classname;
+ $description = '';
+ if (!empty($module->description)) {
+ $description = $module->description;
+ } elseif (!empty($module->desc)) {
+ $description = $module->desc;
+ }
+ if ($description && $langs->transnoentitiesnoconv($description) !== $description) {
+ $description = $langs->trans($description);
+ }
+
+ $models[] = array(
+ 'name' => $name,
+ 'classname' => $classname,
+ 'label' => !empty($module->name) && $langs->transnoentitiesnoconv($module->name) !== $module->name ? $langs->trans($module->name) : $name,
+ 'description' => $description,
+ 'type' => $module->type,
+ 'is_enabled' => !empty($enabledModels[$name]),
+ 'is_default' => ($defaultModel === $name),
+ );
+ }
+ }
+
+ usort($models, function ($a, $b) {
+ return strcasecmp($a['label'], $b['label']);
+ });
+
+ return $models;
+}
if (!function_exists('timesheetweek_enable_document_model')) {
/**
@@ -152,6 +318,28 @@ function timesheetweek_disable_document_model($model)
$defaultpdf = getDolGlobalString('TIMESHEETWEEK_ADDON_PDF', 'standard');
$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
+// EN: Instantiate a lightweight object to validate numbering modules.
+// FR: Instancie un objet léger pour valider les modules de numérotation.
+$sampleTimesheet = new TimesheetWeek($db);
+
+// EN: Retrieve active document models from the database.
+// FR: Récupère les modèles de documents actifs depuis la base de données.
+$enabledModels = array();
+$sql = 'SELECT nom FROM '.MAIN_DB_PREFIX."document_model WHERE type='timesheetweek' AND entity IN (0, ".((int) $conf->entity).')';
+$resql = $db->query($sql);
+if ($resql) {
+ while ($obj = $db->fetch_object($resql)) {
+ $enabledModels[$obj->nom] = 1;
+ }
+ $db->free($resql);
+}
+
+// EN: Prepare metadata arrays for rendering.
+// FR: Prépare les tableaux de métadonnées pour l'affichage.
+$numberingModules = timesheetweek_collect_numbering_modules($dirmodels, $langs, $sampleTimesheet, $selected);
+$documentModels = timesheetweek_collect_document_models($dirmodels, $langs, $enabledModels, $defaultpdf);
+$pageToken = function_exists('newToken') ? newToken() : ''; // Defensive check for legacy.
+
$title = $langs->trans('ModuleSetup', 'TimesheetWeek');
$helpurl = '';
@@ -164,92 +352,51 @@ function timesheetweek_disable_document_model($model)
print '