786 Commits

Author SHA1 Message Date
31e5f59bea Incremented version to 4.73.051 to reflect updates in fpdi2/src, ensuring the PDF membership card functionality maintains its original location. Related to SM-9493. 2026-08-21 10:18:22 -04:00
b6d4c6fb8e SM-9493 — Refactored compte.php to restore PDF membership card functionality, ensuring proper handling of unauthenticated access. Updated version to 4.73.050 to reflect these changes and maintain PDF location integrity. 2026-08-21 10:09:47 -04:00
c1a3899aef SM-9493 — Updated compte.php to handle PDF membership card display logic outside CMS, ensuring proper redirection for unauthenticated users. Adjusted version to 4.73.049 to reflect these changes. Updated documentation on Git push policies to clarify synchronization responsibilities for users. 2026-08-21 09:56:54 -04:00
8489d15523 Refined radar mail to CRM documentation by clarifying ticket references and project details. Updated the description of the development ticket and emphasized the need for finalized signal formats before coding. Adjusted language for consistency and clarity. 2026-08-20 17:05:58 -04:00
c9fd397def Refined radar mail to CRM integration by updating the project structure and clarifying the relationship between emails and CRM records. Added exception handling for email domains and improved documentation on signal processing and project management. Updated ticket reference to IDEE-4 for tracking purposes. 2026-08-20 16:58:06 -04:00
67a5c6b9ba MSIN-4578 — Updated event filtering to only include past events from the last 10 days instead of 90. Adjusted related localization strings and incremented version to 4.73.048 to reflect these changes. 2026-08-20 14:51:18 -04:00
7f0a7f04cd MSIN-4578 — Updated event filtering logic to include only active events with valid bib sequences. Enhanced SQL query to refine date criteria for upcoming events and incremented version to 4.73.047 to reflect these changes. 2026-08-20 14:47:36 -04:00
daa563a2b3 MSIN-4578 — Enhanced event management by adding functions to determine event dates and past status. Updated SQL queries to filter events based on date criteria and improved sorting logic for upcoming and past events. Added new CSS styles for visual differentiation of past events and updated localization for better user guidance. Incremented version to 4.73.046 to reflect these changes. 2026-08-20 14:43:21 -04:00
74b4146fd3 MSIN-4579 — Added functionality to clear milestones in the order management system, including new AJAX actions and corresponding PHP functions. Enhanced the UI with new CSS styles for the clear action button and updated localization for new text elements. Incremented version to 4.73.044 to reflect these changes. 2026-08-20 14:33:28 -04:00
14cdb2defe Updated Gitea and Bitbucket configuration guidelines to enable sync on every push and clarified prerequisites for server settings. Adjusted instructions to prevent push errors related to Git LFS and improved overall deployment documentation for better clarity and usability. 2026-08-20 13:50:27 -04:00
08743915f6 MSIN-4577 — Added strMetaDescription initialization in bib_commande and bib_ops files to ensure proper metadata handling. Updated inc_header to set default values for strMetaTitle and strMetaDescription if not already defined. Incremented version to 4.73.042 to reflect these changes. 2026-08-20 13:12:16 -04:00
97701b5fa3 MSIN-4579 — Implemented manual milestone management features, including the ability to mark milestones as done and view their history. Updated PHP functions to handle new actions and added corresponding AJAX endpoints. Enhanced the UI with new CSS styles for better usability and added localization for new dialog elements. Incremented version to 4.73.041 to reflect these changes. 2026-08-20 13:02:32 -04:00
a9f2f1128f MSIN-4578 — Added a flag for footer script handling in bib_commande and bib_ops to prevent HTML notices when reusing the 'compte.php' page. Updated conditional checks in inc_footer_scripts.php for improved script execution. This change enhances stability and prevents unexpected token errors in the footer scripts. 2026-08-20 11:25:49 -04:00
e9f06843ca MSIN-4579 — Enhanced segment note functionality by adding support for both chip and bib notes in the order management system. Updated PHP functions to handle new note types, modified AJAX actions, and adjusted the UI for better usability with new CSS styles. Incremented version to 4.73.040 to reflect these changes. 2026-08-20 10:41:35 -04:00
46975b8cab MSIN-4579 — Implemented new features for segment and global notes in the order management system. Added PHP functions for saving notes, updated AJAX actions, and enhanced the UI with new CSS styles for better usability. Incremented version to 4.73.039 to reflect these changes. 2026-08-20 10:33:24 -04:00
efe8798188 MSIN-4579 — Updated the mockup for order notes with enhanced CSS styles for improved layout and usability. Added new elements for note handling and adjusted existing styles for better visual consistency. Incremented the layout dimensions and refined button labels for clarity. 2026-08-20 10:18:41 -04:00
687cb76ca2 MSIN-4578 — Refined bib and chip order management by optimizing PHP functions for improved performance and adding new features for better user interaction. Updated CSS for enhanced visual consistency and usability. Incremented version to 4.73.039 to reflect these changes. 2026-08-20 09:57:50 -04:00
c303d2c682 MSIN-4578 — Improved the bib and chip order management interface with updated CSS for better readability and usability. Enhanced PHP functions to incorporate new button styles and localization adjustments. Incremented version to 4.73.038 to reflect these changes. 2026-08-19 16:59:41 -04:00
78286540fe MSIN-4578 — Updated bib and chip order management functionality. Enhanced UI elements and CSS for better readability and usability. Adjusted PHP functions to support new milestone and actual date reporting, including improved localization for user interface texts. Incremented version to 4.73.037 to reflect these changes. 2026-08-19 16:56:47 -04:00
fd74116911 MSIN-4578 — Enhanced bib management and account access functionality. Updated role handling to include dynamic scope management for roles in inscriptions_eve_roles and added new functions for account access management in inscriptions_com_acces. Improved permission checks and role assignment logic, ensuring proper integration with existing systems. Enhanced comments for better clarity and understanding of the changes. 2026-08-19 16:27:46 -04:00
d7bf446a92 MSIN-4578 — Updated SQL script for the bib manager and account kit functionality. Added dynamic column handling for role_scope in inscriptions_eve_roles and created a new table inscriptions_com_acces for account access management. Adjusted permissions and roles to ensure proper assignment and deletion logic. Enhanced comments for clarity and execution notes for environment specifications. 2026-08-19 16:27:19 -04:00
c6249d3a90 MSIN-4577 — Enhanced the UI chooser functionality by adding new toggle elements and improving accessibility features. Updated CSS for better layout and visual feedback on section states. Adjusted PHP functions to support new status indicators for registration details, including cancellation and refund statuses. Incremented version to 4.73.036 to reflect these changes. 2026-08-19 16:20:17 -04:00
2319698997 MSIN-4577 — Refactored locking mechanisms for bib and chip orders to allow assignment even when certain ranges are locked. Updated version to 4.73.034 to reflect these changes, including improved user interface texts and SQL entries for better localization. Enhanced functions to support the new logic for handling locked states in order processing. 2026-08-19 15:43:01 -04:00
6522814666 MSIN-4578 — Introduced the bib manager functionality for global operations, allowing for better management of bib assignments and locks. Updated version to 4.73.031 to reflect these changes, including new SQL entries for localization and enhanced user interface texts for clarity. Adjusted various functions to support the new locking mechanisms for bib and chip orders. 2026-08-19 15:14:01 -04:00
6d1a60eea5 MSIN-4577 — Enhanced the bib and chip order functionality by introducing conditional logic for handling different order types. Updated the version to 4.73.025 to reflect changes, including improved Excel export handling and updated help texts for better clarity. Adjusted SQL scripts to align with new features and ensure accurate data representation. 2026-08-19 14:25:05 -04:00
050c0dcd9d MSIN-4577 — Enhanced bib and chip order functionality by adding conditional logic for handling different order types in the XML generation process. Updated version to 4.73.025 to reflect these changes. Improved user interface text for clarity and added new SQL entries for better localization support. 2026-08-19 13:56:39 -04:00
6d7e1ad4b6 MSIN-4577 — Implementation of the bib and chip order page, including new AJAX actions for saving milestones and segment flags. Updated the version to 4.73.024 to reflect these changes. Enhanced CSS for the order page layout and added necessary functions for handling order types. 2026-08-19 13:43:33 -04:00
d9dd2cd1c8 MSIN-4573 — Implementation of que_id alignment for preprod/prod during import, including enhancements for shared que_id allocation and AUTO_INCREMENT management. Updated version to 4.73.023 to reflect these changes. 2026-08-19 11:40:07 -04:00
0ef11c9847 Fix typo in comment header of inc_settings.php 2026-08-18 15:18:42 -04:00
efb5c59221 Update Gitea and Bitbucket configurations to prevent push errors
Modified the deployment checklist and Git model documentation to clarify the use of HTTPS for Gitea authentication and to specify that Bitbucket mirroring should only use a 1-hour interval without sync-on-push. This change aims to avoid issues with push declines on Windows due to conflicting hooks. Additionally, updated the parameters documentation to reflect the new HTTPS URLs and the importance of avoiding sync-on-push settings.
2026-08-18 15:10:44 -04:00
06edca9d5a MSIN-4576 — Mise à jour de la logique de gestion des rabais dans le panier
Cette mise à jour introduit des modifications dans la fonction `fxSetPanier` pour gérer les rabais de manière plus robuste. Les variables pour les rabais sont initialisées correctement, et des vérifications supplémentaires sont ajoutées pour s'assurer que les montants sont crédités correctement lorsque l'`epr_id` d'origine ne correspond pas à celui du nouveau panier. De plus, la version du code a été incrémentée à 4.73.022 pour refléter ces changements.
2026-08-18 12:22:36 -04:00
5e65be07dc MSIN-4482 — Ajout de la gestion des ID externes pour éviter les doublons dans ChronoTrack
Cette mise à jour introduit une logique pour résoudre les ID externes lors de l'importation et de l'inscription, en utilisant `par_external_id` pour les imports de fichiers et `par_id_original` pour les inscriptions en ligne. De plus, des fonctions ont été ajoutées pour vérifier l'existence de colonnes spécifiques et pour éviter les doublons d'entrées dans ChronoTrack. Les messages d'erreur ont été améliorés pour une meilleure clarté lors de l'importation des participants.

Les modifications incluent également des ajustements dans la vérification du contenu et la normalisation des données pour garantir une intégration fluide avec l'API ChronoTrack.
2026-08-15 10:56:59 -04:00
dec1f8338d MSIN-4520 — Correction du mapping pour éviter les doublons de colonnes (v4.73.018)
Le mapping a été ajusté pour garantir qu'un champ ne puisse recevoir qu'une seule colonne, évitant ainsi les écrasements silencieux. Des améliorations ont été apportées à l'interface utilisateur pour avertir les utilisateurs en cas de conflit de colonnes, et le serveur refuse désormais les mappings en conflit. Ces changements visent à améliorer la fiabilité de l'importation des résultats et à prévenir les erreurs de données.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:45:15 -04:00
7d74461b69 MSIN-4520 — RETOUR ARRIERE de l'import a l'etat connu fonctionnel (v4.73.018)
php/inc_fx_import_resultats.php est restaure tel quel depuis a7f1081d (v4.73.008),
dernier etat ou les dossards entraient correctement, confirme par l'utilisateur.

Annule la serie de rustines de la soiree (4.73.012 -> 4.73.017) :
auto-save du profil de mapping, mapping strict par nom, repli par position
conditionnel, purge du profil au reset, sanitize avant sauvegarde, et le blocage
des doublons de champ (popup UI + refus serveur) qui empechait de corriger le
mapping a l'ecran.

Priorite : rendre l'import utilisable pour une course en cours. La refonte de cette
section est documentee dans .cursor/rules/projet/10-import-mapping-dette.mdc et doit
etre concue avant tout nouveau code.

Aucun fichier ChronoTrack touche.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:39:52 -04:00
1c5ed0048e MSIN-4520 — un champ MS1 ne peut recevoir qu'une seule colonne (v4.73.017)
Cause racine du dossard vide / avec une date : le mapping est stocké champ MS1 ->
colonne. Rien n'empêchait deux colonnes de viser le même champ, et la DERNIERE
écrasait la première en silence. « Bib » correctement mappé en haut de l'écran était
donc remplacé par une colonne plus bas (vide, ou contenant une date).

Trois barrières :

1. UI — choisir un champ déjà pris libère l'autre colonne (remise à « non importé »)
   et affiche un avertissement, au lieu de créer un doublon invisible.
2. Serveur — fxImportResultatsValidateMapping refuse le mapping et nomme les colonnes
   en conflit. Comme l'import appelle la validation, un doublon ne peut plus écrire.
3. Filet — fxImportResultatsBuildColIndex garde la PREMIERE colonne au lieu de la
   dernière, pour que la bonne colonne ne soit plus écrasée.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:34:08 -04:00
1aefdebb80 MSIN-4520 — mapping mémorisé mais assaini avant sauvegarde (v4.73.016)
Demande : ne plus redemander le mapping au 2e dépôt du même fichier. La sauvegarde
auto du profil est donc conservée (et la réapplication auto aussi).

Problème corrigé : le profil mémorisait aussi une association aberrante faite une
seule fois (Dossard posé sur la colonne date de naissance) puis la rejouait à chaque
import — d'où un dossard qui recevait une date, ou restait vide.

fxImportResultatsSanitizeMappingForSave() retire, avant mémorisation, toute colonne
dont l'en-tête est reconnu sans ambiguïté comme un AUTRE champ. Les colonnes non
reconnues par le catalogue ne sont pas touchées : le choix de l'opérateur n'est
jamais écrasé quand rien ne le contredit.

Complète 4.73.014 (repli par position refusé s'il contredit l'en-tête) et 4.73.015
(le reset purge le profil auto déjà pollué).

Upsert sur par_external_id inchangé : réimporter corrige, avec ou sans dossard.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:25:06 -04:00
8ad01f0222 MSIN-4520 — reset efface aussi le profil auto pourri (v4.73.015)
Cause racine du dossard qui reçoit une date : le profil auto sauvegardé lors du
premier import raté avait mémorisé le mapping fautif (Dossard posé sur la colonne
date de naissance). Le matching par nom le reproduit fidèlement à chaque réimport.

Le bouton Reset données import supprime désormais le profil « Profil import (auto) »
de l'événement, pour repartir d'un mapping neuf. Les profils nommés manuellement
sont conservés (l'opérateur les contrôle).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:18:01 -04:00
2aac0789f6 MSIN-4520 — dossard : repli par position rétabli sauf contradiction (v4.73.014)
La 4.73.013 supprimait le repli par position dès qu'un nom d'en-tête était sauvé.
Effet de bord : quand le libellé de la colonne dossard n'est ni retrouvé dans le
profil ni reconnu par le catalogue de guess, no_bib n'était plus mappé du tout et
le dossard ressortait vide après import.

Le repli par position est remis, mais refusé quand l'en-tête à cette position est
reconnu comme un AUTRE champ (le cas qui écrivait une date de naissance dans
no_bib). On garde donc la protection sans perdre les colonnes non cataloguées.

Aligné côté PHP (fxImportResultatsBuildColIndex) et JS (applyMappingObj).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:15:57 -04:00
9a040f0895 MSIN-4520 — mapping import strict par nom de colonne (v4.73.013)
Un profil réappliqué sur un fichier aux colonnes déplacées pouvait écrire la
mauvaise valeur (ex. date de naissance dans no_bib) : le repli par position
s'appliquait même quand le nom d'en-tête sauvé était introuvable dans le fichier.

Le repli par position ne sert plus que si la colonne n'a aucun nom sauvé (profil
legacy ou en-tête vide). Un nom connu mais absent du fichier laisse le champ non
mappé : un champ vide est récupérable, un champ faux ne l'est pas.

Aligné côté PHP (fxImportResultatsBuildColIndex) et côté JS (applyMappingObj)
pour que l'écran de mapping ne propose pas non plus un champ erroné.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:08:04 -04:00
18856d96ce MSIN-4520 — Increment version code to 4.73.012 to implement automatic saving of import profiles upon successful import, eliminating the need for manual naming. Updated the import functionality to ensure profiles are saved without blocking the import process, enhancing user experience and efficiency. 2026-08-14 18:03:37 -04:00
11d9545666 MSIN-4574 — Increment version code to 4.73.011 to implement loading of information on chronotrack_api.php, including label management for email notifications. Updated the ChronoTrack API to enhance the display of text and auto-creation of information, ensuring better user experience and functionality. 2026-08-14 17:56:39 -04:00
eaf94ddd6f MSIN-4574 — Increment version code to 4.73.010 to implement email notification options for ChronoTrack events, with default settings disabled. Added functionality to manage email preferences through the API and UI, ensuring participants can opt-in per event. Enhanced the UI for better user experience with a new switch component for email settings. 2026-08-14 10:52:46 -04:00
39cff96b4a MSIN-4574 — Increment version code to 4.73.009 to reflect changes allowing ChronoTrack push without a bib number. Removed redundant checks for empty bib values in the ChronoTrack API synchronization functions to streamline participant classification and entry payload building. 2026-08-14 09:26:30 -04:00
a7f1081d17 MSIN-4520 — Implement functions for improved handling of Excel/CSV imports, including conversion of cell values to strings and building column indices based on headers. Update import logic to ensure accurate mapping of 'no_bib' values. Increment version code to 4.73.008 to reflect changes in import header matching and data processing. 2026-08-14 09:14:56 -04:00
1cd4220674 MSIN-4520 — Add fxImportResultatsDbAffectedRows function to reliably retrieve affected rows from the database. Update delete operations to utilize this new function for improved accuracy. Increment version code to 4.73.007 to reflect changes in import reset logic and ensure proper handling of participant deletions. 2026-08-14 08:50:24 -04:00
dc1819f777 ajouter phil adm 2026-08-13 17:17:28 -04:00
659a8f9f72 MSIN-4574 — Increment version code to 4.73.006 and refine the ChronoTrack API to ensure entry notes are always sent but not compared during content verification. Updated documentation to clarify the handling of entry notes and the implications for data discrepancies. Enhanced the verification logic to prevent false discrepancies related to entry notes, improving overall data integrity and synchronization processes. 2026-08-11 19:40:21 -04:00
07d593a742 MSIN-4574 — Increment version code to 4.73.005 and enhance the ChronoTrack API by improving the handling of entry notes. Updated logic to ensure accurate retrieval of entry notes, including support for labels without prefixes in Custom Import. Enhanced the verification process for fetching entry entities to include note columns, ensuring better synchronization and data integrity. Documentation updated to reflect these changes and improve user understanding of the new note handling mechanisms. 2026-08-11 19:36:37 -04:00
d346c22886 MSIN-4574 — Increment version code to 4.73.004 and enhance the ChronoTrack API by improving the handling of entry notes. Implemented logic to fetch entry notes accurately, addressing issues with missing data in event/entry lists. Updated documentation to clarify the new entry note handling and verification processes, ensuring better synchronization and user understanding. 2026-08-11 19:28:51 -04:00
c7527bdb03 MSIN-4574 — Increment version code to 4.73.003 and enhance the ChronoTrack API by implementing logic to omit Canadian provinces when the associated country is not Canada, logging the reason for omission. Improved feedback messages in the API to clarify the handling of location data and ensure accurate synchronization. Documentation updated to reflect these changes and enhance user understanding of the new location handling rules. 2026-08-11 18:50:59 -04:00
97affc5080 MSIN-4574 — Increment version code to 4.73.002 and enhance the ChronoTrack API by prioritizing data accuracy over speed in push operations. Implemented a new rule for handling updates based on entry IDs, ensuring that existing entries are updated with PUT requests while new entries are created with POST bulk requests. Improved feedback messages for synchronization processes to clarify the status of participant updates. Documentation updated to reflect these changes and enhance user understanding of the new push logic. 2026-08-11 18:16:36 -04:00
7a5f01247e MSIN-4574 — Increment version code to 4.73.001 and enhance the ChronoTrack API by implementing synchronous confirmation for user interactions, addressing the "dead click" issue. Improved feedback mechanisms during manual pushes to ensure clarity and responsiveness in the UI. Documentation updated to reflect these changes and enhance user understanding of the confirmation process. 2026-08-11 18:01:14 -04:00
995f9389f8 MSIN-4574 — Increment version code to 4.72.999 and enhance the ChronoTrack API by implementing a new bulk push method for faster data synchronization. Updated the handling of discrepancies to ensure accurate updates with unitary PUT requests for content gaps. Improved UI feedback for button interactions during operations, enhancing user experience and operational efficiency. Documentation updated to reflect these changes and clarify the new processes. 2026-08-11 17:57:10 -04:00
562dd5b98d MSIN-4574 — Increment version code to 4.72.997 and enhance the ChronoTrack API by refining the content verification process. Implemented changes to ensure accurate handling of location fields, introduced a new loading panel for content verification, and optimized the synchronization process to reduce server errors. Documentation updated to clarify the relationship between entry fields and their corresponding keys, improving user understanding and data integrity. 2026-08-11 17:47:16 -04:00
97bc40c6ff MSIN-4574 — Increment version code to 4.72.994 and enhance the ChronoTrack API by updating the alias mapping for payload keys. Documentation updated to clarify the relationship between entry fields and their corresponding keys in the ChronoTrack system, improving data verification processes. 2026-08-11 17:34:22 -04:00
bb67084f36 MSIN-4574 — Increment version code to 4.72.993 and implement content verification for payload discrepancies between MS1 and ChronoTrack entries. Enhance the ChronoTrack API UI with a new button for verifying content, and update the synchronization process to support targeted resends of discrepancies. Update documentation to reflect these changes and improve user understanding of the verification functionality. 2026-08-11 17:27:09 -04:00
597387a66e MSIN-4574 — Increment version code to 4.72.992 and enhance the ChronoTrack API UI for operation settings. Update labels and structure for better clarity in configuration and diagnostic sections, improving user experience and support functionalities. 2026-08-11 17:19:57 -04:00
b1fe965cd4 MSIN-4574 — Increment version code to 4.72.991 and implement phase E of the ChronoTrack API, introducing support for orphan entries in the diagnostic process. Update documentation to clarify the roles of new UI elements and enhance user understanding of the diagnostic functionalities. 2026-08-11 17:02:55 -04:00
ac4ec942c3 MSIN-4574 — Increment version code to 4.72.990 and implement phase D of the ChronoTrack API, introducing the fx_chronotrack_probe.php module for diagnostic support. Update documentation to clarify the roles of various code files and enhance user understanding of the API's functionality. 2026-08-11 16:58:10 -04:00
9d8f9bd639 MSIN-4574 — Increment version code to 4.72.989 and refine ChronoTrack API error handling. Ensure bibs are only assigned to confirmed entries, prevent duplicate error messages, and enhance clarity in error reporting. This update improves user experience and data integrity during synchronization processes. 2026-08-11 16:55:42 -04:00
9387364f85 Increment version code to 4.72.986 and implement a reload feature after successful bib assignment in the fiche gestion module, ensuring the details banner is aligned correctly. This update enhances user experience by providing immediate feedback on changes. 2026-08-11 16:51:19 -04:00
7021a7ecca MSIN-4574 — Increment version code to 4.72.985 and implement phase C of the ChronoTrack API, introducing dedicated modules for reading entries, lifecycle management, and cron jobs. Update documentation to clarify the roles of various code files, enhancing overall data handling and integration capabilities. 2026-08-11 16:47:30 -04:00
2d2c8855a7 MSIN-4574 — Increment version code to 4.72.984 and modify ChronoTrack API to always send bibs regardless of entry status (CONF, DQ, DNS, etc.), improving flexibility in bib assignment. This change addresses previous restrictions that limited bib updates to confirmed entries only. 2026-08-11 16:44:50 -04:00
70caeb8d42 MSIN-4574 — Increment version code to 4.72.983 and implement phase B of the ChronoTrack API, focusing on HTTP push functionality via fx_chronotrack_push.php. Update documentation to clarify the roles of various code files in the synchronization process, enhancing overall data handling and integration capabilities. 2026-08-11 16:36:52 -04:00
6d0d306334 MSIN-4574 — Increment version code to 4.72.982 and implement phase A of the ChronoTrack API, focusing on payload mapping from MS1 to CT via fx_chronotrack_payload.php. Update documentation to reflect the source of truth for entry writing. This update enhances data synchronization processes and prepares for future integration steps. 2026-08-11 16:26:07 -04:00
f458ec1dd4 MSIN-4574 — Increment version code to 4.72.981 and enhance ChronoTrack API by improving error messaging for bib conflicts, adding functionality to clear existing bib holders, and refining UI messages for better clarity. This update aims to streamline the handling of bib conflicts and improve user experience during synchronization processes. 2026-08-11 15:53:17 -04:00
5a3078f3de MSIN-4574 — Update ChronoTrack API to ensure location_country uses ISO-2 codes (e.g., CA) instead of ISO-3, enhancing data accuracy for participant entries. Increment version code to 4.72.980 to reflect these changes. 2026-08-11 14:29:35 -04:00
deac4ce2ca MSIN-4574 — Enhance "Sonde lecture pays" functionality in ChronoTrack API by adding support for participant identification via bib number and name, improving user experience and diagnostics. Increment version code to 4.72.979 to reflect these changes. 2026-08-11 14:18:45 -04:00
31e14bb693 MSIN-4574 — Implement "Sonde lecture pays" functionality in ChronoTrack API for read-only access to location data, enhancing diagnostics without modifying existing records. Increment version code to 4.72.978 to reflect these changes. 2026-08-11 14:13:32 -04:00
9c2769de05 MSIN-4574 — Refactor ChronoTrack API to streamline error handling and improve response time for API calls. Increment version code to 4.72.978 to reflect these changes. 2026-08-11 14:09:31 -04:00
128d0a2e25 MSIN-4574 — Update ChronoTrack API to improve location_country handling by using country names instead of ISO codes, enhancing diagnostics and user experience. Increment version code to 4.72.977 to reflect these changes. 2026-08-11 13:59:13 -04:00
6e9e6d65a5 MSIN-4574 — Restore error messages in ChronoTrack API and increase POST timeout to 120 seconds for improved reliability. Increment version code to 4.72.976 to reflect these changes. 2026-08-11 13:50:45 -04:00
4d364a6ebc MSIN-4574 — Update ChronoTrack API to include a dedicated "Sonde pays" button for improved diagnostics and enhance the rendering of probe results with location_country details. Increment version code to 4.72.975 to reflect these changes. 2026-08-11 13:48:25 -04:00
c0f9d9712b MSIN-4574 — Enhance ChronoTrack API synchronization by implementing retry logic for HTTP requests, adjusting batch sizes for entries, and improving error handling. Update version code to 4.72.974 to reflect these changes. 2026-08-11 12:17:14 -04:00
d206a00015 MSIN-4574 — Enhance ChronoTrack API integration by adding location_country handling and improving country coverage diagnostics. Update version code to 4.72.971 to reflect these changes. 2026-08-11 09:38:29 -04:00
03b2783f57 MSIN-4574 — Enhance registration cancellation and restoration processes by updating participant records to clear bib numbers and log modification dates. Implement ChronoTrack synchronization for withdrawn and restored entries, ensuring accurate alignment with event statuses. Update version code to 4.72.969 and date code to 2026-08-11 to reflect these changes. 2026-08-11 09:08:01 -04:00
83c809c0e0 MSIN-4515 / MSIN-4511 — Refactor column union logic to maintain panel order (Face > Endos > Info) while introducing a fixed key position. Update comments for clarity on conflict resolution and preserve insertion order in output without altering existing column relationships. 2026-08-10 18:42:14 -04:00
1420488ca8 SM-9353 — Optimize SQL queries in fxShowRecherche and fxShowRechercheinscription functions by replacing correlated COUNT(*) with EXISTS, significantly improving performance and reducing server lock times. No version code change. 2026-08-10 18:26:53 -04:00
17e63d2024 MSIN-4570 — Add QR code link functionality to the sidebar, enabling users to create QR codes directly from the super admin interface. This includes updates to the sidebar logic and UI elements for better navigation. No version code change. 2026-08-10 17:51:00 -04:00
9dfc66607a MSIN-4546 — Update fxGetQuantites function to use COUNT DISTINCT for accurate participant counts, ensuring historical semantics with GROUP BY pec_id_original. Adjust version code to 4.72.965 to reflect these changes. 2026-08-05 20:45:57 -04:00
b452d10db9 MSIN-4512 — Enhance PDF generation for distribution prints by adding orientation handling based on POST data. Update related functions to ensure consistent portrait orientation for introductory pages and implement a new function for adding pages with specified orientation. Increment version code to 4.72.964 to reflect these changes. 2026-08-05 18:05:13 -04:00
1ecc448df1 MSIN-4568 — Implement unlink functionality for ChronoTrack events, allowing users to disconnect from the current event and select a new one. This includes updates to the UI for unlinking, backend logic for resetting configurations, and handling participant entries. Ensure safe unlinking without affecting existing entries in ChronoTrack. 2026-08-05 17:30:29 -04:00
59d41c995f MSIN-4567 — Add normalization function for athlete sex in ChronoTrack API, mapping MS1 values to ChronoTrack enums (M, F, NB, NOT SPECIFIED). Update handling for non-binary and unspecified cases. This change enhances data consistency in athlete records. 2026-08-05 13:42:47 -04:00
44a37136d3 MSIN-4564 — Implement layout changes for the "Attribuer maintenant" flow, including a new "Fermer" button and adjustments to button visibility during batch and reset modes. Update version code to 4.72.963 and date code to 2026-08-05 to reflect these changes. 2026-08-05 08:56:19 -04:00
a996e1bde0 MSIN-4557 — Update registration management to include new filter switch for 'Sans dossard' and 'Annulées', modify related CSS classes, and increment version code to 4.72.962 to reflect these changes. 2026-08-04 16:55:57 -04:00
135bcca57c MSIN-4557 — Enhance registration management with new CSS styles for refund status, implement switch for cancelled entries, and update version code to 4.72.961 to reflect these changes. 2026-08-04 16:52:48 -04:00
35d61e10c4 MSIN-4557 — Implement status display enhancements for registrations, including new refund status indicators and updated CSS styles. Update version code to 4.72.960 to reflect these changes. 2026-08-04 16:39:50 -04:00
8fe115be50 MSIN-4482 — Implement batched deletion functions to optimize database operations and prevent MySQL locking issues during imports. Update version code to 4.72.958 to reflect these changes. 2026-08-04 09:54:26 -04:00
00ce5930b9 MSIN-4482 — Optimize SQL queries in fxImportResultatsResetPreview for improved performance and accuracy in import counters. Update version code to 4.72.957 and date code to 2026-08-04 to reflect these changes. 2026-08-04 09:30:24 -04:00
9df304aa8b MSIN-4436 — Update query parameter handling in fxInscrGestion functions to always return active list of registrations. Adjust related comments for clarity and consistency. This change ensures that the list is populated on initial visits and during pagination. 2026-08-03 16:08:57 -04:00
55afbec595 MSIN-4556 — Add functionality to copy switches from the source section to the target section in the UI kit. Update version code to 4.72.956 to reflect these changes. 2026-08-03 16:05:24 -04:00
1b7cb2ee20 MSIN-4554 — Enhance team-related question handling in distribution print functions. Implement logic to ensure team names are correctly retrieved and displayed based on participant roles. Increment version code to 4.72.955 to reflect these changes. 2026-08-03 15:55:18 -04:00
d474e725ff MSIN-4553 — Update identity display in details section to show name and bib number in the title bar, even when the section is collapsed. Adjust styles for improved layout and responsiveness. Increment version code to 4.72.954 to reflect these changes. 2026-08-03 15:24:01 -04:00
60ec309175 MSIN-4544 — Improve handling of language links and session data in mobile and header scripts to prevent PHP notices. Ensure default values are set for language links when not present, enhancing stability and user experience. 2026-08-03 15:16:07 -04:00
d910def81b MSIN-4553 — Update identity display in details section to always show name and bib number if present. Adjust styles for improved layout. Increment version code to 4.72.953 to reflect these changes. 2026-08-03 15:03:13 -04:00
8a11a1b55f MSIN-4553 — remettre nom (+ plaque dossard) en tête de fiche.
Corrige le mauvais placement dans Détails : identité toujours dans l'entête, plaque seulement s'il y a un numéro.
2026-08-03 14:34:16 -04:00
82386e216e MSIN-4553 — Enhance identity display in details section by adding name and bib number. Update styles for improved layout and responsiveness. Increment version code to 4.72.951 to reflect these changes. 2026-08-03 14:29:06 -04:00
9df342de1a MSIN-4469 — Implement production locking mechanism to prevent modifications during printing. Update related functions and UI elements to respect the production lock state, ensuring no assignments, resets, or modifications can occur while in this state. Increment version code to 4.72.950 to reflect these changes. 2026-08-03 14:11:34 -04:00
8bf5280e6a MSIN-4512 — Update fxBibOutputDistPrintPdf function to include new parameter for handling registrations without a bib number. Adjust related logic for PDF generation to prioritize POST data for CE runs. Enhance functionality for improved distribution handling. 2026-08-03 13:57:01 -04:00
a77863e98c MSIN-4512 — Introduce On/Off switch functionality to replace checkbox for binary choices in the UI kit. Update related styles and rendering functions to support the new switch component. Increment version code to 4.72.949 to reflect these changes. 2026-08-03 13:48:56 -04:00
c1294cca94 MSIN-4512 — Implement toggle for including registrations without a bib number in the PDF distribution. Update related functions to handle new input and adjust database interactions accordingly. Increment version code to 4.72.948 to reflect these changes. 2026-08-03 13:46:03 -04:00
9f119d64f9 MSIN-4515 — Update UI styles for production fields, enhancing visual feedback for selected options and improving interaction dynamics. Introduce polling mechanism for production panel updates without page refresh. Increment version code to 4.72.947 to reflect these changes. 2026-08-03 13:37:08 -04:00
de820e8d1b MSIN-4515 — Refine UI kit for production fields by enhancing side option selection and updating related styles and scripts. Introduce new side pick functionality and improve interaction handling. Adjust version code to 4.72.945 to reflect these enhancements. 2026-08-03 13:29:39 -04:00
bb70347581 MSIN-4515 — Enhance UI kit for production fields by introducing selectable side options (Face/Endos/Info) and updating related styles and scripts. Adjust version code to 4.72.942 to reflect these changes. 2026-08-03 12:45:35 -04:00
b8f1a60ea4 MSIN-4539 / MSIN-4550 — Update bib-fetching logic to show loader on deferred loading scenarios for dist_print and qty_summary tools. Adjust version code to 4.72.941 to reflect changes in loading behavior and UI responsiveness. 2026-08-03 12:26:01 -04:00
da074ff0a5 MSIN-4550 — Implement PDF header label fitting and truncation functions to enhance layout. Introduce fxBibDistPrintTruncateToWidthMm and fxBibDistPrintFitHeaderLabel for dynamic text handling based on available width. Update table header rendering to support multi-line headers and improve overall PDF output quality. 2026-08-03 12:24:44 -04:00
b6d561ae1e MSIN-4546 — Refine server timing profiling for bib management by enabling opt-in functionality via URL parameter ?bib_timing=1. Update documentation to clarify usage and remove automatic activation in development environments. Adjust related HTML output for improved user guidance. 2026-08-03 12:22:48 -04:00
17406830f7 MSIN-4550 — Update UI kit to enhance fit warning messages under the selected list, including recalculations per section. Adjust styles for better visibility and modify related functions for improved user experience. Increment version code to 4.72.940. 2026-08-03 12:19:02 -04:00
94e2a17fb3 MSIN-4546 — Introduce fxBibGlobalBatchToolShellVisible function to optimize visibility logic for the ops/global panel, reducing reliance on fxInfosBibRange. Update render functions to utilize new visibility checks and increment version code to 4.72.939. 2026-08-03 11:44:28 -04:00
589c788b97 MSIN-4546 — Implement production panel loading via AJAX on first click, enhancing user experience by deferring content loading until necessary. Update version code to 4.72.938. 2026-08-03 11:41:00 -04:00
32b66a1375 MSIN-4546 — Enhance timing profiling in renderBibToolNav function by adding timing marks before and after key rendering steps. Update production section to support deferred loading with a placeholder. Improve overall performance tracking. 2026-08-03 11:39:23 -04:00
e357aa5d86 MSIN-4546 — Update server timing profiling to be always active in development environment. Adjust version code to 4.72.937. 2026-08-03 11:34:24 -04:00
89cd709ed2 MSIN-4546 — Add ?bib_timing=1 server timing report to locate menu load cost.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 11:23:42 -04:00
31a2f8a22c MSIN-4546 — Optimize event statistics retrieval by implementing a shell-first approach for aggregated data, reducing database load and improving performance. Update version code to 4.72.935. 2026-08-03 11:18:18 -04:00
f99520d852 MSIN-4546 — Add editable race detail functionality and optimize quantity counting logic. Introduce new AJAX action for race details and enhance quantity retrieval with caching to improve performance. Update version code to 4.72.934. 2026-08-03 11:10:34 -04:00
431f36725a MSIN-4512 — Refactor workspace visibility logic in the bib distribution print panel to improve user experience. Introduce a new function to manage the visibility of workspace surfaces based on the active tool and workspace. Update version code to 4.72.933. 2026-08-03 10:40:29 -04:00
b016511064 MSIN-4512 — Implement draggable fixed items in the bib distribution print panel, allowing for improved user interaction and layout management. Update styles for selected items and ensure mandatory keys are always present in the output. Update version code to 4.72.932. 2026-08-03 09:14:05 -04:00
ce763e350d MSIN-4512 — Enhance the UI for the bib distribution print panel by introducing fixed items that cannot be dragged or removed, improving user interaction. Update styles for selected items and add new fit warning messages. Update version code to 4.72.931. 2026-08-03 09:05:19 -04:00
4f7fa08f76 MSIN-4512 — Refine the bib distribution print panel UI by consolidating multi-race configurations into a single collapsible section, enhancing user experience. Update styles for improved layout consistency and add new translations for race-related terms. Update version code to 4.72.930. 2026-07-31 10:07:37 -04:00
d60da379a4 MSIN-4512 — Update UI styles for improved visual consistency in the dual-panel layout, including adjustments to chip backgrounds, borders, and hover effects. Update version code to 4.72.927. 2026-07-31 09:14:50 -04:00
8e3bdaed2c MSIN-4512 — Revamp UI kit for the bib distribution print panel with mockup A implementation, introducing a dual-panel layout and SortableJS for enhanced user interaction. Update version code to 4.72.926. 2026-07-31 09:09:03 -04:00
3f07ddd763 MSIN-4512 — Integrate UI kit for enhanced user experience in the bib distribution print panel, including the addition of the ms1-ui-chooser for improved column management. Update version code to 4.72.925. 2026-07-31 08:50:14 -04:00
5e7738f1dd MSIN-4512 — Enhance PDF distribution by adding display order functionality for columns, including options to move columns up and down, and apply settings to all races. Update version code to 4.72.924. 2026-07-30 21:16:24 -04:00
f9e2bbf77e MSIN-4512 — Enhance PDF distribution functionality by adding support for column orientation and base fields. Update selection parsing and saving logic to accommodate new features. Update version code to 4.72.923. 2026-07-30 19:48:00 -04:00
eb20442b86 Update te_sans_inscription_en_ligne.png to improve visual assets for non-online registration process. 2026-07-30 15:33:57 -04:00
c2051c0b72 MSIN-4540 — Prevent discounts from being applied without online registration or payment. Update logic in event link handling and navigation menu. Ensure proper function inclusion for event result checks. 2026-07-30 15:27:48 -04:00
a17a6f801e MSIN-4532 — Enhance the non-online registration process by adding an optional cancel/restore block for participant entries, improving UI interactions. Update version code to 4.72.922. 2026-07-30 13:55:18 -04:00
64d1fff1b9 MSIN-4532 — Introduce a unified save functionality for participant identity, event, and questions in the non-online registration process. Enhance UI with a bib display next to participant names and update version code to 4.72.921. 2026-07-30 13:49:47 -04:00
dbe4b428f7 MSIN-4532 — Implement rapid entry for participant creation outside of online registration, including question handling and UI enhancements. Update version code to 4.72.919. 2026-07-30 13:33:14 -04:00
79d6fbfed8 MSIN-4534 — Refactor bib assignment logic to streamline refresh processes, ensuring a single paint operation post-GO and deferring UI state synchronization until final stats are available. Update version code to 4.72.918. 2026-07-30 11:56:22 -04:00
a4ab01fa22 MSIN-4530 — Update sorting options to always display levels 1–4 in batch processing, removing collapsible elements. Fix reset behavior to ensure all levels are visible. Update version code to 4.72.917. 2026-07-30 11:48:44 -04:00
c4479f3c76 MSIN-4530 — Implement collapsible sorting options for levels 2–4 in batch processing, enhancing UI clarity. Update version code to 4.72.916. 2026-07-30 11:42:21 -04:00
0b83835926 MSIN-4533/4536/4539 — Enhance bib assignment functionality with bulk reset capabilities, improved UI handling for CRUD operations, and optional full-screen loader for AJAX requests. Update version code to 4.72.915. 2026-07-30 11:34:06 -04:00
5caa1238d7 MSIN-4526 — Refactor bib assignment logic to eliminate unnecessary AJAX calls for checkbox visibility, enhancing performance. Update version code to 4.72.913. 2026-07-30 09:58:53 -04:00
8f3f7d4e83 MSIN-4379 — Refactor team mode handling to ensure unique bib assignment based on team name, enhancing batch processing logic. Update version code to 4.72.912. 2026-07-30 09:02:39 -04:00
39cf05979e MSIN-4379 — Implement team mode synchronization in batch processing to ensure consistent UI display and prevent race conditions. Update version code to 4.72.911. 2026-07-30 08:29:32 -04:00
7a6d7fbef7 MSIN-4516 — Improve Excel XML generation by implementing a more efficient data handling approach, reducing memory usage for large files. Update version code to 4.72.911. 2026-07-29 19:59:51 -04:00
f03149782d MSIN-4516 — Refactor Excel XML generation to avoid preg_replace for large files, preventing empty output. Update version code to 4.72.910. 2026-07-29 18:48:43 -04:00
bee203f67e MSIN-4516 — Update Excel generation process to handle long-running tasks more effectively by removing time limits and implementing a resume feature for interrupted generations. Adjust version code to 4.72.909. 2026-07-29 18:44:44 -04:00
d5040c825b MSIN-4516 — Enhance Excel generation by increasing timeout limit and optimizing cell creation methods for improved performance. Update version code to 4.72.908. 2026-07-29 18:34:32 -04:00
87e6c24772 MSIN-4516 — Fix notices équipe en vue dossards + alléger simu globale.
Sécurise renderBibView (no_equipe/pec_nom_equipe), joint les infos équipe au reset, et évite le calcul des trous sur analysis/simulate global.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 18:23:03 -04:00
b0b7c33f05 MSIN-4516 — Optimize batch processing by modifying fxInfosBibRange to improve performance and remove unused fxGetNextAvailableBib call. Update version code to 4.72.906. 2026-07-29 18:00:12 -04:00
92480480fc MSIN-4515 — Add 'nom_equipe' field to production bib generation and update related SQL queries. Increment version code to 4.72.904. 2026-07-29 17:47:56 -04:00
d0d7ac23e7 Revert "MSIN-4469 — Implement polling mechanism for Excel generation status with UI feedback. Update version code to 4.72.904."
This reverts commit f0dcefa8fa.
2026-07-29 17:31:55 -04:00
a3dafea0c0 Revert "MSIN-4469 — Update Excel generation process to maintain Production tab visibility and enhance UI feedback during loading. Increment version code to 4.72.905."
This reverts commit 84c9b69f2d.
2026-07-29 17:31:55 -04:00
84c9b69f2d MSIN-4469 — Update Excel generation process to maintain Production tab visibility and enhance UI feedback during loading. Increment version code to 4.72.905. 2026-07-29 17:29:09 -04:00
f0dcefa8fa MSIN-4469 — Implement polling mechanism for Excel generation status with UI feedback. Update version code to 4.72.904. 2026-07-29 17:24:10 -04:00
0fa5cbe05b MSIN-4515 — Add country name and ISO-2 code fields to production bib generation. Update version code to 4.72.903. 2026-07-29 16:55:10 -04:00
35def3db3b MSIN-4514 — Enhance sorting functionality to include 'pay_id' for batch processing, ensuring proper handling of country/nationality data. Update version code to 4.72.902. 2026-07-29 16:45:21 -04:00
f2ca7fc2cc MSIN-4520 — Implement lightweight Excel sheet inventory and sampling functions to avoid timeouts on large multi-sheet files. Update version code to 4.72.901. 2026-07-29 15:53:57 -04:00
d26124809d MSIN-4482 — Update fxPromoteurHubGetEventLinks function to hide reports section when "Sans inscription en ligne" is active. Increment version code to 4.72.900. 2026-07-29 15:12:25 -04:00
b9623f6c62 MSIN-4520 — Improve AJAX status refresh functionality with visual feedback during database updates. Increment version code to 4.72.899. 2026-07-29 12:09:37 -04:00
4f686375d6 MSIN-4521 — Add emergency contact fields to participant profiles, including name and telephone. Update related UI elements and SQL queries for data handling. Increment version code to 4.72.897. 2026-07-29 12:07:48 -04:00
9fa08b6105 MSIN-4521 — Clarify git push rules across multiple documents to enforce explicit user requests only. Update workflow guidelines to prevent unauthorized pushes and ensure compliance with project protocols. 2026-07-29 12:01:51 -04:00
e8b92c9d9f MSIN-4520 — Fix reset import button: visible feedback, clear stuck busy overlay, recount via AJAX.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 11:59:38 -04:00
549fcc0dc5 MSIN-4520 — Add UI enhancements for anomaly handling during import, including a banner for displaying anomaly counts and a download button for CSV files. Update version code to 4.72.895. 2026-07-29 11:53:26 -04:00
865c5be52c MSIN-4520 — Enhance birthdate parsing to support various formats and add functionality for downloading CSV of import anomalies. Update version code to 4.72.894. 2026-07-29 11:42:21 -04:00
09cd78d92e MSIN-4521 — Normalize gender handling in participant profiles to use M/F/N/A codes instead of h/f/n/a. Update related functions and UI elements for consistency. Increment version code to 4.72.893. 2026-07-29 11:29:46 -04:00
cc029a2998 MSIN-4521 — Implement features for editing participant profiles, events, and questions outside of online registration. Update styles and JavaScript for new functionality. Increment version code to 4.72.892. 2026-07-29 11:22:32 -04:00
98dd17379d MSIN-4514 — Enhance question sorting to include participant info fields and improve SQL queries for better handling of NULL values. Increment version code to 4.72.891. 2026-07-29 10:43:35 -04:00
797ed2e333 MSIN-4514 — Update question sorting logic to improve query performance and remove unnecessary filters. Increment version code to 4.72.889. 2026-07-29 10:31:18 -04:00
c7a226857a MSIN-4514 — Fix SQL query to handle non-existent epr_id_original in question sorting logic. Increment version code to 4.72.888. 2026-07-29 10:25:07 -04:00
c3b71dc445 MSIN-4436 / MSIN-4514 — Update question sorting functionality to include global event questions and improve query logic for better performance. Increment version code to 4.72.887. 2026-07-29 10:19:33 -04:00
bb60d6742c MSIN-4514 — Refactor batch sorting functionality to support up to 4 levels of sorting. Update related functions to handle new sorting structure and ensure backward compatibility. Increment version code to 4.72.886. 2026-07-29 09:59:13 -04:00
c86c1ed215 MSIN-4520 — Implement dropdown mappings for countries and provinces in the import process. Enhance mapping logic to support both automatic and manual resolution of country and province IDs. Increment version code to 4.72.885. 2026-07-29 09:33:39 -04:00
1590295f6e MSIN-4520 — Add support for region (pro_id) and country (pay_id) in import process. Implement functions to repair mojibake issues and normalize keys for bilingual headers. Increment version code to 4.72.884. 2026-07-29 09:27:31 -04:00
78ac7a8625 MSIN-4520 — règle agent : commentaire JIRA version + date/heure après chaque push.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-28 21:31:41 -04:00
6fa9377397 Enhance Excel import process by implementing automatic reapplication of event profiles based on sheet configurations. Update metadata handling to include sheet details and improve mapping logic for multi-sheet scenarios. Increment version code to 4.72.883. 2026-07-28 21:26:38 -04:00
dc86373b2a Enhance Excel import functionality by adding support for multi-sheet selection and improved metadata handling. Update AJAX import process to accommodate new sheet options. Increment version code to 4.72.882. 2026-07-28 18:12:27 -04:00
ed534eded7 Implement promoteur mode functionality by injecting environment notice markers and enhancing session management. Update related functions for event handling and improve UI interactions. Increment version code to 4.72.887. 2026-07-28 14:53:48 -04:00
8e9b5c8eb1 Enhance event filtering by introducing an optional button for active events with end dates within the last 5 days. Update UI elements to reflect the new filter functionality, including a toggle for private events. Increment version code to 4.72.886. 2026-07-28 14:50:06 -04:00
c83f28fb9c Update event filtering logic to use explicit date subtraction for improved clarity and maintainability. Increment version code to 4.72.885. 2026-07-28 14:46:49 -04:00
0a0bd3a418 Implement session management for promoteur login, enhancing user experience without altering client account. Add event filtering for active events within the last 5 days and update UI elements accordingly. Increment version code to 4.72.884. 2026-07-28 14:43:49 -04:00
4407da6616 Refactor asset paths to use same-origin URLs, avoiding CORS issues. Update script and link references across multiple files for consistency. Increment version code to 4.72.883. 2026-07-28 11:44:20 -04:00
64d6b63937 Add support for total kit access in event link retrieval by introducing grants check. Increment version code to 4.72.882. 2026-07-28 11:39:01 -04:00
cd65a2c2dd Update default bib production fields to include 'épreuve' alongside 'n° dossard' for improved information display. Increment version code to 4.72.881. 2026-07-27 21:14:29 -04:00
6793dd0e80 Refactor global bib assignment logic to support optional end quantity in batch processing. Remove deprecated free assignment functions and update related JavaScript and CSS for consistency. Increment version code to 4.72.880. 2026-07-27 20:16:56 -04:00
18da7b1eed Enhance error handling in bib production Excel generation by returning HTML responses with error messages. Update JavaScript for improved payload construction in JSON format for field selections. Increment version code to 4.72.880. 2026-07-27 18:49:32 -04:00
fe4c596dda Refactor error handling in bib production Excel generation to return HTML responses with error messages instead of redirecting. Update JavaScript to build payload for field selections in JSON format, improving data handling. Increment version code to 4.72.879. 2026-07-27 18:36:36 -04:00
dcdfbadd1a Update AJAX event access handling to prevent unauthorized requests. Include necessary file for access checks in user management functions. Increment version code to 4.72.878. 2026-07-27 18:32:26 -04:00
c8962b0a6a Refactor bib production fields handling to support JSON format for field selections, enhancing data integrity and simplifying parsing logic. Update AJAX responses for better error handling and user feedback. Increment version code to 4.72.877. 2026-07-27 18:26:37 -04:00
4088b22bb8 Refactor bib production fields to implement auto-save functionality and improve UI layout. Update translations for saved messages and apply selections to all races. Increment version code to 4.72.875. 2026-07-27 18:18:37 -04:00
035f165f9f Implement functionality to copy first race selections to all races in bib production fields. Update UI components and backend logic to support this feature, including new translations and version increment to 4.72.874. 2026-07-27 18:12:24 -04:00
e1610ee19c Enhance bib production fields by implementing a new builder for Face, Endos, and Info selections. Update UI components and backend logic for improved data handling, including serialization of field keys and sides. Increment version code to 4.72.873. 2026-07-27 18:00:17 -04:00
b5d2938d5e Implement new production fields for bib information, allowing users to add and manage details for Face, Endos, and Info. Update UI components and backend logic for improved functionality. Modify related translations and increment version code to 4.72.872. 2026-07-27 17:49:36 -04:00
2b617058b3 Refactor Excel output fields for event production, consolidating field selection into a unified list. Update related functions and comments for clarity. Increment version code to 4.72.871. 2026-07-27 17:30:13 -04:00
5e50c78ab1 Implement production fields for Excel output, allowing selection of Face, Endos, and Info for each event. Add AJAX functionality to save field selections and update styles for the new fields. Increment version code to 4.72.870. 2026-07-27 17:06:45 -04:00
22e8e7c486 Hotfix: hub et dossards affichent Plus disponible quand epr_qte=0 (plus de faux infinity illimite).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-25 22:13:02 -04:00
a62cca6e45 Enhance product display functionality by adding 'pro_date_limite' to the product selection query and updating the rendering logic to include the sale end date. Adjust comments to reflect these changes and improve clarity on the product display section. Increment version code. 2026-07-25 16:46:14 -04:00
83dc15ec9f Enhance workflow documentation by adding cost-benefit approach guidelines in both entreprise and projet rules. Update comments in dossard print functions for clarity on PDF formatting and printing specifications. Refine layout instructions in dossard layout file for improved user understanding. Increment version code. 2026-07-25 10:38:03 -04:00
3320a26ada Add layout management functions for dossard PDF generation. Implement features for loading, saving, and normalizing layouts with support for dynamic box configurations. Enhance field catalog for extensibility and improve comments for clarity. Increment version code. 2026-07-25 10:22:43 -04:00
f0e0feeeb9 Update dossard PDF layout to refine positioning of number and name boxes. Adjust percentage values for better alignment and centering, enhancing overall layout clarity. Improve comments for better understanding of layout adjustments. Increment version code. 2026-07-25 10:13:15 -04:00
762b31083e Refactor dossard PDF layout to implement independent boxes for number and name, enhancing text centering and positioning. Update layout calculations for improved alignment and dynamic font sizing based on available space. Enhance comments for clarity. Increment version code. 2026-07-25 10:08:52 -04:00
358673cada Refactor dossard PDF layout to improve background calibration and text positioning. Adjust margins and dimensions for the printable zone, ensuring better alignment of the number and name. Update comments for clarity and implement dynamic font sizing based on ascent height. Increment version code. 2026-07-25 10:02:50 -04:00
eed49c4745 Refactor dossard PDF layout to improve text positioning and sizing. Adjust margins and dimensions for the printable zone, ensuring better alignment of the number and name. Update comments for clarity and implement dynamic font sizing based on glyph height. Increment version code. 2026-07-25 09:59:44 -04:00
8eb0c644f3 Refactor dossard PDF layout to enhance text positioning and font sizing. Update layout calculations for the printable zone and adjust name placement under the number. Improve comments for clarity and ensure dynamic font sizing accommodates both width and height constraints. Increment version code. 2026-07-25 09:57:14 -04:00
6f7d8c5ef8 Refactor dossard PDF layout to improve text alignment and font sizing. Adjust name positioning to align left under the number and implement dynamic font sizing for the number based on available space. Enhance comments for better clarity. Increment version code. 2026-07-25 09:54:07 -04:00
ed992d2903 Update dossard layout to center large number and adjust name positioning. Modify QR code placement and enhance comments for clarity. Increment version code. 2026-07-25 09:51:50 -04:00
aee3467918 Refactor dossard PDF generation to standardize layout for 1 participant. Update page size to 8.5x5.5" and adjust positioning of text and QR code. Enhance template resolution logic and improve comments for clarity. Increment version code. 2026-07-24 20:09:56 -04:00
087ef47e15 Refactor dossard layout and background placement for PDF generation. Update dimensions to fit 5.5x8.5" bibs on Letter-sized pages, ensuring proper centering and scaling of background images. Adjust QR code and text positioning to align with new layout parameters. Increment version code. 2026-07-24 19:36:09 -04:00
aabb419e7d Add default and custom dossard templates in JPG format. Update image path resolution to include PNG options and enhance image type detection for PDF generation. Increment version code. 2026-07-24 19:33:05 -04:00
3a7c1f4bb7 Replace default PNG templates with JPG versions and add support for image type detection in PDF generation. Update template resolution logic to include new image formats. Increment version code. 2026-07-24 19:29:49 -04:00
36eb81d8e8 Enhance permission sorting in fxEveAccesShowCompteForm to prioritize 'hors kit total' entries and improve display of action permissions. Adjust select element width for better UI presentation. Increment version code. 2026-07-24 19:27:12 -04:00
d4ab2e2dc7 Refactor permission checks in fxEveAccesHasPermission and fxEveAccesHasAnyPermission functions to enhance logic for 'perm_grants_all' propagation. Update comments for clarity regarding key propagation rules. Increment version code. 2026-07-24 19:23:34 -04:00
8cd023e224 Add functionality to print bibs in PDF format for event management. Introduce new translation keys and implement print button logic in the event details view. Increment version code. 2026-07-24 19:17:30 -04:00
8788f22a81 Add functions to handle 'perm_grants_all' column and update permission checks for event kits. Refactor related logic to respect new permission propagation rules. Increment version code. 2026-07-24 19:10:19 -04:00
fd7930b139 Update SQL for 'Sans inscription en ligne' event type to include icon and ensure correct updates. Add logic to set icon if missing and adjust event attachment query for flexibility. Increment version code. 2026-07-24 17:57:08 -04:00
a68f54be80 Refactor fxImportResultatsSaveQuestionAnswer to ensure que_actif is set correctly during question updates and insertions. Simplify SQL logic for handling pec_id and improve error handling for database queries in fxInscrGestionFetchFicheProfile. Increment version code. 2026-07-24 17:53:03 -04:00
038bd7bd26 Implement fxImportResultatsEnsureQuestionsVisibleFiche function to activate questions during import for MSIN-4482. Update SQL queries to manage visibility of questions based on event type and ensure correct filtering of pec_id. Adjust logic in fxInscrGestionFetchFicheProfile to accommodate non-online registration scenarios. Increment version code. 2026-07-24 17:49:14 -04:00
69a97c0044 Enhance import functionality for events without online registration in MSIN-4482. Introduce new SQL queries and functions to manage event types and guard against invalid imports. Update AJAX handling to enforce type restrictions and improve user feedback in the event panel. Increment version code. 2026-07-24 17:45:47 -04:00
26a470fe1e Refactor fxImportResultatsCommit to improve handling of pec_id and pec_id_original during imports for MSIN-4482. Update SQL queries to ensure pec_actif is set correctly and prevent collisions with unique constraints. Enhance logic for updating participant records based on active status and original IDs. Increment version code. 2026-07-24 17:31:48 -04:00
80c791c7e1 Enhance fxImportResultatsCommit and fxImportResultatsReset functions for MSIN-4482 by updating SQL queries to ensure correct handling of pec_id_original and pec_actif flags during imports. Refactor participant deletion logic to improve performance using subqueries. Add diagnostic SQL for verifying import integrity. Increment version code. 2026-07-24 17:20:19 -04:00
05ce600e09 Refactor fxImportResultatsResetPreview to improve SQL query readability and performance by storing the escaped origin in a variable. Add diagnostic SQL query to count visible participants in the list based on active PEC status. Enhance the event panel UI to display database status with refresh functionality. Increment version code. 2026-07-24 17:06:46 -04:00
c6477f7757 Enhance result import functionality for MSIN-4482 by adding reset and preview capabilities. Implement fxImportResultatsResetPreview to count potential deletions and fxImportResultatsReset to remove imported data based on confirmation. Update AJAX handling in ajax_import_resultats.php to support these new actions. Ensure pec_actif is set to 1 for active entries in the import process. Increment version code. 2026-07-24 17:02:55 -04:00
8498af24f9 Implement fxImportResultatsGroupBlockedByType function to categorize and handle blocked import rows in fxImportResultats for MSIN-4482. Enhance error reporting by adding detailed messages for header rows, missing unique identifiers, unmapped races, and database update failures. Update the commit function to utilize the new error handling structure. Increment version code. 2026-07-24 16:26:45 -04:00
171a48a411 Add temporary unique ID generation for pec_id_original and par_id_original in fxImportResultats for MSIN-4482. Implement fxImportResultatsTempOriginalId function to ensure non-zero unique values during insertion. Update SQL queries to utilize generated IDs. Increment version code. 2026-07-24 16:22:28 -04:00
d63c92e36a Implement header row filtering and unique identifier generation in fxImportResultats for MSIN-4482. Add functions to detect header-like rows and create unique panier/commande identifiers. Enhance error handling during database operations. Increment version code. 2026-07-24 16:19:54 -04:00
8929236e85 Enhance participant import functionality in MSIN-4482 by updating the import phases and adding new fields for emergency contact details. Implement commit action in AJAX handling for result mapping validation. Increment version code. 2026-07-24 16:16:00 -04:00
84e65340b6 Enhance fxImportResultatsRenderEventPanel and ajax_import_resultats.php for improved AJAX handling in MSIN-4482. Update AJAX URL construction to include domain, add error handling for JSON parsing, and ensure panel visibility. Implement output buffering to prevent HTML pollution in JSON responses. Increment version code. 2026-07-24 16:09:37 -04:00
a1b6fa8911 Refactor fxImportResultatsComIdForEve to use org_id instead of com_id for event sharing templates in MSIN-4482. Update SQL queries and comments for clarity. Ensure compatibility with legacy data structures. Increment version code. 2026-07-24 15:44:25 -04:00
1ca75a4ed4 Implement UI enhancements for participant import without transaction in MSIN-4482. Add new styles for import dropzone and integrate import functionality into event management panel. Update navigation for improved access to import features. Increment version code. 2026-07-24 15:39:50 -04:00
db7b2e9f46 Add new project rules for MSIN-4482 — Implement import and quick entry for ChronoTrack without online registration. Document decisions, phases, and scope for development clarity. Increment version code. 2026-07-24 15:27:50 -04:00
b67fdaee78 Remove obsolete inbox telephone rules file from project directory, streamlining the codebase and eliminating unnecessary documentation. Increment version code. 2026-07-24 15:27:25 -04:00
fd250996b2 Update .gitignore to include new entry for local inbox directory used for briefs, ensuring that non-code files are excluded from version control. Increment version code. 2026-07-24 13:33:03 -04:00
797223e976 MSIN-4479 — Update alert messages in bib-assignments.js and inc_fx_promoteur.php to use new dataset keys for error handling. Ensure count function handles null results correctly to prevent JSON pollution. Increment version code to 4.72.869. 2026-07-24 13:05:56 -04:00
5961b40349 Refactor login.php to streamline session handling by moving the require statements for inc_functions.php and inc_fx_messages.php above the session check. This change improves code organization and clarity. Increment version code. 2026-07-23 16:02:51 -04:00
447dc5a7dc MSIN-4478 — Update role delegation logic to prevent total kits from being delegable. Adjust comments and UI messaging to clarify restrictions on promoter invitations. Increment version code. 2026-07-23 15:47:11 -04:00
5406e7e3db MSIN-4478 — Add functionality for managing pending invites and account activation for promoteur access. Implement methods to refresh reset tokens, revoke other roles, and deactivate orphan invite stubs. Update invitation handling to support reactivation of previously deactivated accounts. Increment version code. 2026-07-23 15:22:57 -04:00
685a1fd4f3 MSIN-4469 — Enhance fxBibProductionXmlSetFormula function to support cached values for Excel output, preventing empty totals in Excel/LibreOffice until recalculation occurs. Update fxBibProductionBuildOrderXml to utilize this new functionality for improved total calculations. Increment version code. 2026-07-23 12:55:42 -04:00
f93f6d14ff MSIN-4469 — Refactor fxBibProductionBuildOrderXml function to improve handling of Excel row counts and remove unnecessary empty rows. Update logic for shifting rows and add validation for printer lines in the output. Increment version code. 2026-07-23 12:50:47 -04:00
d15a68285c MSIN-4469 — Update fxBibProductionBuildSupplierXml function to clarify header cell styles for printed bibs. Modify Excel template to reflect new styling conventions for improved clarity in output. Increment version code. 2026-07-23 12:36:15 -04:00
f3233eb19b Implement fxNormalizeCmsPageLinks function to correct internal CMS links in HTML content. Update index.php to utilize this new function for processing page text, enhancing link handling for various content types. Increment version code. 2026-07-23 11:47:36 -04:00
a763a7b230 Update version code to 4.72.868 and date code to 2026-07-23 for production release. Adjust comments to reflect changes related to cache-busting for CSS/JS loader. 2026-07-23 11:18:45 -04:00
f76fb95fcd CON-333 — Refine static database synchronization logic to ensure that the display of differing content is contingent on the presence of actual differences. Update conditional checks to enhance clarity in the output when differences exist. Increment version code. 2026-07-23 10:09:23 -04:00
5b6faf93f9 CON-333 — Add functionality to remove excess "poubelle" entries from the info table during synchronization. Introduce a new action for users to trigger this process, ensuring that only unnecessary entries are deleted while preserving valid labels. Update user interface messaging to clarify the operation and its implications. Increment version code. 2026-07-23 09:28:14 -04:00
88a8010a18 CON-333 — Enhance static synchronization process by adding functionality to automatically remove excess entries during synchronization. Update user messaging to clarify that excess entries will be removed when possible, while ensuring no deletions occur. Revise documentation to reflect these changes in the workflow. Increment version code. 2026-07-23 09:24:27 -04:00
5ceeec7fdd CON-333 — Update documentation and workflow for database promotion processes, emphasizing the restriction of SQL execution to dev preprod environments only. Enhance clarity on deployment steps and introduce guidelines for removing excess roles and permissions during synchronization. Increment version code. 2026-07-23 09:16:19 -04:00
9697b37263 CON-333 — Update role permissions synchronization to use role_code instead of role_id for improved portability across databases. Introduce new functions for handling role permissions and enhance existing synchronization logic to support non-destructive updates. Update user interface to include a new "apply_all" action for comprehensive synchronization without deletions. Increment version code. 2026-07-23 09:00:04 -04:00
18b667d24b CON-333 — Implement soft synchronization for static tables, allowing for insertion of missing rows and updates to existing records without deletion. Introduce new functions for handling insert columns and info row checks. Update dashboard to reflect non-destructive actions and improve user messaging for clarity. Increment version code. 2026-07-23 08:53:44 -04:00
c154196090 MSIN-4328 — Increase AJAX timeout for preview synchronization in the ChronoTrack admin interface to improve user experience during long operations. Enhance error messaging for better clarity on network issues and session handling. Update participant classification function to allow optional payload building. Increment version code. 2026-07-22 21:57:21 -04:00
1306770f2e MSIN-4328 — Introduce verbose logging option in the ChronoTrack API for detailed entry logs. Update push panel UI to include a checkbox for verbose logs and adjust related functions to conditionally log detailed information based on user selection. Increment version code. 2026-07-22 21:49:57 -04:00
ab063aeb23 MSIN-4328 — Simplify the push panel in the ChronoTrack admin interface by removing the detail count display and updating related messaging for clarity. Adjust comments to reflect changes in the exclusivity section. Increment version code. 2026-07-22 21:45:22 -04:00
36631dc201 MSIN-4328 — Remove automatic loading of sync logs in fxChronotrackApiAdminRenderPageScript to streamline user experience. Update comments to clarify log access method, ensuring better understanding of log visibility options. Increment version code. 2026-07-22 21:44:07 -04:00
4851a50279 MSIN-4328 — Enhance ChronoTrack admin panel UI by updating the push panel to display exclusive and detail counts. Improve loading status visuals with a new layout and messaging for better user experience. Adjust CSS for improved responsiveness and clarity. Increment version code. 2026-07-22 21:41:12 -04:00
03cb227b5d MSIN-4328 — Update ChronoTrack admin panel to enhance event form rendering with a focus option for full-screen operation. Modify UI elements for clarity and improve navigation links. Adjust layout for better responsiveness and user experience. Increment version code. 2026-07-22 21:35:36 -04:00
1612d91ac6 MSIN-4328 — Add new functions for rendering event forms and auto-sync features in the ChronoTrack admin panel. Enhance user interface with compact summaries and configuration options for automatic synchronization. Update existing messaging for clarity and improve overall functionality. Increment version code. 2026-07-22 21:28:52 -04:00
e27e2b5bc0 MSIN-4461 — Implement new functions for handling entry notes in ChronoTrack, including indexing questions and mapping responses for participants. Update SQL queries to enhance data retrieval for active questions. Ensure synchronization logic accommodates custom notes effectively. Increment version code. 2026-07-22 18:02:20 -04:00
b18407f64b MSIN-4461 — Update API functions to support entry_note_test for custom notes in ChronoTrack. Modify button titles and messaging in the admin panel for clarity. Enhance synchronization logic to extract entry_note values from entries, ensuring accurate handling of custom notes. Increment version code. 2026-07-22 17:56:38 -04:00
46e34d4dcc MSIN-4328 — Introduce new function for handling payloads in POST API without entry_id, enhancing synchronization logic for bib conflicts. Update existing functions to improve clarity on bib handling and error management during entry processing. Increment version code. 2026-07-22 16:13:19 -04:00
839d865d7c MSIN-4328 — Refine messaging in fxChronotrackApiAdminRenderPushPanel for clarity on sending options. Enhance error handling in fxChronotrackApiSyncPushEvent to manage exceptions gracefully. Update ajax_chronotrack_api.php to ensure proper JSON response formatting. Increment version code. 2026-07-22 15:57:13 -04:00
10e6a09640 MSIN-4328 — Enhance fxChronotrackApiSync with new functions for handling bib conflicts and payload adjustments. Update fxChronotrackApiAdminRenderPageScript to clarify log messaging regarding bib status. Improve synchronization logic for participant entries, ensuring accurate handling of bib assignments and conflicts. Increment version code. 2026-07-22 15:42:15 -04:00
e747b180aa MSIN-4328 — Update messaging in fxChronotrackApiAdminRenderPageScript for clarity on bib status and conditions for sending bibs. Modify logic in fxChronotrackApiSync to ensure bibs are sent immediately upon confirmation status, improving synchronization accuracy. Increment version code. 2026-07-22 15:25:52 -04:00
57e281b46f MSIN-4328 — Enhance logging clarity in fxChronotrackApiSync by detailing participant entry information, including bib status and names. Update the admin page script to provide clearer messaging on log entries for better user understanding. Increment version code. 2026-07-22 15:25:09 -04:00
847f2f57a7 MSIN-4328 — Introduce a new button for full resync in the ChronoTrack admin panel, allowing all eligible participants to be resent regardless of last push date. Update JavaScript to handle the new button's functionality and enhance logging for successful entries. Adjust push preparation logic to accommodate full resync options. Increment version code. 2026-07-22 15:19:51 -04:00
23def197c8 MSIN-4328 — Update SQL queries in ajax_bib_range.php and inc_fx_promoteur.php to set par_maj alongside no_bib and par_date_bib, ensuring accurate timestamping for participant updates. Enhance fxChronotrackApiSync to include par_date_bib in filtering logic. Increment version code. 2026-07-22 15:15:38 -04:00
b9e4395297 MSIN-4444 — Add functions to handle team name retrieval and team group key generation, improving team management in the API. Update logic to classify participants into solo and team groups, ensuring proper handling of team representatives and blocked entries. Adjust messages for clarity in team-related errors. Increment version code. 2026-07-22 14:57:56 -04:00
0a19621949 MSIN-4475 — Refactor CSS for improved layout and responsiveness in assignment modes. Update JavaScript to enable immediate tool display without tile clicks, and adjust PHP for seamless workspace transitions. Increment version code to 4.72.867. 2026-07-22 14:45:33 -04:00
162419069b MSIN-4419 — Enhance refund cancellation hint with updated text and dynamic rescan functionality. Update version code to 4.72.865. 2026-07-22 14:34:19 -04:00
b74d5ac34a MSIN-4475 — Update CSS for badge auto assignment to ensure proper display without overflow. Adjust PHP functions to allow temporary unlocking for promoters and update version code to 4.72.864. 2026-07-22 14:22:43 -04:00
26b658acf8 MSIN-4445 — Refactor free assignment logic to allow all solo races (with or without existing sequences) to be assigned without creating new sequences. Update related functions and improve documentation for clarity. Increment version code to 4.72.863. 2026-07-22 14:14:52 -04:00
25c626be45 MSIN-4474 — Implement return URL preservation for event modifications and participant management. Enhance navigation logic to ensure users return to the original list after actions like save or cancel. Introduce new functions to validate and resolve return URLs, maintaining consistency across the application. Increment version code. 2026-07-22 12:29:03 -04:00
b038537d1c MSIN-4474 — Enhance participant and event access logic by refining conditions for editable fields based on promoter status. Implement legacy access handling for event modifications and ensure proper identity preservation during updates. Increment version code. 2026-07-22 12:25:16 -04:00
4b6e461b32 MSIN-4328 — Refactor API response handling in fxChronotrackApiAdminRenderPageScript to improve error management and user feedback. Update logic to ensure job continuation despite errors in batches, and enhance progress rendering with detailed status messages. Adjust sync entry posting to utilize fixed sub-lots for efficiency, reducing API call volume. Increment version code. 2026-07-22 12:03:56 -04:00
dfab716014 MSIN-4328 — Implement new function to derive registration choice names from entries, enhancing race data handling. Update race label formatting to include registration choice when available, improving clarity in dropdown options. Increment version code. 2026-07-22 11:53:21 -04:00
aa61dfef15 MSIN-4328 — Introduce new functionality to normalize registration choice data for events and enhance race label formatting. Merge reg_choice names by race ID to improve clarity in dropdown labels. Update existing functions to streamline data handling and presentation. Increment version code. 2026-07-22 11:52:34 -04:00
7858823205 MSIN-4328 — Enhance race label formatting by including registration choice names when available. Introduce new functions to index and apply reg_choice labels to race rows, improving clarity in race data presentation. Increment version code. 2026-07-22 11:47:28 -04:00
4bb4d189e6 desjardins.lp@ms1timing.com 2026-07-22 11:33:40 -04:00
9143f7a967 MSIN-4473 — Update promoteur hub link styles to align with button design by adding border-radius. Increment version code to 4.72.862. 2026-07-22 11:29:01 -04:00
ad57a54407 MSIN-4379 & MSIN-4455 — Update auto bib assignment logic for team events. Refactor to utilize existing team bibs and enhance error handling. Implement logic to load used bibs before assignment, ensuring no duplicates. Increment version code. 2026-07-22 11:07:17 -04:00
fd6ec9b876 MSIN-4471 — Refactor PDF generation for improved layout and pagination handling. Adjust header and footer dimensions based on subtitle presence, and implement logic to correctly count and display intro pages. Update SQL documentation to enhance clarity in participant instructions. Increment version code. 2026-07-22 08:32:28 -04:00
db5b1b9da3 MSIN-4471 — Improve image handling in PDF generation by enforcing correct image type detection and ensuring proper width calculations. Update image insertion logic to accommodate various image formats and enhance layout consistency. Increment version code. 2026-07-22 08:04:58 -04:00
a8c588f41f MSIN-4471 — Update image handling and SQL documentation for bib distribution. Enhance image insertion function to support optional maximum width and centering. Modify SQL to include specific image references and improve clarity in participant instructions. Increment version code. 2026-07-22 08:00:47 -04:00
b97fa8a337 MSIN-4471 — Update PDF distribution labels for participant names and checkboxes. Introduce new functions for handling logo paths and inline text rendering in PDF documents. Adjust SQL insert statements for improved clarity and idempotency. Increment version code. 2026-07-22 07:50:44 -04:00
2d706fb628 MSIN-4471 — Enhance bib distribution document handling by adding new columns for participant names and checkboxes. Adjust maximum question display for improved readability. Update version code and documentation content for clarity and usability. Increment version code to 4.72.861. 2026-07-22 07:43:03 -04:00
9233c65351 MSIN-4471 — Refactor PDF margin handling in image and HTML rendering functions to improve layout consistency. Introduce methods for accessing margins in the PDF class, ensuring compatibility with various document structures. Increment version code. 2026-07-22 07:36:30 -04:00
7120453ec8 MSIN-4471 — Add functions for improved PDF document handling, including image path resolution, HTML rendering, and image insertion. Enhance the introduction page keys for document printing. Increment version code. 2026-07-22 07:30:49 -04:00
215d3777db MSIN-3487 — Update QR code generation for event results to support inline PNG rendering, enhancing user experience by allowing direct downloads. Simplify access control and improve code organization with public settings. Increment version code. 2026-07-22 07:12:28 -04:00
22f45f98ff MSIN-3487 — Add function to generate binary PNG QR for event results with optional logo. Update existing QR generation functions for improved URL handling. Enhance UI to display and download event result QR codes. Increment version code. 2026-07-22 07:06:27 -04:00
99aa63f0e1 MSIN-3487 — Add temporary UI for managing additional event fields, including a form for entering and saving result links. Implement feedback messages for link status and enhance navigation with new buttons. Increment version code. 2026-07-22 07:02:00 -04:00
e701fd5543 MSIN-3487 — Enhance kc.php to support QR short link entry for event results and key chains. Implement redirection based on event ID and improve database query handling. Update inc_fonctions.php to include extra event functions. Increment version code. 2026-07-22 06:54:42 -04:00
e85f25c21d MSIN-4470 — Add page numbering functionality to PDF generation for race bibs. Introduce UTF-8 to Windows-1252 text conversion for FPDF compatibility. Update footer and header handling in PDF output. Increment version code. 2026-07-21 17:30:02 -04:00
f74fbc3092 MSIN-4470 — Implement functionality to position the PDF tool detail dynamically based on the selected tool. Update race label handling in production XML generation to avoid repetition. Add default slot for tool detail in the UI. Increment version code to 4.72.860. 2026-07-21 15:30:04 -04:00
f29ed77969 MSIN-4470 — Refactor bib workspace navigation and action buttons for improved accessibility and user experience. Update styles for workspace elements and enhance button interactions. Increment version code to 4.72.858. 2026-07-21 15:19:49 -04:00
31a7c9d012 MSIN-4469 — Introduce development unlock feature for production events, allowing temporary editing of ranges. Update UI elements and language keys for user guidance. Increment version code to 4.72.856. 2026-07-21 14:45:47 -04:00
afdf784215 MSIN-4469 — Enhance production locking mechanism for bib ranges, adding user feedback for printing status and updating related UI elements. Increment version code to 4.72.855. 2026-07-21 14:34:27 -04:00
0a35370a81 MSIN-4469 — Implement locking mechanism for production events to prevent modifications during printing. Add validation checks in AJAX and PHP functions, and update language keys for user feedback. Increment version code. 2026-07-21 14:26:56 -04:00
2cb9417bbf MSIN-4468 — Add validation message for minimum total quantity in race management. Update language keys for improved user feedback. Increment version code. 2026-07-21 13:24:21 -04:00
35f1a15fc7 MSIN-4468 — Enhance quantity validation in AJAX and JavaScript for race management. Implement error handling for total quantity below registrations and update related UI elements. Increment version code to 4.72.853. 2026-07-21 13:19:01 -04:00
39c062eafc MSIN-4468 — Implement total capacity management in AJAX and update related JavaScript functions for quantity handling. Adjust CSS grid layout for statistics display. Increment version code to 4.72.852. 2026-07-21 13:14:38 -04:00
8027155bde Update CSS for bib race summary layout by adding left margin to identity head cell. Increment version code to 4.72.851. 2026-07-21 12:33:06 -04:00
c6be4c38a6 MSIN-4467 — Remove 'anomalies' button from bib tool navigation and update JavaScript logic for tool categorization. Increment version code to 4.72.850. 2026-07-21 12:28:52 -04:00
eedb5d45d4 MSIN-4467 — Update accordion functionality in race detail blocks to allow both panels to be closed while ensuring at most one remains open. Refine comments for clarity. Increment version code to 4.72.849. 2026-07-21 12:17:29 -04:00
6d6e3c68f5 MSIN-4467 — Set default state for race expansion to false in bib tool, allowing users to choose which event details to expand. Update comments for clarity on functionality. Increment version code. 2026-07-21 12:06:58 -04:00
14727ce6b9 MSIN-4467 — Enhance accordion functionality for race detail blocks in JavaScript, ensuring only one panel remains open at a time. Update CSS for secondary headers with new background colors and hover effects. Increment version code to 4.72.848. 2026-07-21 12:02:24 -04:00
52d354bc9f MSIN-4467 — Update CSS for secondary headers to improve visual compactness and styling. Add new language keys for registration quantities and bib assignment in PHP. Increment version code to 4.72.847. 2026-07-21 11:58:24 -04:00
9732763fc8 MSIN-4467 — Refactor CSS for bib race summary and team mode components, enhancing layout with flexbox and improving responsiveness. Update JavaScript to handle team mode selection and display. Increment version code to 4.72.846. 2026-07-21 11:49:44 -04:00
4bc61c370c Update CSS styles for bib race summary and menu components, adjusting border and background colors for enhanced visual appeal. Increment version code to 4.72.845. 2026-07-21 11:36:26 -04:00
62037febe2 MSIN-4467 — Refactor CSS for bib race summary groups, enhancing layout with flexbox for better alignment and spacing. Update padding, borders, and background colors for improved visual consistency. Increment version code. 2026-07-21 11:33:31 -04:00
fd25dacaee MSIN-4467 — Update CSS for bib tool navigation layout, modifying grid structure for improved responsiveness. Enhance JavaScript logic to include additional tools in operations section. Remove deprecated navigation elements from the PHP rendering function. Increment version code. 2026-07-21 11:30:45 -04:00
397d82a458 MSIN-4467 — Enhance bib management workspace with new grouping for quantities and bibs, update CSS for improved layout and styling, and increment version code to 4.72.844. 2026-07-21 11:27:23 -04:00
2d86789938 MSIN-4467 — Implement new workspace architecture for bib management, including preparation, production, and operations sections. Update CSS for improved layout and styling of workspace elements. Increment version code to 4.72.843. 2026-07-21 10:27:01 -04:00
f9d304484e MSIN-4465 — Refactor report access functions to support V2 permissions and enhance legacy menu handling. Update SQL queries for better data retrieval and add comments for clarity. Increment version code. 2026-07-20 14:54:24 -04:00
f0bdefd805 MSIN-4419 — Update SQL query for participant statistics to count total participations without deduplication by email. Adjust comments for clarity regarding participation counting. Increment version code. 2026-07-20 12:54:44 -04:00
b4637ddeca MSIN-4459 — Update participant list rendering to include team names with improved inline display. Adjust CSS for better layout and visual consistency. Increment version code to 4.72.842. 2026-07-20 12:35:28 -04:00
fb2de52739 MSIN-4459 — Enhance team display in participant list with visual grouping and indentation for teammates. Update CSS for new styles and increment version code to 4.72.841. 2026-07-20 12:34:12 -04:00
90f496c684 MSIN-4459 — Refactor participant list rendering to improve layout and clarity. Update SQL query for team member sorting and adjust CSS for new styling conventions. Increment version code to 4.72.840. 2026-07-20 12:31:48 -04:00
f7057bd7cb MSIN-4459 — Refactor participant list to display individual entries per participant, excluding certain roles. Update SQL queries for improved data retrieval and modify CSS for new styling conventions. Increment version code to 4.72.839. 2026-07-20 12:26:32 -04:00
afac2f175e MSIN-4459 — Refactor participant list to include individual bib numbers and team details. Update SQL queries and PHP functions for enhanced data retrieval. Modify CSS to remove deprecated styles. Increment version code to 4.72.838. 2026-07-20 12:19:55 -04:00
44b7e4cf84 MSIN-4459 — Update participant list to include team number and enhance duplicate bib checks. Modify SQL queries and PHP functions for improved data handling. Adjust CSS for new layout elements. Increment version code to 4.72.837. 2026-07-20 12:12:06 -04:00
39fd110388 MSIN-4459 — Implement team member display under captain in participant list. Update CSS for layout adjustments and add new SQL query to fetch team members. Increment version code to 4.72.836. 2026-07-20 12:01:41 -04:00
da4de58b08 Aligner les règles agent sur le modèle entreprise/projet/ops.
Conserve les conventions Inscription sous projet/ et ajoute ops Git/deploy.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-19 12:05:59 -04:00
211f1e2638 MSIN-4452 — Update badge styles in promoteur hub for improved visual alignment and reduced alert intensity. Adjust CSS properties for danger, warning, info, and soft badges. Increment version code to 4.72.835. 2026-07-16 11:41:07 -04:00
64ef6c2a8e MSIN-4454 — Update product display in promoteur hub to show sold quantities instead of available. Adjust labels and HTML structure for clarity and consistency in the user interface. Increment version code accordingly. 2026-07-16 11:27:11 -04:00
0ff8299fdc MSIN-4451 — Add participant count under the "entries sold" KPI in the promoteur hub. Introduce new CSS styles for displaying participant information and update SQL queries to calculate unique participants. Increment version code to 4.72.834. 2026-07-16 11:24:11 -04:00
37f8bec215 Update 'promoteur_hub_flash_inscrits' label to reflect total entries sold instead of registered. This change clarifies the terminology used in the interface, aligning it with the intended meaning of the data. Increment version code accordingly. 2026-07-16 11:00:04 -04:00
7b94e0daa0 Remove unused sorting parameters from AJAX response in bib range actions, streamlining the data returned for improved performance. This change simplifies the response structure while maintaining the necessary HTML output. Increment version code to 4.72.834. 2026-07-16 10:55:10 -04:00
e9752ddcb7 MSIN-4456 — Enhance sorting functionality for team names in bib assignments. Introduce new functions to ensure reliable ordering by team name and implement PHP-based sorting for participants when the primary sort criterion is a team. Update AJAX responses to include additional sorting parameters. Increment version code to 4.72.833. 2026-07-16 10:42:33 -04:00
26b85d1892 MSIN-4446 — Enhance AJAX responses for bib range actions by including summary HTML and sequence counts. Update JavaScript functions to handle new response structure, ensuring accurate display of sequence information after create, delete, and refresh actions. Increment version code to 4.72.832. 2026-07-16 10:07:06 -04:00
527e3909e8 MSIN-4445 — Refine free assignment logic by enhancing duplicate bib number checks and improving feedback mechanisms for overlapping ranges. Update backend functions to ensure accurate detection of assigned bibs within specified ranges and adjust UI components to reflect these changes. Increment version code to 4.72.830. 2026-07-16 10:02:14 -04:00
ce63b9325e MSIN-4445 — Enhance free assignment functionality by adding duplicate bib number warnings for overlapping ranges. Implement backend checks for bibs already assigned to other events and update UI components to display relevant feedback. Increment version code to 4.72.829. 2026-07-16 09:58:07 -04:00
77799add6d MSIN-4445 — Implement free assignment functionality for solo events without sequence creation. Add new actions for analysis, simulation, and planning of free ranges. Update UI components and JavaScript logic to support the new features, including feedback mechanisms for quantity validation. Increment version code to 4.72.828. 2026-07-16 09:51:34 -04:00
b72ea70242 MSIN-4464 — Update audit history button styles for improved visibility and user experience. Change button design to a more discreet circular format, adjusting colors and dimensions for better integration into the UI. Increment version code to 4.72.827. 2026-07-15 13:21:15 -04:00
d23d471de4 MSIN-4464 — Implement audit history feature for participant fields. Add functionality to retrieve and display recent changes for specific fields, enhancing traceability of modifications. Introduce a new button in the UI to access the audit history, along with corresponding styles and JavaScript logic for improved user interaction. Update relevant backend functions to support this feature, ensuring proper access control and data retrieval. 2026-07-15 13:18:44 -04:00
3ac01a6f00 MSIN-4464 — Revamp event selection form in audit logs. Replace dropdown with a search input and direct ID entry for improved usability. Enhance event filtering with live search capabilities and update layout for better accessibility, ensuring a more intuitive experience for users managing audit logs. 2026-07-15 13:08:19 -04:00
ffb679626e MSIN-4464 — Refactor audit event selection in admin interface. Update SQL query to improve sorting logic for events and enhance the event selection form with a live search feature. Adjust layout and add direct input for event IDs, improving usability and accessibility for users managing audit logs. 2026-07-15 13:06:05 -04:00
c8aa6394f5 MSIN-4464 — Enhance audit logging functionality for participant management. Introduce new functions to track last touch events and handle cancellation/restoration of participant registrations. Update admin interface to display recent logs and improve oversight of audit activities, ensuring better traceability of changes made to participant records. 2026-07-15 13:04:09 -04:00
1260270c7d MSIN-4464 — Implement audit logging for participant updates. Introduce functions to track changes to 'no_bib', 'no_bib_remis', and participant status, ensuring traceability of modifications. Enhance the admin interface to support these audit features, improving oversight and accountability in participant management. 2026-07-15 13:00:01 -04:00
97ac1807b0 MSIN-4464 — Add configuration for fiche audit traceability in admin interface. Introduce a new sidebar section for audited fields and retention, enhancing the user experience by providing easy access to audit-related features. 2026-07-15 12:57:18 -04:00
260c617e35 Remove reference to MSIN-4460 from UI messaging in admin kit management. Clarify text regarding delegable kits to enhance user understanding without altering functionality. 2026-07-15 12:40:01 -04:00
689b0a12f7 MSIN-4460 — Update delegation logic for kits in admin interface. Remove restrictions on delegable kits, allowing super admins to decide on delegation per kit. Clarify UI messaging regarding kit assignments and delegation options for improved user understanding. 2026-07-15 12:38:18 -04:00
dbe25c8a77 MSIN-4457 — Adjust layout of event access form by modifying column sizes for improved responsiveness. Update label for days input to enhance clarity, ensuring a better user experience in the event management interface. 2026-07-15 12:32:04 -04:00
7b881be0a7 MSIN-4457 — Refactor event selection UI to improve usability by removing absolute positioning and implementing a responsive height for result lists. This change enhances the visibility of event options and prevents clipping issues, ensuring a better user experience during event searches. 2026-07-15 12:28:46 -04:00
44241a5010 MSIN-4457 — Enhance event selection UI by adding scrollable result lists for better usability. Update input fields to include position-relative styling and adjust result limits in SQL queries to accommodate more historical events, improving the overall user experience in event searches. 2026-07-15 12:26:22 -04:00
6202665862 MSIN-4457 — Update event search logic to prioritize recent events. Modify SQL query to sort by event end date and ID, improving the visibility of upcoming events in search results. Increase result limit to enhance user experience. 2026-07-15 12:22:50 -04:00
9cf4367521 MSIN-4430 — Add button for bib confirmation in inscriptions management interface. This change introduces a new button for confirming bib entries, enhancing user interaction and streamlining the process. The previous button implementation has been removed to avoid duplication. 2026-07-15 12:16:59 -04:00
2dae908074 MSIN-4328 — Add custom field probe functionality to ChronoTrack API. Implement a new button for custom field synchronization in the admin interface, allowing for the testing of custom fields similar to import processes. Enhance backend logic to handle custom field creation and validation, improving the overall synchronization capabilities and user experience. 2026-07-15 12:03:42 -04:00
66590a2627 MSIN-4328 — Enhance ChronoTrack API sync logic to include check-in status and bib retrieval date. Update filtering to account for 'no_bib_remis_date' and improve participant data retrieval by adding relevant fields. This change aims to improve the accuracy of participant synchronization and check-in tracking. 2026-07-15 12:02:26 -04:00
36c6e5dd2b MSIN-4328 — Update bib assignment logic in ChronoTrack API sync process. Ensure bib numbers are only assigned when the entry status is confirmed and the entry is not already linked to a ChronoTrack entry. This change improves the accuracy of bib assignments during synchronization. 2026-07-15 09:26:19 -04:00
80624eebfe MSIN-4328 — Refactor countdown functionality for screen refresh in ChronoTrack API. Simplify UI messages and remove unused countdown logic for auto sync. Enhance user experience by providing clearer feedback on the status refresh interval and streamline related JavaScript functions. 2026-07-15 09:20:38 -04:00
05fc3b0048 MSIN-4328 — Implement countdown for next auto sync in ChronoTrack API. Enhance UI to display countdown timer for upcoming cron jobs and update push panel messages for clarity on sync operations. Introduce new functions for managing auto sync metadata and improve overall user feedback during synchronization processes. 2026-07-15 09:18:35 -04:00
4bfd89d358 MSIN-4328 — Remove logging of 'push_skip' in participant sync to reduce spam during preparation. Blocked participants will still be visible in the UI without cluttering logs. 2026-07-15 09:14:49 -04:00
4ce59663ad MSIN-4448 — Pastille dossard cliquable dans la liste (édition à la demande).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-15 08:56:12 -04:00
0e42cc45d1 Implement inline editing for bib numbers and status confirmation in the participant management interface. Enhance CSS for better layout and usability, including wider columns for editable bibs and improved button styles. Update JavaScript to handle inline edits and confirmations, ensuring a smoother user experience. This change addresses MSIN-4448 and improves overall functionality in the inscriptions management system. 2026-07-15 08:52:27 -04:00
a18197b31c Refactor auto ChronoTrack sync script to enhance logging and error handling. Remove local heartbeat logging and update cron execution feedback. Improve admin interface with clearer instructions and a new button for manual sync diagnostics. Implement auto-sync diagnostics to provide detailed status and logs for events, facilitating better monitoring and troubleshooting of synchronization processes. 2026-07-14 12:46:27 -04:00
c3340605f2 Enhance auto ChronoTrack sync script with logging and error handling. Implement local heartbeat logging to track script execution and improve feedback during cron job execution. Update admin interface to include a button for manual cron testing and provide clearer instructions for using the sync functionality. This change aims to improve monitoring and troubleshooting of synchronization processes. 2026-07-14 12:43:54 -04:00
0e0e1cf2a2 Add sync logs functionality to ChronoTrack API. Implement a new method for retrieving the latest synchronization logs for events, enhancing the admin interface with a button to view logs. Update AJAX handling to support log retrieval and improve user feedback during synchronization processes. This change aims to provide better visibility into sync operations and facilitate troubleshooting. 2026-07-14 12:27:14 -04:00
eac42923fd Implement auto-sync functionality in ChronoTrack API, including new methods for managing auto-sync events and handling expiration. Update push panel messages for clarity on cron job execution and enhance user feedback. This change supports improved synchronization management and lays the foundation for future enhancements. 2026-07-14 12:15:34 -04:00
0db8da9810 Implement auto-sync configuration for ChronoTrack API. Add functionality to save auto-sync settings, including frequency and end date, enhancing user control over synchronization. Update push panel to reflect new options and improve user feedback during the sync process. This change lays the groundwork for future cron integration. 2026-07-14 12:08:03 -04:00
f9a75e8aba Add auto-repair logic for push synchronization in ChronoTrack API. Implement a mechanism to handle pending pushes when all eligible participants are ready, ensuring accurate differential tracking. Update status messages during the process to enhance user feedback and improve overall synchronization reliability. 2026-07-14 11:58:32 -04:00
c567fd1adf Add sync_mark_current action to ChronoTrack API for updating last push timestamp without sending data. Enhance push panel with a new button for manual refresh, improving user control over synchronization status. Update SQL query to prevent timestamp auto-update during entry ID persistence, ensuring accurate differential tracking. 2026-07-14 11:56:42 -04:00
71e57d6eb1 Refactor ChronoTrack API push panel to enhance user experience with clearer status messages and improved layout. Update button actions and add collapsible details section for better visibility of synchronization results. This change aims to streamline participant data management and provide more intuitive feedback during the push process. 2026-07-14 11:53:07 -04:00
7ce5dd0941 Enhance ChronoTrack API integration by adding support for direct links to athlete profiles and persisting entry IDs. Update CSS for improved layout on management pages. Adjust AJAX handling to accommodate new entry ID parameters, ensuring seamless synchronization and user experience. This change aims to streamline access to athlete data and improve overall functionality in the ChronoTrack system. 2026-07-14 11:48:12 -04:00
a860db05d3 Refactor ChronoTrack API synchronization to streamline entry counting and improve performance. Introduce new functions for fetching total entries without detailed scanning, enhancing efficiency. Adjust AJAX handling for synchronization processes and update timeout settings for better reliability. This change aims to optimize data handling during participant synchronization. 2026-07-14 11:37:59 -04:00
1b9d62c69e Add fxChronotrackApiSyncEpreuveLabel function for improved race labeling in exclusion messages. Update error handling to include detailed race information when mapping issues occur, enhancing clarity in synchronization logs. 2026-07-14 11:18:26 -04:00
bdc9c8fdde Enhance ChronoTrack API synchronization by adding validation for missing first and last names. Introduce new error codes and messages for participants lacking these fields, improving data integrity checks during synchronization. This change ensures that participant data is complete before processing, enhancing overall reliability. 2026-07-14 11:14:27 -04:00
784ef238a2 Implement changes to ChronoTrack API synchronization to track pending pushes and last successful push timestamps. Introduce logic to filter participants eligible for pushing based on their modification dates relative to the last push. Adjust push confirmation messages and update the handling of push job results to ensure accurate reporting of pending and successful pushes. This enhances the reliability and efficiency of participant data synchronization. 2026-07-14 11:05:12 -04:00
746ddea4b2 Adjust ChronoTrack API synchronization parameters to a cautious batch and chunk size of 100 for improved validation. Introduce a performance testing function to evaluate optimal batch sizes for entry posting, enhancing error handling and efficiency in participant data synchronization. This change aims to calibrate the push process before full implementation. 2026-07-14 09:54:41 -04:00
d1733b2848 Update ChronoTrack API synchronization parameters to improve performance and error handling. Increase chunk sizes for data processing and adjust timeout settings for HTTP requests. Introduce adaptive entry posting to handle failures more efficiently, reducing the number of HTTP calls. This change enhances the reliability and efficiency of participant data synchronization. 2026-07-14 09:53:14 -04:00
13e6f6dcf2 Enhance push synchronization process in ChronoTrack API by implementing chunked data handling for improved performance and user feedback. Introduce job management functions to store and manage push jobs, reducing session load. Update AJAX handling for push preparation and execution, allowing for better error handling and progress tracking. Increment batch size for synchronization to optimize HTTP requests. This change significantly improves the efficiency and reliability of participant data synchronization. 2026-07-14 09:42:22 -04:00
2e17136f01 Refactor promoteur back URL logic to prioritize new hub structure, enabling event focus functionality. Introduce fxPromoteurHubOrLegacyBackUrl for streamlined navigation based on event ID availability. Update related functions for consistency and clarity. This change enhances user experience by ensuring proper redirection based on session and event context. 2026-07-14 08:35:59 -04:00
19b9369e77 Implement event focus functionality in promoteur hub, allowing automatic scrolling and menu opening based on event ID. Update back URL logic to prioritize the new hub structure and enhance deep-linking capabilities. Increment version code to 4.72.823 to reflect these changes. 2026-07-14 08:29:58 -04:00
c6bc9ce4b0 Update bib matching logic in inscription management to set 'exact' as the default option and adjust related comments for clarity. Enhance search reset functionality to include a query parameter for all inscriptions. Refactor conditions for matching to improve consistency in handling exact and partial matches. 2026-07-14 08:25:59 -04:00
b9a8d5ffd2 Implement course status column in inscription management, enhancing the display of race statuses. Update CSS for new status pill styling and adjust grid layout for better responsiveness. Increment version code to 4.72.822 to reflect these changes. 2026-07-14 08:18:27 -04:00
1f7a92c8ea Enhance access control for super admin actions by adding validation for report preferences and question approvals. Update action handling to include new permissions for 'actif' and 'validequestion', ensuring proper session checks and error responses. Refactor action array for clarity. Increment version code to reflect these changes. 2026-07-13 19:26:34 -04:00
899ba89a2b Update version code to 4.72.821, enhance error handling in JavaScript and PHP for better user feedback, and improve access control for report preferences in super admin actions. Refactor alert messages and database insert logic to provide clearer error messages and ensure proper session handling. 2026-07-13 19:23:36 -04:00
2eafd998c0 Update version code to 4.72.819 and enhance access control for report preferences in super admin actions, ensuring proper session handling and permissions. Refactor JSON response handling for specific actions. 2026-07-13 18:50:55 -04:00
b0cfc629f9 Refactor access control for super admin sessions to eliminate bypass permissions, ensuring that only specific kits and extras are accessible. Update version code to 4.72.818 to reflect these changes. 2026-07-13 18:03:05 -04:00
3da6977daf Refactor inscription management permissions to enhance control over sensitive actions, specifically excluding refunds from general edit permissions. Update version code to 4.72.817 to reflect these changes. 2026-07-13 17:57:02 -04:00
e52c3b7b45 Refactor inscription management access control to implement granular permissions for transaction actions. Update CSS for improved button layout and alignment, ensuring consistent styling across action items. Increment version code to 4.72.816 to reflect these changes. 2026-07-13 17:53:55 -04:00
6c56fb7af7 Implement exact and partial matching options for bib search in inscription management. Update CSS for improved layout and styling of the new matching interface. Increment version code to 4.72.815 to reflect these changes. 2026-07-13 17:47:10 -04:00
dc3d6a5c0b Refactor inscription management to improve localization handling by replacing hardcoded strings with dynamic keys. Enhance CSS for better layout and responsiveness in the inscription management UI. Increment version code to 4.72.814 to reflect these changes. 2026-07-13 17:42:21 -04:00
8272192815 Refactor crop handling in inscription management to improve accuracy of target area measurements. Update CSS comments for clarity on spotlight functionality. Increment version code to 4.72.813 to reflect these changes. 2026-07-13 17:38:09 -04:00
1c8782db3c Update inscription management query logic to handle "Sans dossard" conditions for solo and team entries. Increment version code to 4.72.812 to reflect these changes. 2026-07-13 17:34:19 -04:00
cc2bd15658 Refactor inscription management UI to eliminate hardcoded language strings, replacing them with dynamic keys for better localization. Update CSS for improved grid layout and visual consistency, including responsive adjustments for mobile views. Increment version code to 4.72.811 to reflect these changes. 2026-07-13 17:32:09 -04:00
080e8bdcd9 Enhance visual consistency for inscription management panels by standardizing color schemes and styles across various components. Update CSS for improved panel aesthetics, including border-radius adjustments and box shadows. Increment version code to 4.72.809 to reflect these changes. 2026-07-13 17:26:25 -04:00
0ea8a48591 Implement responsive grid layout for the inscription management form, enhancing mobile and tablet views. Update form fields for better structure and accessibility, including multi-status filters and action buttons. Increment version code to 4.72.807 to reflect these changes. 2026-07-13 17:23:25 -04:00
d3bb32eb97 Enhance multi-page AJAX handling by enforcing context for translations in ajax_inscr_gestion and ajax_promoteur_hub. Introduce new language keys for improved user feedback and error messages. Increment version code to 4.72.806 to reflect these changes. 2026-07-13 17:17:53 -04:00
af2ddba2fa Implement email resend functionality for event access invitations, including success and error handling. Update language translations for resend button and messages. Increment version code to 4.72.804 to reflect these changes. 2026-07-13 16:59:49 -04:00
8191970086 Implement invitation handling for account switching in mobile interface. Introduce signature validation for invite links to prevent spoofing and enhance security. Update logout redirection to support both reset and invite links. Increment version code to 4.72.803 to reflect these changes. 2026-07-13 16:55:47 -04:00
b948f401d1 Enhance QR scanning interface by adjusting CSS for improved layout and centering of the QR camera view on open. Introduce JavaScript function to focus the QR camera view upon opening and ensure proper sizing of the QR box. Increment version code to 4.72.802 to reflect these changes. 2026-07-13 16:45:16 -04:00
5f9ce3f0e7 Enhance QR scanning permissions in inscriptions management to align with V2 API. Introduce fallback logic for permissions and streamline role checks. Update SQL to reflect new permission handling for targeted roles. No version code increment required. 2026-07-13 16:40:45 -04:00
2c11be5796 Refactor permissions for inscriptions management to align with V2 API standards. Remove deprecated permission keys and update related functions for QR scanning access. No version code increment required. 2026-07-13 15:57:57 -04:00
a1a74565a4 Update CSS to allow text selection for help button and enhance JavaScript error handling by removing timeout for showing ready state. Increment version code to 4.72.801 to reflect these changes. 2026-07-13 15:39:11 -04:00
77607608e7 Enhance OCR functionality for bib numbers by introducing multipart image upload support and improving error handling. Update JavaScript to handle image processing and cropping more effectively, including a new method for converting canvas to JPEG blob. Increment version code to 4.72.800 to reflect these changes. 2026-07-13 15:28:45 -04:00
33eb70261d Increment version code to 4.72.799 to reflect recent updates in settings. 2026-07-13 15:25:51 -04:00
a1aefe7bbe Update OCR API key in settings for Google Cloud Vision integration. This change enables the OCR functionality for bib numbers by providing the necessary API credentials. No version code increment required. 2026-07-13 15:24:37 -04:00
a929a8d859 Implement OCR functionality for bib numbers using Google Cloud Vision. Introduce new methods for enabling OCR, processing images, and extracting numbers from OCR text. Update JavaScript to handle cloud-based OCR processing and improve error handling. Increment version code to 4.72.798 to reflect these changes. 2026-07-13 15:12:54 -04:00
e2d8dee1a6 Refactor OCR canvas processing to enhance contrast and improve cropping logic for video and image sources. Introduce a new method for capturing frames from video sessions, prioritizing ImageCapture for better clarity on mobile devices. Increment version code to 4.72.797 to reflect these changes. 2026-07-13 15:01:25 -04:00
e7662a304e Enhance OCR functionality by improving number extraction logic to handle various formats and ensuring better accuracy. Introduce a new method for preprocessing the OCR canvas to enhance contrast for better recognition. Update video frame capture to consider the visible rectangle for improved cropping. Increment version code to 4.72.796 to reflect these changes. 2026-07-13 14:17:30 -04:00
7efc81235d Update CSS styles for mobile scanning interface to improve button dimensions and positioning, ensuring better visibility and user experience. Introduce a new method in JavaScript to center the capture view within the viewport, enhancing usability during scanning. Increment version code to 4.72.795 to reflect these changes. 2026-07-13 14:07:53 -04:00
8efbb4c4a7 Update session handling in AJAX responses to improve user experience. Modify error messages to include session codes instead of generic messages, prompting a page reload for expired sessions. Increment version code to 4.72.794 to reflect these changes. 2026-07-13 14:04:10 -04:00
d3b9fab7ce Update OCR settings to enhance number recognition accuracy by increasing maximum width. Adjust CSS styles for button dimensions and positioning, improving user interface consistency. Increment version code to 4.72.793 to reflect these changes. 2026-07-13 14:01:32 -04:00
93378a5a2d Adjust video frame capture dimensions and button size for improved OCR alignment. Update related comments and increment version code to 4.72.792 to reflect these changes. 2026-07-13 13:46:01 -04:00
5fefdccd17 Update video frame capture dimensions to improve OCR accuracy and adjust related styles. Increase button size to 70% width and 40% height for better alignment with the OCR reading area. Increment version code to 4.72.791 to reflect these changes. 2026-07-13 13:43:06 -04:00
a18f25dc84 Enhance video frame capture functionality by introducing a new method to calculate the visible rectangle for object-fit:cover. Update the captureCenterFrame function to utilize this new method, improving cropping accuracy based on the display element. Increment version code to 4.72.790 to reflect these changes. 2026-07-13 13:41:13 -04:00
8a9369bbaf Update styles for mobile scanning interface and enhance user guidance. Adjust button dimensions and positioning for better visibility. Improve hint messages for scanning instructions and update version code to 4.72.789, aligning with MSIN-4401 requirements for enhanced event management functionality. 2026-07-13 13:38:20 -04:00
397eb4651b Refactor account link rendering in mobile interface to improve user navigation. Move the full account link functionality to inc_header.php for better organization and visibility control. Update version code to 4.72.788, aligning with MSIN-4401 requirements for enhanced event management functionality. 2026-07-13 13:35:10 -04:00
ec15b07f21 Add functionality to render a full account link in the mobile interface for users with extended permissions. Implement new functions to determine visibility and URL for the link, enhancing user navigation to the dashboard or account page. Update styles for the new button and increment version code to 4.72.787, aligning with MSIN-4401 requirements for improved event management functionality. 2026-07-13 13:19:08 -04:00
2970007d2b Enhance email invitation functionality in fxEveAccesPromoteurSendInviteMail to include a mobile access link for users. Update message content to guide users on accessing their accounts via mobile, improving user experience and accessibility. This change aligns with MSIN-4401 requirements for better event management functionality. 2026-07-13 13:15:01 -04:00
ee9d5943c9 Refactor fxAdminCanEditEveAccesKits function to utilize a global array for access control in kit management. Move the list of editors to inc_settings.php for better organization and maintainability. This change aligns with MSIN-4401 requirements for improved event management functionality. 2026-07-13 12:36:52 -04:00
f232a862ac Refactor fxAdminCanEditEveAccesKits function for improved access control in kit management. Move the function definition to inc_functions.php to ensure it is available for super admin without loading the entire inc_fonctions_sl.php file. Update logic to validate user permissions based on session information, aligning with MSIN-4401 requirements for enhanced event management functionality. 2026-07-13 12:34:38 -04:00
0fcb7dc9f8 Add access control for kit management in event administration. Introduce the function fxAdminCanEditEveAccesKits to validate user permissions based on email or login. Update event access page to restrict modification actions and enhance UI feedback for non-editable states, aligning with MSIN-4401 requirements for improved event management functionality. 2026-07-13 12:31:22 -04:00
ccf895c8fb Update UI components for improved accessibility and readability in event management. Add styling for access keys and codes, ensuring better visual distinction. Refactor HTML structure in the super admin panel for enhanced layout consistency. Increment version code to 4.72.786. 2026-07-13 12:25:36 -04:00
95aa7c9cf5 Add functionality to manage 'sections ouvertes' in event registration. Implement checks for the presence of the 'role_fiche_sections_open' column and update related functions to ensure sections are opened based on user roles. Enhance UI to reflect these changes in the registration management interface, aligning with MSIN-4401 requirements for improved event management functionality. 2026-07-13 12:19:05 -04:00
9ec4fd9ead Implement multi-selection filter for race status in the registration management interface. Enhance UI with a mobile-friendly design for better user interaction. Update backend logic to handle selected statuses and ensure proper filtering in requests. This aligns with MSIN-4401 requirements for improved event management functionality. 2026-07-13 11:52:58 -04:00
dd33cb6873 Implement access control for dashboard viewing in promoteur hub. Introduce permission checks to restrict access based on user roles, enhancing security and user experience. Update event panel rendering logic to conditionally display features based on available permissions, aligning with MSIN-4401 requirements for improved event management functionality. 2026-07-13 11:43:54 -04:00
4de952967d Refactor fxPromoteurHubGetEventLinks to enhance event access management by distinguishing between legacy and V2 permissions. Update documentation to clarify menu access based on user roles, ensuring a more intuitive experience for super admins and promoters. This aligns with MSIN-4401 requirements for improved event management functionality. 2026-07-13 11:41:25 -04:00
5b87d02d9e Refactor event access management to support V2 features, updating permission checks and enhancing UI elements for better user experience. Remove legacy API keys and streamline access permissions in fxEveAcces functions. This aligns with MSIN-4401 requirements for improved event management functionality. 2026-07-13 11:36:30 -04:00
7d08e7ec7c Refactor account management functionality to enhance password reset process. Exclude 'compte_reset' from session checks in compte.php to prevent conflicts with active sessions. Implement safe return link handling after logout in inc_fx_compte.php, ensuring users can navigate back to the reset page securely. Add user notifications for active sessions during password reset attempts, aligning with MSIN-4401 requirements for improved user experience. 2026-07-13 11:19:13 -04:00
4816bcf024 Enhance password reset functionality by updating the SQL query to clear the reset token upon successful password change. Introduce a new function to activate invite status for accounts after password creation, ensuring a smoother transition from invite to active status. Update localization keys for account status to improve user feedback. This aligns with MSIN-4401 requirements for enhanced event management functionality. 2026-07-13 10:41:35 -04:00
37dd64e4c4 Update localization keys for existing account messages in fxEveAccesPromoteurT function to simplify feedback for users. This change enhances clarity in user notifications regarding account status, aligning with ongoing improvements in event management functionality. 2026-07-13 10:19:03 -04:00
29af6c75cf Enhance event access invitation form with AJAX email verification and improved feedback handling. Add new localization keys for account existence checks and update UI elements for better user experience. This aligns with MSIN-4401 requirements for streamlined event management functionality. 2026-07-13 10:16:12 -04:00
591d93d699 Implement event access invitation and revocation handling in compte.php, ensuring proper processing before HTML output to prevent blank pages. Update inc_tableau_eve_acces.php to reflect new error handling and success messaging. Enhance fxEveAccesPromoteurInvite function to handle mail sending failures gracefully. This aligns with MSIN-4401 requirements for improved event management functionality. 2026-07-13 10:11:19 -04:00
5cd00a1ff1 Enhance event access management for promoters by adding invitation and revocation functionalities. Update the UI to handle success and error messages, and improve session handling for actor identification. This aligns with MSIN-4401 requirements for a more robust event management experience. 2026-07-13 10:08:25 -04:00
fdf34afcd3 Refactor promoteur hub integration to support V2 features, including new dashboard links and menu updates. Remove legacy event access checks and streamline menu display for improved user experience. This aligns with MSIN-4401 requirements for a more cohesive event management interface. 2026-07-13 10:02:51 -04:00
c1d730ac98 Refactor hub promoteur access management to support V2 only, removing legacy event handling. Update related functions and documentation to reflect changes in event access permissions and menu integration. This aligns with MSIN-4401 requirements for improved event management. 2026-07-13 09:57:29 -04:00
5f73db0a59 MSIN-4401 Implement event access management features, including team invitation capabilities and menu integration
This commit introduces functions for managing event access, allowing promoters to invite team members and manage permissions. The `fxEveAccesCanTeamInvite` function is added to check if a user can invite others, and the UI is updated to include access management options in the navigation. Additionally, relevant includes are added to ensure the necessary functions are available. These changes enhance the event management experience for promoters.
2026-07-13 09:49:12 -04:00
54a7e1dae9 Add logo path function and enhance logo image creation for QR key chains
This commit introduces a new function to define the logo path for QR key chains and enhances the image creation process by adding functionality to load, resize, and center the logo within a specified size. If the logo file is missing, a fallback to a text drawing is implemented. These changes improve the visual presentation of QR codes with logos.
2026-07-11 18:53:02 -04:00
33706bb195 Implement global batch completion UI, enhance JavaScript handling, and increment version code to 4.72.784
This commit introduces a new user interface for displaying the completion status of global batch assignments, including success, warning, and error states. The CSS is updated to style the completion messages appropriately. Additionally, JavaScript functions are added to manage the display of these messages based on assignment results. Localization keys are also added for improved user feedback. The version code is incremented to 4.72.784 to reflect these changes.
2026-07-09 12:48:02 -04:00
9e8055bfee Refactor global simulation view handling, enhance styles, and increment version code to 4.72.783
This commit refines the rendering of the global simulation view by introducing a new wrapper function for better structure and maintainability. The CSS is updated to improve the layout and appearance of the simulation toolbar and title. Additionally, JavaScript functions are added to manage the insertion and removal of global simulation views. The version code is incremented to 4.72.783 to reflect these changes.
2026-07-09 12:30:17 -04:00
4fefc8c053 Refactor global batch section handling in JavaScript, enhance CSS for collapsible sections, and increment version code to 4.72.782
This commit refines the JavaScript functions for managing global batch sections, improving the retrieval of section bodies and ensuring proper toggling functionality. The CSS is updated to support collapsible sections, allowing for better organization of content. Additionally, the version code is incremented to 4.72.782 to reflect these changes.
2026-07-09 12:23:11 -04:00
2aa398b1f0 Implement collapsible sections for global batch assignments, enhance styles, and increment version code to 4.72.781
This commit introduces collapsible sections for global batch assignments in the user interface, improving organization and user experience. The CSS is updated to style these sections, including headers and content areas, while the JavaScript is modified to handle the toggling functionality. Additionally, the version code is incremented to 4.72.781 to reflect these changes.
2026-07-09 12:20:08 -04:00
91ee36c121 Add batch global analysis, simulation, and creation functionalities; update styles and localization, increment version code to 4.72.780
This commit introduces new actions for batch global analysis, simulation, and creation without existing sequences. The JavaScript and PHP files are updated to handle these new functionalities, including validation and response handling. CSS styles are enhanced for better layout and user experience. Localization keys are added for improved clarity in user messages. The version code is incremented to 4.72.780 to reflect these changes.
2026-07-09 12:15:04 -04:00
dc55a855c3 Enhance anomaly block styles and functionality, increment version code to 4.72.779
This commit introduces new styles for the anomaly blocks, including collapsible sections and improved padding. The JavaScript is updated to handle click and keyboard events for toggling these sections, enhancing user interaction. The version code is incremented to 4.72.779 to reflect these changes.
2026-07-09 11:44:15 -04:00
1f96cce839 Update CSS styles for bib tool, enhance JavaScript tippy configuration, and increment version code to 4.72.778
This commit modifies the CSS to change overflow properties for menu items and adds new styles for help button text visibility. The JavaScript is updated to include z-index and appendTo properties for tippy tooltips, improving their display. The version code is incremented to 4.72.778 to reflect these changes.
2026-07-09 11:40:11 -04:00
8b5968e1da Update styles for bib tool buttons and detail panel, increment version code to 4.72.777
This commit enhances the CSS for the bib tool buttons, improving hover and active states with new color and box-shadow effects. The detail panel styles are also updated for better visual consistency. The version code is incremented to 4.72.777 to reflect these changes.
2026-07-09 11:34:02 -04:00
9d2223af3b Add view range button functionality and update styles for bib module, increment version code to 4.72.776
This commit introduces a new function to render a view range button for the bib module, enhancing the user interface with improved styling for the button and its container. The CSS is updated to ensure proper alignment and spacing. The version code is incremented to 4.72.776 to reflect these changes.
2026-07-09 11:29:32 -04:00
afdd50489f Update bib quantity summary panel to reflect new totals and improve localization, increment version code to 4.72.776
This commit modifies the rendering logic of the bib quantity summary panel to include updated totals for 'sans_bib', 'places_dispo', and 'avec_bib'. The localization keys are adjusted for better clarity, and unnecessary elements are removed to streamline the display. The version code is incremented to 4.72.776 to reflect these changes.
2026-07-09 11:26:02 -04:00
b85b01f2d2 Refactor registration management styles and remove unused filter section, update version code to 4.72.775
This commit cleans up the CSS by removing unnecessary styles related to the registration management form and eliminates the filter subsection from the PHP rendering function. The version code is incremented to 4.72.775 to reflect these changes.
2026-07-09 11:21:01 -04:00
fdecff66d5 Refactor registration management styles and enhance AJAX handling, update version code to 4.72.773
This commit refines the CSS styles for the registration management form, adjusting margins and padding for better layout. It also updates the JavaScript to include the epr_id in the delete action, improving the handling of AJAX requests. Additionally, the version code is incremented to 4.72.773 to reflect these changes.
2026-07-09 11:16:12 -04:00
e5f8ca953e Enhance registration management UI with new styles and localization, update version code to 4.72.772
This commit introduces new CSS styles for the registration management form, improving the visual structure and user experience. A new subsection for filters is added, along with a localized label for cancelled registrations. The version code is incremented to 4.72.772 to reflect these updates.
2026-07-09 11:11:03 -04:00
0d01affb89 Refactor cancellation cause handling and localization for orphan bibs, update version code to 4.72.773
This commit simplifies the cancellation cause logic by removing references to 'modif' and updating related localization keys to focus on 'annule' and 'transfert'. CSS comments and SQL scripts are also adjusted to reflect these changes, enhancing clarity and consistency in the user interface. The version code is incremented to 4.72.773 to reflect these updates.
2026-07-09 10:47:53 -04:00
ee7e463d36 Enhance orphan bib handling and localization, update version code to 4.72.772
This commit introduces new localization keys for orphan bib statuses, improving user feedback on cancellation and transfer scenarios. The logic for identifying orphan bibs is refined, ensuring accurate classification based on cancellation reasons. Additionally, a new function is added to standardize the cause key for orphan bibs. The version code is incremented to 4.72.772 to reflect these updates.
2026-07-09 10:24:10 -04:00
ea52e72086 Update anomaly navigation badge functionality and localization, increment version code to 4.72.771
This commit enhances the anomaly navigation badge by updating the label format to include a count of items to review. The JavaScript and PHP files are modified to support this new format, improving user feedback on the number of anomalies. Additionally, new localization keys are added for the formatted label in both French and English. The version code is incremented to 4.72.771 to reflect these updates.
2026-07-08 15:04:30 -04:00
6dd1ca3aad Enhance bib tool styles and functionality, increment version code to 4.72.770
This commit introduces new CSS styles for the bib tool buttons and detail panel, improving their visual presentation and interactivity. It also adds functionality for displaying anomaly navigation badges, enhancing user experience. The version code is incremented to 4.72.770 to reflect these updates.
2026-07-08 14:55:53 -04:00
885dcf9b8f Update CSS styles for bib tool buttons and detail panel, increment version code to 4.72.767
This commit enhances the visual presentation of the bib tool buttons and detail panel by introducing new styles for various button states and backgrounds. The CSS updates improve usability and aesthetics, particularly for the help buttons and detail sections. The version code is incremented to 4.72.767 to reflect these changes.
2026-07-08 14:48:45 -04:00
545cbe5dd4 Enhance bib tool navigation and detail functionality, update version code to 4.72.766
This commit refines the bib tool navigation layout by implementing a grid-based structure and adding a detail panel for tool descriptions. New CSS styles improve the visual presentation and responsiveness of the tool buttons. Additionally, JavaScript functions are introduced to manage tool detail titles and keyboard interactions, enhancing user experience. The version code is incremented to 4.72.766 to reflect these updates.
2026-07-08 14:46:25 -04:00
74700fcc19 Implement new bib tool navigation layout and detail panel (MSIN-4440), update version code to 4.72.765
This commit introduces a redesigned navigation layout for the bib management tools, featuring a grid-based button arrangement and a unified detail panel for tool content. New CSS styles are added for improved visual presentation and responsiveness. Additionally, the version code is incremented to 4.72.765 to reflect these updates.
2026-07-08 14:41:17 -04:00
1b76f0fece Refactor bib tool layout and remove unused SQL file, update version code to 4.72.764
This commit streamlines the bib management interface by removing unnecessary CSS styles and PHP functions related to the previous layout. The SQL file for visual zones is deleted as it is no longer needed. The version code is incremented to 4.72.764 to reflect these updates.
2026-07-08 12:21:34 -04:00
f47f472aaa Refactor bib tool layout for improved UI and update version code to 4.72.763
This commit enhances the bib management interface by restructuring the layout of tool sections into a more compact and organized format. New CSS styles are introduced for better spacing and alignment, and a new function is added to render tool group rails. The version code is incremented to 4.72.763 to reflect these updates.
2026-07-08 12:19:41 -04:00
bfac5a92cc Enhance bib management UI with new sections and update version code to 4.72.762
This commit introduces several new sections in the bib management interface, including configuration, monitoring, reports, actions, and races, each with updated titles and structured layouts. CSS styles are adjusted for better spacing and presentation. Additionally, new language keys are added for improved localization. The version code is incremented to 4.72.762 to reflect these updates.
2026-07-08 12:13:56 -04:00
23ca040ac0 Enhance participant data handling in PDF distribution and update version code to 4.72.761
This commit adds the original participant ID to the selection for better data integrity and introduces a new function to generate a key for mapping participant responses. The SQL queries are updated to reflect these changes, ensuring compatibility with legacy data structures. Additionally, the version code is incremented to 4.72.761 to reflect these updates.
2026-07-08 12:07:54 -04:00
21b28a0d06 Implement PDF distribution panel for bib management (MSIN-4433)
This commit introduces a new feature for generating and managing PDF distributions of bibs. It adds a new AJAX action to handle requests for the distribution panel, along with corresponding JavaScript functions to refresh and initialize the panel. CSS styles are updated to enhance the layout of the distribution panel, and new PHP functions are added to support the generation and metadata handling for the PDF prints. The version code is incremented to 4.72.759 to reflect these changes.
2026-07-08 12:00:58 -04:00
76533287dd Implement contextual return functionality for bib management and enhance UI interactions
This commit introduces several enhancements related to the bib management system, specifically focusing on contextual return paths from various panels. New functions are added to encode and decode return URLs, allowing users to navigate back to specific sections (e.g., anomalies, quantity summary) seamlessly. Additionally, JavaScript functions are updated to manage UI interactions, such as expanding panels based on URL parameters. The overall user experience is improved by ensuring that the correct panels are focused and displayed upon return. This change is part of the ongoing efforts to enhance the bib assignment management system (MSIN-4436).
2026-07-08 11:34:31 -04:00
dbffb288d6 Add bib anomaly registration link and update styles for anomaly details
This commit introduces a new feature to display a registration link for bib anomalies, enhancing user navigation. It includes new CSS styles for better layout and presentation of anomaly details. Additionally, the PHP functions are updated to support the rendering of the registration link, improving the overall functionality of the bib anomaly management system.
2026-07-08 11:26:19 -04:00
17611a9009 Update team summary display in bib event management and increment version code to 4.72.758
This commit modifies the team summary feature to correctly display the unit count for teams by changing the translation parameter. Additionally, the version code is updated to reflect this change.
2026-07-08 11:18:19 -04:00
90c126a014 Update team summary feature in bib event management and increment version code to 4.72.757
This commit enhances the team summary display by adding support for multiple teams in the CSS and PHP functions. The previous team stack styles are removed in favor of a more streamlined approach. The version code is updated to reflect these changes.
2026-07-08 11:13:57 -04:00
cfffe77c93 Implement team summary feature in bib event management and update version code to 4.72.756
This commit introduces a new feature to display the number of active teams for team-based events. It includes CSS styles for the team summary layout and updates to the PHP functions to retrieve and render team data. The version code is incremented to reflect these changes.
2026-07-08 11:11:47 -04:00
588d573703 Refactor batch sorting functionality and update version code to 4.72.755
This commit enhances the batch sorting mechanism by introducing new functions to normalize and manage sorting keys for bib assignments. The AJAX handlers are updated to utilize these new sorting parameters, improving the overall flexibility and accuracy of batch operations. Additionally, the version code is incremented to reflect these changes.
2026-07-08 11:05:00 -04:00
7d342216f8 Enhance bib event configuration with inter-epreuve option and update version code to 4.72.754
This commit introduces a new feature allowing the same bib numbers across different epreuves, enhancing event management flexibility. It includes updates to the CSS for improved layout, JavaScript functions to manage visibility of configuration notes, and new database entries for the inter-epreuve setting. The version code is incremented to reflect these changes.
2026-07-08 10:32:03 -04:00
5184544d6f Implement quantity summary panel and global configuration settings for bib assignments (MSIN-4424)
This commit introduces a new AJAX action for retrieving a summary of quantities related to bib assignments, enhancing event management capabilities. A collapsible configuration panel is added for global settings, allowing users to manage bib assignment options more effectively. The CSS is updated to style the new elements, and relevant database entries are created to support the new features. The version code is incremented to 4.72.751 to reflect these changes.
2026-07-08 10:26:52 -04:00
70f4dac177 MSIN-4424 Implement inter-epreuve duplicate handling and update version code to 4.72.750
This commit introduces functionality to allow the same bib numbers across different epreuves, enhancing the flexibility of event management. A new AJAX action, `save_allow_inter_epr`, is added to manage this setting, along with corresponding JavaScript to handle user interactions. The database functions are updated to support this feature, ensuring proper validation and anomaly reporting. The version code is incremented to reflect these changes.
2026-07-08 10:17:15 -04:00
a7e027177c Enhance bib range handling and update version code to 4.72.749
This commit introduces a new parameter to the `fxInfosBibRange` function to conditionally include gaps in the statistics calculation, improving data accuracy. Additionally, the AJAX handler is updated to support a 'lite' mode for optimized data retrieval. JavaScript functions are modified to pass this new option, enhancing the overall functionality of the bib assignments. The version code is incremented to reflect these changes.
2026-07-08 10:04:02 -04:00
c2972aa015 Implement reset all apply functionality for epreuve and update version code to 4.72.747
This commit introduces a new action, `reset_all_apply`, in the AJAX handler to reset all bib numbers for participants in a specified epreuve. It includes validation for locked ranges and updates the database accordingly. Additionally, new CSS styles are added for the reset button, and JavaScript functions are implemented to manage the button's state based on participant conditions. The version code is incremented to reflect these changes.
2026-07-08 09:57:49 -04:00
4cf2ef98d0 Enhance fxPromoteurHubGetEventLinks function to conditionally load access management and update version code to 4.72.746
This commit modifies the `fxPromoteurHubGetEventLinks` function to include a conditional check for the existence of the `fxInscrGestionCanAccess` function. If it does not exist, the `inc_fx_inscriptions_gestion.php` file is loaded to evaluate access rights. This change improves the modularity and functionality of the promoteur hub by ensuring that access management is properly handled in AJAX contexts.
2026-07-08 09:50:09 -04:00
1322511cfd Refactor identity header styles and update version code to 4.72.745
This commit refines the CSS for the identity header, adjusting alignment, padding, and gap properties for better layout and user interaction. The toolbar is restructured to enhance the visual hierarchy, and the version code is incremented to reflect these updates.
2026-07-08 09:46:44 -04:00
ed3ca4e6ea Refactor CSS styles for identity panel and update version code to 4.72.744
This commit modifies the CSS for the identity panel, changing alignment properties and adjusting gaps for improved layout. Additionally, a new toolbar is introduced for the identity header, enhancing user interaction with a button for viewing ranges. The version code is incremented to reflect these updates.
2026-07-08 09:42:33 -04:00
75f17f5846 Enhance fxInfosBibRange function to include gap handling and update version code to 4.72.743
This commit modifies the `fxInfosBibRange` function to add a new parameter for including gaps in the statistics calculation. The logic for generating SQL queries is updated to conditionally include or exclude gaps based on this parameter, improving the accuracy of the statistics retrieved. Additionally, the version code is incremented to reflect these changes.
2026-07-08 09:35:38 -04:00
f13a5d3c01 Implement global batch panel functionality with deferred loading and increment version code to 4.72.742
This commit introduces a new action for the global batch panel in the AJAX handler, allowing for deferred loading of the panel upon user interaction. The JavaScript is updated to manage the loading state and ensure the panel is populated with relevant data when opened. Additionally, a new rendering function is added to handle the display of the global batch panel shell, improving user experience by ensuring that the panel only loads when necessary. The version code is incremented to reflect these enhancements.
2026-07-08 09:31:03 -04:00
6d3fd04232 Implement preloader enhancements for quantity management and increment version code to 4.72.741
This commit introduces a loading overlay for heavy page loads in the quantity management section, improving user experience during data retrieval. Additionally, a loader is displayed upon clicking specific links to enhance feedback while waiting for server responses. Minor adjustments are made to the JavaScript handling of anomalies, ensuring smoother interactions. The version code is incremented to reflect these updates.
2026-07-08 09:27:01 -04:00
47a2b051ba IMSIN-4425mplement quantity management enhancements in bib assignments and increment version code to 4.72.740
This commit introduces new JavaScript functions for managing quantity data in the bib assignments, including `bibApplyQteData` for updating UI elements and `bibSaveQte` for handling quantity submissions via AJAX. Additionally, the PHP file is updated to render a new panel for anomalies, improving the overall functionality and user experience. The version code is incremented to reflect these changes, ensuring proper versioning in the application.
2026-07-08 09:25:44 -04:00
40625572e6 Refactor range locking function and increment version code to 4.72.739
This commit updates the function name from `fxBibIsRangeLocked` to `fxBibRangeIsLocked` for improved clarity and consistency in the codebase. Additionally, the version code is incremented to reflect this change, ensuring proper versioning in the application.
2026-07-08 09:15:21 -04:00
fde81a3985 Update localized messages in fxBibStaticFallback and increment version code to 4.72.738
This commit modifies the French translation for the global batch assignment message in the `fxBibStaticFallback` function to ensure proper formatting. Additionally, the version code is updated to reflect these changes, maintaining consistency in the application versioning.
2026-07-08 09:11:31 -04:00
da8fc64458 MSIN-2365 Implement global batch assignment enhancements with improved error handling and UI feedback
This commit introduces several updates to the global batch assignment functionality, including new CSS styles for unavailable states, enhanced JavaScript functions for batch processing, and improved error handling in AJAX requests. Language support is expanded with new localized messages for user guidance. The version code is incremented to reflect these changes, aiming to enhance user experience and maintainability in the bib management system.
2026-07-08 09:09:25 -04:00
d535f3d9c5 Enhance AJAX promoteur hub functionality with improved language handling and text retrieval
This commit updates the `ajax_promoteur_hub.php` and `inc_fx_promoteur_hub.php` files to improve language handling and text retrieval for the promoteur hub. The language is now determined based on session data, with a fallback to French if the specified language is invalid. New functions are introduced to manage text retrieval from the database, ensuring that legacy entries from `ajax_promoteur_hub.php` are handled correctly. Additionally, the text retrieval logic is streamlined to enhance maintainability and user experience across the promoteur hub.
2026-07-08 09:02:43 -04:00
43750fb8f5 MSIN-2365 Implement batch global analysis and simulation for solo events
This commit introduces two new actions, `batch_global_analysis` and `batch_global_simulate`, to handle batch processing for solo events in the bib management system. The PHP file `ajax_bib_range.php` is updated to include logic for analyzing and simulating batch assignments, enhancing user feedback with detailed statistics. Additionally, new CSS styles are added to improve the presentation of the global batch interface, and JavaScript functions are implemented to facilitate the collection and submission of batch plans. These changes aim to streamline the batch processing workflow and improve user experience in managing solo event assignments.
2026-07-08 08:59:41 -04:00
4522ddde8d Enhance promoteur hub functionality with AJAX loading and access control improvements
This commit introduces significant updates to the promoteur hub, including the addition of a new JavaScript file for AJAX handling of event statistics and links. The PHP file `ajax_promoteur_hub.php` is modified to support new actions for fetching event statistics and links, improving the user experience with better error handling and loading indicators. Additionally, a new batch access control function is implemented in `inc_fx_promoteur_hub.php` to streamline permission checks for events. The version code is incremented to reflect these enhancements, aiming to improve functionality and maintainability across the promoteur hub.
2026-07-08 08:48:23 -04:00
dd4a83043a 4436 Add payment session handling and mode checks in inc_fonctions.php and inc_fx_panier_js.php
This commit introduces new functions for managing payment session data, including `fxPaiementSessionComId`, `fxPaiementCanUseModesPromoteur`, and `fxPaiementCanUseModesAdmin`. These functions enhance the payment processing logic by determining the appropriate payment modes based on user roles (promoteur or admin). The existing payment display functions in `inc_fx_panier_js.php` are updated to utilize these new functions, improving the clarity and maintainability of the payment handling process. These changes aim to enhance user experience and ensure proper access control for payment options.
2026-07-07 16:09:11 -04:00
96765aac84 MSIN-4435 Add inc_v2_assets.php integration across multiple files
This commit integrates the `inc_v2_assets.php` file into several PHP scripts, including `compte.php`, `inc_footer.php`, `inc_tableau_gestion_epreuves.php`, `inc_tableau_inscriptions_gestion.php`, and `mobile.php`. Additionally, it registers new scripts for improved functionality in the `inscriptions gestion` context. The version code is incremented to reflect these updates, aiming to enhance asset management and script handling across the application.
2026-07-07 15:27:33 -04:00
b3e3c72be5 Implement batch processing enhancements for bib management system
This commit introduces a new action, `batch_apply_chunk`, to facilitate chunked processing of batch assignments in the bib management system. It enhances the AJAX handling for batch operations, allowing for better progress tracking and user feedback during the assignment process. Additionally, new JavaScript functions are added to manage batch progress display, and CSS styles are updated to support the new loading indicators. The version code is incremented to reflect these changes, aiming to improve user experience and efficiency in batch processing.
2026-07-07 15:16:23 -04:00
d90df7c46f Refactor bib range validation and error handling in management system
This commit enhances the JavaScript functions for bib range validation by introducing new error handling mechanisms and improving user feedback. The previous preview functionality has been removed, and error messages are now displayed more effectively. CSS styles are updated to support the new error message display. Additionally, the version code is incremented to reflect these changes, aiming to improve user experience and data integrity in the bib input process.
2026-07-07 15:04:27 -04:00
20c1f898a1 Enhance bib range validation and user feedback in management system
This commit introduces new JavaScript functions for validating range inputs for quantity and end values in the bib management system. It adds visual feedback for invalid fields and updates the preview display based on user input. CSS styles are also added to highlight invalid fields and error messages. Additionally, new SQL entries are created for localized validation messages, improving user guidance. The version code is incremented to reflect these enhancements, aiming to improve user experience and data integrity in the bib input process.
2026-07-07 15:01:55 -04:00
666c4ad180 Update CSS and PHP for bib management enhancements
This commit introduces several updates to the CSS and PHP files related to the bib management system. Key changes include the addition of overflow handling and layout adjustments in the CSS to improve the display of bib containers and lines. The PHP function for rendering the range lock button is modified to include a new wrapper div for better structure. Additionally, the version code is incremented to reflect these updates. These changes aim to enhance the user interface and maintainability of the bib management features.
2026-07-07 14:55:25 -04:00
eca8b34d88 Update bib management for MSIN-4429: Refine quantity and end input handling
This commit enhances the bib management system by updating the CSS layout for quantity and end inputs, ensuring proper alignment and display. It also modifies PHP functions to introduce a new column for the "or" separator between quantity and end values, improving the user interface. Additionally, new SQL entries are added to support the updated functionality, including localized labels and hints for user guidance. These changes aim to improve usability and clarity in the bib input process.
2026-07-07 14:53:02 -04:00
ec024e6fba MSIN-4429 Implement range input functionality for quantity and end values in bib management
This commit introduces new JavaScript functions and PHP methods to enhance the handling of range inputs for quantity and end values in the bib management system. It includes the addition of input fields for quantity and end values, along with logic to manage their states based on user interaction. The CSS is updated to support the new layout and visual states for these fields. Additionally, data attributes are added to improve the handling of range validation and preview functionality. These changes aim to improve user experience and data integrity in the bib input process.
2026-07-07 14:49:57 -04:00
f35d651ffc MSIN-4432
Update participant statistics queries in fxInfosBibRange and fxInfosBibEpreuve functions

This commit modifies the SQL queries in the `fxInfosBibRange` and `fxInfosBibEpreuve` functions to improve the accuracy of participant statistics. The `nb_utilises` count now reflects distinct bib numbers, ensuring correct tallying for teams sharing bibs. Additionally, the logic for fetching event statistics is refined to handle team configurations more effectively, enhancing the overall data retrieval process. These changes aim to improve data integrity and clarity in participant reporting.
2026-07-07 14:40:21 -04:00
2aed8052d0 Enhance loading indicators and AJAX handling in promoteur hub
This commit updates the loading mechanism for event statistics in the promoteur hub by introducing a new inline loader with an image and message. It also improves the AJAX loading process by adding a wait message and ensuring the loader is displayed during data fetching. Additionally, the error handling for AJAX requests is refined to hide the loader upon completion, enhancing user experience and clarity in the loading process.
2026-07-07 14:33:57 -04:00
2570785030 Refactor account tracing and session handling in compte.php and related files
This commit removes the `fxTraceCompte` functionality from various locations in `compte.php`, `inc_fx_compte.php`, and other related files to streamline the account management flow. The tracing logic is eliminated to enhance performance and reduce complexity. Additionally, the session handling for login redirection is improved by simplifying the logic and removing unnecessary function calls. These changes aim to improve maintainability and clarity in the account management system.
2026-07-07 14:30:53 -04:00
c7482016ce Implement superadmin login redirection and session handling improvements
This commit introduces a new function, `fxSuperadmRedirectToLoginIfNeeded`, to streamline the login redirection process for superadmin users. It enhances session management by checking for user authentication and allowing legacy account access under specific conditions. Additionally, the login flow is updated to ensure users are redirected appropriately after login, improving the overall security and user experience in the superadmin interface. These changes aim to enhance maintainability and clarity in session handling across various superadmin pages.
2026-07-07 14:15:48 -04:00
20ab5a8f08 Add event stats rendering and AJAX loading for promoteur hub 2026-07-07 13:48:59 -04:00
29c411a23c Implement enhanced tracing functionality in inc_fx_trace_compte.php and integrate bootstrap call in inc_start_time.php
This commit introduces new functions for logging and host validation in the account tracing system, improving the ability to track user actions in development and pre-production environments. Additionally, it adds a bootstrap function call in inc_start_time.php to initialize tracing early in the request lifecycle. These changes aim to enhance debugging capabilities and ensure reliable logging across different environments.
2026-07-07 13:32:25 -04:00
eeb08bc563 Enhance account tracing functionality and error handling in inc_fx_trace_compte.php
This commit updates the account tracing logic by adding a check for both development and pre-production environments to determine if tracing is enabled. Additionally, it introduces a fallback error logging mechanism to capture log entries when the log file is not writable, improving the robustness of the tracing functionality. These changes aim to enhance debugging capabilities and ensure reliable logging in various environments.
2026-07-07 13:28:10 -04:00
316583d108 Enhance account tracing and redirect logic in compte.php and inc_fx_compte.php
This commit introduces comprehensive tracing functionality to the account management flow by adding calls to `fxTraceCompte` and `fxTraceCompteRedirect` at key points in the login and routing processes. The changes improve the tracking of user actions and session states, facilitating better debugging and monitoring of account-related activities. Additionally, the promoteur hub event retrieval function is updated to include tracing, enhancing visibility into hub interactions. These updates aim to improve maintainability and user experience in the account management system.
2026-07-07 13:24:49 -04:00
fb278bfdd6 Refactor session handling and redirect logic for login flow
This commit enhances the session management and redirect behavior in the login process. It introduces a new function, `fxCompteRedirectAfterLoginConsume`, to streamline the handling of post-login redirects while preventing loops for login-related pages. Additionally, comments are updated for clarity regarding the session's role in protecting access to certain pages. These changes aim to improve user experience and maintainability of the authentication flow.
2026-07-07 10:57:03 -04:00
138448212e Refactor promoteur hub access logic and update comments for clarity
This commit simplifies the `fxPromoteurHubShouldUse` function by directly returning the result of `fxPromoteurHubIsAvailable`, enhancing code readability. Additionally, comments in `compte.php` are updated to clarify the landing behavior for the promoteur hub and participant home, improving understanding of the session handling logic. These changes aim to streamline the promoteur hub functionality and improve maintainability.
2026-07-07 10:50:57 -04:00
762ac838b6 Refactor promoteur hub logic and enhance session handling
This commit updates the promoteur hub functionality by introducing a new function, `fxPromoteurHubIsAvailable`, to streamline the logic for determining hub access. The existing `fxPromoteurHubShouldUse` function is modified to incorporate this new check, improving clarity and maintainability. Additionally, comments are added to clarify session handling in the `fxLoginCompte` function, ensuring better understanding of public login behavior. These changes aim to enhance the robustness of the promoteur hub interface and improve code readability.
2026-07-07 10:45:03 -04:00
93cb9f5e81 Refactor authentication logic in superadmin interface and streamline session handling
This commit updates the authentication process in `index.php` and `rapport_new.php` by introducing new functions for checking user permissions, specifically `fxSuperadmIsLoggedIn` and `fxSuperadmReportsAllowed`. The session handling is simplified, removing redundant checks and enhancing clarity. Additionally, the memory limit setting in `rapport_new.php` is adjusted for consistency. These changes aim to improve the security and maintainability of the superadmin interface.
2026-07-06 19:47:51 -04:00
4c9725b282 Enhance error handling and user feedback in static synchronization process
This commit updates the connection timeout for MySQL operations and introduces new functions for managing connection errors and flash messages. The `fxStaticSyncConnectError` and `fxStaticSyncFormatConnectionError` functions provide clearer error reporting during synchronization. Additionally, the user interface is improved to display relevant flash messages, enhancing the overall user experience and robustness of the synchronization workflow.
2026-07-06 15:38:32 -04:00
a8274dd640 Update key column references and enhance error handling in static synchronization functions
This commit modifies key column references in the `fxStaticSyncTableDefinitions` function to align with updated database schema identifiers. Additionally, it introduces a new utility function, `fxStaticSyncKeyColumnsValid`, to validate key columns before executing queries, improving error handling in the `fxStaticSyncFetchRowsByKey` and `fxStaticSyncLoadKeyedRows` functions. The changes aim to enhance the robustness of the synchronization process and provide clearer error feedback during data operations.
2026-07-06 15:34:20 -04:00
2792b06d79 Enhance static synchronization functions with new utilities and improved error handling
This commit introduces several new functions to enhance the static synchronization process, including `fxStaticSyncNormalizeCellValue`, `fxStaticSyncSqlOrderByKeys`, and `fxStaticSyncGetTableRowCount`. These additions improve data handling and error reporting during synchronization. The `fxStaticSyncLoadKeyedRows` function is updated to provide more detailed feedback on physical and duplicate row counts, while the user interface is enhanced to display duplicate key counts in the source data. Overall, these changes aim to improve the robustness and clarity of the database synchronization workflow.
2026-07-06 15:29:44 -04:00
3c0188fba4 Remove database confirmation requirement for client environments in synchronization process
This commit eliminates the need for database name confirmation when syncing to client environments, simplifying the user experience. The `fxStaticSyncRequiresDbConfirm` function is removed, and related logic is adjusted in the synchronization workflow. Additionally, the user interface is updated to reflect these changes, providing clearer messaging about the synchronization process and reinforcing the read-only nature of the client production environment.
2026-07-06 15:24:07 -04:00
5f65e1a339 Refactor data synchronization functions for improved clarity and status reporting
This commit renames and refines several functions related to data synchronization, enhancing the clarity of status reporting. The `fxStaticSyncResolveDataStatus` function is replaced with `fxStaticSyncFinalizeCompareResult`, which consolidates status determination logic. Additionally, user feedback is improved in the synchronization process, with clearer messages regarding data discrepancies and alignment. These changes aim to streamline the synchronization workflow and enhance overall user experience.
2026-07-06 15:17:04 -04:00
709044efef Add enhanced data synchronization functions and improve user feedback
This commit introduces new functions for confirming database names during synchronization, enhancing safety when syncing to production environments. The `fxStaticSyncResolveDataStatus` and `fxStaticSyncFormatDataDiffSummary` functions are added to provide clearer status reporting and summaries of data discrepancies. Additionally, the synchronization process is refined to improve user feedback, including clearer messages regarding data alignment and discrepancies. These changes aim to enhance the robustness and clarity of the database synchronization workflow.
2026-07-06 14:58:59 -04:00
bac8ed2be0 Enhance database synchronization functions and improve schema validation
This commit introduces new functions for resolving and comparing database schema columns, allowing for better handling of structural discrepancies during synchronization. The `fxStaticSyncCopyTable` function is updated to check for schema alignment before proceeding with data copying, providing clearer error messages when structures do not match. Additionally, the synchronization process is refined to allow for read-only operations towards production environments, enhancing safety and user feedback. Overall, these changes aim to improve the robustness and clarity of the database synchronization workflow.
2026-07-06 14:33:05 -04:00
7c7bea3666 Add database synchronization utility functions and enhance error handling
This commit introduces several utility functions for database synchronization, including error handling for MySQL operations and retrieval of table metadata. The `fxStaticSyncDbLastError`, `fxStaticSyncExecQuery`, `fxStaticSyncGetTableColumnNames`, and `fxStaticSyncGetTableEngine` functions are added to improve the robustness of the synchronization process. Additionally, the `fxStaticSyncCopyTable` function is updated to handle column mismatches more gracefully and to provide clearer feedback on the synchronization results, enhancing overall user experience and error reporting in the database synchronization workflow.
2026-07-06 14:23:27 -04:00
670fd20e44 Implement sidebar static synchronization logic and update sync function documentation
This commit introduces a new variable for sidebar static synchronization based on the HTTP_HOST, enhancing the conditions under which static database synchronization is allowed. Additionally, the documentation for the sync function is updated to reflect alignment with the new logic, improving clarity on its operational context. These changes aim to streamline the synchronization process and enhance the overall functionality of the superadmin interface.
2026-07-06 14:11:06 -04:00
338f5ed6f6 Refine ChronoTrack API configuration and enhance error handling
This commit updates the ChronoTrack API configuration by introducing a variable for the base URL, allowing for easier management of test and production environments. It also improves the admin interface to display the API endpoint status, indicating whether the test or production API is in use. Additionally, error handling in the OAuth token fetching process is enhanced to provide clearer messages when access tokens are absent or when errors occur, improving overall user feedback and robustness of the API.
2026-07-03 20:58:43 -04:00
53d406ae74 Enhance ChronoTrack API configuration and improve base URL handling
This commit refines the configuration of the ChronoTrack API by introducing a variable for the base URL, allowing for easier adjustments. It also adds a conditional definition for the token key based on the environment, ensuring appropriate settings for both test and production environments. Additionally, the admin interface is updated to display the API endpoint when configured, enhancing user feedback and clarity. These changes aim to improve the overall configuration management and user experience within the API.
2026-07-03 20:56:28 -04:00
29f836e615 Implement orphan management functionality in ChronoTrack API
This commit introduces new features for handling orphan entries within the ChronoTrack API. It adds a new endpoint for syncing orphan entries, enhancing the ability to identify and manage entries that are present in the system but not eligible for transfer. The user interface is updated to include a dedicated section for displaying orphan entries, improving clarity and user interaction. Additionally, the synchronization logic is refined to compute and report orphan entries effectively, aiming to enhance overall data management and user experience in the API.
2026-07-03 20:49:19 -04:00
38b7e9b72c Enhance ChronoTrack API functionality with new PUT methods and payload management
This commit introduces new functions for handling PUT requests within the ChronoTrack API, improving the management of entry updates. It includes the implementation of payload processing functions for both POST and PUT requests, ensuring that entry IDs are appropriately excluded from the payloads. Additionally, the synchronization logic is refined to differentiate between creating and updating entries, enhancing the overall clarity and efficiency of the API. These changes aim to streamline entry management and improve user experience in the API.
2026-07-03 20:41:59 -04:00
f2519dc987 Enhance ChronoTrack API push functionality and improve user feedback
This commit updates the ChronoTrack API push process by refining the user interface messaging for manual pushes, clarifying the status of participants, and improving the confirmation prompts. Key changes include the introduction of new variables for tracking eligible participants and orphans, as well as enhanced HTML output for better clarity on the current state of entries. These improvements aim to streamline the push process and enhance the overall user experience in managing participant data.
2026-07-03 20:35:43 -04:00
8c7f3e13ed Refactor superadmin UI components and enhance ChronoTrack API integration
This commit introduces a new UI structure for the superadmin interface by implementing the fxSuperadmV2Panel functions, which improve the organization and presentation of event access and ChronoTrack API sections. The changes include the addition of collapsible panels for better user interaction and visual clarity. Additionally, the versioning in the settings file is updated to reflect the latest changes. These enhancements aim to streamline the user experience and improve the overall functionality of the superadmin interface.
2026-07-03 20:27:30 -04:00
0343a8e4a7 Refactor session management and enhance user feedback in superadmin interface
This commit improves session management within the superadmin interface by introducing a new session validation function that ensures active sessions are maintained and provides JSON responses for session expiration. Additionally, the session timeout duration is standardized, and user feedback is enhanced with alerts for expired sessions. The codebase is streamlined by removing deprecated session handling logic, improving overall clarity and maintainability. These changes aim to enhance user experience and ensure robust session handling across the application.
2026-07-03 20:06:59 -04:00
57912b60ae Refactor ChronoTrack API entry management and remove deprecated functions
This commit streamlines the entry management process within the ChronoTrack API by removing deprecated functions related to entry withdrawal and deletion. The admin interface is updated to eliminate unused actions, enhancing clarity and usability. Additionally, comments are refined to improve code readability. These changes aim to simplify the codebase and improve the overall user experience in managing entries.
2026-07-03 12:47:49 -04:00
2370a2b344 Implement withdraw functionality and enhance entry management in ChronoTrack API
This commit introduces new functions for managing entry withdrawal within the ChronoTrack API, including the ability to probe and withdraw entries based on their status. The admin interface is updated to include buttons for testing withdrawal actions, providing clearer user feedback. Additionally, the synchronization logic is enhanced to summarize entry statuses and handle batch withdrawals more effectively. These changes aim to improve the overall management of entries and enhance user experience in the API.
2026-07-03 12:41:30 -04:00
f065eb5cb6 Enhance ChronoTrack API entry deletion and session management
This commit improves the entry deletion process within the ChronoTrack API by introducing a new function to handle deletion attempts with context requirements. It also enhances session management by implementing a session validation function that provides JSON responses for session expiration, improving user feedback during AJAX calls. Additionally, the synchronization logic is updated to utilize the new deletion function, ensuring better error handling and reporting. These changes aim to streamline entry management and enhance the overall user experience within the API.
2026-07-03 12:28:38 -04:00
3a0161a0bb Update ChronoTrack API entry wipe functionality and enhance user feedback
This commit modifies the entry wipe functionality in the ChronoTrack API admin interface. The wipe button is now disabled when there are no entries to delete, and the confirmation prompt has been updated to clarify the action's scope. Additionally, the API response for the wipe action now returns a warning state when no entries are found, providing clearer messaging about the visibility of entries. These changes aim to improve user experience and ensure accurate communication regarding entry management.
2026-07-03 12:16:48 -04:00
88f0e8138e Enhance ChronoTrack API entry synchronization and reporting
This commit introduces improvements to the entry synchronization process within the ChronoTrack API. Key changes include the renaming of functions for clarity, the implementation of pagination for fetching entries, and enhanced reporting of entry counts before and after synchronization actions. Additionally, the admin interface is updated to provide clearer feedback regarding the state of entries, including details on entries found before a wipe and remaining entries after synchronization. These enhancements aim to improve the overall user experience and data management capabilities of the API.
2026-07-03 12:13:02 -04:00
7071b3792a Implement ChronoTrack Live entry wipe functionality and enhance synchronization logic
This commit introduces a new feature to wipe all entries from ChronoTrack Live for a specific event, including user confirmation prompts and error handling. Additionally, it enhances the participant synchronization process by refining the handling of entry data and improving the reporting of synchronization results. The admin interface is updated to include a new button for the wipe action, providing clearer user feedback and control over event data management. These changes aim to streamline the management of ChronoTrack entries and improve the overall user experience within the API.
2026-07-03 10:33:11 -04:00
cef2e672cb Enhance participant synchronization and team handling in ChronoTrack API
This commit introduces new functions to better manage team-related data during participant synchronization. Key updates include the addition of team identification and role labeling, as well as improved handling of blocked team registrations. The participant summary now includes team-specific fields, enhancing the clarity of participant data. Additionally, the admin interface is updated to provide clearer messaging regarding blocked team registrations, improving user feedback during synchronization processes. These changes aim to streamline participant management and enhance the overall user experience within the ChronoTrack API.
2026-07-03 10:04:11 -04:00
ec1e98d898 Enhance participant synchronization and error handling in ChronoTrack API
This commit introduces a new function to classify blocked participants by type, improving the handling of synchronization errors related to missing or invalid data. The changes include updates to the participant classification logic, allowing for better reporting of issues such as duplicate bibs, missing external IDs, and unmapped races. Additionally, the push preview functionality is enhanced to display blocked participants clearly, improving user feedback during the synchronization process. These updates aim to streamline participant management and enhance the overall user experience within the ChronoTrack API.
2026-07-03 09:56:48 -04:00
338734db7a Enhance MS1 loader functionality and integrate loading indicators in ChronoTrack API
This commit updates the MS1 loader to include a message parameter for inline HTML usage, ensuring consistent messaging during AJAX operations. Additionally, new functions are introduced to render loading indicators and assets, improving user feedback during data synchronization processes. The integration of these features aims to enhance the overall user experience and clarity within the ChronoTrack API admin interface.
2026-07-03 09:46:32 -04:00
411f2a4d8f Add probe push functionality and enhance error handling in ChronoTrack API
This commit introduces a new 'sync_probe_push' action to the ChronoTrack API, allowing for the probing of participant data synchronization. It includes a new button in the admin interface for initiating probe pushes, along with improved error handling in the API response messages. Additionally, the batch size for synchronization is adjusted, and the entry payload building process is refined to include event IDs and registration choices, enhancing the overall synchronization capabilities and user experience.
2026-07-03 09:21:40 -04:00
9ea4dd2ecf Refactor ChronoTrack API event handling and synchronization logic
This commit updates the ChronoTrack API to improve event handling and synchronization features. Changes include enhanced messaging for closed events, adjustments to the push panel functionality, and refined status handling for event synchronization. The logic for managing event states is streamlined, allowing for clearer user interactions and better management of synchronization processes. These improvements aim to enhance the overall usability and effectiveness of the ChronoTrack API in event management.
2026-07-03 09:14:47 -04:00
903feebcdb Add synchronization functionality to ChronoTrack API
This commit introduces new synchronization features to the ChronoTrack API, including the ability to preview and push participant data to ChronoTrack. A new push panel is added to the admin interface, allowing users to view eligible participants and initiate synchronization. Additionally, backend functions are implemented to handle synchronization requests, enhancing the overall data management capabilities of the API. These changes aim to streamline the process of updating participant information within the ChronoTrack system.
2026-07-03 09:10:57 -04:00
2037b27c4c Enhance ChronoTrack API with entry handling and diagnostic improvements
This commit introduces several enhancements to the ChronoTrack API, focusing on entry handling and diagnostic capabilities. New functions are added to normalize and summarize entry data, improving the management of entry-related information. Additionally, the diagnostic functionality is updated to include entry endpoints, providing more comprehensive insights during API diagnostics. These changes aim to enhance the overall usability and effectiveness of the ChronoTrack API for event management.
2026-07-03 09:03:03 -04:00
beb84339e2 Add new table definitions for 'inscriptions_pays' and 'inscriptions_provinces' in static sync configuration
This commit introduces new entries for the 'inscriptions_pays' and 'inscriptions_provinces' tables in the fxStaticSyncTableDefinitions function. Each entry includes a label, keys, and columns to ignore, enhancing the static synchronization configuration for better data management.
2026-07-02 20:22:22 -04:00
df9f964f11 Refactor ChronoTrack API functions for improved race and registration choice handling
This commit enhances the ChronoTrack API by refining functions related to race and registration choice entities. Key updates include the introduction of new functions for collecting race and registration choice entities, improved normalization logic, and adjustments to existing functions for better clarity and functionality. Additionally, terminology is updated to ensure consistency across the API, enhancing overall usability and integration with the ChronoTrack system.
2026-07-02 19:51:35 -04:00
3aea111392 Add diagnostic functionality to ChronoTrack API for event management
This commit introduces a new diagnostic feature in the ChronoTrack API, allowing users to run diagnostics on events and retrieve detailed reports. The functionality includes a new diagnostic panel in the admin interface, which displays API diagnostic results, including OAuth status and endpoint responses. Additionally, the backend is updated to handle diagnostic requests, ensuring comprehensive error handling and reporting. This enhancement aims to improve troubleshooting and support for event management within the ChronoTrack system.
2026-07-02 19:46:57 -04:00
55b7ce7061 Enhance ChronoTrack API admin interface with registration choice support and improved race terminology
This commit updates the ChronoTrack API admin interface to incorporate support for registration choices, enhancing the functionality of race mapping. Terminology is refined throughout the interface, replacing "race" with "course" where appropriate to improve clarity. New functions are added to handle registration choices, ensuring a more comprehensive integration with the ChronoTrack API. Additionally, existing functions are modified to accommodate these changes, improving overall user experience and interaction.
2026-07-02 19:29:00 -04:00
f826a0e409 Implement event link picker and enhance race mapping display in ChronoTrack API admin interface
This commit introduces a new function to render an event link picker in the ChronoTrack API admin interface, allowing users to search and select events more efficiently. The event page rendering logic is updated to conditionally display the link picker based on event status. Additionally, the race mapping display is enhanced to show saved mappings more clearly, improving user interaction and overall experience when managing events.
2026-07-02 19:24:51 -04:00
00ee7484df Add race status display and reload functionality in ChronoTrack API admin interface
This commit enhances the ChronoTrack API admin interface by adding a status display for race loading and a button to reload races. The status updates provide user feedback during the loading process, indicating success or failure. Additionally, the race loading logic is improved to handle network errors gracefully, ensuring a better user experience when interacting with the API.
2026-07-02 19:20:23 -04:00
6728a541a8 Enhance documentation and clarify ChronoTrack API credentials usage in settings file
This commit adds comments in the `inc_settings.php` file to clarify the default values for ChronoTrack API credentials and their intended usage. It also notes that the credentials can be overridden by other switch blocks, improving the understanding of the configuration process for developers.
2026-07-02 19:11:28 -04:00
c3aed5e9fe Fix syntax error in ChronoTrack API credentials in settings file
This commit corrects a syntax error in the `inc_settings.php` file by adding a missing quotation mark to the `service_pass` entry. This change ensures proper parsing of the credentials, maintaining the integrity of the ChronoTrack API configuration.
2026-07-02 18:46:48 -04:00
2e691df76b Update ChronoTrack API credentials in settings file
This commit updates the ChronoTrack API configuration in the `inc_settings.php` file by adding the client ID, client secret, service user, and service password. These changes are essential for establishing a connection with the ChronoTrack API, ensuring proper functionality and access to its features.
2026-07-02 18:45:10 -04:00
89d389c76d Update version code to 4.72.726 and enhance superadmin sidebar layout
This commit increments the version code to 4.72.726 and introduces structural improvements to the superadmin sidebar. The HTML layout is refined with the addition of a dedicated body section for the V2 panel, while CSS adjustments enhance padding, margins, and overall styling for a more cohesive user experience. These changes aim to improve accessibility and visual organization within the sidebar.
2026-07-02 18:40:11 -04:00
7e3eb9522f Refactor superadmin sidebar layout and styling for improved user experience
This commit updates the superadmin sidebar by restructuring the HTML layout and enhancing the CSS styles. The sidebar now features a dedicated legacy section and a new V2 panel with improved visual hierarchy and organization. Key changes include updated padding, margins, and background styles to create a cohesive and user-friendly interface. Additionally, the sidebar navigation has been refined for better accessibility and interaction.
2026-07-02 18:37:40 -04:00
a94077874d Implement V2 sidebar panel for superadmin with enhanced access management
This commit introduces a new V2 sidebar panel in the superadmin interface, featuring improved layout and organization for access management tools. The panel includes sections for production synchronization and legacy access controls, with updated styling for better user experience. Additionally, the CSS is enhanced to support the new sidebar design, ensuring a cohesive look and feel across the application.
2026-07-02 18:35:54 -04:00
fee6f5c69d Refactor ChronoTrack API integration in event modification form
This commit updates the event modification form to conditionally display the ChronoTrack API section with a toggle button for better user interaction. The rendering function is adjusted to improve layout and heading levels, ensuring a consistent and user-friendly experience. Additionally, the inline rendering logic is refined to enhance the overall integration of ChronoTrack features.
2026-07-02 18:26:26 -04:00
2b4060770a Enhance ChronoTrack API event page rendering and form integration
This commit updates the `fxChronotrackApiAdminRenderEventPage` function to include an optional parameter for inline rendering, allowing for a more flexible display of the ChronoTrack API section. The event modification form is also adjusted to conditionally render the ChronoTrack API section based on the event ID, improving user experience and navigation. Additionally, the layout is refined to ensure proper heading levels based on the inline status.
2026-07-02 18:20:34 -04:00
da9feee83b Add ChronoTrack API section to event modification form
This commit introduces a new section in the event modification form for configuring the MSIN API ChronoTrack. It includes a card layout with a title, description, and a button linking to the ChronoTrack configuration page. Additionally, a button is added in the sidebar for quick access to the ChronoTrack section, enhancing user navigation and integration of ChronoTrack features within the application.
2026-07-02 18:17:02 -04:00
0906eb6548 Add database selection instructions for ChronoTrack SQL script
This commit updates the MSIN API ChronoTrack SQL script to include important instructions for selecting the appropriate database before execution. It adds comments to guide users in uncommenting the relevant `USE` statement based on their environment, ensuring proper execution and preventing errors. No changes to the table structure were made in this update.
2026-07-02 18:12:14 -04:00
121186a0b9 Update version code and integrate MSIN API ChronoTrack functionality
This commit updates the version code to 4.72.725 and introduces the `fxSettingsApplyChronotrackApi` function to manage ChronoTrack API credentials based on the environment. It also adds relevant UI elements in the sidebar and form sections for ChronoTrack configuration, enhancing the integration of ChronoTrack features within the application.
2026-07-02 18:10:51 -04:00
841fea5103 Add bib anomaly detection for unconfigured sequences
This commit introduces a new function, `fxBibSqlEpreuveHasBibRanges`, to check for the presence of bib ranges in events. It enhances the existing anomaly detection by adding support for cases where no bib ranges are configured, allowing for better reporting of duplicate bibs. Additionally, relevant language strings are added for both French and English to support multilingual reporting. The version code is incremented to reflect these changes.
2026-07-02 12:46:21 -04:00
81e55ea0ec Add out-of-range bib anomaly detection and reporting
This commit introduces a new function, `fxBibAnomalyOutOfRangeBibs`, to identify and report bibs assigned outside of configured sequences for events. It enhances the existing anomaly collection by including this new category and updates the rendering logic to display out-of-range anomalies in the user interface. Additionally, relevant language strings are added for multilingual support. The version code is incremented to reflect these changes.
2026-07-02 12:34:45 -04:00
3ee8ec05be Implement loader functionality for AJAX calls and enhance user feedback
This commit introduces a new loader mechanism for AJAX requests, improving user experience by providing visual feedback during data fetching processes. The `bibFetch` function is created to manage loader visibility, ensuring a consistent loading experience across various AJAX calls. Additionally, a new language string for the loading message is added to support multilingual feedback. The version code is incremented to reflect these changes.
2026-07-02 11:47:47 -04:00
a5ad7a0ec6 Refactor gestion fiche card layout and update rendering functions
This commit enhances the CSS styles for the gestion fiche card layout, transitioning from a rolodex to a grid format for improved organization and readability. The PHP rendering functions are updated to support this new layout, including logic for full-width value display. The version code is incremented to reflect these changes.
2026-07-02 10:27:57 -04:00
011aa65d19 Refactor gestion fiche styling and update PHP rendering logic
This commit refines the CSS styles for gestion fiche sections, introducing a new rolodex layout for improved visual presentation and mobile responsiveness. The PHP function for rendering key-value lists is updated to support the new layout. Additionally, the version code is incremented to reflect these changes.
2026-07-02 10:14:14 -04:00
6e386d95ee Enhance styling and functionality for gestion fiche sections
This commit introduces new CSS styles for the gestion fiche sections, improving the layout and visual presentation of details. It adds background colors and flexbox layouts for better readability on mobile devices. Additionally, the PHP file is updated to ensure specific sections are collapsed or expanded based on user interactions, enhancing the user experience. The version code is incremented to reflect these changes.
2026-07-02 10:09:37 -04:00
ef11a5a011 Refactor loader integration and enhance preloader UI
This commit refines the MS1 loader integration across scripts, ensuring a smoother user experience during loading processes. It updates the preloader UI to align with the new design, improving visual consistency. The version code is incremented to reflect these changes.
2026-07-02 09:26:13 -04:00
d7f3b0156b Implement MS1 loader functionality and update preloader UI
This commit introduces the MS1 loader functionality across various scripts, enhancing the user experience during loading processes. It integrates the loader into form submissions and AJAX calls, ensuring consistent feedback for users. Additionally, the preloader UI is updated to utilize the new loader design, improving visual consistency. The version code is incremented to reflect these changes.
2026-07-02 09:14:34 -04:00
5936cdb61c Add legacy promoteur functionality and migration enhancements
This commit introduces several enhancements related to the legacy promoteur system. It adds a new function to check if the legacy promoteur site is enabled and updates various functions to utilize this check, ensuring that legacy features are only accessible when appropriate. Additionally, it refines the migration process for legacy accounts, including improved handling of migration statistics and event access. The admin interface is updated to reflect these changes, enhancing the overall management of legacy promoteur accounts. The version code is updated to reflect these changes.
2026-07-01 14:14:25 -04:00
abb63c293f Enhance session management and access control for super admins
This commit introduces improvements to session management by implementing a new check for super admin sessions, allowing them to bypass certain access restrictions. It updates the logout function to retain super admin session data and modifies event access functions to grant permissions based on super admin status. Additionally, the session timeout duration is adjusted for better alignment with site requirements. The version code is updated to reflect these changes.
2026-07-01 13:48:43 -04:00
b1699cd10c Add legacy report functionality and update migration statistics display
This commit introduces a new function to generate a comprehensive report of legacy promoters, including details such as group, last connection, and migration date. The admin interface is updated to display migration statistics and legacy report data, enhancing the visibility of pending and completed migrations. The UI is refined for better user experience, ensuring clear presentation of account statuses. The version code is updated to reflect these changes.
2026-07-01 13:42:44 -04:00
048c4a398f Add access management enhancements and migration statistics retrieval
This commit updates the access management functionality by ensuring role_id is updated on duplicate key entries and adds error handling for unregistered access attempts. Additionally, it introduces a new function to retrieve migration statistics for legacy accounts, providing insights into pending and completed migrations. The version code is updated to reflect these changes.
2026-07-01 12:57:01 -04:00
57143d538e Add role assignment management features in admin interface
This commit introduces a new section for managing role assignments within the admin interface. It adds functionality to display active and historical assignments for roles, enhancing the user experience by allowing admins to view and filter assignments. Additionally, the sidebar is updated to include the new 'assignations' page, ensuring consistent navigation. The version code is updated to reflect these changes.
2026-07-01 12:48:54 -04:00
4d3260b2ca Add role deletion functionality in admin interface
This commit introduces the ability to delete roles within the admin interface. It adds new functions to check if a role can be deleted based on active assignments and historical data. The UI is updated to include delete buttons with confirmation prompts, enhancing user interaction and management capabilities. The version code is updated to reflect these changes.
2026-07-01 12:41:10 -04:00
0b3ea4811a Update sidebar menu item rendering to include icon handling
This commit enhances the sidebar menu item rendering by introducing a check for menu icons. If an icon is provided, it is displayed; otherwise, a default folder icon is shown. This improves the visual representation of menu items in the admin interface, ensuring a more consistent user experience. The version code is updated to reflect these changes.
2026-07-01 12:38:43 -04:00
30c4103212 Enhance permission inspection features in admin interface
This commit introduces new functions for displaying permission badges in various sections of the admin interface, improving visibility for super admins. It updates the sidebar and management sections to include these badges, ensuring that access rights are clearly indicated. Additionally, new CSS styles are added for better layout and alignment of the badges. The version code is updated to reflect these changes.
2026-07-01 12:35:52 -04:00
080ba99b2f Remove unused permission inspection functions to streamline codebase 2026-07-01 12:03:11 -04:00
7bd586b619 Implement permission inspection features and enhance admin interface
This commit introduces new functions for permission inspection, allowing super admins to toggle and manage access rights more effectively. It adds visual elements for permission badges in the admin interface and updates the sidebar navigation to include a migration section for legacy accounts. Additionally, CSS styles are enhanced for better user experience and clarity. The version code is updated to reflect these changes.
2026-07-01 12:00:22 -04:00
2a5c81ecc3 Refactor sidebar titles and enhance CSS styles for improved navigation in admin interface
This commit updates the sidebar titles in the admin interface to use consistent heading levels and introduces new CSS styles for better visual hierarchy. The changes include the addition of classes for main titles and subtitles, enhancing the overall user experience and readability. The version code is updated to reflect these changes.
2026-07-01 11:00:35 -04:00
63b0a1d4c5 Refactor sidebar navigation and improve documentation management in admin interface
This commit enhances the sidebar navigation by consolidating sections for site management, access management, and documentation. It introduces a new help section for production deployment and streamlines the documentation links for better accessibility. Unused code related to module selection has been removed, improving maintainability. The version code is updated to reflect these changes.
2026-07-01 10:58:23 -04:00
885a5b652b Enhance sidebar navigation and documentation management in admin interface
This commit introduces a revamped sidebar navigation in the admin interface, adding new sections for site management, access management, and documentation. It improves user experience by providing clearer access to various functionalities, including a new hub for managing pages and modules. Additionally, the code structure is refined to ensure better organization and maintainability. The version code is updated to reflect these changes.
2026-07-01 10:47:22 -04:00
c19f70031c Enhance search functionality in registration management with single result redirection
This commit introduces logic to automatically redirect users to a detailed view when a single result is returned from a search query in the registration management interface. It improves user experience by streamlining navigation after search submissions. Additionally, the code structure is refined to ensure that search results are fetched only when necessary, enhancing performance. The version code is updated to reflect these changes.
2026-06-30 15:46:48 -04:00
7c9968063a Implement search form submission and UI enhancements for registration management
This commit introduces new JavaScript functions to handle search context detection and form submission within the registration management interface. It enhances the user experience by automatically submitting the search form when a number is detected in the BibScan session. Additionally, new CSS styles are added to improve the layout of the search fields, ensuring better alignment and spacing. The version code is incremented to 4.72.720 to reflect these changes.
2026-06-30 15:40:06 -04:00
a955932086 Refine refund success messaging and UI for cancellation in registration management
This commit updates the refund success messaging to clarify the cancellation process, ensuring users understand how to proceed with cancellations in the registration system. It introduces an inline cancel button within the explanatory banner, enhancing user interaction and visibility. Additionally, the cancel button is now hidden to prevent duplication, improving the overall user experience during the refund process.
2026-06-30 15:36:15 -04:00
8933753fe0 MSIN-4417 Update refund context handling in registration management
This commit refines the refund context handling by removing the unnecessary boolean parameter for Super Admin in the `fxRefundComputeContext` function, simplifying the logic. It ensures that refunds are consistently linked to the correct event context across various functions, enhancing authorization checks and calculations. The version code is incremented to 4.72.719 to reflect these changes.
2026-06-30 15:33:40 -04:00
797e66fec3 MSIN-4417 Refactor refund handling in registration management to support event context
This commit enhances the refund functionality by introducing event context handling for refunds. It updates the logic to ensure that refunds are linked to the correct event, improving authorization checks and refund calculations. Additionally, the refund context is now passed through various functions to ensure accurate processing. The version code is incremented to 4.72.718 to reflect these changes.
2026-06-30 15:13:06 -04:00
0d042deaf2 MSIN-4416 Enhance refund functionality in registration management with new documentation panel
This commit introduces a new documentation panel for the refund process in the registration management interface. It adds a function to render the refund documentation aside from the main refund form, improving user guidance during the refund process. Additionally, CSS styles are added to ensure proper layout and responsiveness of the new panel. The version code is updated to 4.72.717 to reflect these changes.
2026-06-30 14:40:48 -04:00
5e767e77a9 Update CSS styles for cancellation badges and increment version code to 4.72.716
This commit enhances the visual representation of cancellation badges in the UI by adding new CSS styles for better alignment and spacing. Additionally, the version code has been updated to reflect the latest changes. These updates improve the overall user experience in the registration management interface.
2026-06-29 13:47:24 -04:00
afff43abd6 MSIN-4405 Implement cancellation cause logic and UI enhancements
This commit introduces a new function, `fxGetAnnulationCause`, to determine the reason for a cancelled registration (e.g., transfer, modification, or true cancellation) without altering the database. Additionally, it adds CSS styles for displaying cancellation badges in the UI, enhancing the visual representation of registration statuses. The changes improve user experience by providing clear and informative feedback on registration cancellations throughout the management interface.
2026-06-29 13:31:28 -04:00
bcc7d7974e MSIN-4415 Enhance event display in promoteur hub with section title
This commit adds a section title for the events grid in the promoteur hub, improving the visual structure and clarity of the displayed information. The title indicates the availability of events for sale, enhancing user engagement and overall experience within the interface.
2026-06-29 12:01:32 -04:00
50e8fea3e9 MSIN-4415 Add product display functionality in promoteur hub
This commit introduces new features for displaying products available for sale within the promoteur hub. It adds a function to retrieve product data, including quantities available, sold, and in carts, enhancing the event experience by providing users with relevant product information. Additionally, CSS styles are added to improve the visual presentation of the product tiles. The changes ensure that product availability is clearly communicated to users, improving overall engagement and usability in the event management interface.
2026-06-29 11:59:20 -04:00
377f4993f8 MSIN-4413 Enhance captain role logic in profile fetching function
This commit refines the logic for determining the captain role within the `fxInscrGestionFetchFicheProfile` function. It introduces a new condition to ensure that the captain title is only displayed for team events, improving the accuracy of role representation. The changes enhance the clarity of participant profiles by correctly reflecting the captain status based on event type, thereby improving the overall user experience in the registration management interface.
2026-06-29 11:51:15 -04:00
8be4bafce1 Refine PayPal refund display logic to enhance user experience
This commit improves the handling of PayPal refunds by ensuring that no summary is displayed when there are no refunds available. The logic now checks for empty refund details before rendering the summary section, preventing unnecessary display of empty elements. This change enhances the clarity of the user interface by only showing relevant information, thereby improving the overall user experience in the registration management interface.
2026-06-29 11:47:48 -04:00
bdd0454240 Refactor PayPal refund handling to improve currency and ID retrieval logic
This commit enhances the handling of PayPal refunds by refining the logic for retrieving currency and identifiers. It introduces new utility functions, `strFirst` and `floatFirst`, to ensure that fallback values are correctly utilized when the refunds section is empty or when API reporting fails. This change improves the robustness of the refund summary display, ensuring accurate information is presented to users.
2026-06-29 11:43:58 -04:00
f52816da32 Enhance refund panel functionality in registration management
This commit introduces a new function, `focusRefundPanel`, to improve user experience by automatically scrolling the refund panel into view and focusing on the amount input field when the panel is loaded. Additionally, the refund panel is repositioned above the transaction panel to ensure better visibility. The changes streamline the interaction process, making it easier for users to access and input refund details.
2026-06-29 11:36:08 -04:00
7c88dd595d Implement compact PayPal refund summary feature in registration management
This commit introduces a new function, `renderPayPalRefundSummaryHtml`, to provide a concise overview of PayPal refunds within the registration management interface. The summary displays essential information such as status, captured amount, total refunded, and remaining balance, enhancing the user experience by presenting key details in a more accessible format. Additionally, the existing code is updated to utilize this new summary function, improving the overall clarity and usability of transaction information.
2026-06-29 11:33:07 -04:00
028a9af4aa MSIN-4412 Enhance transaction resume feature in registration management with detailed PayPal refund information
This commit improves the transaction resume functionality by adding detailed PayPal refund information to the transaction summary. The changes include a refined SQL query to retrieve complete command details and the integration of a new section displaying PayPal capture details if available. This enhancement provides users with a comprehensive view of their transactions, including refunds, directly within the management interface, thereby improving usability and transparency.
2026-06-29 11:24:02 -04:00
b5daa5a4ba Enhance bib management with restore functionality on duplicate check
This commit adds a restore feature for bib numbers in the registration management interface. When a duplicate bib is detected, the previously entered value is restored, improving user experience by preventing accidental data loss. The changes include new functions to manage the restoration process and updates to the existing AJAX handling to ensure the correct bib value is maintained during interactions. This enhancement builds on the previously implemented duplicate check feature, further refining the registration workflow.
2026-06-29 11:12:14 -04:00
811cf02f16 MSIN-4406 Implement duplicate bib check feature in registration management
This commit introduces a new AJAX functionality to check for duplicate bib numbers during the registration process. It adds a case in `ajax_inscr_gestion.php` to handle duplicate checks, returning relevant participant information and policies for handling duplicates. The front-end script in `inc_footer_scripts.php` is updated to incorporate this check before saving a bib, enhancing user interaction with confirmation dialogs for duplicate assignments. Additionally, new functions are added in `inc_fx_inscriptions_gestion.php` to manage duplicate policies and retrieve existing participants with the same bib number, improving data integrity and user experience in the registration management interface.
2026-06-29 11:09:10 -04:00
3b007261d6 MSIN-4404 Refactor AJAX handling for "bib retrieved" status toggle in registration management
This commit refines the AJAX functionality for toggling the "bib retrieved" status in the registration management interface. It introduces a confirmation dialog for removing the status, enhancing user interaction. The preloader display logic is also improved, ensuring a smoother user experience during the status update process. Additionally, the code structure is cleaned up for better readability and maintainability.
2026-06-29 10:58:31 -04:00
dfc5b8866a Implement transaction resume feature in registration management
This commit introduces a new AJAX-based feature to display transaction details in the registration management interface. A button labeled "Afficher la transaction" is added, allowing users to toggle the visibility of transaction summaries independently from refunds. The transaction details are fetched dynamically, enhancing user experience by providing immediate access to relevant information. Additionally, corresponding labels for the button are defined in both French and English to support internationalization. The changes improve the overall usability and functionality of the management interface.
2026-06-29 10:52:36 -04:00
d8252fd77f Update confirmation button labels and reposition in registration management
This commit modifies the labels for the confirmation button in both French and English to provide clearer instructions. The button is also repositioned to appear last in the management form for better user experience. Additionally, corresponding changes are made in the SQL file to reflect the updated labels in the database. These enhancements aim to improve clarity and usability in the registration management interface.
2026-06-29 10:46:32 -04:00
2d518e54ce Enhance registration management display with new column headers and responsive design
This commit introduces a new grid layout for the registration management list, adding column headers for better organization and clarity. The CSS has been updated to ensure proper alignment and styling of the headers, while also implementing responsive behavior to hide the header in mobile view. The version code has been updated to 4.72.713.
2026-06-29 10:41:48 -04:00
46c75f14b6 Enhance OCR scanning interface and improve internationalization handling
This commit updates the CSS for the OCR scanning interface, increasing the aspect ratio and adjusting dimensions for better usability. It also introduces a new function, `fxInscrGestionScanLabel`, to provide robust labels for scanner buttons, ensuring that users receive clear instructions regardless of language settings. The version code has been updated to 4.72.712.
2026-06-29 10:37:09 -04:00
10d800959f Refactor BibScanSession to improve internationalization handling in status messages
This commit enhances the `BibScanSession` class by introducing a new function, `scanMsg`, to manage the retrieval of translated status messages. It replaces direct attribute access with this function to ensure that raw internationalization keys are not displayed to users. The changes improve the user experience by providing clearer and properly translated messages during the scanning process, including camera access and number detection feedback.
2026-06-29 10:28:21 -04:00
0b5da05d42 Enhance management form usability with improved CSS styles and enable OCR scanning
This commit introduces several CSS enhancements to the editable management form, increasing padding, font sizes, and button dimensions for better touch accessibility on mobile devices. Additionally, it sets the OCR scanning feature to always be available on the management form, ensuring a seamless user experience during data entry. The version code has been updated to 4.72.711.
2026-06-29 10:00:30 -04:00
8f04833b6a Update ajax_inscr_gestion.php to improve error handling and include necessary function dependencies
This commit adds a required file inclusion for the `fxEveAccesResolveEveIdFromParId` function to ensure it is available during the execution of the 'par_statut_course' case. Additionally, it simplifies the error response for invalid actions by removing detailed diagnostic information, streamlining the error handling process while maintaining essential feedback for AJAX requests.
2026-06-29 09:47:57 -04:00
61bf838108 Implement error handling and JSON response for ajax_inscr_gestion.php
This commit enhances the error handling in the `ajax_inscr_gestion.php` file by introducing a shutdown function that captures fatal errors and returns a structured JSON response instead of a generic 500 error. Additionally, it adds diagnostic information for invalid actions, improving the debugging process. These changes aim to provide clearer feedback to users and developers when issues arise during AJAX requests.
2026-06-29 09:46:05 -04:00
6ab8c8c61f Enhance promoteur hub functionality by defining a threshold for unlimited events
This commit introduces a new constant, `PHUB_SEUIL_ILLIMITE`, to define the quantity threshold at which an event is considered "unlimited." The `fxPromoteurHubGetEpreuveStats` function has been updated to incorporate this threshold, allowing for better handling of event statistics and improving the accuracy of the sold tickets and remaining quantities display. These changes aim to enhance the clarity and functionality of the promoteur hub.
2026-06-27 17:41:30 -04:00
bedc53df7f chore: stop tracking v3_ci4/vendor (Composer artifacts)
Le dossier vendor/ avait ete committe par accident (milliers de fichiers), ce qui faisait geler l'IDE. Ajoute au .gitignore et retire du suivi (fichiers conserves sur disque). composer.lock reste versionne.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-27 17:38:03 -04:00
91bf29089e Add Composer artifacts to .gitignore to prevent vendor directory commits 2026-06-27 17:37:45 -04:00
ca452f9ade Update promoteur hub statistics display and refine CSS for better user experience
This commit enhances the `fxPromoteurHubGetEpreuveStats` function to improve the aggregation and presentation of event statistics, including sold tickets and check-ins. Additionally, it refines the CSS styles for the promoteur hub, ensuring a more cohesive and visually appealing layout for statistics and event tiles. These updates aim to provide users with clearer insights and a more organized interface for event management.
2026-06-27 17:35:37 -04:00
c47d826f8e Refactor promoteur hub rendering and update CSS for improved layout
This commit refines the rendering functions for the promoteur hub, introducing `fxPromoteurHubRenderFlash` and `fxPromoteurHubRenderEpreuvesGrid` to separate the display of the flash summary and the grid of event tiles. The CSS has been updated to enhance the layout, including new styles for the flash section and action buttons, ensuring a more organized and visually appealing interface. Additionally, the version code has been incremented to 4.72.710.
2026-06-27 17:12:59 -04:00
3e8112ec0b Implement statistics and layout enhancements for promoteur hub
This commit introduces a new function, `fxPromoteurHubGetEpreuveStats`, to aggregate and return statistics for events, including sold tickets, check-ins, and waiting list details. Additionally, it adds extensive CSS styles for the promoteur hub, enhancing the layout and visual presentation of statistics, alerts, and event tiles. These changes improve the user experience by providing clearer information and a more organized interface for event management.
2026-06-27 17:03:26 -04:00
67d7675e11 Add fxLegalLabelIsDraft function to improve label validation
This commit introduces the `fxLegalLabelIsDraft` function, which checks if a given label is a draft based on its content. The function trims the label and evaluates conditions such as emptiness and the presence of underscores, enhancing the clarity and maintainability of the cookie consent management system. This addition supports better label handling in the context of legal page requirements.
2026-06-27 15:00:03 -04:00
f32a4300f0 Enhance footer social links styling for improved layout and accessibility
This commit updates the CSS styles for the footer's social links, ensuring a more consistent and user-friendly layout. Changes include adjustments to the display properties and margins of social link elements, enhancing their alignment and visual appeal. Additionally, the font size of the cookie management icon has been refined for better integration within the footer design.
2026-06-27 14:58:07 -04:00
1b30f7e0ea Refactor cookie consent management to improve label handling
This commit updates the `fxEnsureLegalPage` function to utilize the `fxLegalLabelIsDraft` function for checking and updating French and English labels. This change enhances the clarity and maintainability of the code by ensuring that labels are only updated when they are in draft status, aligning with the overall improvements in cookie consent management.
2026-06-27 14:57:01 -04:00
33f5c3d0dd Update version code to 4.72.708 in inc_settings.php 2026-06-27 14:55:13 -04:00
58342532cd Enhance cookie management UI and functionality
This commit updates the cookie management interface in the footer by replacing the existing cookie icon with a new SVG representation, improving visual consistency. It also refines the associated CSS styles for better user interaction and accessibility. Additionally, the cookie consent text retrieval function has been simplified to ensure clearer language and compliance with privacy regulations. These changes collectively enhance the user experience and maintainability of the cookie consent system.
2026-06-27 14:54:52 -04:00
06d8a065dc Update cookie consent language and policy details for clarity and compliance
This commit enhances the cookie consent management by updating the language used in the consent messages and policy sections. The changes provide clearer explanations of cookie usage, including essential, analytics, and marketing cookies. Additionally, the policy now includes details on managing cookie preferences, cross-border data transfers, and user rights under Quebec Law 25, ensuring better compliance with privacy regulations and improving user understanding.
2026-06-27 14:49:52 -04:00
ce7c430046 Enhance cookie consent management and UI improvements
This commit refines the cookie consent management system by introducing a new function for handling cookie preferences and updating the UI elements in `inc_footer.php` and `inc_header.php`. It ensures that analytics and marketing scripts are executed based on user consent, improving compliance with privacy regulations. Additionally, it enhances the cookie consent interface, providing users with clearer options for managing their cookie preferences, and updates related functions for better maintainability.
2026-06-27 14:48:48 -04:00
19679875df Implement cookie consent management across various scripts
This commit introduces a comprehensive cookie consent management system by integrating the `fxCookiesAllowMarketing()` function across multiple files. It ensures that marketing-related scripts, such as Facebook Pixel and Google Analytics, are only executed if the user has consented to cookies. Additionally, it enhances the cookie consent UI in `inc_footer.php` and `inc_header.php`, improving user experience and compliance with privacy regulations. The changes also include refactoring of cookie handling functions for better maintainability.
2026-06-27 14:44:44 -04:00
4c08938403 Update promoteur hub layout and SQL for gestion integration
This commit modifies the layout of the promoteur hub by swapping the content of the left and right columns, ensuring that the management section is displayed correctly alongside the reports. Additionally, it updates the SQL script to include safety measures for missing labels and redefines the "Gestion" header in the promoteur hub, ensuring a consistent user experience across the interface.
2026-06-27 12:30:43 -04:00
6d885caedf Update inscriptions management to transition from mobile to gestion
This commit replaces references to mobile inscriptions with gestion inscriptions across multiple SQL files. It includes the addition of new button labels, search prompts, and QR code handling for the gestion interface, ensuring a consistent user experience. The changes enhance the overall structure and maintainability of the event management system while providing clearer instructions and improved functionality for users.
2026-06-27 12:25:36 -04:00
8f428a5c18 Refactor event management system to transition from mobile to gestion
This commit updates the event management system by replacing references to mobile inscriptions with gestion inscriptions. It includes changes to AJAX endpoints, helper functions, and UI elements to reflect this transition. The updates enhance the overall structure and maintainability of the codebase while ensuring that user access and permissions are properly managed for the new gestion interface.
2026-06-27 12:10:19 -04:00
9acc8102e8 Add AJAX functionality for event registration management and QR code scanning
This commit introduces new AJAX endpoints for managing event registrations, including a dedicated script for handling QR code scans. The new functionality validates user sessions and event access, providing JSON responses for both successful and error states. Additionally, it enhances the overall structure of the event management system by integrating various helper functions for improved maintainability and user experience.
2026-06-27 12:09:56 -04:00
116347036f Enhance mobile event registration search functionality with new prompts and criteria handling
This commit introduces a search prompt for the mobile event registration interface, guiding users on how to display registrations. It also implements logic to manage search criteria, ensuring that the search state is preserved during pagination and navigation. These improvements enhance user experience by providing clearer instructions and maintaining context during searches.
2026-06-27 11:54:40 -04:00
c4199c5557 Enhance mobile event management interface with new labels and version update
This commit introduces new labels for displaying invoice and participant modification actions in the mobile event management interface, improving clarity for users. Additionally, the version code has been updated to reflect these changes. These enhancements contribute to a more user-friendly experience in managing event registrations.
2026-06-27 11:47:41 -04:00
98ed9f6f8e Refactor mobile event management actions and update version code
This commit removes the visual grouping for event management actions on mobile and simplifies the button layout. The buttons for editing, refunding, canceling, and restoring registrations have been streamlined for better usability. Additionally, the version code has been updated to reflect the latest changes. These modifications enhance the overall clarity and functionality of the mobile event management interface.
2026-06-27 11:40:53 -04:00
236601fcf6 Implement new mobile event management actions layout and enhance button functionality
This commit introduces a visually distinct group for managing event actions on mobile, including buttons for editing, refunding, canceling, and restoring registrations. Each button now features icons for improved clarity and user experience. Additionally, a new button for sending invoice details via email is added, enhancing the overall functionality of the mobile event management interface. Version and date constants are also updated.
2026-06-27 11:37:52 -04:00
373483b4eb Enhance refund display in mobile event management by introducing a collapsible summary panel for invoice details. This update improves user experience by providing bilingual labels for the transaction visibility toggle and a visually distinct layout for better clarity during the refund process. 2026-06-27 11:27:46 -04:00
95536edec5 Enhance refund process in mobile event management by integrating a summary of the invoice alongside the refund form. This update includes the addition of a resume display for the invoice, ensuring users can view the details of what they are refunding. The changes improve the user experience by providing clearer context during the refund request process. 2026-06-27 11:21:21 -04:00
e486c22bfc Refactor refund process by removing the inc_tableau_remboursement.php file and updating the refund handling logic in ajax_refund.php and inc_fx_paypal.php. The changes include enhanced authorization checks for promoters, improved session management for refund tokens, and better integration of user information in refund records. This update streamlines the refund workflow and enhances security and maintainability. 2026-06-27 11:14:41 -04:00
bc407a7c20 Add refund functionality to mobile event management
This commit introduces a new refund feature in the mobile event management system. It includes an AJAX-based refund form that allows users to request refunds directly from the event management interface. The refund button is conditionally displayed based on user permissions, and the form is dynamically loaded to enhance user experience. Additionally, the refund processing logic is integrated with existing PayPal functions to ensure seamless transaction handling. This update improves the overall usability and functionality of the event management system.
2026-06-26 14:45:09 -04:00
bc26c37315 Refactor refund form rendering in commandes.php to integrate author information directly into the display. The previous debug output has been removed and replaced with a structured information panel that includes the order number and the name of the person processing the refund. This change enhances the clarity and usability of the refund interface. 2026-06-26 14:34:47 -04:00
74ee34634e Refactor PayPal refund process by introducing a modular approach for refund context and rendering. This update consolidates refund logic into dedicated functions in inc_fx_paypal.php, enhancing maintainability and clarity. The commandes.php file is updated to utilize these new functions, improving the overall structure and readability of the refund handling code. 2026-06-26 14:26:06 -04:00
f1e43c911b Update access permissions from 'inscriptions_mobile' to 'inscriptions_gestion' across multiple files
This commit refactors the access control logic to replace references to 'inscriptions_mobile' with 'inscriptions_gestion', aligning the permission structure with the new management system. Changes include updates to SQL scripts, PHP functions, and documentation comments to ensure consistency in permission handling for event management. This enhances clarity and maintains the integrity of the access control system.
2026-06-26 14:16:09 -04:00
8570b73257 Refactor access control in documentation.php to restrict POST actions and display messages based on user login status. This change enhances security by ensuring only logged-in users can perform actions and receive appropriate feedback on the page. 2026-06-25 21:55:35 -04:00
c97478ba8a Add documentation section to inc_droite.php with navigation links 2026-06-25 20:15:38 -04:00
6f03e76540 Add WYSIWYG editor integration using Trumbowyg in doc_edition.php
This commit introduces the Trumbowyg WYSIWYG editor specifically for the `doc_edition.php` page, enhancing the document editing experience. It includes the necessary CSS and JavaScript files from CDN, along with custom styles and initialization scripts for the editor. This change does not affect other editors like TinyMCE or CKEditor used elsewhere on the site, ensuring a focused enhancement for this page.
2026-06-25 19:30:37 -04:00
365c51f6c8 Enhance event search functionality by prioritizing event ID matches
This commit updates the SQL query in `ajax_newpromoteur.php` to prioritize matches based on event IDs when searching for events. It introduces a new integer conversion for the search query to facilitate this matching. Additionally, the event name display in `inc_footer.php` is modified to include the event ID for better user context. These changes aim to improve the event search experience in the promoteur hub.
2026-06-25 18:29:51 -04:00
953775d9ca Refactor event access management by replacing SQL views with inline queries
This commit removes the deprecated SQL views `v_eve_acces_actif` and `v_eve_acces_permissions`, replacing them with inline SQL queries in the PHP code. This change avoids issues with non-copiable DEFINER attributes in views and enhances the flexibility of access management. The new functions `fxEveAccesActifBody`, `fxEveAccesActifSql`, and `fxEveAccesPermSql` are introduced to encapsulate the SQL logic directly within the application, improving maintainability and performance.
2026-06-25 18:24:39 -04:00
683e2d69d0 Update promoteur hub styles and enhance event section rendering
This commit modifies the CSS for the promoteur hub, introducing new styles for card elements, including updated colors, borders, and shadows for improved aesthetics. It also enhances the event section rendering by adding a specific class to the event title, ensuring consistent styling across the promoteur hub. Additionally, the version code is incremented to reflect these changes.
2026-06-25 17:15:37 -04:00
a45be35d6c Enhance promoteur hub card styling and update event card structure
This commit introduces new CSS styles for the promoteur hub card, including a background color, border, and shadow effects to improve visual presentation. It also updates the HTML structure of the event card, incorporating a new card icon and enhancing the event title styling for better readability. Additionally, it modifies the logic for displaying document triggers in the promoteur hub announcements section, ensuring a more cohesive user experience.
2026-06-25 17:06:21 -04:00
9a0ea70034 Refactor event modal to event panel and enhance UI interactions
This commit renames the `fxPromoteurHubRenderEventModal` function to `fxPromoteurHubRenderEventPanel`, transitioning from a modal to a collapsible panel for event details. It updates the HTML structure accordingly, improving user interaction with a toggle button and a rotating caret icon for better visual feedback. Additionally, the CSS is enhanced to support the new panel design, ensuring a more cohesive user experience in the promoteur hub.
2026-06-25 17:02:48 -04:00
d95f84bff7 Add event management links and enhance SQL entries
This commit introduces a new function, `fxPromoteurHubGetEventLinks`, to generate management and report links for events, improving the event management interface. It also updates the SQL script to include new entries for the 'Gestion' section in both French and English, enhancing the localization of the promoteur hub. These changes aim to streamline event management and improve user navigation within the promoteur hub.
2026-06-25 16:59:01 -04:00
7cee99e8e0 Add legacy event ID retrieval and merge with access events
This commit introduces a new function, `fxPromoteurHubGetLegacyEventIds`, to retrieve legacy event IDs for a given account. It updates the `fxPromoteurHubGetEvents` and `fxShowPromoteurHubMenuLink` functions to merge legacy event IDs with current access event IDs, ensuring comprehensive event access management. Additionally, it modifies SQL insert statements to update participant space labels for clarity. These changes aim to enhance the event management functionality and improve user experience.
2026-06-25 16:54:12 -04:00
ade9608e72 Refactor promoteur hub logic and introduce new checks
This commit refines the promoteur hub integration by replacing the existing function `fxPromoteurHubComUsesV2` with `fxPromoteurHubShouldUse`, which combines access checks and page existence validation. It updates the account management logic to ensure proper redirects based on the new checks, enhancing the user experience for promoteur hub accounts. Additionally, it introduces a new function to verify the existence of the promoteur hub page, streamlining the access management process.
2026-06-25 16:49:03 -04:00
0dcdcfc829 Implement promoteur hub integration and UI enhancements
This commit introduces the promoteur hub functionality by adding new checks and redirects based on the user's account type. It updates the account management logic to handle promoteur hub users, ensuring proper navigation and access. Additionally, it enhances the CSS for promoteur hub elements, improving the visual presentation. These changes aim to streamline user experience for promoteur hub accounts and ensure proper access management.
2026-06-25 16:45:03 -04:00
a5b680c273 Enhance JIRA integration with improved category handling and error logging
This commit refines the JIRA request handling by restructuring the `jira_resolve_categorie_interne` function to better manage the internal category based on the provided input. It introduces a new function, `jira_set_categorie_interne`, to update the category for JIRA issues, enhancing the integration's functionality. Additionally, it improves error logging for user creation and ticket submission processes, providing clearer feedback on failures. These changes aim to enhance the robustness and user experience of the JIRA integration.
2026-06-25 15:55:00 -04:00
2d8fe939a3 Update JIRA ticket creation and error handling in AJAX scripts
This commit modifies the JIRA ticket creation function to structure the 'categorie_interne' field as an associative array, improving data formatting. Additionally, it enhances error handling in the AJAX script by adding localized error messages for better user feedback during ticket submission failures. These changes aim to improve the robustness and user experience of the JIRA integration.
2026-06-25 15:43:07 -04:00
1f8616e4d6 Update event access management UI with enhanced table layout and styling
This commit improves the event access management interface by updating the table structure to include hover effects and a dark header for better visibility. It refines the display of role information, including icons and labels, and adjusts the layout for permissions, assignments, and status indicators to enhance clarity and usability. These changes aim to provide a more intuitive experience for managing event access.
2026-06-23 16:09:30 -04:00
5d686b28a4 Enhance event access management with email search functionality and URL adjustments
This commit introduces a new email search feature in the event access management interface, allowing users to find accounts by email for access assignment. It updates the `fxEveAccesCompteAdminUrl` function to include an optional event focus parameter, improving URL handling. Additionally, the AJAX endpoint is modified to return user names along with emails, enhancing the data available for selection. These changes aim to streamline the process of assigning event access and improve overall usability.
2026-06-23 15:18:43 -04:00
c047e36a62 Add event access management functions and UI integration
This commit introduces new functions for managing event access, including `fxEveAccesCompteAdminUrl` and `fxEveAccesEventFormGroupByCom`, which group active kits and extras by event ID. Additionally, it integrates the event access panel into the admin interface, enhancing the user experience with a structured display of access assignments and improved navigation for managing event permissions.
2026-06-23 15:15:48 -04:00
2855967e81 Enhance event access management with validation and UI improvements
This commit adds validation to ensure at least one kit is assigned before granting individual rights, improving the logic for event access management. It also updates the UI to provide clearer options for assigning kits and permissions, enhances the display of assigned kits and rights, and improves the overall layout for better usability.
2026-06-23 15:10:43 -04:00
439a52eb0f Refactor event access display and enhance UI layout
This commit updates the event access management interface by replacing the previous block layout with a structured table format for better clarity and usability. It simplifies the role selection process and improves the visual presentation of event kits. Additionally, it includes conditional messaging for cases with no access, ensuring a more intuitive user experience.
2026-06-23 15:05:05 -04:00
6a4d1761f3 Enhance event access management UI with improved layout and functionality
This commit updates the event access management interface by introducing a more user-friendly layout for displaying event access assignments. It adds dynamic role and permission selection options, enhances the visual presentation of event kits, and improves the overall usability of the form elements. Additionally, it includes conditional messaging for cases with no events or kits, ensuring a clearer user experience.
2026-06-23 15:01:05 -04:00
77a2508c71 Refactor event access management UI and logic
This commit enhances the event access management interface by updating the layout and improving the usability of form elements. It introduces a clearer display of event access assignments and refines the logic for handling active kits. Additionally, it conditionally displays the legacy promoter section based on the current context, ensuring a smoother transition towards the new access management system.
2026-06-23 14:43:58 -04:00
fd26c90098 Implement event access grouping and enhance UI for event management
This commit introduces a new function, `fxEveAccesCompteFormGroupByEvent`, to group active kits and extras by event ID for improved access management in the super admin interface. It also updates the UI to provide clearer information on event access, including a new section for legacy promoter events, and enhances the form elements for better usability. Additionally, the logic for retrieving promoter events is refined to handle empty cases more gracefully.
2026-06-23 14:41:03 -04:00
4e6eb3191b Add access management section to sidebar in admin interface
This commit introduces a new section in the admin sidebar for managing access, specifically adding a link for "Kits d'accès". The logic determines the active state based on the current script and query parameters, enhancing navigation for event access management.
2026-06-23 14:32:52 -04:00
2cf8f5e7ec restructurer les acces 2026-06-23 14:19:57 -04:00
bc8bc86b74 Refactor access control and UI elements for event management
This commit updates the event management access control logic in `inc_fx_eve_acces.php` to ensure proper permission checks for managing bibs. It also simplifies the UI in `inc_tableau_promoteur.php` by removing conditional rendering for the management link, ensuring it is always displayed. Additionally, the comment in `inc_tableau_gestion_epreuves.php` is updated for clarity.
2026-06-23 12:36:56 -04:00
0b39f08ec7 Update .htaccess for improved routing and PHP handler configuration
This commit modifies the .htaccess file to implement a redirect for non-www domains to www for production clients, while ensuring compatibility with development environments. It also refines the routing rules for CI4 and legacy routes, and adds a PHP handler configuration for version 8.1, enhancing server-side processing capabilities.
2026-06-23 11:44:52 -04:00
100accfe34 Remove unused 'variables' table definition from static synchronization configuration 2026-06-23 10:10:06 -04:00
b3b24b08c4 Refactor database connection settings and introduce profile management functions
This commit adds structured database connection profiles for client and CRM environments, enhancing the management of database connections in different environments (preprod and prod). It introduces new functions, `fxSettingsUseDbProfiles` and `fxSettingsUseCrmProfile`, to streamline the assignment of connection parameters, improving code maintainability and readability. Additionally, it removes hardcoded connection details from the main logic, promoting a more dynamic configuration approach.
2026-06-23 10:08:10 -04:00
74da043805 Remove unused static synchronization table definitions from configuration 2026-06-23 09:39:17 -04:00
8eda3d40c3 Refactor static database synchronization configuration and connection handling
This commit updates the static database synchronization logic by removing hardcoded connection details from the `fxStaticSyncClientDatabaseConfigs` function. It now relies on configurations defined in `inc_settings.php` for client connections. The `fxStaticSyncEnvironmentDefinitions` and `fxStaticSyncOpenConnections` functions are modified to utilize these new configurations, enhancing flexibility and maintainability in managing database connections for different environments.
2026-06-23 09:37:49 -04:00
d74d06a3b0 Enhance static database connection handling and synchronization logic
This commit updates the `fxStaticSyncClientDatabaseConfigs` function to return detailed database connection configurations for both preprod and prod clients. It introduces a new class, `clsMysqlStaticSync`, to manage MySQL connections with a short timeout, improving connection handling. The `fxStaticSyncConnect` function is refined to utilize this new class, and the synchronization UI is updated to reflect the new client configurations. These changes streamline database interactions and enhance the overall synchronization process.
2026-06-23 09:20:28 -04:00
1797e0c91e Refactor static database connection handling and update synchronization functions
This commit removes hardcoded database connection details from the `fxStaticSyncClientDatabaseConfigs` function, returning an empty array instead. It also refines the `fxStaticSyncConnect` function to utilize `mysqli_init` for improved connection management and introduces a new `fxStaticSyncOpenConnections` function to reuse existing connections. Additionally, the script execution time limit is increased in `sync_static_db.php` to accommodate longer operations.
2026-06-23 09:17:25 -04:00
a23f492621 Refactor static database synchronization access and update UI elements
This commit enhances the static database synchronization feature by refining the `fxSuperadmStaticSyncAllowed` function to improve access control. The UI is updated to conditionally display synchronization options in both the header and sidebar, ensuring that only authorized users can access these tools. This change streamlines the user experience while maintaining security in the admin interface.
2026-06-23 09:12:20 -04:00
02384d3404 Remove unused bib input focus handling in mobile registration and increment version code to 4.72.699 2026-06-22 15:45:11 -04:00
547390286b Enhance mobile registration feedback and fallback text handling
This commit improves the mobile registration module by refining the feedback messages displayed during the bib scanning process. It introduces additional conditions for fallback text to ensure users receive appropriate guidance based on their input. The version code is incremented to 4.72.698 to reflect these updates.
2026-06-22 15:43:48 -04:00
596f3a55c1 Enhance mobile registration UI and functionality for QR and bib scanning
This commit introduces improvements to the mobile registration module by adding a new parameter for QR scanning and enhancing the UI for bib input sections. It includes new functions for rendering collapsible sections in the mobile interface, improving user interaction and accessibility. The CSS is updated to accommodate new styles and layout adjustments, ensuring a better user experience. The version code is incremented to 4.72.697 to reflect these changes.
2026-06-22 15:42:22 -04:00
30b17be43e Refactor mobile bib scanning functionality and enhance UI feedback
This commit refines the mobile bib scanning process by introducing a new capture element and updating the UI to provide clearer feedback during scanning. It removes the unused OCR interval and enhances the event handling for capture actions. Additionally, new messages are added for user guidance, and the version code is incremented to 4.72.696 to reflect these changes. The SQL file is updated to include new translation keys for improved user instructions.
2026-06-22 15:12:34 -04:00
adcb663925 Enhance OCR functionality and UI feedback for mobile registration
This commit improves the Optical Character Recognition (OCR) process in the mobile registration module by introducing a global OCR worker for better management and performance. It updates the UI to include a new loading message during the initialization phase and refines the scanning process. The version code is incremented to 4.72.695 to reflect these enhancements. Additionally, the SQL file is updated to include the new loading message in multiple languages.
2026-06-22 15:07:01 -04:00
e8883cfbf8 Refactor mobile registration QR scanning and update UI elements
This commit streamlines the mobile registration module by removing unused QR mode functionalities and updating the UI for the bib scanning process. It introduces new feedback messages and adjusts the layout for improved user experience. The version code is updated to 4.72.694 to reflect these changes. Additionally, the SQL file is modified to align with the new bib scan terminology.
2026-06-22 15:02:54 -04:00
fd469bdc22 Enhance mobile registration with OCR for dossier scanning and update UI elements
This commit introduces Optical Character Recognition (OCR) functionality for scanning dossier numbers in the mobile registration module. New functions are added to manage the OCR process, including starting and stopping the camera, and updating the UI with relevant feedback messages. Additionally, the version code is updated to 4.72.693 to reflect these enhancements. Unused SQL files related to previous QR code modes have been removed to streamline the codebase.
2026-06-22 14:56:54 -04:00
22534b4cbf Implement OCR functionality for dossier scanning in mobile registration
This commit enhances the mobile registration module by introducing Optical Character Recognition (OCR) capabilities for scanning dossier numbers. New functions are added to manage the OCR process, including preloading the OCR worker, starting and stopping the OCR loop, and updating the UI with scanning status messages. Additionally, the UI is improved with new elements for photo input and feedback messages. The version code is updated to 4.72.692 to reflect these changes.
2026-06-22 14:50:34 -04:00
b5c979058f Enhance QR code scanning functionality and UI for mobile registration
This commit introduces significant updates to the QR code scanning feature in the mobile registration module. It adds new functions for managing QR modes (participant and dossier), improving user interaction with the QR scanner. The UI is enhanced with new elements for mode selection and feedback messages, ensuring a clearer user experience. Additionally, the version code is updated to 4.72.691 to reflect these changes.
2026-06-22 14:36:57 -04:00
6e751b552f Refactor static sync access control and update UI elements
This commit replaces the `fxAdminStaticSyncAllowed` function with `fxSuperadmStaticSyncAllowed` to streamline access control for static database synchronization tools. The UI is updated accordingly to reflect this change, ensuring that only authorized users in development and pre-production environments can access these tools. This enhancement improves the overall user experience and maintains security in the admin interface.
2026-06-22 14:10:24 -04:00
3e3a0f1de7 Add static sync functionality and update UI for development tools
This commit introduces the `fxAdminStaticSyncAllowed` function to control access to static database synchronization tools, ensuring only authorized users in development and pre-production environments can access them. The UI is updated to display a new section for these tools in the admin interface, enhancing the overall user experience and access management for development tasks. Additionally, the `.gitignore` file is updated to exclude the `/output/` directory from version control.
2026-06-22 14:08:16 -04:00
87783ad99c Add reactivation functions for event access and update UI for revoked permissions
This commit introduces new functions `fxEveAccesReactivate` and `fxEveAccesReactivateExtra` to handle the reactivation of revoked event access and extra permissions. It also enhances the UI to display a history of revoked permissions, allowing users to reactivate them easily. Additionally, the logic for checking active access has been streamlined, improving overall user experience and access management.
2026-06-19 19:46:45 -04:00
f881b42f7f Update text editing access control and enhance environment checks
This commit modifies the `fxAdminIsDevPreprod` function to improve environment validation for text editing tools, ensuring access is restricted to the appropriate development and pre-production environments. Additionally, it introduces a 403 Forbidden response in `traduction.php` for unauthorized access attempts, enhancing security and user feedback.
2026-06-19 14:37:08 -04:00
d118f5bce7 Add error handling for missing bib assignments and enhance anomaly reporting
This commit introduces new functions to manage errors related to missing bib assignments, specifically for the 'bib_auto_miss' type. It includes logic to resolve open errors when a bib is manually assigned and adds a new function to retrieve and display anomalies for registrations without assigned bibs. Additionally, the UI is updated to provide users with actionable links for resolving these anomalies, improving overall user experience and error management.
2026-06-19 13:39:39 -04:00
c12cca2de9 Enhance mobile registration search functionality and update version code
This commit introduces new functions for resetting search parameters and building search reset URLs in the mobile registration module. It improves the search logic by allowing for more flexible filtering options, including updates to the SQL query conditions for team names and order numbers. Additionally, the version code is incremented to 4.72.690 to reflect these changes.
2026-06-19 13:35:23 -04:00
9c290a66ca Update mobile registration access functions and enhance event permission checks
This commit modifies the mobile registration access logic to utilize the new `fxEveAccesHasInscrMobileWebAccess` function, improving permission handling for mobile event registrations. Additionally, it updates the `fxInscrMobileGetPromoteurEveIds` and `fxInscrMobileCanAccess` functions to streamline access checks. New functions for retrieving event IDs with mobile access permissions are introduced, enhancing overall access control and user experience.
2026-06-19 13:31:00 -04:00
5d99fef4ed Refactor event access functions and streamline mobile registration logic
This commit updates the `fxEveAccesCanQrTest` function to clarify permission handling for QR testing. It also simplifies the `fxInscrMobileCanScan` function by directly returning the permission check result. Additionally, redundant code related to rendering mobile registration fields is removed, enhancing the overall efficiency and readability of the mobile registration logic.
2026-06-19 13:26:49 -04:00
9310c12536 Add event access management functions and enhance mobile permissions
This commit introduces several new functions for managing event access, including `fxEveAccesComHasV2ForEvent`, `fxEveAccesGrantExtra`, and `fxEveAccesRevokeExtra`, which facilitate the granting and revocation of permissions for events. Additionally, the mobile registration logic is updated with the new `fxInscrMobileCanDo` function to streamline permission checks for various actions. These enhancements improve the overall access control and user experience across the application.
2026-06-19 13:20:52 -04:00
e9ad3d8a92 Enhance event access control and integrate new permission checks
This commit introduces the `fxEveAccesCanManageBibV4` and `fxEveAccesCanQrTest` functions to improve event access management. It updates various files to utilize these new permission checks, ensuring that only authorized users can manage event registrations and access QR testing features. Additionally, redundant session checks are removed, and redirection logic is refined for better user experience across the application.
2026-06-19 12:49:19 -04:00
d5fdb227dd Update mobile registration menu labels and enhance SQL entries for V2
This commit modifies the mobile registration menu labels to reflect the new versioning, changing the text to "Gestion des inscriptions V2" in French and "Registration management V2" in English. Additionally, it introduces a new function, `fxInscrMobilePromoteurMenuLabel`, to standardize the label retrieval process. The icon for the mobile registration link is also updated to improve visual consistency across the interface.
2026-06-19 12:43:54 -04:00
460d5df27f Implement mobile login enhancements and UI updates
This commit introduces a mobile login feature with validation messages for username and password fields, improving user experience. It also updates the header and footer to conditionally render elements based on mobile entry status, ensuring a cohesive design. Additionally, CSS styles are added to enhance the mobile interface, and the version code is incremented to 4.72.689 to reflect these changes.
2026-06-19 12:39:47 -04:00
fb4f74d48f Update mobile registration links and enhance access logic
This commit modifies the mobile registration links to point to the correct routes, ensuring proper navigation for users. Additionally, it refines the logic for displaying mobile access based on user permissions, introducing a new function to check for legacy promoteur menu access. These changes improve the user experience and maintain consistency in the mobile module's functionality.
2026-06-19 12:32:43 -04:00
52f9b632fb Enhance mobile text management and internationalization support
This commit introduces several new functions for managing text localization in the mobile module, including `fxInscrMobileInitTextes`, `fxInscrMobileLoadTextes`, and `fxInscrMobileResolveTexte`. These changes improve the handling of multilingual text by loading and resolving localized strings based on the selected language. Additionally, the mobile page initialization logic is updated to ensure proper text loading, and existing text retrieval methods are refactored for consistency and clarity.
2026-06-19 12:29:58 -04:00
af2b0a7faa Refactor mobile registration access logic to utilize new permission functions
This commit updates the mobile registration access checks across multiple files to use the newly implemented `fxInscrMobileCanAccess` and `fxInscrMobileCanScan` functions. The changes enhance the permission handling for mobile event registrations, ensuring a more robust access control mechanism. Additionally, the mobile URL generation is updated for consistency, and new functions for managing event access permissions are introduced to streamline the codebase.
2026-06-19 11:55:00 -04:00
7de632d9fc gestion des droit 2026-06-19 11:45:53 -04:00
6403f371fe Enhance batch and auto selection UI with new prompts and visibility logic; increment version code to 4.72.688
This commit introduces a new prompt for sequence selection in batch and auto modes, improving user guidance. The visibility logic for various action groups is refined based on selection states, ensuring a clearer interface. Additionally, CSS styles are updated to support the new prompt, enhancing the overall user experience in the bib management system.
2026-06-19 11:43:57 -04:00
717f11e3d5 Refactor fxBibTexte function to include fallback for missing keys and add new fxBibStaticFallback function for bilingual support
This commit modifies the `fxBibTexte` function to utilize a new fallback mechanism via `fxBibStaticFallback`, which provides default translations for missing or incorrectly entered keys in both French and English. Additionally, the error handling in the bib view is updated to ensure proper display of messages for unassigned and duplicate bibs. These changes enhance the robustness and user experience of the bib management system.
2026-06-19 11:32:09 -04:00
026473dd59 Implement cancellation label function and update SQL for bilingual support in bib view
This commit introduces a new function, `fxBibViewCancelledLabel`, to handle the display of cancellation labels in both French and English. Additionally, SQL updates ensure that the correct cancellation text is set for both languages, enhancing the user experience in the bib view. These changes improve the clarity and accessibility of cancellation information for participants.
2026-06-19 11:12:20 -04:00
16b8637192 Enhance cancellation handling in bib view and update SQL queries for participant retrieval; increment version code to 4.72.687
This commit improves the handling of cancelled participants in the bib view by refining the logic for displaying cancellation status and updating the associated SQL queries for participant retrieval. The version code is incremented to reflect these changes, ensuring consistency in versioning.
2026-06-19 11:09:47 -04:00
f9e1715de1 Update cancellation styling in bib view and increment version code to 4.72.686
This commit enhances the visual representation of cancelled participants in the bib view by applying a new background color and adding a label for cancelled status. The version code is updated to reflect these changes, ensuring consistency in versioning.
2026-06-19 11:04:56 -04:00
e045ff3f98 Implement cancellation styling in bib view and update SQL queries for participant filtering; increment version code to 4.72.685
This commit introduces a new CSS class for cancelled bib view rows, enhancing visual distinction with a gradient background and color adjustments. Additionally, SQL queries in the participant filtering logic are updated to remove the cancellation condition, allowing for more flexible data retrieval. The version code is incremented to reflect these changes.
2026-06-19 11:03:28 -04:00
fd1d136842 Add SQL functions for duplicate unit count and anomaly detection in bib management
This commit introduces two new SQL functions: `fxBibSqlDuplicateUnitCount`, which counts the number of units sharing a `no_bib` for an event, and `fxBibSqlIsBibDuplicateAnomaly`, which checks for duplicate anomalies excluding legitimate team sharing. These functions enhance the existing bib management logic by improving the accuracy of duplicate detection. Additionally, the existing SQL queries in `fxInfosBibRange` and `fxBibAnomalyDuplicateBibs` are updated to utilize the new functions for better clarity and efficiency.
2026-06-19 10:56:09 -04:00
a8bcd26cdd Update spacing in summary headers of fxBibEpreuveHeaderSummaries function for improved readability 2026-06-19 10:22:55 -04:00
9c4c11efc9 Enhance batch processing interface by updating visibility logic and adding new UI elements; increment version code to 4.72.684
This commit refines the batch processing interface by improving the visibility logic for various action groups based on selection states. New UI elements, including a prompt for batch picking and a warning for reset actions, are introduced to enhance user guidance. CSS styles are updated to support these changes, ensuring a clearer and more interactive experience. The version code is incremented to reflect these updates.
2026-06-19 10:17:00 -04:00
d405f2aa99 Update CSS styles for mobile registration interface and increment version code to 4.72.683
This commit modifies the CSS for the mobile registration interface, enhancing the visual hierarchy and readability by adjusting font weights and colors. The version code is updated to reflect these changes, ensuring consistency in versioning.
2026-06-19 10:09:46 -04:00
09f57dcce0 Refactor mobile registration profile rendering and update CSS for improved layout
This commit enhances the mobile registration interface by refactoring the profile rendering logic to include a flag for captains and updating the associated CSS for better layout and responsiveness. The version code is incremented to 4.72.682 to reflect these changes, further improving the user experience in mobile registrations.
2026-06-19 10:07:26 -04:00
3a3ebec2bf Enhance mobile registration interface by introducing key-value list rendering functions and updating CSS for improved layout. This commit adds new functions to render participant profile details in a structured format, enhancing readability and organization. The CSS is updated to support the new layout, including adjustments to padding and grid structure. The version code is incremented to 4.72.680 to reflect these changes, further improving the user experience in mobile registrations. 2026-06-19 10:03:01 -04:00
6bffbba726 Enhance mobile registration interface by adding profile details rendering functionality; update CSS for improved layout
This commit introduces new functions to fetch and render participant profile details within the mobile registration interface, including role, name, email, birthdate, and custom questions. The CSS is updated to improve the layout of the profile section. The version code is incremented to 4.72.679 to reflect these changes, enhancing the overall user experience in mobile registrations.
2026-06-19 09:59:10 -04:00
7bb57ded70 Fix bib display logic in mobile registration interface to remove unnecessary prefix; improve clarity of rendered output 2026-06-19 09:29:26 -04:00
36280f0235 Implement QR code scanning functionality in mobile registration interface; enhance backend processing and update styles
This commit introduces a new QR code scanning feature within the mobile registration interface, allowing users to scan QR codes for event registration. The JavaScript logic for handling QR code detection and feedback is added, along with corresponding PHP functions for processing scanned data. CSS styles are updated to improve the layout of the QR scanning panel. The version code is incremented to 4.72.678 to reflect these changes, enhancing the overall user experience in mobile registrations.
2026-06-19 09:19:31 -04:00
b068157f87 Enhance mobile registration interface with new section title and panel summary rendering functions; update CSS for improved layout and styling
This commit introduces new functions for rendering section titles and panel summaries in the mobile registration interface, improving code organization and readability. CSS styles are updated to enhance the layout, including flexbox adjustments for better alignment and spacing. The version code is incremented to 4.72.677 to reflect these changes.
2026-06-19 09:15:09 -04:00
164e26af91 Enhance mobile registration interface with new navigation and page head rendering functions; update styles for improved layout and responsiveness
This commit introduces new functions for rendering navigation and page headers in the mobile registration interface, improving code organization and readability. CSS styles are updated to enhance the layout and responsiveness of the mobile interface, including adjustments to list items and section management. The version code is incremented to 4.72.676 to reflect these changes.
2026-06-19 09:12:09 -04:00
0157f34949 Refactor translation function calls in inc_fx_promoteur.php for improved readability; remove unnecessary echo statements 2026-06-19 09:00:26 -04:00
b64cee25d6 Refactor visibility logic for reset actions in footer scripts; improve user interaction in mobile registration
This commit updates the visibility logic for the reset action group in the mobile registration interface. The new implementation consolidates conditions into a single variable, enhancing readability and maintainability. This change aims to improve user interaction by ensuring the correct display of reset options based on the current state of the registration process.
2026-06-19 08:54:47 -04:00
cdf4e5188a Refactor mobile registration status management; enhance AJAX handling and update version code
This commit encapsulates the refactoring of the mobile registration status management by encapsulating the logic within an immediately invoked function expression (IIFE) for better organization. The AJAX handling for status changes has been improved, ensuring smoother user interactions and error handling. The version code is incremented to 4.72.675 to reflect these updates, further enhancing the mobile user experience.
2026-06-18 16:36:43 -04:00
514fae950c Update SQL query in inc_manager.php to include participant status; enhance data retrieval for mobile registration 2026-06-18 16:31:45 -04:00
d64b18c9fb Refactor mobile registration status management to use select elements; update version code
This commit modifies the mobile registration interface to replace radio buttons with a select dropdown for participant status management. The JavaScript and CSS are updated accordingly to enhance user interaction and maintain layout consistency. The version code is incremented to 4.72.674 to reflect these changes, further improving the mobile user experience.
2026-06-18 16:24:24 -04:00
2cb6a59c96 Implement participant status management in mobile registration; update version code
This commit introduces a new feature for managing participant status within the mobile registration interface. It includes updates to the PHP functions for handling status changes, new JavaScript for dynamic updates, and corresponding CSS styles for improved layout. The version code is incremented to 4.72.673 to reflect these enhancements, further optimizing the user experience in managing participant registrations.
2026-06-18 16:22:02 -04:00
c0b6f0c696 Enhance mobile registration interface with new editable fields and status display; update version code
This commit introduces new CSS styles for the mobile registration interface, including editable fields and a status display section. The PHP code is updated to render these new elements, improving user interaction and information clarity. Additionally, the version code is incremented to 4.72.672 to reflect these enhancements, further optimizing the mobile user experience.
2026-06-18 16:16:14 -04:00
146f692a46 Enhance mobile registration interface and update version code
This commit introduces several improvements to the mobile registration functionality, including the addition of a new function for parsing event IDs and updates to the CSS for a cleaner layout. The logic for displaying event information has been refined, and new JavaScript features have been added to improve user navigation. The version code is incremented to 4.72.671 to reflect these enhancements, further optimizing the mobile user experience.
2026-06-18 13:48:01 -04:00
b7fa26d1e9 Implement mobile registration functionality and update version code
This commit introduces new routing rules in .htaccess for mobile access, adds mobile-specific PHP includes and logic in compte.php, and enhances the footer scripts for mobile page handling. Additionally, new CSS styles are added to support the mobile registration interface. The version code is incremented to 4.72.670 to reflect these changes, improving the overall user experience for mobile users.
2026-06-18 13:35:36 -04:00
a1c8fc787e Enhance AJAX functionality for bib management by introducing a centralized access control function and updating existing actions to utilize it. The version code is incremented to 4.72.669 to reflect these changes, improving security and consistency in AJAX requests. 2026-06-18 13:04:06 -04:00
ef2275c54d Update CSS for improved layout and button styles; enhance PHP functions for translation handling; increment version code 2026-06-18 11:56:34 -04:00
0495ded5c2 Enhance bib management with translation buttons and CSS updates; increment version code
This commit introduces new functions for handling translation buttons in the bib management system, improving user interaction with translated content. The CSS is updated to style these buttons appropriately within the bib summary sections. Additionally, the version code is incremented to 4.72.667 to reflect these enhancements, further improving the user experience in managing bib assignments.
2026-06-18 11:48:10 -04:00
1d86258926 Refactor page visibility logic and update version code
This commit modifies the JavaScript to ensure that the 'hidden' class is removed when a page is displayed, improving the visibility handling of document pages. Additionally, the PHP code is updated to streamline the generation of page elements by adjusting the placement of the 'hidden' attribute. The version code is incremented to 4.72.666 to reflect these changes, enhancing the overall user experience in document navigation.
2026-06-18 10:50:44 -04:00
8258e7ad29 Implement dashboard anomalies feature for bib management
This commit introduces a new functionality to handle dashboard anomalies related to bibs, including duplicate and orphaned bibs. The PHP code is updated to include a new AJAX action for collecting anomalies, while JavaScript functions are added to refresh the anomalies panel dynamically. Additionally, new CSS styles are introduced to enhance the layout and presentation of the anomalies information. The version code is incremented to 4.72.665 to reflect these changes, improving the overall user experience in managing bib assignments.
2026-06-18 10:42:27 -04:00
42bd7c36de Refactor SQL queries to handle 'no_bib' as unsigned integers
This commit introduces a new function, fxBibSqlNoBibUnsigned, to ensure that 'no_bib' values are treated as unsigned integers in SQL queries. The existing SQL statements in ajax_bib_range.php and inc_fx_promoteur.php are updated to utilize this function, enhancing data integrity and comparison accuracy. These changes improve the overall reliability of bib number handling in the application.
2026-06-18 10:37:04 -04:00
0c1fc53331 Implement auto assignment checks and enhance footer scripts; increment version code
This commit adds new JavaScript functions to manage pending bib assignments and alert users when automatic activation is blocked due to unassigned registrations. The PHP code is updated to include a new function that verifies all bibs are assigned before allowing auto activation. Additionally, the version code is incremented to 4.72.664 to reflect these changes, improving the overall functionality and user experience in the bib assignment process.
2026-06-18 10:21:36 -04:00
2b117cdea4 Refactor footer scripts and CSS for auto assignment actions; increment version code
This commit updates the footer scripts to enhance the visibility logic for the auto assignment actions, ensuring the appropriate elements are displayed based on the current state. Additionally, new CSS styles are introduced for the auto configuration layout, improving the organization and alignment of action buttons. The version code is incremented to 4.72.663 to reflect these changes, enhancing the user interface and experience.
2026-06-18 10:13:59 -04:00
77564c0f46 Update CSS styles for grid layout and button design; increment version code
This commit modifies the CSS to adjust grid column widths for better alignment and updates the button styles for improved visual consistency. The version code is incremented to 4.72.660, reflecting these changes. These updates enhance the user interface by providing a more organized layout and refined button aesthetics.
2026-06-18 09:59:06 -04:00
27e581c40a Implement locking mechanism for bib ranges with AJAX support
This commit introduces a locking feature for bib ranges, allowing users to lock or unlock sequences via a new AJAX endpoint. The PHP code is updated to check for locked statuses before performing actions, enhancing data integrity. Additionally, new JavaScript functions are added to handle user interactions for locking and unlocking, along with corresponding CSS styles for visual feedback. This improves the user experience by preventing modifications to locked sequences and providing clear confirmation messages.
2026-06-18 09:55:49 -04:00
33a5e0a410 Add functions for auto assignment availability checks and update SQL for error messaging
This commit introduces two new PHP functions: fxBibRangeDispoFromRow, which calculates available bibs in a sequence, and fxBibIsRangeAvailableForAuto, which checks if a sequence is available for automatic assignment. Additionally, the fxSaveBibAutoConfig function is updated to utilize these checks, enhancing error handling for full sequences. SQL scripts are also updated to include localized error messages for cases when a selected sequence is full, improving user feedback in both French and English.
2026-06-18 09:48:42 -04:00
2a9e38fdea Refactor CSS and PHP for badge layout and rendering improvements
This commit updates the CSS to enhance the layout of auto badges, positioning them to the right and adjusting the grid structure. In the PHP code, the badge description is modified for clarity, and the rendering logic is streamlined by removing unnecessary elements. These changes improve the user interface by providing a more organized and visually appealing display of sequence information.
2026-06-18 09:41:22 -04:00
71d850531d Update CSS and PHP for badge rendering and layout adjustments in batch mode
This commit modifies the CSS to introduce a new layout for auto badges in batch mode, ensuring they are hidden when not needed. The PHP functions are updated to include an optional parameter for displaying the auto badge, enhancing the summary information for sequences. These changes improve the overall user interface and experience by providing clearer visual cues and better organization of sequence data.
2026-06-18 09:38:44 -04:00
2fb6db9bd4 Remove unused auto badge visibility logic and introduce new badge rendering function for active sequences
This commit eliminates the visibility logic for the auto badge in the footer scripts, streamlining the code. Additionally, a new function, fxBibRenderRangeAutoBadge, is introduced to render the auto badge for active sequences, enhancing the display of sequence information. CSS styles are also added for the new badge, improving the visual presentation in the interface. No version code change is included in this update.
2026-06-18 09:33:26 -04:00
d0db73624e Enhance fxBibEpreuveDisplayName function to include inactive status and improve display logic
This commit updates the fxBibEpreuveDisplayName function to provide a more comprehensive display name for events by incorporating both type and category information. It also adds logic to append an "(inactive)" label when the event is not active. Additionally, new SQL entries are introduced to support localized messages for inactive events in both French and English, improving user feedback in the interface. No version code change is included in this update.
2026-06-18 09:24:25 -04:00
b71d1c300b Implement overlap detection and detailed error messaging for bib sequences in AJAX responses
This commit introduces a new function, fxBibGetOverlappingRanges, to check for overlapping bib sequences within a specified event. The validation logic in ajax_bib_range.php is updated to utilize this function, enhancing the accuracy of overlap detection. Additionally, detailed error messages are formatted for both single and multiple overlaps, improving user feedback in the interface. SQL scripts are updated to include new localized messages for overlap conflicts in both French and English. No version code change is included in this update.
2026-06-18 09:19:01 -04:00
bdb33801c9 Refactor button styles and enhance action group layout in event management interface
This commit updates button classes for the auto action feature, replacing success and secondary styles with a new 'btn-bib-auto' class for better visual distinction. It also introduces new CSS styles for the action group layout, ensuring buttons are aligned correctly. The version code has been incremented to 4.72.659 to reflect these changes.
2026-06-17 16:15:57 -04:00
7a3e9395e6 Enhance fxBibAideButton functionality and introduce segment helper functions
This commit modifies the fxBibAideButton function to include an optional parameter for controlling the display of translation buttons. Additionally, new helper functions fxBibBtnAideSegmentOpen and fxBibBtnAideSegmentClose are introduced to streamline the rendering of action segments in the interface. These changes improve the organization and flexibility of the button rendering logic, enhancing the overall user experience. No version code change is included in this update.
2026-06-17 16:13:07 -04:00
8d78de304b Refactor localization handling in event management interface
This commit streamlines the localization process by removing unnecessary parameters from functions and consolidating the logic for retrieving localized strings. The changes enhance the clarity and maintainability of the code, ensuring that the interface dynamically reflects the selected language. No version code change is included in this update.
2026-06-17 16:08:03 -04:00
9c105565d1 Implement localization for JavaScript messages in the event management interface
This commit introduces a new function to inject data attributes for JavaScript localization in the #module-bib element. It also updates various text strings used in the interface to utilize localized messages, enhancing user experience by providing contextually relevant feedback in both French and English. Additionally, a new SQL script is added to populate the database with these localized strings, ensuring they are available for dynamic use. No version code change is included in this update.
2026-06-17 15:54:35 -04:00
69f360eae8 Implement dynamic language handling and improve error messaging in AJAX responses
This commit enhances the AJAX functionality by introducing a new language handling mechanism, allowing for dynamic language selection based on user input. Additionally, error messages throughout the AJAX responses have been updated to utilize the new fxBibMsg function for better localization and clarity. This improves user experience by providing contextually relevant messages in the selected language. No version code change is included in this update.
2026-06-17 15:49:47 -04:00
5e31ea1129 Enhance tooltip functionality in event management interface with new fxBibAideTippyAttr function
This commit introduces the fxBibAideTippyAttr function to provide dynamic tooltips for buttons in the event management interface. The function retrieves contextual help text, improving user experience by offering additional information on hover. Updates to button elements ensure tooltips are displayed where relevant, enhancing clarity and usability. No version code change is included in this update.
2026-06-17 15:45:51 -04:00
8a13fbfc0d Update event management interface for improved localization and error handling
This commit enhances the event management interface by replacing static text with localized strings using the fxBibTexte function. Changes include updates to button labels, error messages, and section headers to ensure better language support and clarity for users. No version code change is included in this update.
2026-06-17 15:41:17 -04:00
5e64d0ecf6 Refactor team mode labels and improve language handling in event management interface
This commit updates the team mode option labels to utilize the fxBibTexte function for better localization support. The changes enhance the clarity of the interface by ensuring that the labels are dynamically generated based on the selected language. No version code change is included in this update.
2026-06-17 15:37:38 -04:00
3b8c867c72 Fix URL formatting in event management navigation link for better readability 2026-06-17 15:11:15 -04:00
e55d30d61b Enhance event management interface with new quantity input feature and update version code to 4.72.657
This commit introduces a new compact quantity input feature for managing event quantities, improving user interaction. Additionally, CSS styles have been refined for better layout consistency across the interface. The version code has been incremented to reflect these updates.
2026-06-17 15:01:05 -04:00
57bceba425 Remove diagnostic script and update version code to 4.72.656
This commit deletes the temporary diagnostic script used for troubleshooting includes and connections, as it is no longer needed. Additionally, the version code has been incremented to reflect this change.
2026-06-17 14:56:19 -04:00
04925de3dc Refactor CSS layout for identity blocks and update version code to 4.72.655
This commit enhances the CSS styles for the event management interface, specifically refining the layout of identity blocks with improved grid settings, padding, and border styles. Additionally, the version code has been incremented to reflect these updates.
2026-06-17 14:51:03 -04:00
8fa390a5e6 Update CSS styles for improved layout and increment version code to 4.72.654
This commit modifies the CSS styles to enhance the layout of the event management interface, including adjustments to flex properties and padding for better responsiveness. The version code has been updated to reflect these changes.
2026-06-17 14:46:07 -04:00
a26de022e2 Enhance event management interface with new identity header and summary features
This commit introduces new CSS styles and PHP functions to improve the event management interface. It adds a visually cohesive identity header for events, displaying relevant summaries such as team/individual status and registration counts. The version code has been incremented to 4.72.653 to reflect these updates.
2026-06-17 14:41:33 -04:00
05fd4d35ff Add new header summary features for event management interface
This commit introduces new CSS styles and PHP functions to enhance the event management interface with summary badges for available quantities and sequence counts. The collapsible blocks now display relevant summaries, improving user navigation and information visibility. The version code has been incremented to 4.72.652 to reflect these updates.
2026-06-17 14:35:58 -04:00
b44a100bad Add collapsible functionality for management blocks in event interface
This commit introduces a collapsible feature for the management blocks in the event interface, allowing users to expand or collapse sections for better navigation. New CSS styles have been added to support this functionality, and a button for toggling the visibility of the content has been implemented. The version code has been incremented to 4.72.651 to reflect these changes.
2026-06-17 14:32:49 -04:00
ff5533bee1 Add session handling for navigation context in waiting list management
This commit enhances the waiting list management functionality by preserving the original navigation context through session variables. It introduces logic to store and retrieve the 'from' parameter, ensuring users return to the correct page after actions. Additionally, the back navigation URL generation has been updated to utilize the new context-aware functions, improving user experience and code maintainability.
2026-06-17 14:27:09 -04:00
af07182682 Refactor back navigation URL handling in event management interface
This commit introduces a new function, fxGetPromoteurBackUrl, to streamline the generation of back navigation URLs based on the event ID and the 'from' parameter. The event management interface has been updated to utilize this function, improving code maintainability and enhancing user navigation experience. The changes ensure that the correct return URL is generated based on the context of the user's navigation.
2026-06-17 14:20:15 -04:00
3f04015669 Add conditional navigation item for managing event epreuves based on quantity modifiability
This commit introduces a new navigation item in the promoteur panel that appears only if the event's quantity is modifiable. The SQL query to check the modifiability has been added, and the corresponding UI element is displayed conditionally. Additionally, the icon for the existing management link has been updated for consistency. These changes enhance the user experience by providing relevant options based on event settings.
2026-06-17 14:16:27 -04:00
b773f767b7 Refactor event management interface by replacing V2 with new gestion epreuves functionality
This commit updates the event management interface by replacing the previous V2 implementation with a new gestion epreuves section. The associated PHP files have been modified to include the new functionality, and the old V2 files have been removed. CSS styles have been cleaned up to reflect these changes. The version code has been incremented to 4.72.650 to signify the updates made.
2026-06-17 14:13:08 -04:00
8f49173141 Add new functionality for managing event V2 in promoteur panel
This commit introduces a new section in the promoteur panel for managing event V2, including a dedicated function to display event details and readonly quantities. The UI has been updated with new CSS styles for better layout and presentation. Additionally, a new navigation item has been added for accessing the V2 management interface. The version code has been incremented to 4.72.649 to reflect these changes.
2026-06-17 14:03:58 -04:00
6e2f357f14 Update readonly notice text in event management panel for improved clarity and increment version code to 4.72.648 in inc_settings.php. 2026-06-17 13:38:19 -04:00
be68e4f21e Add MySQL connection diagnostics to diag.php
This commit introduces a new section in diag.php to test MySQL connections for different environments (Main, Prod, PreProd) and outputs the connection status along with error messages if applicable. Additionally, it removes the previous promoteur file check and enhances the overall diagnostic output for better clarity. This aims to improve troubleshooting capabilities for database connectivity issues.
2026-06-17 13:36:39 -04:00
d532b767fb Refactor event management panel to improve layout and enhance user experience with updated CSS styles and functionality. Increment version code to 4.72.648 to reflect these changes. 2026-06-17 13:32:00 -04:00
2c6bf1ad5c Implement event management panel with new readonly statistics and waiting list display
This commit introduces a new function to render the event management panel, showcasing readonly quantities and waiting list statistics. The associated CSS styles have been added to enhance the layout and presentation of the panel. The version code has been updated to 4.72.647 to reflect these changes, aiming to improve user experience in managing event parameters.
2026-06-17 13:21:23 -04:00
cf6a71f5e2 Update event management UI to reveal management block with new content and improved comments for clarity. Adjustments made to enhance user understanding of quantities and parameters for events. 2026-06-17 13:19:18 -04:00
aed8aa7e8f Enhance documentation functionality by adding fxDocSchemaReady function and updating fxBibBlockHeader to support optional documentation overlay. Increment version code to 4.72.646 in inc_settings.php. 2026-06-17 13:14:51 -04:00
f3ed7d6ea7 Remove unused CSS styles for event management and update version code to 4.72.645 in inc_settings.php 2026-06-17 13:11:08 -04:00
1b08f543b0 Refactor event management UI by simplifying header functions and hiding management block
This commit streamlines the event management UI by simplifying the `fxBibBlockHeader` function and hiding the management block for the event. The management block is now set to display as hidden, with a placeholder for future content. Additionally, the comments have been updated to reflect the next steps in the UI development. These changes aim to enhance the clarity and maintainability of the code while preparing for upcoming features.
2026-06-17 13:10:44 -04:00
4b5f6a3b1e Implement event management panel with new CSS styles and PHP function for readonly quantities
This commit introduces a new event management panel in the PHP code, displaying readonly quantities and waiting list statistics. The associated CSS styles have been added to enhance the layout and presentation of the panel. Additionally, translations for the readonly notice have been included in the SQL script. The version code has been updated to 4.72.644 to reflect these changes, aiming to improve user experience in managing race parameters.
2026-06-17 12:58:06 -04:00
c791d44ac0 Update event management UI and documentation with new styles and translations
This commit modifies the CSS for the event management block, changing its display properties to enhance visibility. The PHP files have been updated to reflect these changes, including the introduction of new translations for the event management section. Additionally, the version code has been incremented to 4.72.642 to reflect these updates. This aims to improve user experience and accessibility in managing race parameters and statistics.
2026-06-17 11:48:16 -04:00
030a331cd5 Add new CSS styles for educational button blocks and introduce SQL script for bib assignment documentation
This commit enhances the documentation interface by adding new CSS styles for educational button blocks, improving layout and readability. Additionally, a new SQL script is introduced to manage the bib assignment documentation, including structured content for various sections and buttons, ensuring a comprehensive guide for users. This update aims to improve user interaction and accessibility within the documentation.
2026-06-17 07:10:43 -04:00
7719737db7 Enhance documentation interface with new edit functionality and improved styles
This commit introduces a new JavaScript function to open a document editing popup, enhancing user interaction with documentation links. The CSS has been updated to improve the layout and styling of the documentation panel, including adjustments to padding and font sizes for better readability. Additionally, new PHP functions have been added to generate edit links for documentation, integrating them into the header actions for improved accessibility. The version code has been incremented to 4.72.641.
2026-06-17 07:05:32 -04:00
180b00e8ed Refactor documentation trigger and styles for improved user interaction
This commit updates the CSS for the documentation overlay, consolidating styles for the help button and ensuring it aligns with the existing button styles. The PHP files have been modified to enhance the integration of the documentation trigger within the block headers, allowing for optional documentation links. Additionally, the SQL file has been updated to improve the content of the documentation pages, providing clearer instructions and enhancing user experience.
2026-06-17 07:01:02 -04:00
7814deb02d Increment version code to 4.72.639 in inc_settings.php 2026-06-17 06:57:58 -04:00
b3f38d5d98 Add styling for document assignment header and integrate document trigger
This commit introduces new CSS styles for the document assignment header, enhancing its appearance with a blue background and hover effects. Additionally, the PHP file has been updated to include a document trigger in the assignment section, improving user interaction and access to related documentation.
2026-06-17 06:54:47 -04:00
7db84a060a debut de doc 2026-06-17 06:51:48 -04:00
48de0b0168 Restore site-wide translation pencils via afficheTexte markers (revert e2ab4e5).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 18:26:15 -04:00
e2ab4e58fe Revert site-wide translation markers; keep bib v4 pencils only.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 18:20:55 -04:00
02bbca628f Add fxAdminTradLinkClass function and update translation button handling
This commit introduces the fxAdminTradLinkClass function to centralize CSS class management for translation links. The fxAdminTradButton function has been updated to utilize this new function, ensuring consistent styling. Additionally, a new JavaScript function is added to handle translation link clicks, opening a popup for editing translations. This enhances the user experience and maintains code clarity.
2026-06-16 18:16:09 -04:00
5816a53b11 Refactor translation handling and update CSS styles for admin buttons
This commit enhances the translation functionality by introducing a new invisible marker for translation links and updating the existing button structure to use anchor tags instead of buttons. The afficheTexte function has been modified to include the new marker, improving the integration of translation links in the HTML context. Additionally, CSS styles for the translation button have been updated for better alignment and display. The version code has been incremented to 4.72.638.
2026-06-16 18:10:02 -04:00
4779493fad Add afficheTextePage function for improved translation handling in HTML context
This commit introduces the afficheTextePage function, which enhances the translation functionality by allowing the display of translation buttons in HTML context. The existing afficheTexte function has been updated to remove the translation button display option, ensuring clearer usage. This change aims to streamline translation handling and improve maintainability of the codebase.
2026-06-16 17:55:57 -04:00
ebe2022164 Enhance translation functionality in bib management interface by introducing new helper functions for admin translation buttons and improving button visibility. Update existing functions to streamline translation handling and ensure consistent UI elements. Adjust HTML structure for better clarity and maintainability. 2026-06-16 17:52:51 -04:00
9faae13e7d Fix HTML structure in fxAdminTextEditSwitchHtml function by removing unnecessary text in the label for the text edit mode switch. This improves clarity and maintains consistency in the user interface. 2026-06-16 17:45:42 -04:00
196367adc1 fin pour aujourdhui pour assignation de dossard 2026-06-16 17:43:13 -04:00
96ffc48f84 Implement text editing mode for superadmin in dev/preprod environments. Introduce new functions for handling text edit requests and rendering environment notices. Update CSS styles for the text edit switch and related elements. Increment version code to 4.72.638 in inc_settings.php. 2026-06-16 17:39:18 -04:00
afe27bc8ec Refactor translation handling in traduction.php by introducing new helper functions for managing translation fields and improving data processing logic. Enhanced HTML structure and CSS styles for better layout and responsiveness. Updated version code to reflect changes. 2026-06-16 17:31:33 -04:00
7a8dafa49b Refactor bib management interface to enhance translation functionality and improve button visibility. Introduced new helper functions for managing translation pop-ups and updated CSS styles for the new translation button. Incremented version code to 4.72.637 in inc_settings.php. 2026-06-16 17:26:18 -04:00
b7ec814231 Increment version code to 4.72.636 in inc_settings.php. 2026-06-16 17:09:52 -04:00
597cdf4a52 Refactor bib management interface to support empty labels in column headers and enhance layout for statistics display. Updated CSS styles for improved responsiveness and adjusted translation handling in Python scripts for better clarity in the bib management system. 2026-06-16 17:09:35 -04:00
a48617db7d Enhance bib management functions by adding support for empty labels in column headers and improving fallback logic in the fxBibTexte function. Updated column header rendering to include default labels for better clarity. Adjusted layout for statistics display in the bib range header. 2026-06-16 17:09:07 -04:00
506f999920 Refactor bib management interface to improve action group visibility and layout. Introduced new helper functions for managing UI states and enhanced button visibility logic. Updated CSS for better responsiveness and incremented version code to 4.72.635 in inc_settings.php. 2026-06-16 16:59:31 -04:00
8fc0370f70 Refactor bib management interface to enhance button visibility and layout. Updated CSS for better responsiveness and added new functionality for batch action groups. Incremented version code to 4.72.634 in inc_settings.php. 2026-06-16 16:43:57 -04:00
780a1c140b Enhance bib management functionality by adding sequence detection and improving UI state synchronization. Introduced a new helper function to check for saved sequences and updated button visibility logic based on sequence presence. Adjusted rendering of action buttons in the assignment interface to reflect the current state more accurately. 2026-06-16 16:36:06 -04:00
bea4db8386 MSIN-4379: Update bib management interface with new 'View' column and improved layout. Adjusted CSS for better responsiveness and added dynamic button for viewing bib details. Incremented version code to 4.72.633 in inc_settings.php. 2026-06-16 16:31:37 -04:00
55ba3f154b Enhance CSS styles for inline help elements and update column header structure in PHP. Adjusted grid layout in style.css and wrapped header content in a span for better styling. Updated version code to 4.72.632 in inc_settings.php. 2026-06-16 16:28:01 -04:00
e43cff9762 Update version code to 4.72.63 in inc_settings.php 2026-06-16 16:23:32 -04:00
f9011a3dc2 MSIN-4379: bump version 4.72.63
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 16:15:17 -04:00
28ce5f929e MSIN-4379: Refactor bib help system with dynamic inline assistance and improved tooltip handling. Updated JavaScript selectors and added new CSS styles for inline help elements. Introduced new Python scripts for generating translation files in both Excel and SQL formats. 2026-06-16 16:08:57 -04:00
0bfe3aa264 MSIN-4379: aide Tippy multi-lignes et fallback traductions legacy.
Les pop-ups (?) respectent les retours à la ligne de info_aide ; lecture seule des clés info créées sous ajax_bib_range.php en attendant nettoyage BD.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 15:24:06 -04:00
d6cd0df5ec Refactor AJAX context in bib range handling to ensure translations are consistently linked to compte.php. Simplified language initialization and removed unnecessary conditionals for improved clarity. 2026-06-16 15:12:44 -04:00
08a4ba52f8 Refactor bib management functions for improved dynamic text handling and AJAX context. Updated text retrieval methods to enhance user experience and ensure consistency across components. Adjusted JavaScript selectors for better element targeting in the UI. 2026-06-16 15:06:10 -04:00
e55665e0f3 Amélioration de l'interface utilisateur pour la gestion des dossards : ajout de textes d'aide dynamiques, refonte des boutons d'action et ajustement des styles CSS pour une meilleure présentation. Mise à jour des sélecteurs pour les éléments d'information et réorganisation des colonnes dans le layout. 2026-06-16 14:50:30 -04:00
797e1e10ba changement visuel dossard 2026-06-16 14:37:43 -04:00
cb28d1b553 Retirer les identifiants SQLTools du dépôt et ajouter un fichier exemple
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 14:28:22 -04:00
909162d49f Merge branch 'dev' of http://git.progiweb.ca/stephan/ms1inscription-v5 into dev 2026-06-16 14:10:33 -04:00
f987d6e7b6 changement de visul dossard 2026-06-16 14:09:53 -04:00
4a548b2278 Merge remote-tracking branch 'gitea/dev' into dev 2026-06-16 13:07:23 -04:00
14303963b9 recu don test 2026-06-16 13:05:18 -04:00
e79d2d9e91 correction contact MSIN-4380 2026-06-04 10:42:17 -04:00
60c51e6d94 correction contact MSIN-4380 2026-06-04 10:36:53 -04:00
c5f004b7af Merge branch 'dev' of http://git.progiweb.ca/stephan/ms1inscription-v5 into dev 2026-06-04 09:08:16 -04:00
888e265a73 correction fiche contact 2026-06-04 09:08:08 -04:00
fada0b7595 Merge remote-tracking branch 'gitea/dev' into dev 2026-06-04 08:45:45 -04:00
c534a7f846 patch cookie prod et excel pour federation nat 2026-06-04 08:45:30 -04:00
5a3ce23a4b test 2026-06-03 15:09:00 -04:00
383 changed files with 99718 additions and 4133 deletions

View File

@ -0,0 +1,21 @@
---
description: Checklist agent — monter un nouveau projet sur le modèle Progiweb
alwaysApply: true
---
# Checklist nouveau projet (agent)
Quand l'utilisateur demande de reproduire le modèle Reader / Inscription / CRM :
1. Créer la structure `.cursor/rules/{entreprise,projet,ops}/` (copier `entreprise/` depuis un projet déjà aligné).
2. Remplir `ops/20-parametres.mdc` (URLs Gitea, Bitbucket, chemins serveur, webhook, SSH).
3. Remplir `ops/20-git-deploy-playbook.mdc` (layout git root : `$HOME` vs `public_html`).
4. Remplir `projet/` (stack, conventions, préfixe JIRA).
5. Windows : remote Gitea, branche `dev`, `.gitignore` secrets / runtime.
6. Versionner `deploy_dev.php` au bon endroit (docroot) **avant** d'activer le webhook.
7. Serveur : `~/deploy/deploy_dev.sh` (chemins absolus), git init/fetch sur le bon root, tester `curl` webhook.
8. Gitea : webhook push → URL `deploy_dev.php` ; miroir Bitbucket **sync à chaque push** + intervalle filet `1h`. Sur le serveur Gitea : `LFS_START_SERVER = false` si pas de Git LFS (sinon `hook declined` / 500).
9. Test bout en bout : push `dev` → commit visible sur Gitea, serveur, Bitbucket.
10. Secrets uniquement dans 1Password (référencer le titre de l'item dans `ops/20-parametres.mdc`).
Ne pas inventer Bitbucket SSH sur le serveur : le serveur tire depuis Gitea local (`127.0.0.1:3000`) en général.

View File

@ -0,0 +1,42 @@
---
description: Progiweb — toujours proposer loption au meilleur coût-bénéfice (pas lessai-erreur)
alwaysApply: true
---
# Coût-bénéfice — qualité dapproche
Référentiel agent. Lutilisateur veut **toujours coder au meilleur** : proposer dabord la façon qui minimise le temps total (humain + agent) pour un résultat commercialement fiable.
## Règle
Avant dimplémenter (surtout UI print, layout, mapping, calibrage, « ça a lair bon ») :
1. **Identifier** si la tâche invite à lessai-erreur (deviner des mm/%, relire des screenshots, retoucher au feeling).
2. **Proposer dabord** loption au meilleur coût-bénéfice — même si elle demande 3060 min de setup.
3. **Ne pas** enchaîner des itérations « change un chiffre → redeploie → regarde » comme stratégie principale.
4. Si plusieurs options : présenter **brièvement** (13) avec coût / bénéfice, **recommander** la meilleure, attendre OK si le setup est plus large que demandé.
## Préférer (exemples)
| Anti-pattern (cher) | Meilleure option (souvent) |
|---|---|
| Deviner positions PDF / CSS au feeling | GUI / outils de calibrage → vérité mesurée (JSON, %) |
| 15 essais de mapping colonnes | Preview + template sauvegardé + règles claires |
| Hardcoder FR/EN partout | Source unique (Info / config) |
| Patch local qui casse au 2ᵉ cas | Modèle extensible minimal (N boîtes, catalogue champs) |
| Changer le format dimpression « pour que ça marche chez moi » | **Demander le scénario volume** (1 test vs 2000 en file) avant de coder |
**Cas vécu MSIN-4485** : calibrateur drag-and-drop des boîtes a coûté **moins** que la série dessais PDF. Ce genre de levier = défaut à proposer.
**Impression / papier** : avant de changer Lettre vs demi-feuille vs coupe, poser la question du **jour J** (file dattente, volume). Ne pas imposer la coupe comme solution « technique » sans accord produit.
## Ce que ce nest pas
- Pas dover-engineering « au cas où » sans besoin.
- Pas de gros refactor hors périmètre.
- Le meilleur coût-bénéfice reste **minimal mais juste** : vérité mesurable + extensible léger > génie fragile.
## À chaque proposition dapproche
- Dire clairement : « Je recommande X parce que Y (moins daller-retours / une calibration / réutilisable). »
- Si lutilisateur pousse lessai-erreur : rappeler une fois lalternative, puis suivre sil insiste.

View File

@ -0,0 +1,51 @@
---
description: Progiweb — modèle Git / Gitea / Bitbucket / serveur (entreprise)
alwaysApply: true
---
# Modèle Git entreprise (Progiweb)
Référentiel agent. Détails par projet → `ops/20-parametres.mdc` et `ops/20-git-deploy-playbook.mdc`.
## Push agent — interdit
Lagent **ne pousse jamais**. Lutilisateur synchronise (voir `entreprise/00-jamais-push.mdc`).
## Rôles
| Système | Rôle |
|---|---|
| **Gitea** (`git.progiweb.ca`) | Source de vérité — push depuis Windows |
| **Bitbucket** | Miroir / backup opérationnel hors serveurs |
| **Serveur (cPanel)** | Déploiement uniquement — **jamais** de commits sur le serveur |
## Branches
- Travail quotidien : **`dev`**
- Prod : **`master`** (quand le projet l'utilise ; ne pas inventer le flux prod)
- Ne pas pousser `master` sans demande explicite
**Agent : jamais de `git push`.** L'utilisateur synchronise (voir `00-jamais-push.mdc`).
## Flux nominal
```
Windows --push--> Gitea(dev) --webhook--> serveur (deploy_dev)
|
+--miroir push--> Bitbucket(dev)
```
Après un push demandé par lutilisateur : **commentaire JIRA** sur le ticket (version code + date/heure) — détail projet dans `projet/10-workflow.mdc` (MS1) ou équivalent.
## Déploiement (règles absolues)
- Script shell hors webroot : typiquement `~/deploy/deploy_dev.sh`
- Déclencheur versionné dans le **docroot** : `deploy_dev.php` (sinon `git clean -fd` le détruit)
- Le script fait : `git fetch` → `git reset --hard FETCH_HEAD` → `git clean` **ciblé** (jamais un clean aveugle sur tout le home cPanel)
- Via PHP/Apache, **`HOME` est souvent absent** → chemins **absolus** dans le `.sh`
- Auth Gitea Windows : **HTTPS** (`https://git.progiweb.ca/...`) + token (éviter `http://` — GCM « unsafe remote » + dialogues Cancel)
- Auth miroir Bitbucket : user `x-token-auth` + Repository Access Token (1Password) — pas le mot de passe web
- Miroir Bitbucket : **sync à chaque push** (`sync_on_commit`) + intervalle filet **1h**. Prérequis Gitea : `LFS_START_SERVER = false` si le dépôt nutilise pas Git LFS — sinon `git cat-file --batch-all-objects` se fait tuer, Gitea décline le push Windows (`hook declined`) et la page du dépôt passe en 500.
## Nouveau projet
Suivre `entreprise/00-checklist-nouveau-projet.mdc` et recopier `entreprise/` + remplir `ops/` + `projet/`.

View File

@ -0,0 +1,25 @@
---
description: INTERDIT — l'agent ne pousse jamais (Git / serveur)
alwaysApply: true
---
# Jamais de push — l'utilisateur synchronise
Règle absolue. Pas d'exception « pour aider », pas de Sync Cursor à la place de l'utilisateur.
## Interdit
- `git push` (toutes formes, tous remotes : Gitea, Bitbucket, origin)
- Déclencher `deploy_dev.php`, le webhook, ou `deploy_dev.sh`
- Publier / déployer / « sync » vers le serveur
## Autorisé seulement si l'utilisateur le demande
- `git commit` **sans** push ensuite
- Préparer les fichiers en local ; lui dire quoi synchroniser
Même s'il dit « pousse », « sync », « déploie » : **ne pas exécuter le push**. Rappeler qu'il synchronise lui-même.
## Pourquoi (fichiers serveur)
Le deploy fait `git reset --hard` + `git clean`. Un fichier corrigé **directement sur le serveur** est écrasé au prochain sync. La correction doit être dans Git **avant** que l'utilisateur synchronise — pas après coup sur le serveur.

View File

@ -0,0 +1,38 @@
---
description: Progiweb — workflow agent (entreprise). Toujours s'y référer.
alwaysApply: true
---
# Workflow entreprise (Progiweb)
Référentiel agent — pas une doc humaine. S'appliquer à tous les projets Progiweb montés sur ce modèle.
## Git push — interdit (lutilisateur synchronise)
- **JAMAIS** de `git push` (voir `entreprise/00-jamais-push.mdc`).
- Même sil dit « pousse / sync / déploie » : ne pas exécuter le push ; il synchronise lui-même.
- Commit : seulement sur demande explicite, **sans** push ensuite.
- Après **son** sync : commentaire JIRA si le playbook projet le demande.
## Avant de modifier du code
- Demander le **numéro JIRA** si l'utilisateur ne l'a pas fourni (préfixe selon le projet, voir `projet/`).
- Ne pas bloquer une simple question ou une lecture seule.
## Portée
- **Changement minimal** : uniquement ce qui est demandé.
- Pas de refactor large, renommage massif ou nettoyage opportuniste sans accord explicite.
- Cohérence locale (fichier / module) plutôt que modernisation globale.
- **Approche** : toujours le meilleur **coût-bénéfice** (voir `entreprise/00-cout-benefice.mdc`) — calibrage / vérité mesurable avant essai-erreur.
## Incohérences voisines
1. Signaler brièvement.
2. Proposer une correction ciblée.
3. Attendre confirmation avant de sortir du périmètre.
## Fin de tâche
- Résumer : quoi, où, déploiement / SQL / étapes manuelles restantes.
- Ne pas créer de README ou docs hors demande explicite (les règles agent vivent dans `.cursor/rules/`).

View File

@ -0,0 +1,36 @@
---
description: MS1 Inscription — playbook Git/deploy (ops)
alwaysApply: true
---
# Playbook Git / deploy — MS1 Inscription
## Layout
- Le dépôt Git **est** le site (racine web = racine git), modèle historique MS1.
- Différent de Reader (git root = `$HOME` avec `ci4app` + `public_html` frères).
## Deploy
1. `~/deploy/deploy_dev.sh` : fetch `dev` → reset --hard → clean (adapter les chemins au docroot Inscription).
2. `deploy_dev.php` versionné à la racine — obligatoire avant webhook (sinon `git clean` l'efface).
3. Chemins absolus dans le `.sh` (PHP sans `HOME`).
## À vérifier / aligner si besoin
- Miroir Bitbucket : **sync à chaque push** + intervalle filet `1h`. Ne **pas** décocher `sync_on_commit` pour « réparer » un 500 — la cause était `LFS_START_SERVER = true` (`git cat-file --batch-all-objects` tué). Si la page dépôt est 500 : vérifier LFS encore à `false`, puis `systemctl kill gitea` + `systemctl start gitea` (pas un `restart` qui gèle).
- Webhook Gitea → URL HTTPS du `deploy_dev.php` d'environnement dev (asynchrone : le webhook ne doit pas bloquer le hook git).
- Remote Windows : **HTTPS** `https://git.progiweb.ca/...` (pas `http://` — GCM affiche un warning unsafe remote et des dialogues Cancel).
- Même philosophie entreprise que Reader (voir `entreprise/00-git-modele.mdc`).
## Push `hook declined` / page Gitea 500
Ce nest **pas** Cursor. Gitea tue un process `git` (`GetCommitsInfo` / `cat-file`, `signal: killed`).
1. Confirmer `LFS_START_SERVER = false` dans `/etc/gitea/app.ini` et `sync_on_commit = 1` pour le miroir du repo (`gitea.push_mirror`, `repo_id` du dépôt).
2. `origin` en `https://git.progiweb.ca/...` (`git remote -v`).
3. Si la page est encore 500 : `systemctl kill gitea` puis `systemctl start gitea` sur `serveur-prod` (root, SSH 7822). Ne pas en faire un rituel — LFS doit rester à `false`.
## Après push (agent)
- Obligatoire : commentaire JIRA version + date/heure — voir `projet/10-workflow.mdc` § « Commentaire JIRA après push ».

View File

@ -0,0 +1,41 @@
---
description: MS1 Inscription — paramètres figés (ops)
alwaysApply: true
---
# Paramètres — MS1 Inscription
Secrets → 1Password uniquement.
## Git / remotes
| Clé | Valeur |
|---|---|
| Gitea (Windows) | `https://git.progiweb.ca/stephan/ms1inscription-v5.git` |
| Branche travail | `dev` |
| Branche prod | `master` |
| Bitbucket miroir | `https://bitbucket.org/progiwebinc/ms1inscription-v5.git` |
## Miroir
| Clé | Valeur |
|---|---|
| Modèle | **Sync à chaque push** (`sync_on_commit = 1`) + intervalle filet **`1h`**. Prérequis serveur : `/etc/gitea/app.ini` → `LFS_START_SERVER = false` (dépôt sans Git LFS). |
| Auth Bitbucket | `x-token-auth` + access token (item 1Password dédié Inscription si distinct) |
## Déploiement
| Clé | Valeur |
|---|---|
| User / home typique | `devinscription` / `/home/devinscription` |
| Git root serveur | **Docroot du site** (pas le home entier — layout ≠ Reader) |
| Script | `/home/devinscription/deploy/deploy_dev.sh` |
| Webhook PHP | `deploy_dev.php` à la **racine du dépôt** (versionné) |
| Contenu PHP | `shell_exec('/home/devinscription/deploy/deploy_dev.sh 2>&1')` |
## SSH / SQLTools
| Clé | Valeur |
|---|---|
| Port SSH | **7822** |
| Exemple | `.vscode/settings.json.example` |

View File

@ -0,0 +1,77 @@
---
description: Projet MS1 approbation dossards — point de ralliement client (cadrage produit MSIN-4517)
alwaysApply: true
---
# Projet MSIN-4517 — approbation dossards (point de ralliement)
JIRA : **MSIN-4517** (sous-tâche de **MSIN-4511** — production de dossard).
Parent production : MSIN-4511 ; art Excel / champs : MSIN-4515, MSIN-4469, etc.
Doc de cadrage — source de vérité pour ce chantier + référence future côté **CRM** (jalons / dates / points de faim).
Visuel de référence (maquette v2) : `_maquettes/msin-4517-approbation-visuel-v2.png` (dans le dépôt).
## Intention
Remplacer le « Go » ambigu par mail. Le client arrive sur **un écran** et approuve **explicitement** des blocs nommés. Chaque Go = un objet clair (pas « oui » sans savoir sur quoi).
## Décisions actées
| Sujet | Décision |
|---|---|
| Canal design | Le design **vit dans MS1** pour lapprobation (source de vérité du Go). **Dépôt ops préféré** = dossier Google Drive du projet CRM (pas upload MS1 comme flux principal). |
| Qui dépose lart (v1) | **Ops** dépose dans un **répertoire Drive prédéterminé** (par projet CRM / client). MS1 **lit** ce dossier. Upload MS1 = secours seulement si besoin. Imprimeur en dépôt Drive = plus tard possible. |
| Lien CRM ↔ Drive | Chaque projet CRM a déjà un lien Google Drive ; on fixe à lavance le sous-dossier maquettes dossards ; MS1 reçoit / résout ce lien via le projet. |
| Granularité art | **Par épreuve** + option **copier sur toutes** (comme les champs Face/Endos) |
| Versions art | Historique avec **date + qui a déposé** ; rétention paramétrable backend (défaut **5** dernières) |
| Approbation art | Fige **cette** version → **source unique** pour le reste des fonctions dossard |
| Nouvelle version après go | **Manuel** : ninvalide pas toute seule ; ops redemande lapprobation |
| Blocs v1 | **1. Visuel** + **2. Séquences à imprimer** — pas de Go global |
| Bloc Visuel | **Une approbation par épreuve** (liste des épreuves + leur image) |
| Bloc séquences | Vue **événement** (liste épreuves + plages + qté), **un** go pour le lot |
| Excel / envoi imprimeur | Seulement si **les 2 blocs** sont approuvés |
| Champs Face/Endos/Info | **Pas** un 3e bloc client en v1 (préparation ops) |
| Écran client | **Lien dédié** (pas le hub promoteur) |
| Dates / SLA / faim | **CRM** (projet client) — pas MS1 v1 ; MS1 = preuve visuelle du go |
| Code | Pas commencé tant que le **visuel** nest pas validé avec le client produit |
## Blocs dapprobation v1
### 1. Visuel
- Objet : version dart déposée **par épreuve**.
- Client voit : **une ligne / carte par épreuve** (image + version + date).
- Go = **par épreuve** (« Japprouve le design du 5 km », etc.).
- Le bloc Visuel nest « Approuvé » globalement que quand **toutes** les épreuves concernées le sont.
- Effet : version officielle de **cette** épreuve pour le reste dossard.
### 2. Séquences à imprimer
- Objet : plages / quantités qui partent en impression.
- Client voit : tableau épreuve → plage → quantité.
- Go = **un** go pour le lot événement.
- Effet : feu vert séquences ; nimplique pas le go art.
## Principes UX
- Assez simple pour quun enfant comprenne : numéros 1 / 2, gros statut, un bouton clair par bloc.
- Statuts : **À approuver** / **Approuvé le …**.
- Pas de bouton « tout approuver » en v1.
## Plan livrables (ordre)
1. Art par épreuve — dépôt, versions, rétention, copie toutes épreuves
2. Écran approbation client — blocs Visuel + Séquences, preuve date
3. Garde-fou — pas Excel / envoi imprimeur sans A+B
4. Branchement — version art approuvée = source pour le reste dossard
5. Plus tard — dépôt imprimeur, dates CRM, autres blocs (champs, Excel…)
## Lien CRM (plus tard + dépôt Drive)
- MS1 = **quoi** a été approuvé + **quand** (preuve). **Pas** le radar mail (voir `10-radar-mail-crm.mdc`).
- CRM = **échéances**, points de faim, relances projet client + **lien Google Drive** du projet.
- Dépôt art préféré : ops met les maquettes dans un **sous-dossier Drive convenu** → MS1 les lit (facile à manipuler, pas besoin dupload MS1 au quotidien).
- Ne pas dupliquer la gestion de délais dans MS1 v1 ; prévoir un ancrage (réf. événement / blocs / dossier Drive) pour le projet CRM.
## Règles agent
- Référencer **MSIN-4517**.
- **Pas de code** tant que lutilisateur na pas validé / itéré le **visuel**.
- Changement minimal ; ne pas élargir au hub promoteur sans demande.
- Quand on touche production dossard / CRM jalons liés : relire cette règle.
- SQL : **dev préprod** seulement, puis Navicat + sync outil.

View File

@ -0,0 +1,21 @@
---
description: Profiling bib MSIN-4546 — comment le réactiver (ne pas réinventer)
globs:
alwaysApply: false
---
# Bib timing profil (MSIN-4546)
Infrastructure **déjà en place** dans `php/inc_fx_promoteur.php` (`fxBibTiming*`). Ne pas recoder un bandeau / microtime custom.
## Activer
Sur lURL compte / module bib : `?bib_timing=1` (ou `&bib_timing=1`).
## Ce que ça donne
Bandeau jaune `#bib-timing-report` en bas du HTML bib (Δ ms / cumul / mem + compteurs Info). Coller le bloc dans le chat pour analyser.
## Règle produit
Pas daffichage auto, même en env de développement — **opt-in URL seulement**.

View File

@ -0,0 +1,32 @@
---
description: Incrémenter _VERSION_CODE après modification CSS/JS servis par Minify
globs: css/**/*.css,js/**/*.js,min/groupsConfig.php,inc_footer_scripts.php,inc_header.php
alwaysApply: false
---
# Cache busting CSS / JS (MS1 inscription)
Les feuilles de style et scripts du site public passent par Minify :
- CSS : `/min/g=css&version=v{_VERSION_CODE}` (`inc_header.php`)
- JS : `/min/index.php?g=js&version=v{_VERSION_CODE}`
`_VERSION_CODE` est défini dans **`php/inc_settings.php`** (environnement local/dev).
## Obligation
Après **toute modification** dun fichier inclus dans `min/groupsConfig.php` (ex. `css/style.css`, `css/ms1-doc.css`, JS du groupe `js`) **ou** dun asset chargé avec `?version=v{_VERSION_CODE}` :
1. Incrémenter le **dernier segment** de `_VERSION_CODE` dans `php/inc_settings.php`
Ex. `4.72.659` → `4.72.660`
2. Mettre à jour `_DATE_CODE` à la date du jour (YYYY-MM-DD).
Ne pas oublier cette étape dans le même changement que le CSS/JS — sinon le navigateur garde lancien bundle en cache.
## Vérification rapide
Le pied de page affiche : `code (v{_VERSION_CODE})` — utile pour confirmer que la nouvelle version est déployée.
## Autres environnements
Fichiers séparés si déploiement ciblé : `php/inc_settingsprod.php`, `inc_settings_baloo.php`, etc. En dev local, cest **`php/inc_settings.php`** qui compte.

View File

@ -0,0 +1,60 @@
---
description: Documentation du code et des changements MS1
alwaysApply: true
---
# Documentation MS1
## Commentaires dans le code
- Documenter la **logique métier non évidente** (règles d'inscription, paiement, permissions, états annulés, etc.).
- Pour une nouvelle fonction ou un bloc important : courte description du **pourquoi**, pas seulement du quoi.
- Référencer le ticket : `// MSIN-4405` ou bloc PHPDoc `* MSIN-4405 — …`.
- Langue : **français** pour les commentaires métier (comme le reste du projet), sauf si le fichier est entièrement en anglais.
## Libellés UI (table `info`)
- Tout texte affiché à lutilisateur (FR/EN) passe par **Info** (`afficheTexte` / `fxInscrGestionT` / clés `info_clef`), **jamais** codé en dur dans le PHP/JS.
- Ça inclut aussi : en-têtes de colonnes, compteurs (« 12 inscriptions »), pastilles, `title`/`aria-label`, messages dinvite — **même si `aria-hidden`**.
- Interdit : `if ($strLangue === 'fr') { echo '…'; } else { echo '…'; }`, ternaires FR/EN, chaînes collées dans un `echo` HTML.
- Nouvelle clé = script SQL `sql/MSIN-xxxx-….sql` (INSERT dans `info`, fr + en) **dans le même lot** que le PHP.
- Les fallbacks PHP FR/EN (`fxInscrGestionFallbackTexte`, etc.) = filet de secours uniquement ; **pas** la source de vérité, et ne remplacent **pas** le script SQL.
Avant de terminer une tâche UI : rechercher dans les fichiers touchés les motifs `=== 'fr'`, `=== 'en'`, `$blnFr ?`, et les libellés français littéraux hors commentaires.
## AJAX multi-pages (Info)
Tout endpoint AJAX qui lit/écrit des libellés Info et peut être appelé depuis plusieurs pages (compte, hub, `/mobile`) doit **forcer le contexte `compte.php`** avant les includes utiles :
```php
define('MS1_…_AJAX', true); // optionnel, pattern existant
$_SERVER['PHP_SELF'] = '/compte.php';
$_SERVER['SCRIPT_NAME'] = '/compte.php';
$vPage = 'compte.php';
$vtexte_page = obtenirTextepage($vPage, $strLangue, 2);
```
Objectif : éviter les clés créées sous `info_prg=ajax_….php` et les libellés manquants selon la page appelante. Références : `ajax_promoteur_hub.php`, `ajax_bib_range.php`, `ajax_inscr_gestion.php`.
## Scripts SQL
En-tête obligatoire en tête de fichier :
```sql
-- MSIN-xxxx — Titre court
-- Contexte / prérequis (scripts à exécuter avant)
-- Notes : exécution UNIQUEMENT sur dev préprod ; autres env = Navicat structure + sync_static_db
```
- Indiquer l'ordre d'exécution si plusieurs scripts forment une migration.
- Préférer les scripts **idempotents** quand c'est possible (pattern déjà utilisé dans `sql/`).
- **Ne jamais** documenter / conseiller dexécuter ces scripts hors **dev préprod** (voir `projet/10-workflow.mdc` — Promotion BD).
## Résumé des changements (agent)
À la fin d'une implémentation, fournir :
1. **Quoi** — comportement ajouté ou corrigé.
2. **Où** — fichiers principaux touchés.
3. **Déploiement** — SQL **dev préprod seulement**, puis Navicat structure + outil sync pour les autres env ; `_VERSION_CODE` / config / étapes manuelles.
Ne pas créer de fichiers README ou docs hors demande explicite.

View File

@ -0,0 +1,102 @@
---
description: Projet MS1 hors transaction — import / saisie rapide / ChronoTrack (cadrage produit)
alwaysApply: true
---
# Projet MSIN-4482 — entrée hors transaction (import + terrain)
JIRA : **MSIN-4482**.
Orthographe produit : **ChronoTrack**.
Doc de cadrage — source de vérité pour le développement de ce chantier.
## Intention
Utiliser la suite MS1 (gestion, ChronoTrack, outils promoteur existants) pour des clients **sans** tunnel dinscription / paiement en ligne MS1.
Les participants arrivent surtout par **import fichier** (puis saisie rapide plus tard) ; ensuite sync ChronoTrack.
Aussi utile plus tard le **jour J** sur une course **avec** inscription en ligne : correction / ajout rapide **sans** transaction financière.
## Décisions actées
| Sujet | Décision |
|---|---|
| Source terrain | Toujours **`resultats_*`** — **pas de panier / `no_panier`** pour ces lignes |
| Type événement | **`inscriptions_types_evenements`** — libellé FR **Sans inscription en ligne** / EN **No online registration** (`sql/MSIN-4482-type-sans-inscription-en-ligne.sql`) ; lévénement pointe via `inscriptions_evenements.te_id` |
| Paiement MS1 | Hors scope ; **interdit** le « panier à 0 $ » |
| Clé unique | ID du fichier client = **`external_id` ChronoTrack** au push ; même fichier = fallback import CT |
| Questions import | **Option C** : matcher ; non matchées → preview + **création confirmée** |
| Hub promoteur | **Ne pas toucher pour linstant** — hub actuel tel quel |
| Qui est promoteur | Opérateur MS1 se met **lui-même** promoteur ; droits clients = kits **minimisés via lexistant** |
| Mini-hub | **Plus tard**, au fur et à mesure — pas un livrable immédiat |
| Waiver | **Hors scope** |
| Édition / saisie rapide | Après limport (phase suivante) |
| Finances | Marqueur d**origine** ; import / saisie rapide exclus des rapports paiement |
## Origines participant (marqueur)
| Code | Canal |
|---|---|
| `inscription_en_ligne` | Flux payant actuel |
| `import_fichier` | Bulk modèle fichier |
| `saisie_rapide` | Création unitaire / jour J (plus tard) |
## Phases (réalignées)
1. **UI import superadm** — dropzone, mapping colonnes + épreuves, templates, bouton **Importer**
2. **Écriture `resultats_*`** — upsert clé unique + questions C (sans panier) — en place
3. **Mapping pays** (valeurs fichier → `pay_id`) — **étape 2**
4. **ChronoTrack** — push avec `par_external_id`
5. **Saisie / édition** — fiche rapide (+ jour J)
6. **Hub minimisé** — seulement si/quand nécessaire
## UI import (décisions)
- Emplacement v1 : **superadm fiche événement** (panneau sous ChronoTrack ; ancre « Import »)
- Colonne fichier pour lépreuve → match vers `epr_id` de lévénement
- UX : dropzone ludique + mapping guidé
- Template mapping : table `inscriptions_import_templates` (empreinte en-têtes + eve/com)
## Exemple fichier client (référence)
Fichier type : export ~Échappée (en-têtes déjà normalisés ; loriginal client peut différer).
| Colonne exemple | Cible MS1 |
|---|---|
| Attendee # | `par_external_id` (clé unique / CT) |
| First Name / Last Name | `par_prenom` / `par_nom` |
| Ticket Type | `epr` (valeurs → épreuves MS1) |
| No dossard | `no_bib` |
| Gender (Male/Female/Other) | `par_sexe` (normaliser à lécriture) |
| Age / Birth Date | `par_age` / `par_naissance` |
| Nationalité, urgence, allergies, condition médicale, langue… | questions (option C) |
**Visibilité liste / CT** : les coquilles `resultats_epreuves_commandees` doivent avoir **`pec_actif = 1`** (sinon absentes de la liste inscriptions et du sync). Les `I{eve}X…` = faux `no_commande` technique, pas un vrai panier.
~1800 lignes, ~1012 valeurs distinctes de Ticket Type (distances / jours).
## Fichiers
| Rôle | Fichier |
|---|---|
| SQL templates | `sql/MSIN-4482-import-resultats-templates.sql` |
| SQL type eve | `sql/MSIN-4482-type-sans-inscription-en-ligne.sql` |
| SQL fix liste | `sql/MSIN-4482-fix-pec-actif-import.sql` |
| Logique + UI | `php/inc_fx_import_resultats.php` |
| AJAX | `superadm/ajax_import_resultats.php` |
| Branche fiche eve | `superadm/php/inc_form.php` |
| CSS | `superadm/css/style_blue.css` |
## Hors scope (maintenant)
- Hub promoteur / mini-hub
- Waiver
- Écriture `resultats_*` (prochaine étape après UI)
- Import inverse CT → MS1
## Règles agent
- Changement minimal ; référencer **MSIN-4482**.
- **Pas de panier** pour ces participants — écrire dans `resultats_*`.
- **Ne pas modifier le hub promoteur** tant que ce nest pas redemandé.
- Ne pas inventer de waiver pour « signature de dossier ».
- SQL : **dev préprod** seulement, puis Navicat + sync outil.

View File

@ -0,0 +1,52 @@
---
description: MSIN-4520 — dette technique import participants (mapping colonnes) ; à revoir en entier avant d'y retoucher
alwaysApply: true
---
# Import participants — section à refaire (MSIN-4520)
**Constat utilisateur (2026-08-14, en pleine course)** : « faut tout revoir cette section-là ».
Soirée de patchs empilés (4.73.012 → 4.73.017) sur `php/inc_fx_import_resultats.php`.
Le nombre de versions en une soirée **est** le symptôme : on a rustiné au lieu de concevoir.
**Avant de retoucher cette section : lire cette note en entier et proposer un redesign, pas un patch.**
## Objectif produit (source de vérité)
Course **sans inscription en ligne**, fichier client (CSV/Excel) :
1. Je dépose un fichier → **ça marche**.
2. Je le redépose une 2ᵉ fois → **ça écrase** via la **clé unique** (`par_external_id`).
3. **Avec ou sans dossard** — le dossard n'est pas obligatoire.
4. **Ça n'invente rien** : ni question créée en douce, ni colonne devinée à la place de l'opérateur.
## Défauts connus (à corriger par conception)
| # | Défaut | Note |
|---|---|---|
| 1 | Le menu déroulant **offre un champ déjà pris** | **Le vrai fix** : un champ MS1 déjà utilisé doit **disparaître du pull-down**. Impossible de choisir deux dossards. |
| 2 | Ma « solution » = popup + nettoyage après coup | Jugée **stupide** par l'utilisateur, à raison : on avertit après la faute, et ça vide des sélections par surprise. À remplacer par le point 1. |
| 3 | Mapping stocké **champ → colonne** | Structure fragile : deux colonnes sur un champ = écrasement silencieux. Repenser la structure (colonne → champ, ou contrainte forte). |
| 4 | Repli par **position** dans `fxImportResultatsBuildColIndex` | Rustiné 3 fois en une soirée (4.73.013/014/017). Logique devenue illisible. À réécrire d'un coup. |
| 5 | Profil mémorisé rejoue une **erreur figée** | L'auto-save a mémorisé « Dossard = colonne date » et le rejouait à chaque import. Sanitize ajouté = rustine. |
| 6 | **Questions créées sans confirmation** à l'import | `que_tri = 900` = signature des questions auto-créées. L'utilisateur n'avait rien accepté et en a eu. |
| 7 | Le **reset** ne supprime pas les questions créées | Il efface participants / pec / réponses, mais `inscriptions_questions` reste pollué. |
| 8 | Aucun **aperçu de contrôle** avant écriture | Un dossard qui reçoit une date devrait sauter aux yeux avant l'import, pas se découvrir dans la liste. |
## Ce qui a été rustiné ce soir (à ne pas prendre pour une base saine)
- `4.73.012` auto-save du profil — **cause du déraillement**
- `4.73.013` mapping strict par nom → dossard **vide**
- `4.73.014` repli par position sauf contradiction
- `4.73.015` reset purge le profil auto
- `4.73.016` sanitize du mapping avant sauvegarde
- `4.73.017` interdiction 2 colonnes → 1 champ (UI popup + refus serveur + première colonne gagnante)
## Règles agent pour cette section
- **Pas de patch supplémentaire** : proposer un redesign du mapping et le faire valider **avant** de coder.
- Priorité 1 = **retirer du pull-down les champs déjà pris** (supprime la classe de bug à la racine).
- Ne **jamais** créer de question sans action explicite ; prévoir le nettoyage des questions auto-créées.
- Aucune mémorisation de mapping ne doit pouvoir rejouer une association contredite par l'en-tête.
- Tester le scénario réel : fichier `Bib / Team Name / Firstname / Lastname / Race / Club / Catégorie`, ~3600 lignes, réimport 2 fois.
- Rappel coût-bénéfice : une vérité mesurable (aperçu, contrôle) coûte moins que des essais successifs en production un soir de course.

View File

@ -0,0 +1,26 @@
---
description: Loader coureur Ms1 — dès quun AJAX / tâche risque dêtre longue
globs: js/v2/**/*.js,js/ms1-loader.js
alwaysApply: false
---
# Loader coureur (Ms1Loader) — règle produit bib / v2
Aussitôt quun clic / une tâche JS **risque** dêtre long (AJAX deferred, 1er chargement panneau, PDF, batch) → **afficher le bonhomme qui court immédiatement** (`Ms1Loader` / `bibFetch(..., { loader: true })`).
## Ne pas
- Partir un `fetch` / travail lourd avec `loader: false` « pour éviter de bloquer » si lutilisateur attend du feedback.
- Attendre la réponse pour seulement ensuite montrer le loader.
## Pattern bib
```js
bibFetch('/ajax_bib_range.php', { … }, { loader: true });
```
Compteur `bibLoaderDepth` : plusieurs appels concurrents OK.
## Exceptions rares
Uniquement si overlay volontairement indésirable **et** feedback local déjà visible (ex. placeholder inline). Documenter le ticket. Sinon = `loader: true`.

View File

@ -0,0 +1,38 @@
---
description: Conventions PHP legacy MS1 inscription (site + superadm)
globs: *.php, php/**/*.php, superadm/**/*.php, ajax*.php, auto_*.php
alwaysApply: false
---
# Conventions PHP MS1
Code applicatif legacy (racine, `php/`, `superadm/`). Ne pas imposer ces règles à `v3_ci4/`, `rapports/libs/`, vendors ou librairies tierces.
## Style à respecter
- **Suivre le fichier ouvert** : indentation, `array()`, guillemets, style des `if` — ne pas « moderniser » PHP dans un diff parallèle.
- **Préfixes de variables** du projet : `$str`, `$int`, `$tab`, `$bln`, `$mem`, `$v…` pour config globale.
- **Fonctions métier** : préfixe `fx` (`fxNbItemsPanier`, `inc_fonctions.php`). Réutiliser les helpers existants avant d'en créer.
- **Includes** : `php/inc_settings.php`, `php/inc_fonctions.php`, patterns `inc_*` / `inc_fx_*` déjà en place.
## Qualité
- Code **propre et lisible** dans la zone modifiée ; pas de code mort ni de `var_dump` laissé en prod.
- **Pas de sur-ingénierie** : pas d'abstraction pour un seul appel, pas de classes si le module voisin est procédural.
- Sécurité : échapper les sorties HTML, requêtes préparées / fonctions BD existantes — ne pas introduire de concat SQL brute.
## Version et assets
- CSS/JS servis par Minify : voir la règle `css-js-version-bump` — incrémenter `_VERSION_CODE` et `_DATE_CODE` dans `php/inc_settings.php`.
## Exemple de référence ticket
```php
// MSIN-4290
define('QR_SECRET_KEY', '…');
/**
* MSIN-4405 — Détermine, en LECTURE SEULE, pourquoi une inscription est annulée.
*/
function fx…()
```

View File

@ -0,0 +1,88 @@
---
description: Radar mail ↔ CRM — porte client (cadrage, pas un tampon métier)
alwaysApply: true
---
# Radar mail → CRM — deux plans
Cadrage 2026-08-15, raffiné 2026-08-20. Idée **IDEE-4**. Développement **MSOP-1** (espace MSOP).
Pas de code tant que le format du **signal** nest pas calé sur 23 mails réels.
## Intention
Aujourdhui le travail vit dans le **mail** (personne de la semaine) + le **CRM** (Drive déjà mappé au client). On décroche : fil client ↔ imprimeur sans nous, « logo beau » pris pour un Go, relais déquipe cassé.
La porte propre : **mail → fiche CRM du client / projet de lannée** et, sur la fiche, **les mails rattachés**. Si cest un dossard, linfo métier (Drive, lien MS1) est déjà là.
## Deux plans (ne pas fusionner)
| Plan | Rôle | Exemple |
|---|---|---|
| **1. Tampon métier** | Go nommé, daté, dans loutil qui assume lerreur | Écran approbation dossards **MSIN-4517** (visuel + séquences) |
| **2. Radar** | Lire les mails, rattacher au client, poser un **signal** | Tous types de demandes, pas seulement dossards |
- LIA **signale**, elle **ne tamponne pas**. « Le logo est beau » → signal *possible Go visuel*. Le vrai Go reste MS1.
- Le Drive client CRM est **déjà là** (`Inscription-Logos-Doss`, etc.). Ne pas remapper.
- Pas de boîte mail séparée comme process déquipe (centaines de tâches). Le canal de travail = **fiche CRM**.
- Pas de moteur qui « retrouve tout » par mot-clé comme système. Le radar **colle le fil au client / projet**.
## Périmètre lecture
Défaut dès le **jour 1** : **toutes** les boîtes `@ms1timing.com`. Pas une liste blanche. Pas « on commence par `info@` ».
Pas `@progiweb.ca`.
On **exclut** via une table dexceptions (modifiable sans recoder). Gmail Watch / Pub/Sub sabonne à chaque boîte du domaine **sauf** celles en exception.
| Type | Effet | Exemple v0 |
|---|---|---|
| **Boîte** | Ne pas ouvrir, pas de Watch | `confirmation@ms1timing.com` (transactionnel inscription) |
| **Expéditeur** | Ignorer même sil atterrit ailleurs | même adresse, si des copies existent |
Un fil dans plusieurs boîtes = une ligne (`thread_id` Gmail).
## Porte v0 (produit)
```
Mail ouvert → lien / bouton → fiche CRM du client / projet
fiche CRM → fils mail rattachés
→ si skill dossards : Drive + lien MS1 (approbation)
```
Le n° dans un code (`DOSS 1842`, `SITE 1842`) = n° **client / projet CRM** (à confirmer si besoin). Code **optionnel** : sil manque, lIA propose le client, un humain confirme.
## Moteur vs skills
- **Moteur** (stable) : mail → extraire qui / objet / CC / PJ → skill → client CRM → signal + confiance.
- **Skill** (ça évolue) : une fiche par type de demande. Ajouter une tâche = une fiche, pas un nouveau projet.
Skill v0 (seulement deux) :
| id | Détecte | Code | Action sur la fiche |
|---|---|---|---|
| `dossards` | dossard, bib, imprimeur, séquence, logo dossard | `DOSS` | Signal + alerte si imprimeur en CC et nous absents + lien MSIN-4517 |
| `site` | ouverture site, correction inscription, changement formulaire | `SITE` | Signal / demande CRM (pas de Go MS1) |
Vérité = **CRM**. On ninvente pas un client depuis un mail. Pas dans le CRM → libellé `Radar/à-mettre-dans-CRM`, on crée dans le CRM, on **relance**. Une correction de libellé est **gardée** (règle adresse → client / projet / skill).
Un fil Gmail peut avoir **plusieurs skills** (dossards + site dans le même mail) → plusieurs libellés, pas un seul.
## Signal (contenu minimum)
Date, de/à/CC, skill, client proposé, résumé 2 phrases, imprimeur en copie ?, « ça ressemble à un oui ? », lien Gmail, lien CRM. Jamais un feu vert dimpression.
## Évolution
1. Liste signaux + confirmation (feuille OK 2 semaines si le write CRM nest pas prêt).
2. Écrire le signal **sur la fiche client CRM** (Drive déjà lié).
3. Lien inverse : ouvrir le mail → bouton fiche CRM (add-on Gmail ou équivalent).
4. Skills 3+ : copier une fiche (TQ, facturation, ChronoTrack…).
5. Plus tard : auto-création si confiance haute **et** code présent.
## Règles agent
- Relire cette note **et** `10-approbation-dossards.mdc` si on touche dossards + mail/CRM.
- Ne pas coder le radar dans MS1. MS1 = tampon (4517) + séquences / Face-Endos. Radar = étage CRM / mail.
- Ne pas proposer une boîte `dossards@` comme solution principale (rejeté : trop de tâches, relais hebdo).
- Ne pas traiter un mail « oui / logo beau » comme un Go.
- Changement minimal ; pas de crawler Gmail maison en v1.
- Ticket code : **MSOP-1** (espace MSOP). Idée : **IDEE-4**. Pas de code sous IDEE.
- Ne pas proposer un démarrage `info@` seulement : le domaine entier moins exceptions, dès v1.

View File

@ -0,0 +1,11 @@
---
description: MS1 Inscription — stack et préfixe JIRA (projet)
alwaysApply: true
---
# Projet MS1 Inscription — stack
- Stack : PHP legacy (racine / `php/` / `superadm/`) + modules CI4 optionnels (`v3_ci4/`).
- Préfixe JIRA : **`MSIN-xxxx`**.
- Scripts SQL : `sql/MSIN-xxxx-description-courte.sql`.
- Workspace local typique : `C:\dev_v4\ms1inscription4`.

View File

@ -0,0 +1,112 @@
---
description: MS1 UI kit v2 — consulter AVANT tout nouvel écran / panneau (surtout choisir des champs)
alwaysApply: true
---
# UI kit v2 MS1 — source de vérité visuelle
**Obligatoire** avant de coder un nouvel écran de **choix de champs / colonnes / ordre** (ou tout panneau outil v2) : relire cette règle.
Quand lutilisateur demande « choisir des champs », « colonnes », « ordre daffichage », « appliquer à toutes » → **même réalité visuelle** = mockup **A**, pas une grille de checkboxes ni ↑↓ années 80.
## Visuel canonique — mockup A (tous les choosers)
**Fichier de vérité** : `_maquettes/msin-4512-mockup-A-dual-panel.png`
| Zone | Comportement |
|---|---|
| Gauche **Disponibles** | Recherche + **chips / pills** (pas une grille de cases nues) |
| Droite **Sélectionnés / Sur le PDF** | Liste ordonnée avec **poignée drag** (SortableJS), pas flèches ↑↓ |
| Orientation / options | Segmented control / pills (pas radios « formulaire ») |
| Budget / fit | Barre fine (P6) |
| Multi-épreuves | Entête (orient/budget) toujours ouvert. Chaque épreuve = même boîte repliable (1re ouverte). Apply-all depuis lépreuve cliquée. Pas de fusion entête↔1re épreuve. |
| Exceptions | (même boîte que toutes les épreuves) |
### Apply-all (P4) — règle produit
- Libellé : **Appliquer à toutes les épreuves** (Info).
- Source = lépreuve courante / celle quon vient de régler (souvent la 1ʳᵉ), **pas** un panneau maître séparé.
- Questions absentes sur une cible = ignorées.
- Garde-fous (confirm si déjà custom) = plus tard si besoin.
### Interdit (retour 80s)
- Grille dense de checkboxes « Champs de base / Questions »
- Boîte « Configuration commune » en plus des épreuves
- Monter/descendre comme UI principale dordre (drag = défaut)
- Réinventer un look différent à chaque ticket « choisir des champs »
## Code (à aligner sur mockup A)
| Couche | Fichier |
|---|---|
| PHP | `php/inc_fx_ms1_ui.php` (`fxMs1UiRender*`) |
| CSS | `css/style.css` — `.ms1-ui-*` (variables = escape hatch thème) |
| JS | `js/v2/ms1-ui-chooser.js` + **SortableJS** pour le drag |
| Mockup | `_maquettes/msin-4512-mockup-A-dual-panel.png` |
Les helpers actuels (cases / ↑↓) sont un **pont** : tout nouvel écran chooser et le refactor MSIN-4512 doivent viser le dual-panel A, pas le form legacy.
## Gate
1. Besoin = P2/P3/P4 ? → ouvrir mockup A, réutiliser le kit.
2. Pattern nouveau hors chooser → proposer mockup dans `_maquettes/` → validation → code.
3. Métier (PDF, SQL) après le visuel.
## Escape hatch
CSS variables sur `.ms1-ui-chooser` (`--ms1-ui-accent`, radius, etc.) — pas forker le composant pour une couleur.
## Mobile
Flex-wrap, zones tactiles ~44px, drag SortableJS OK touch ; pas de largeurs fixes cassantes.
## Chrome hors chooser (inchangé)
| Surface | Rôle |
|---|---|
| Fiche `inscr-gestion-fiche-section--*` | Palette terrain |
| `#module-bib` `.bib-tool-detail` | Cadre panneau outil |
## Patterns (catalogue)
| Id | Besoin | Réalité visuelle |
|---|---|---|
| P1 | Panneau outil bib | `.bib-tool-detail` |
| P2 | Choisir champs | Mockup A — panneau **Disponibles** (chips) |
| P3 | Ordre | Mockup A — panneau droit + **drag** SortableJS |
| P4 | Appliquer à toutes | Bouton unique ; copie depuis épreuve source |
| P5 | Orientation / choix exclusif | Pills / segmented (mockup A) |
| P6 | Fit / budget | Barre mm / statut |
| P7 | Choix binaire On/Off | **Switch** (`.ms1-ui-switch` / `fxMs1UiRenderSwitch`) — **pas** checkbox |
### P7 — On/Off (règle produit)
- Besoin = un seul interrupteur vrai/faux (inclure X, activer Y) → **switch On/Off**.
- Interdit : checkbox nue / « crochet form » pour un binaire.
- Garder **checkbox / chips** seulement pour **choix multiples** (plusieurs champs / options en parallèle).
- Segmented (P5) = 2+ options **exclusives labelisées** (Portrait / Paysage). Switch = un flag, libellé à côté.
- Code : `fxMs1UiRenderSwitch()` + CSS `.ms1-ui-switch*`.
## Marqueur migration
Root : `fxMs1UiKitRootAttrs()` → `data-ms1-ui-kit="1"` + `data-ms1-ui-screen` + `data-ms1-ui-patterns` + `data-ms1-ui-ticket`.
Commentaire : `// MS1-UI-KIT — …`
Grep : `data-ms1-ui-kit` / `MS1-UI-KIT`.
### Inventaire
| screen | Patterns | Ticket | Statut |
|---|---|---|---|
| `bib-dist-print` | P2P7 | MSIN-4512 | **mockup A** + switch P7 include sans no_bib |
| `bib-prod-fields` | P2P4 + side | MSIN-4515 | **mockup A** + pastilles Face/Endos/Info (pas de fit) |
| Face/Endos / exports futurs | P2P4 | — | même mockup A quand on touche |
## Anti-patterns
- Nouveau chooser sans mockup A
- Boîte « config commune » séparée
- Checkboxes / ↑↓ comme UI finale
- Checkbox pour un On/Off binaire (→ switch P7)
- HTML one-off hors `ms1-ui-*`
- FR/EN en dur ; JS v2 dans `inc_footer_scripts.php`

View File

@ -0,0 +1,36 @@
---
description: MS1 modules v2 — bib v4 et gestion inscriptions (hors legacy)
globs: js/v2/**,php/inc_v2_assets.php,inc_tableau_gestion_epreuves.php,inc_tableau_inscriptions_gestion.php,ajax_bib_range.php,ajax_inscr_gestion.php,php/inc_fx_promoteur.php,php/inc_fx_inscriptions_gestion.php
alwaysApply: false
---
# Modules v2 MS1
## Périmètre v2 (non prod, développement actif)
- **Bib assignments v4** — `#module-bib`, `fxShowBibTool4`, `ajax_bib_range.php`
- **Gestion inscriptions v2** — `#module-inscr-gestion`, `inc_fx_inscriptions_gestion.php`, `ajax_inscr_gestion.php`
## Règle JS
- **Ne jamais** ajouter de code v2 dans `inc_footer_scripts.php`.
- Nouveau JS v2 → `js/v2/` (fichier existant ou nouveau selon le module).
- Enregistrer le script sur la page concernée : `fxV2RegisterScript('bib-assignments')` ou `'inscr-gestion'`.
- Config PHP → JS : `data-*` sur le module (bib) ou `window.MS1_V2_INSCR_GESTION` (inscr).
- UI chooser : `js/v2/ms1-ui-chooser.js` chargé **avant** `bib-assignments` (auto via `fxV2RenderFooterScripts`).
## Legacy (ne pas toucher sauf pont minimal)
- `.btn_bib` jQuery, `fxShowBibTool2/3`, `inc_tableau_dossards.php`
## Fichiers de référence
| Rôle | Fichier |
|------|---------|
| Enregistrement / rendu scripts | `php/inc_v2_assets.php` |
| Bib v4 client | `js/v2/bib-assignments.js` |
| Inscr gestion client | `js/v2/inscr-gestion.js` |
## Visuel
Source de vérité : **`projet/10-ui-kit-v2.mdc`** (palette, boutons, patterns P1P6, gate avant nouvel écran). Ne pas dupliquer ni inventer une palette ici.

View File

@ -0,0 +1,83 @@
---
description: Workflow MS1 — JIRA, validation avant gros changements, code legacy, promotion BD
alwaysApply: true
---
# Workflow MS1 inscription
## Numéro JIRA (MSIN-xxxx)
- Au début d'une tâche de développement (nouvelle feature, correctif, refactor), **demander le numéro JIRA** si l'utilisateur ne l'a pas fourni.
- Préfixe courant features : `MSIN-xxxx`. Continuité / ops BD peut aussi être `CON-xxxx`.
- Ne pas bloquer une simple question ou une lecture du code — seulement quand on s'apprête à modifier des fichiers.
- Référencer le ticket dans le code et les scripts SQL : `// MSIN-4290` ou `-- MSIN-4379 — description`.
- Nommer les nouveaux scripts SQL : `sql/MSIN-xxxx-description-courte.sql`.
## Promotion BD (règle absolue)
Ordre **non négociable** pour faire passer structure + données vers dautres environnements :
1. **Scripts SQL** (`sql/MSIN-*.sql`) → exécutés **uniquement** sur **dev préprod**.
2. **Navicat** → copie de **structure** (préprod → autres env : dev prod, client préprod, client prod).
3. **Outil soft sync** (`php/sync_static_db.php`, CON-333) → peupler / aligner les **données** (manquants + corrections).
**Interdit** : demander ou suggérer de « rejouer le SQL » sur prod, préprod client, ou tout env autre que **dev préprod**.
- Table absente / Erreur lecture ailleurs = dabord **structure Navicat** depuis préprod, **puis** sync outil.
- `info` : jamais de DELETE (auto-création `afficheTexte`). Soft only.
- Catalogues (droits, pages, doc…) : pas dauto-création ; lalignement données passe par loutil, pas par un second import SQL.
## Portée des changements
- **Changement minimal** : corriger uniquement ce qui est demandé.
- Pas de refactor large, renommage massif ou « nettoyage » opportuniste sans accord explicite.
- Pour le code legacy (~10 ans), privilégier la cohérence locale (fichier / module touché) plutôt qu'une modernisation globale.
- Approche : **meilleur coût-bénéfice** (`entreprise/00-cout-benefice.mdc`) — ex. calibrateur GUI plutôt quune série dessais PDF.
## Incohérences repérées
Quand du code voisin contredit le pattern du fichier ou du module :
1. **Signaler** brièvement l'incohérence à l'utilisateur.
2. **Proposer** une correction ciblée ou l'alignement sur le pattern dominant du module.
3. **Attendre confirmation** avant de corriger du code hors périmètre de la tâche.
## Fin de tâche
- Résumer ce qui a changé (fichiers, comportement).
- Pour le SQL : rappeler quil sexécute **seulement en dev préprod**, puis structure Navicat + sync outil pour le reste — **pas** « exécuter ce script en prod ».
- Mentionner les étapes manuelles restantes (déploiement code, Navicat, sync soft, cache).
## Git push — jamais (lutilisateur synchronise)
- **JAMAIS** de `git push` (voir `entreprise/00-jamais-push.mdc`).
- Même sil dit « pousse / sync / déploie » : ne pas exécuter le push.
- Flux : corriger en local → dire que cest prêt → **lui** synchronise.
- Le webhook serveur ne se déclenche **que** après **son** sync.
## Commentaire JIRA après sync (obligatoire)
Quand **lutilisateur** a synchronisé vers Gitea / `dev`, **poster un commentaire** sur le ticket JIRA de la tâche (`MSIN-xxxx` ou `CON-xxxx`).
1. Lire `_VERSION_CODE` (et `_DATE_CODE` si présent) dans `php/inc_settings.php`.
2. Noter date/heure du push en **America/Toronto** (`YYYY-MM-DD HH:mm`).
3. Via MCP Atlassian `addCommentToJiraIssue` :
- `cloudId` : `progiwebinc.atlassian.net` (ou UUID via `getAccessibleAtlassianResources`)
- `issueIdOrKey` : le ticket de la tâche
- `contentFormat` : `markdown`
- Corps (format fixe) :
```text
Déployé code v{_VERSION_CODE} — push {YYYY-MM-DD HH:mm} (America/Toronto) — branche {branche} — {_DATE_CODE}
```
Exemple :
```text
Déployé code v4.72.883 — push 2026-07-28 21:27 (America/Toronto) — branche dev — 2026-07-28
```
- Si plusieurs tickets touchés : commenter **chaque** ticket listé dans le message de commit / demandé par lutilisateur.
- Si pas de ticket identifiable : **demander** le numéro avant de commenter (ne pas inventer).
- Ne pas dupliquer le même commentaire pour le même push (un commentaire par push réussi).
- Confirmer brièvement à lutilisateur que le commentaire JIRA est posté.

7
.gitignore vendored
View File

@ -7,6 +7,12 @@
node_modules/ node_modules/
dist/ dist/
# Composer artifacts (ne jamais committer vendor/)
/v3_ci4/vendor/
# IDE — ne jamais committer les connexions SQL (copier settings.json.example)
.vscode/settings.json
# Compiled Java class files # Compiled Java class files
*.class *.class
@ -68,6 +74,7 @@ Thumbs.db
/test/ /test/
/tpp/ /tpp/
/qr/ /qr/
/output/
/bp/ /bp/
/fnq/ /fnq/
/images/backgrounds/ /images/backgrounds/

View File

@ -1,24 +1,21 @@
RewriteEngine On RewriteEngine On
RewriteBase / RewriteBase /
# ==================================== # Redirect www — prod client seulement (en dev le redirect etait commente pour la meme raison)
# CI4 ROUTES PRIORITAIRES RewriteCond %{HTTP_HOST} !^www\. [NC]
# ==================================== RewriteCond %{HTTP_HOST} !^preprod\. [NC]
RewriteCond %{HTTP_HOST} !\.progiweb\.net$ [NC]
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=302,L]
# CI4 (dev / v3) — inoffensif en prod si v3 absent
RewriteRule ^test/?$ v3/index.php/test [E=IS_CI4:1,L] RewriteRule ^test/?$ v3/index.php/test [E=IS_CI4:1,L]
RewriteRule ^admin-dashboard/?$ v3/index.php/admin-dashboard [E=IS_CI4:1,L] RewriteRule ^admin-dashboard/?$ v3/index.php/admin-dashboard [E=IS_CI4:1,L]
RewriteRule ^resultats-nouveaux/?$ v3/index.php/resultats-nouveaux [E=IS_CI4:1,L] RewriteRule ^resultats-nouveaux/?$ v3/index.php/resultats-nouveaux [E=IS_CI4:1,L]
# ====================================
# STOP SI CI4
# ====================================
RewriteCond %{ENV:IS_CI4} =1 RewriteCond %{ENV:IS_CI4} =1
RewriteRule ^ - [L] RewriteRule ^ - [L]
# ==================================== RewriteCond %{SCRIPT_FILENAME} !-d
# LEGACY ROUTES RewriteCond %{SCRIPT_FILENAME} !-f
# ====================================
RewriteRule ^kc/([\w\-]+)/([\w\-]+)/?$ ./kc.php?param1=$1&param2=$2 [NC,QSA] RewriteRule ^kc/([\w\-]+)/([\w\-]+)/?$ ./kc.php?param1=$1&param2=$2 [NC,QSA]
@ -28,6 +25,9 @@ RewriteRule ^cart/([\w\-]+)/([\w\-]+)/?$ ./panier.php?lang=en&step=$1&code=$2 [N
RewriteRule ^compte/?([\w\-]+)?/?$ ./compte.php?lang=fr&code=$1 [NC,QSA] RewriteRule ^compte/?([\w\-]+)?/?$ ./compte.php?lang=fr&code=$1 [NC,QSA]
RewriteRule ^account/?([\w\-]+)?/?$ ./compte.php?lang=en&code=$1 [NC,QSA] RewriteRule ^account/?([\w\-]+)?/?$ ./compte.php?lang=en&code=$1 [NC,QSA]
RewriteRule ^mobile/?$ ./mobile.php?lang=fr [NC,QSA]
RewriteRule ^mobile/en/?$ ./mobile.php?lang=en [NC,QSA]
RewriteRule ^reserver/([\w\-]+)/(\d+)/?$ ./reserver.php?lang=fr&code=$1&id=$2 [NC,QSA] RewriteRule ^reserver/([\w\-]+)/(\d+)/?$ ./reserver.php?lang=fr&code=$1&id=$2 [NC,QSA]
RewriteRule ^book/([\w\-]+)/(\d+)/?$ ./reserver.php?lang=en&code=$1&id=$2 [NC,QSA] RewriteRule ^book/([\w\-]+)/(\d+)/?$ ./reserver.php?lang=en&code=$1&id=$2 [NC,QSA]
@ -36,6 +36,12 @@ RewriteRule ^donate/([\w\-]+)/([\w\-]+)/?/?([\w\-]+)?/?$ ./dons.php?lang=en&code
RewriteRule ^page/([\w\-]+)/?(fr|en)?/?$ ./index.php?code=$1&lang=$2 [NC,QSA] RewriteRule ^page/([\w\-]+)/?(fr|en)?/?$ ./index.php?code=$1&lang=$2 [NC,QSA]
RewriteCond %{REQUEST_URI} !^/(rapports_js|superadm)(/|$) [NC]
RewriteRule ^([\w\-]+)/?(fr|en)?/?([\w\-]+)?/?$ ./evenements.php?code=$1&lang=$2&tab=$3 [NC,QSA] RewriteRule ^([\w\-]+)/?(fr|en)?/?([\w\-]+)?/?$ ./evenements.php?code=$1&lang=$2&tab=$3 [NC,QSA]
RewriteCond %{REQUEST_URI} !^/(rapports_js|superadm/.*)$ # php -- BEGIN cPanel-generated handler, do not edit
# Set the "ea-php81" package as the default "PHP" programming language.
<IfModule mime_module>
AddHandler application/x-httpd-ea-php81___lsphp .php .php8 .phtml
</IfModule>
# php -- END cPanel-generated handler, do not edit

View File

@ -1,4 +1,5 @@
{ {
// Copier vers settings.json (gitignored) et renseigner vos identifiants locaux.
"sqltools.connections": [ "sqltools.connections": [
{ {
"mysqlOptions": { "mysqlOptions": {
@ -11,15 +12,15 @@
"port": 3306, "port": 3306,
"sshOptions": { "sshOptions": {
"port": 7822, "port": 7822,
"host": "104.254.182.111", "host": "YOUR_SSH_HOST",
"username": "devinscription", "username": "YOUR_SSH_USER",
"password": "M-G7+[NdA34H" "password": "YOUR_SSH_PASSWORD"
}, },
"driver": "MariaDB", "driver": "MariaDB",
"name": "astral(dev) ms1inscription", "name": "local dev connection",
"username": "devinscription_bd", "username": "YOUR_DB_USER",
"password": "x$@_v0-l_I((", "password": "YOUR_DB_PASSWORD",
"database": "devinscription_preprod" "database": "YOUR_DATABASE"
} }
] ]
} }

Binary file not shown.

View File

@ -0,0 +1,623 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MSIN-4511 — Préparation commande dossard / puces</title>
<style>
:root {
--bg: #eef1f4;
--card: #fff;
--ink: #1c2430;
--muted: #5c6b7a;
--line: #d5dde5;
--accent: #2f6fed;
--endos: #c62828;
--ok: #1b7a4a;
--warn: #b45309;
}
* { box-sizing: border-box; }
html, body { margin: 0; }
body {
font: 14px/1.45 system-ui, Segoe UI, sans-serif;
color: var(--ink);
background: var(--bg);
}
.wrap { max-width: 1080px; margin: 0 auto; padding: 24px 20px 80px; }
h1 { font-size: 22px; margin: 0 0 6px; }
h2 { font-size: 16px; margin: 0; }
p { margin: 0; }
.lede { color: var(--muted); margin-bottom: 18px; }
nav.tabs { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 18px; }
nav.tabs button {
border: 1px solid var(--line);
background: #fff;
padding: 8px 12px;
border-radius: 8px;
cursor: pointer;
font: inherit;
}
nav.tabs button.on { background: var(--accent); color: #fff; border-color: var(--accent); }
.panel { display: none; }
.panel.on { display: block; }
.box {
background: var(--card);
border: 1px solid var(--line);
border-radius: 10px;
padding: 16px;
margin-bottom: 14px;
}
.box.dash { border-style: dashed; color: var(--muted); }
.n { color: var(--accent); font-weight: 700; margin-right: 6px; }
.row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.grow { flex: 1; }
.muted { color: var(--muted); font-size: 13px; }
.tiny { color: var(--muted); font-size: 12px; }
.grid3, .grid2, .grid5 { display: grid; gap: 12px; }
.grid3 { grid-template-columns: 1fr 1fr 1fr; }
.grid2 { grid-template-columns: 1fr 1fr; }
.grid5 { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
@media (max-width: 800px) {
.grid3, .grid2 { grid-template-columns: 1fr; }
}
.pagebar {
display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
margin-bottom: 14px; padding-bottom: 12px; border-bottom: 1px solid var(--line);
}
.fake-nav { display: flex; gap: 6px; flex-wrap: wrap; }
.fake-nav span {
border: 1px solid var(--line); background: #fff; padding: 6px 10px;
border-radius: 6px; font-size: 13px;
}
.fake-nav span.on { background: var(--accent); color: #fff; border-color: var(--accent); }
.fake-nav span.off { text-decoration: line-through; color: #9aa6b2; background: #f3f5f7; }
.who {
display: inline-block; font-size: 11px; font-weight: 700;
border-radius: 99px; padding: 1px 7px; margin-left: 4px;
border: 1px solid var(--line); color: var(--muted);
}
.who.auto { border-color: #b7e0c4; color: var(--ok); background: #edf8f0; }
.who.edit { border-color: #c9d8f5; color: var(--accent); background: #eef4ff; }
.fold-head { cursor: pointer; user-select: none; }
.chev { display: inline-block; width: 1.2em; color: var(--muted); font-size: 12px; }
.fold-body { margin-top: 12px; }
.fold.is-min .fold-body { display: none; }
.jalon {
display: grid;
grid-template-columns: 1.5fr 160px minmax(120px, 1fr);
gap: 12px;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid var(--line);
}
.jalon:last-child { border-bottom: 0; }
@media (max-width: 700px) {
.jalon { grid-template-columns: 1fr; gap: 4px; }
}
label.field { display: block; font-size: 12px; color: var(--muted); margin-bottom: 4px; }
input[type="date"], input[type="search"] {
width: 100%;
padding: 8px 10px;
border: 1px solid var(--line);
border-radius: 6px;
font: inherit;
}
.stat { font-size: 18px; font-weight: 650; }
.btn {
border: 0;
background: var(--accent);
color: #fff;
padding: 8px 12px;
border-radius: 6px;
font: inherit;
cursor: pointer;
}
.btn.ghost { background: #fff; color: var(--ink); border: 1px solid var(--line); }
.sheet {
border: 1px solid var(--line);
border-radius: 8px;
padding: 10px 12px;
background: #f7f9fb;
}
.epr { border: 1px solid #c9d4e3; border-radius: 8px; padding: 12px; background: #fafbfd; }
table.seg { width: 100%; border-collapse: collapse; }
table.seg th, table.seg td { text-align: left; padding: 8px 6px; border-bottom: 1px solid var(--line); }
table.seg th { font-size: 12px; color: var(--muted); font-weight: 600; }
.switch {
width: 40px; height: 22px; border-radius: 99px; border: 0; padding: 2px;
background: #c5ced6; cursor: pointer; position: relative;
}
.switch.on { background: var(--accent); }
.switch i {
display: block; width: 18px; height: 18px; border-radius: 50%;
background: #fff; transform: translateX(0);
}
.switch.on i { transform: translateX(18px); }
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.chip {
border: 1px solid var(--line); border-radius: 99px; padding: 4px 10px;
background: #fff; font-size: 12px;
}
.card-field {
display: flex; align-items: center; gap: 8px;
border: 1px solid var(--line); border-radius: 8px;
padding: 8px 10px; margin-bottom: 6px; background: #fff;
}
.pills { display: flex; gap: 4px; margin-left: auto; }
.pill { font-size: 11px; font-weight: 700; border-radius: 99px; padding: 2px 8px; border: 1px solid var(--line); color: var(--muted); }
.pill.face.on { background: var(--accent); color: #fff; border-color: var(--accent); }
.pill.endos.on { background: var(--endos); color: #fff; border-color: var(--endos); }
.pill.info.on { background: #fff; color: var(--accent); border-color: var(--accent); }
table.data { width: 100%; border-collapse: collapse; font-size: 13px; }
table.data th, table.data td { text-align: left; padding: 8px; border-bottom: 1px solid var(--line); }
table.data th { font-size: 12px; color: var(--muted); }
.note { background: #eef4ff; border: 1px solid #c9d8f5; border-radius: 8px; padding: 10px 12px; margin-bottom: 14px; }
.warn { background: #fff7ed; border: 1px solid #f0d3a8; border-radius: 8px; padding: 10px 12px; margin: 12px 0; }
.ok { background: #edf8f0; border: 1px solid #b7e0c4; border-radius: 8px; padding: 10px 12px; margin-top: 12px; }
</style>
</head>
<body>
<div class="wrap">
<h1>Préparation commande dossard / puces</h1>
<p class="lede">Maquette HTML — scroll normal, interrupteurs cliquables. Pas de code écran MS1. Ouvre ce fichier dans Chrome / Edge.</p>
<nav class="tabs">
<button type="button" class="on" data-tab="ecran">Page production</button>
<button type="button" data-tab="ou">Où ça vit</button>
<button type="button" data-tab="jalons">Les 5 dates</button>
<button type="button" data-tab="dash">Tableau de bord ops</button>
</nav>
<section id="ecran" class="panel on">
<div class="pagebar">
<button type="button" class="btn ghost">← Dossards (quantités / assignation)</button>
<div>
<strong>Commande dossards / puces</strong>
<div class="tiny">Demi-marathon Caisse Desjardins · page à part, pas un 4e onglet</div>
</div>
</div>
<div class="note">Page dédiée ops. Les interrupteurs des segments mettent à jour les deux fichiers. Cadenas = fichier imprimeur seulement.</div>
<div class="box fold">
<div class="row fold-head">
<span class="chev"></span>
<h2><span class="n">1</span>Dates importantes</h2>
</div>
<div class="fold-body">
<p class="muted" style="margin-bottom:4px">Toutes des jalons (échéances). La colonne réalité se remplira toute seule plus tard, quand laction aura vraiment eu lieu.</p>
<div class="jalon">
<div>
<strong>Le client les veut</strong>
<div class="tiny">Les dossards chez le client pour cette date</div>
</div>
<div>
<label class="field">Jalon</label>
<input type="date" value="2026-09-07">
</div>
<div>
<div class="tiny">Réalité <span class="who auto">auto plus tard</span></div>
<div></div>
</div>
</div>
<div class="jalon">
<div>
<strong>Visuel approuvé</strong>
<div class="tiny">Le visuel doit être tamponné pour cette date</div>
</div>
<div>
<label class="field">Jalon</label>
<input type="date" value="2026-08-20">
</div>
<div>
<div class="tiny">Réalité <span class="who auto">auto plus tard</span></div>
<div></div>
</div>
</div>
<div class="jalon">
<div>
<strong>Envoi imprimeur</strong>
<div class="tiny">Le fichier dossards doit être parti pour cette date</div>
</div>
<div>
<label class="field">Jalon</label>
<input type="date" value="2026-08-22">
</div>
<div>
<div class="tiny">Réalité <span class="who auto">auto plus tard</span></div>
<div></div>
</div>
</div>
<div class="jalon">
<div>
<strong>Réception de limprimeur</strong>
<div class="tiny">On veut les dossards imprimés reçus pour cette date</div>
</div>
<div>
<label class="field">Jalon</label>
<input type="date" value="2026-09-01">
</div>
<div>
<div class="tiny">Réalité <span class="who auto">auto plus tard</span></div>
<div></div>
</div>
</div>
<div class="jalon">
<div>
<strong>Envoi puces</strong>
<div class="tiny">On veut que les puces soient envoyées pour cette date</div>
</div>
<div>
<label class="field">Jalon</label>
<input type="date" value="2026-08-25">
</div>
<div>
<div class="tiny">Réalité <span class="who auto">auto plus tard</span></div>
<div></div>
</div>
</div>
</div>
</div>
<div class="box fold">
<div class="row fold-head">
<span class="chev"></span>
<h2><span class="n">2</span>Deux fichiers Excel</h2>
</div>
<div class="fold-body">
<p class="muted" style="margin-bottom:12px">Un fichier par destinataire. Générer remplit la <em>réalité</em> du jalon correspondant, pas léchéance.</p>
<div class="grid2">
<div class="sheet">
<div class="tiny">Fichier 1 · Imprimeur</div>
<strong>commande-dossards.xlsx</strong>
<p id="out-doss" style="margin:8px 0">5 km · 1200</p>
<p class="tiny">Face / Endos / Info. Séquences « commande dossards ». Verrouille ces séquences. Réalité = envoi imprimeur.</p>
<button type="button" class="btn" style="margin-top:10px" id="btn-doss">Générer le fichier imprimeur</button>
</div>
<div class="sheet">
<div class="tiny">Fichier 2 · Commande puces MS1</div>
<strong>commande-puces.xlsx</strong>
<p id="out-puces" style="margin:8px 0">5 km · 1200 · 201400</p>
<p class="tiny">Plages + qté. Même numéro que le dossard. Ne verrouille pas. Réalité = envoi puces.</p>
<button type="button" class="btn ghost" style="margin-top:10px" id="btn-puces">Générer le fichier puces</button>
</div>
</div>
</div>
</div>
<div class="box fold">
<div class="row fold-head">
<span class="chev"></span>
<h2><span class="n">3</span>Par épreuve</h2>
</div>
<div class="fold-body">
<p class="tiny">Segments = quoi commander. Information à imprimer = Face / Endos / Info. Chaque sous-boîte se replie.</p>
<div class="epr fold" style="margin-top:12px">
<div class="row fold-head">
<span class="chev"></span>
<strong>5 km</strong>
<span class="tiny">2 séquences</span>
</div>
<div class="fold-body">
<div class="box fold" style="margin:0 0 12px; background:#fff">
<div class="row fold-head">
<span class="chev"></span>
<strong>Segments de dossard</strong>
</div>
<div class="fold-body">
<table class="seg">
<thead>
<tr>
<th>Séquence</th>
<th>Commande puces</th>
<th>Commande dossards</th>
<th>Impression</th>
</tr>
</thead>
<tbody>
<tr>
<td>1 à 200</td>
<td><button type="button" class="switch on" data-k="s1p" aria-label="Commande puces 1-200"><i></i></button></td>
<td><button type="button" class="switch on" data-k="s1d" aria-label="Commande dossards 1-200"><i></i></button></td>
<td class="tiny" id="lock-s1">à verrouiller</td>
</tr>
<tr>
<td>201 à 400</td>
<td><button type="button" class="switch on" data-k="s2p" aria-label="Commande puces 201-400"><i></i></button></td>
<td><button type="button" class="switch" data-k="s2d" aria-label="Commande dossards 201-400"><i></i></button></td>
<td class="tiny" id="lock-s2">libre</td>
</tr>
</tbody>
</table>
<p class="tiny" style="margin-top:8px">201400 : puces oui, dossards non = le proprio a déjà le papier. Absent du fichier imprimeur, présent dans le fichier puces.</p>
</div>
</div>
<div class="box fold" style="margin:0; background:#fff">
<div class="row fold-head">
<span class="chev"></span>
<strong>Information à imprimer sur le dossard</strong>
<span class="grow"></span>
<button type="button" class="btn ghost" data-apply>Appliquer à toutes les épreuves</button>
</div>
<div class="fold-body">
<p class="tiny" style="margin-bottom:10px">Copie Face / Endos / Info vers les autres épreuves. Ne copie pas les drapeaux de segments.</p>
<div class="grid2">
<div class="sheet" style="background:#f7f9fb">
<strong>Disponibles</strong>
<input type="search" placeholder="Rechercher…" style="margin-top:8px" disabled>
<div class="chips">
<span class="chip">Prénom</span>
<span class="chip">Nom</span>
<span class="chip">Ville</span>
<span class="chip">Courriel</span>
<span class="chip">Pays (code 2 lettres)</span>
<span class="chip">Nom déquipe</span>
</div>
</div>
<div class="sheet" style="background:#f7f9fb">
<strong>Sur lExcel (ordre)</strong>
<div class="card-field" style="margin-top:8px">
<span class="tiny"></span> N° dossard
<span class="pills">
<span class="pill face on">Face</span>
<span class="pill endos">Endos</span>
<span class="pill info">Info</span>
</span>
</div>
<div class="card-field">
<span class="tiny"></span> Épreuve
<span class="pills">
<span class="pill face">Face</span>
<span class="pill endos">Endos</span>
<span class="pill info on">Info</span>
</span>
</div>
<div class="card-field">
<span class="tiny"></span> Nom contact urgence
<span class="pills">
<span class="pill face">Face</span>
<span class="pill endos on">Endos</span>
<span class="pill info">Info</span>
</span>
</div>
<div class="card-field">
<span class="tiny"></span> Téléphone urgence
<span class="pills">
<span class="pill face">Face</span>
<span class="pill endos on">Endos</span>
<span class="pill info">Info</span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="epr fold" style="margin-top:10px">
<div class="row fold-head">
<span class="chev"></span>
<strong>10 km</strong>
<span class="tiny">1 séquence</span>
</div>
<div class="fold-body">
<div class="box fold" style="margin:0 0 12px; background:#fff">
<div class="row fold-head">
<span class="chev"></span>
<strong>Segments de dossard</strong>
</div>
<div class="fold-body">
<table class="seg">
<thead>
<tr>
<th>Séquence</th>
<th>Commande puces</th>
<th>Commande dossards</th>
<th>Impression</th>
</tr>
</thead>
<tbody>
<tr>
<td>500 à 900</td>
<td><button type="button" class="switch on" data-k="s3p" aria-label="Commande puces 500-900"><i></i></button></td>
<td><button type="button" class="switch on" data-k="s3d" aria-label="Commande dossards 500-900"><i></i></button></td>
<td class="tiny" id="lock-s3">à verrouiller</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="box fold" style="margin:0; background:#fff">
<div class="row fold-head">
<span class="chev"></span>
<strong>Information à imprimer sur le dossard</strong>
<span class="grow"></span>
<button type="button" class="btn ghost" data-apply>Appliquer à toutes les épreuves</button>
</div>
<div class="fold-body">
<p class="tiny">Même boîte que le 5 km — champs propres à cette épreuve.</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="box dash fold is-min">
<div class="row fold-head">
<span class="chev"></span>
<h2>Plus tard — visuel dossard</h2>
</div>
<div class="fold-body">
<p>Le Go client reste MSIN-4517. Ici, au plus une pastille dans lentête dépreuve. Le jalon « visuel approuvé » est léchéance ; la réalité viendra du tampon 4517.</p>
</div>
</div>
</section>
<section id="ou" class="panel">
<h2>Sortir de la barre Quantité / Assignation / Mode spécial</h2>
<p class="lede" style="margin-top:8px">Oui : page à part. Ce nest pas le même métier, pas les mêmes gens, pas le même droit.</p>
<div class="box">
<strong>Aujourdhui</strong>
<p class="muted" style="margin:8px 0">4e onglet dans <code>#module-bib</code>, même ligne que la préparation terrain.</p>
<div class="fake-nav" style="margin-top:8px">
<span>Quantités</span>
<span>Assignation</span>
<span>Mode spécial</span>
<span class="on">Production de dossards</span>
</div>
<p class="tiny" style="margin-top:8px">Tout le monde qui gère les dossards voit longlet. Le catalogue Face/Endos est déjà assez lourd pour quon le charge en différé (MSIN-4546 parlait déjà d« autre page »).</p>
</div>
<div class="box">
<strong>Proposition</strong>
<p class="muted" style="margin:8px 0">La barre terrain reste terrain. La commande devient une page ops.</p>
<div class="fake-nav" style="margin-top:8px">
<span class="on">Quantités</span>
<span>Assignation</span>
<span>Mode spécial</span>
<span class="off">Production</span>
</div>
<p class="muted" style="margin:12px 0 6px">Entrée vers la page dédiée :</p>
<ul class="muted">
<li>Droit à part (pas <code>dossards.manage</code>). Assigner un dossard ≠ commander chez limprimeur.</li>
<li>Pour linstant : nous, administrateurs / ops.</li>
<li>Plus tard : le dashboard (file dossards / file puces) ouvre cette page pour une course.</li>
<li>Depuis le module dossards : éventuellement un lien discret « Commande » seulement si on a le droit — pas un onglet frère.</li>
</ul>
<div class="ok">La page lit les séquences de lévénement (déjà là). Elle nassigne rien. Retour = Quantités / Assignation.</div>
</div>
<div class="warn">
On ne donne pas cette page au promoteur « dossards » par défaut. Excel imprimeur + commande puces + dates = outil interne. Le client voit lapprobation (MSIN-4517), pas la cuisine.
</div>
</section>
<section id="jalons" class="panel">
<h2>Les 5 dates = 5 jalons, plus une réalité chacun plus tard</h2>
<p class="lede" style="margin-top:8px">Ce quon saisit = léchéance. Ce qui se remplira tout seul = le jour où cest vraiment arrivé.</p>
<div class="box">
<table class="data">
<thead>
<tr><th>Jalon (saisie)</th><th>Sens</th><th>Réalité plus tard</th></tr>
</thead>
<tbody>
<tr>
<td>Le client les veut</td>
<td>Les dossards chez le client pour cette date</td>
<td>Quand cest réellement livré / confirmé</td>
</tr>
<tr>
<td>Visuel approuvé</td>
<td>Le visuel doit être tamponné pour cette date</td>
<td>Date du Go MSIN-4517 (lot complet)</td>
</tr>
<tr>
<td>Envoi imprimeur</td>
<td>Le fichier dossards doit partir pour cette date</td>
<td>Date réelle denvoi du fichier imprimeur</td>
</tr>
<tr>
<td>Réception de limprimeur</td>
<td>On veut les dossards imprimés reçus pour cette date</td>
<td>Date réelle du colis reçu</td>
</tr>
<tr>
<td>Envoi puces</td>
<td>On veut que les puces partent pour cette date</td>
<td>Date réelle denvoi du fichier puces</td>
</tr>
</tbody>
</table>
<p class="tiny" style="margin-top:10px">Dashboard = jalon vs réalité, pour voir qui est en retard. CRM peut écrire les jalons plus tard, même table.</p>
</div>
</section>
<section id="dash" class="panel">
<h2>Même data, deux lunettes ops</h2>
<p class="lede" style="margin-top:8px">Le dashboard ninvente rien. Il filtre les segments et affiche les dates de la commande.</p>
<div class="grid2">
<div class="box">
<strong>File imprimeur</strong>
<table class="data" style="margin-top:8px">
<thead><tr><th>Course</th><th>Qté</th><th>Client veut</th><th>Visuel</th><th>Envoi</th><th>Réception</th></tr></thead>
<tbody>
<tr><td>Desjardins 5 km (1200)</td><td>200</td><td>7 sept</td><td></td><td></td><td></td></tr>
<tr><td>Desjardins 10 km</td><td>401</td><td>7 sept</td><td></td><td></td><td></td></tr>
<tr><td>Boréalys marathon</td><td>3 000</td><td>1 sept</td><td>20 juil</td><td>12 août</td><td>18 août</td></tr>
</tbody>
</table>
<p class="tiny">Sans le 201400 (dossards déjà chez le proprio).</p>
</div>
<div class="box">
<strong>File puces</strong>
<table class="data" style="margin-top:8px">
<thead><tr><th>Course</th><th>Qté</th><th>Client veut</th><th>Envoi puces</th></tr></thead>
<tbody>
<tr><td>Desjardins 5 km (1200 + 201400)</td><td>400</td><td>7 sept</td><td></td></tr>
<tr><td>Desjardins 10 km</td><td>401</td><td>7 sept</td><td></td></tr>
<tr><td>Boréalys marathon</td><td>3 000</td><td>1 sept</td><td>12 août</td></tr>
</tbody>
</table>
<p class="tiny">Le 201400 reste ici. Pas de colonne réception imprimeur.</p>
</div>
</div>
</section>
</div>
<script>
const state = { s1p: true, s1d: true, s2p: true, s2d: false, s3p: true, s3d: true };
document.querySelectorAll('nav.tabs button').forEach(function (btn) {
btn.addEventListener('click', function () {
document.querySelectorAll('nav.tabs button').forEach(function (b) { b.classList.remove('on'); });
document.querySelectorAll('.panel').forEach(function (p) { p.classList.remove('on'); });
btn.classList.add('on');
document.getElementById(btn.getAttribute('data-tab')).classList.add('on');
window.scrollTo(0, 0);
});
});
function refreshExcel() {
var doss = [];
var puces = [];
if (state.s1d) doss.push('5 km · 1200');
if (state.s2d) doss.push('5 km · 201400');
if (state.s3d) doss.push('10 km · 500900');
if (state.s1p) puces.push('5 km · 1200');
if (state.s2p) puces.push('5 km · 201400');
if (state.s3p) puces.push('10 km · 500900');
document.getElementById('out-doss').textContent = doss.length ? doss.join(' · ') : 'Aucune séquence — pas de fichier';
document.getElementById('out-puces').textContent = puces.length ? puces.join(' · ') : 'Aucune séquence — pas de fichier';
document.getElementById('lock-s1').textContent = state.s1d ? 'à verrouiller' : 'libre';
document.getElementById('lock-s2').textContent = state.s2d ? 'à verrouiller' : 'libre';
document.getElementById('lock-s3').textContent = state.s3d ? 'à verrouiller' : 'libre';
document.getElementById('btn-doss').disabled = !doss.length;
document.getElementById('btn-puces').disabled = !puces.length;
}
document.querySelectorAll('.fold-head').forEach(function (head) {
head.addEventListener('click', function (e) {
if (e.target.closest('button, input, a, label')) return;
var fold = head.closest('.fold');
var min = fold.classList.toggle('is-min');
var chev = head.querySelector('.chev');
if (chev) chev.textContent = min ? '▶' : '▼';
});
});
document.querySelectorAll('.switch').forEach(function (sw) {
sw.addEventListener('click', function () {
var k = sw.getAttribute('data-k');
state[k] = !state[k];
sw.classList.toggle('on', state[k]);
refreshExcel();
});
});
</script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -0,0 +1,359 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MSIN-4579 — Notes commande + cadenas</title>
<style>
:root {
--bg: #eef1f4;
--card: #fff;
--ink: #1c2430;
--muted: #5c6b7a;
--line: #d5dde5;
--accent: #3d5afe;
--ok: #1b7a4a;
--teal: #1aa37a;
--bar: #3d5afe;
--yellow: #fff59d;
}
* { box-sizing: border-box; }
html, body { margin: 0; }
body {
font: 14px/1.45 system-ui, Segoe UI, sans-serif;
color: var(--ink);
background: var(--bg);
}
.wrap { max-width: 1120px; margin: 0 auto; padding: 24px 20px 80px; }
h1 { font-size: 22px; margin: 0 0 6px; }
h2 { font-size: 16px; margin: 0; }
p { margin: 0; }
.lede { color: var(--muted); margin-bottom: 18px; }
nav.tabs { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 18px; }
nav.tabs button {
border: 1px solid var(--line);
background: #fff;
padding: 8px 12px;
border-radius: 8px;
cursor: pointer;
font: inherit;
}
nav.tabs button.on { background: var(--accent); color: #fff; border-color: var(--accent); }
.panel { display: none; }
.panel.on { display: block; }
.box {
background: var(--card);
border: 1px solid var(--line);
border-radius: 10px;
padding: 16px;
margin-bottom: 14px;
}
.muted { color: var(--muted); font-size: 13px; }
.tiny { color: var(--muted); font-size: 12px; }
.n { color: var(--accent); font-weight: 700; margin-right: 6px; }
.yes {
background: #edf8f0;
border-left: 4px solid var(--ok);
padding: 8px 12px;
margin: 8px 0;
}
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 800px) { .grid2 { grid-template-columns: 1fr; } }
.epr-bar {
background: var(--bar);
color: #fff;
border-radius: 8px 8px 0 0;
padding: 10px 14px;
font-weight: 650;
display: flex;
justify-content: space-between;
}
.epr-panel {
border: 1px solid var(--line);
border-radius: 8px;
overflow: hidden;
background: #fff;
}
.epr-body { padding: 14px 16px 16px; }
table.seg { width: 100%; border-collapse: collapse; }
table.seg th, table.seg td {
text-align: left;
padding: 10px 8px;
border-bottom: 1px solid #e8eef4;
vertical-align: middle;
}
table.seg th { font-size: 12px; color: var(--muted); font-weight: 600; }
table.seg td.note { width: 42%; }
.cell-sw { display: flex; align-items: center; gap: 8px; }
.switch {
width: 40px; height: 22px; border-radius: 99px; border: 0; padding: 2px;
background: var(--teal); cursor: default; position: relative; flex: 0 0 auto;
}
.switch.off { background: #c5ced6; }
.switch i {
display: block; width: 18px; height: 18px; border-radius: 50%;
background: #fff; transform: translateX(18px);
}
.switch.off i { transform: translateX(0); }
.padlock { width: 14px; height: 16px; color: #6b7785; flex: 0 0 auto; }
input.note {
width: 100%;
padding: 7px 9px;
border: 1px solid var(--line);
border-radius: 6px;
font: inherit;
}
.sheet {
border: 1px solid var(--line);
border-radius: 8px;
padding: 14px;
background: #f7f9fb;
}
label.field { display: block; font-size: 12px; color: var(--muted); margin: 10px 0 4px; }
textarea {
width: 100%;
min-height: 72px;
padding: 8px 10px;
border: 1px solid var(--line);
border-radius: 6px;
font: inherit;
resize: vertical;
background: #fff;
}
.btn {
border: 0;
background: var(--accent);
color: #fff;
padding: 8px 12px;
border-radius: 6px;
font: inherit;
cursor: default;
}
.btn.ghost { background: #fff; color: var(--ink); border: 1px solid var(--line); }
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 12px; }
.ver { font-weight: 650; font-variant-numeric: tabular-nums; }
.xl {
border: 2px solid #222;
background: #fff;
font: 12px/1.3 Segoe UI, sans-serif;
overflow: auto;
}
.xl table { width: 100%; border-collapse: collapse; }
.xl th, .xl td {
border: 1px solid #bbb;
padding: 6px 8px;
text-align: center;
}
.xl th { background: #f3f3f3; font-weight: 650; }
.xl .seq { background: #fff; font-size: 15px; }
.xl .puce { font-size: 11px; letter-spacing: .04em; }
.xl .yel { background: var(--yellow); font-weight: 650; }
.xl .note { text-align: left; min-width: 180px; background: #fffbe6; }
.xl .bar { background: #9e9e9e; height: 8px; padding: 0; border-color: #9e9e9e; }
.xl .total { font-weight: 700; }
.xl .left { text-align: left; }
ul.tight { margin: 8px 0 0; padding-left: 18px; color: var(--muted); }
ul.tight li { margin: 4px 0; }
</style>
</head>
<body>
<div class="wrap">
<h1>MSIN-4579 — Notes de commande + cadenas</h1>
<p class="lede">Proposition visuelle. Pas de code. Continuité MSIN-4577 / parent MSIN-4511.</p>
<nav class="tabs">
<button type="button" class="on" data-tab="ecran">Écran MS1</button>
<button type="button" data-tab="excel">Excel (template)</button>
</nav>
<section id="ecran" class="panel on">
<div class="yes">
<strong>Deux notes, deux endroits.</strong>
Tableau = <em>note par séquence</em> (à la place du Cadenas). Cartes fichier = <em>note globale</em> rattachée à la version quon envoie.
</div>
<div class="box" style="margin-top:14px">
<h2><span class="n">3</span>Segments — cadenas à côté du switch, note par séquence</h2>
<p class="muted" style="margin:8px 0 12px">Colonne Cadenas enlevée. Petit cadenas seulement si ce switch-là est barré. Le champ Note = une ligne, la même note part dans les deux Excel si la séquence est commandée des deux côtés.</p>
<div class="epr-panel">
<div class="epr-bar"><span>Segments de dossard</span><span></span></div>
<div class="epr-body">
<table class="seg">
<thead>
<tr>
<th>Séquence</th>
<th>Commande puces</th>
<th>Commande dossards</th>
<th>Note</th>
</tr>
</thead>
<tbody>
<tr>
<td>1 2</td>
<td>
<div class="cell-sw">
<button type="button" class="switch" aria-label="Commande puces 1-2, bloquée"><i></i></button>
<svg class="padlock" viewBox="0 0 14 16" aria-label="Bloqué"><rect x="2" y="7" width="10" height="8" rx="1.5" fill="currentColor"/><path d="M4 7V5a3 3 0 0 1 6 0v2" fill="none" stroke="currentColor" stroke-width="1.6"/></svg>
</div>
</td>
<td>
<div class="cell-sw">
<button type="button" class="switch" aria-label="Commande dossards 1-2, bloquée"><i></i></button>
<svg class="padlock" viewBox="0 0 14 16" aria-label="Bloqué"><rect x="2" y="7" width="10" height="8" rx="1.5" fill="currentColor"/><path d="M4 7V5a3 3 0 0 1 6 0v2" fill="none" stroke="currentColor" stroke-width="1.6"/></svg>
</div>
</td>
<td class="note"><input class="note" value="Lot test — 2 dossards"></td>
</tr>
<tr>
<td>2001 2500</td>
<td>
<div class="cell-sw">
<button type="button" class="switch" aria-label="Commande puces 2001-2500, bloquée"><i></i></button>
<svg class="padlock" viewBox="0 0 14 16" aria-label="Bloqué"><rect x="2" y="7" width="10" height="8" rx="1.5" fill="currentColor"/><path d="M4 7V5a3 3 0 0 1 6 0v2" fill="none" stroke="currentColor" stroke-width="1.6"/></svg>
</div>
</td>
<td>
<div class="cell-sw">
<button type="button" class="switch" aria-label="Commande dossards 2001-2500, bloquée"><i></i></button>
<svg class="padlock" viewBox="0 0 14 16" aria-label="Bloqué"><rect x="2" y="7" width="10" height="8" rx="1.5" fill="currentColor"/><path d="M4 7V5a3 3 0 0 1 6 0v2" fill="none" stroke="currentColor" stroke-width="1.6"/></svg>
</div>
</td>
<td class="note"><input class="note" value="Livraison entrepôt Longueuil"></td>
</tr>
<tr>
<td>3001 3100</td>
<td>
<div class="cell-sw">
<button type="button" class="switch" aria-label="Commande puces 3001-3100"><i></i></button>
</div>
</td>
<td>
<div class="cell-sw">
<button type="button" class="switch off" aria-label="Commande dossards 3001-3100, off"><i></i></button>
</div>
</td>
<td class="note"><input class="note" value="Dossards déjà chez le proprio"></td>
</tr>
</tbody>
</table>
<p class="tiny" style="margin-top:10px">Ligne 30013100 : pas de cadenas (pas bloqué). Switch dossards off = cette séquence nest pas dans le fichier imprimeur ; la note ny va pas. Elle va dans le fichier puces, à côté de 30013100.</p>
</div>
</div>
</div>
<div class="box">
<h2><span class="n">2</span>Note globale — rattachée à la version du fichier</h2>
<p class="muted" style="margin:8px 0 12px">Une note pour limprimeur, une pour les puces. Pied de page du fichier (le texte « 12 jours ouvrables » devient le défaut éditable). Ce nest pas la note de séquence.</p>
<div class="grid2">
<div class="sheet">
<div class="tiny">Fichier 1</div>
<strong>Fichier imprimeur</strong>
<label class="field">Note globale de cette version</label>
<textarea>Confirmer le nombre de dossards (12 jours ouvrables avant la date de réception souhaitée).</textarea>
<div class="actions">
<button type="button" class="btn">Générer le fichier imprimeur</button>
<button type="button" class="btn ghost">Bloquer cette version</button>
<button type="button" class="btn ghost">Télécharger</button>
<span class="ver">v3</span>
</div>
</div>
<div class="sheet">
<div class="tiny">Fichier 2</div>
<strong>Fichier puces MS1</strong>
<label class="field">Note globale de cette version</label>
<textarea>Confirmer le nombre de puces (12 jours ouvrables avant la date de réception souhaitée).</textarea>
<div class="actions">
<button type="button" class="btn">Générer le fichier puces</button>
<button type="button" class="btn ghost">Bloquer cette version</button>
<button type="button" class="btn ghost">Télécharger</button>
<span class="ver">v2</span>
</div>
</div>
</div>
</div>
</section>
<section id="excel" class="panel">
<h2>Template — colonne Note à côté de chaque séquence</h2>
<p class="lede" style="margin-top:8px">Comme sur ton screenshot : après Vérif, une cellule Note par ligne de séquence (colonne Q du gabarit, LP restent cachées). La note globale reste en pied de page.</p>
<div class="xl">
<table>
<thead>
<tr>
<th class="left" rowspan="2">Épreuve</th>
<th class="seq" colspan="5">Séquence</th>
<th class="seq" rowspan="2">Note</th>
</tr>
<tr>
<th class="puce">QUANTITÉ Puce</th>
<th># DÉPART</th>
<th># FIN</th>
<th>Vérif</th>
<th>Vérif</th>
</tr>
</thead>
<tbody>
<tr>
<td class="left">5 km</td>
<td>2</td>
<td class="yel">1</td>
<td class="yel">2</td>
<td></td>
<td></td>
<td class="note">Lot test — 2 dossards</td>
</tr>
<tr><td class="bar" colspan="7"></td></tr>
<tr>
<td class="left">10 km</td>
<td>500</td>
<td class="yel">2001</td>
<td class="yel">2500</td>
<td></td>
<td></td>
<td class="note">Livraison entrepôt Longueuil</td>
</tr>
<tr><td class="bar" colspan="7"></td></tr>
<tr>
<td class="left">Elite</td>
<td>100</td>
<td class="yel">3001</td>
<td class="yel">3100</td>
<td></td>
<td></td>
<td class="note">Dossards déjà chez le proprio</td>
</tr>
<tr><td class="bar" colspan="7"></td></tr>
<tr>
<td class="left total">TOTAL</td>
<td class="total">602</td>
<td></td>
<td></td>
<td class="total">602</td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</div>
<p class="tiny" style="margin-top:10px">Même colonne Note dans le fichier imprimeur (onglet Commande dossards). Une séquence absente dun fichier (switch off) ny a pas de ligne, donc pas de note.</p>
<ul class="tight">
<li><strong>Note de séquence</strong> → cellule à côté de # DÉPART / # FIN (ton screenshot).</li>
<li><strong>Note globale</strong> → pied de page du fichier, figée avec la version (v2 / v3).</li>
</ul>
</section>
</div>
<script>
document.querySelectorAll('nav.tabs button').forEach(function (btn) {
btn.addEventListener('click', function () {
document.querySelectorAll('nav.tabs button').forEach(function (b) { b.classList.remove('on'); });
document.querySelectorAll('.panel').forEach(function (p) { p.classList.remove('on'); });
btn.classList.add('on');
document.getElementById(btn.getAttribute('data-tab')).classList.add('on');
window.scrollTo(0, 0);
});
});
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

467
ajax_inscr_gestion.php Normal file
View File

@ -0,0 +1,467 @@
<?php
/**
* Actions AJAX reservees a la fiche de gestion des inscriptions (v2).
* N'altere pas ajax_promoteur.php ni enregistrer.php (legacy promoteur).
*/
session_start();
// MSIN-4401 — Traductions toujours au nom de compte.php (jamais ajax_inscr_gestion.php).
// Evite les doublons info_prg=ajax_*.php et les libelles manquants selon la page appelante
// (hub, compte, /mobile). Meme pattern que ajax_promoteur_hub / ajax_bib_range.
define('MS1_INSCR_GESTION_AJAX', true);
$_SERVER['PHP_SELF'] = '/compte.php';
$_SERVER['SCRIPT_NAME'] = '/compte.php';
header('Content-Type: application/json; charset=utf-8');
// Filet de securite : transforme tout fatal (include manquant, fonction
// indefinie, parse error...) en reponse JSON exploitable au lieu d'un 500 muet.
$GLOBALS['__inscr_gestion_done'] = false;
register_shutdown_function(function () {
if (!empty($GLOBALS['__inscr_gestion_done'])) {
return;
}
$err = error_get_last();
$strDetail = ($err && in_array($err['type'], array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR), true))
? ($err['message'] . ' @ ' . $err['file'] . ':' . $err['line'])
: 'fatal inconnu';
error_log('[ajax_inscr_gestion] ' . $strDetail);
if (!headers_sent()) {
http_response_code(200);
}
echo json_encode(array('state' => 'error', 'message' => 'fatal', 'detail' => $strDetail));
});
require_once 'php/inc_fonctions.php';
require_once 'php/inc_fx_promoteur.php';
require_once 'php/inc_fx_inscriptions_gestion.php';
// Necessaire pour fxEveAccesResolveEveIdFromParId() appelee dans le case
// 'par_statut_course' : ce fichier n'est sinon charge que paresseusement
// (fxInscrGestionLoadEveAcces) APRES ce premier appel -> fatal undefined function.
require_once 'php/inc_fx_eve_acces.php';
$intComId = intval($_SESSION['com_id'] ?? 0);
$strAction = trim((string)($_REQUEST['a'] ?? ''));
$strLangue = trim((string)($_REQUEST['lang'] ?? 'fr'));
if ($strLangue === '' || !in_array($strLangue, array('fr', 'en'), true)) {
$strLangue = 'fr';
}
$vPage = 'compte.php';
$vtexte_page = obtenirTextepage($vPage, $strLangue, 2);
fxInscrGestionInitTextes($strLangue);
if ($intComId <= 0) {
$GLOBALS['__inscr_gestion_done'] = true;
// MSIN-4401 — code session : le client recharge pour afficher le login.
echo json_encode(array('state' => 'error', 'code' => 'session', 'message' => 'session'));
exit;
}
$tabRetour = array('state' => 'error');
try {
switch ($strAction) {
case 'par_statut_course':
$intParId = intval($_REQUEST['par_id'] ?? 0);
$strStatut = trim((string)($_REQUEST['par_statut_course'] ?? ''));
$intEveId = fxEveAccesResolveEveIdFromParId($intParId);
if ($intEveId <= 0 || !fxInscrGestionCanDo($intComId, $intEveId, 'inscriptions_gestion.statut_edit')) {
$tabRetour = array('state' => 'error', 'message' => 'unauthorized');
break;
}
$tabRetour = fxSetParStatutCourse($intParId, $strStatut, $strLangue);
break;
case 'refund_form':
// Charge le formulaire de remboursement mutualise pour la fiche de gestion (panneau repliable).
$strNoPanier = trim((string)($_REQUEST['no_panier'] ?? ''));
if ($strNoPanier === '') {
$tabRetour = array('state' => 'error', 'message' => 'no_panier_manquant');
break;
}
// Meme source que le Super Admin : la commande vient de inscriptions_panier_acheteurs.
$sqlCmd = "SELECT *, IF(a.com_id <> 0, (SELECT c.com_login FROM inscriptions_comptes c WHERE a.com_id = c.com_id), '') AS usager"
. " FROM inscriptions_panier_acheteurs a, inscriptions_panier_statuts s"
. " WHERE s.sta_id = a.sta_id AND a.no_panier = '" . $objDatabase->fxEscape($strNoPanier) . "'";
$recCommandes = $objDatabase->fxGetRow($sqlCmd);
if (!$recCommandes) {
$tabRetour = array('state' => 'error', 'message' => 'commande_introuvable');
break;
}
// L'eve_id de contexte = evenement de la fiche (participant), pas seulement celui du checkout.
require_once $_SERVER['DOCUMENT_ROOT'] . '/superadm/php/inc_fx_paypal.php';
$intEveContext = intval($_REQUEST['eve_id'] ?? 0);
if ($intEveContext <= 0) {
$intEveContext = intval($recCommandes['eve_id']);
}
if ($intEveContext <= 0 || !fxInscrGestionCanDo($intComId, $intEveContext, 'inscriptions_gestion.refund')) {
$tabRetour = array('state' => 'error', 'message' => 'unauthorized');
break;
}
if (!fxRefundPanierLinkedToEvent($recCommandes['no_panier'], $intEveContext, intval($recCommandes['eve_id']))) {
$tabRetour = array('state' => 'error', 'message' => 'panier_evenement_incompatible');
break;
}
$strRefundToken = fxRefundEnsureToken();
$arrRefundCtx = fxRefundComputeContext($recCommandes, $intEveContext);
// Auteur = promoteur connecte (alimente l'encart "Ajoute par" du formulaire).
$strAuteur = trim((string)($_SESSION['com_info']['com_prenom'] ?? '') . ' '
. (string)($_SESSION['com_info']['com_nom'] ?? ''));
// Markup seul (sans <script>, dernier param false) car injecte via innerHTML :
// le comportement (submit AJAX) est gere par le handler delegue dans inc_footer_scripts.php.
// NB : le resume de la transaction n'est plus inclus ici (MSIN) ; il est desormais
// accessible via le bouton "Afficher la transaction" de la barre Gestion (action transaction_resume).
$strDocAside = '';
if (function_exists('fxDocRenderRefundPromoteurAside')) {
global $vPage;
$memPage = $vPage ?? null;
$vPage = 'compte.php';
$strDocAside = fxDocRenderRefundPromoteurAside();
$vPage = $memPage;
}
$strForm = fxRenderRefundForm($recCommandes, $strRefundToken, $arrRefundCtx, $vDomaine, $strAuteur, null, false, $strDocAside);
$tabRetour = array('state' => 'ok', 'html' => $strForm);
break;
case 'bib_ocr':
// MSIN-4401 — Lecture numero dossard via Google Cloud Vision.
// Image : fichier multipart (prefere) ou base64 POST (legacy).
if (!fxInscrGestionBibOcrCloudEnabled()) {
$tabRetour = array('state' => 'error', 'message' => 'ocr_not_configured');
break;
}
$strImage = '';
if (!empty($_FILES['image']['tmp_name']) && is_uploaded_file($_FILES['image']['tmp_name'])) {
if ((int)$_FILES['image']['error'] === UPLOAD_ERR_OK && (int)$_FILES['image']['size'] > 0
&& (int)$_FILES['image']['size'] <= 4 * 1024 * 1024) {
$strBin = (string)file_get_contents($_FILES['image']['tmp_name']);
$strImage = base64_encode($strBin);
} else {
$tabRetour = array('state' => 'error', 'message' => 'image_invalid');
break;
}
} else {
$strImage = (string)($_POST['image'] ?? '');
}
if ($strImage === '') {
$tabRetour = array('state' => 'error', 'message' => 'image_invalid');
break;
}
$arrOcr = fxInscrGestionVisionReadBibNumber($strImage);
if (!empty($arrOcr['ok']) && !empty($arrOcr['number'])) {
$tabRetour = array(
'state' => 'ok',
'number' => (string)$arrOcr['number'],
);
break;
}
$strSeen = '';
if (!empty($arrOcr['text'])) {
$strSeen = preg_replace('/\D+/', '', (string)$arrOcr['text']);
}
$tabRetour = array(
'state' => 'error',
'message' => (string)($arrOcr['message'] ?? 'no_number'),
'seen' => $strSeen,
'detail' => (string)($arrOcr['detail'] ?? ''),
);
break;
case 'bib_duplicate_check':
// Controle "dossard en double" dans l'evenement (avant enregistrement legacy).
// Ne modifie rien : renvoie qui porte deja ce dossard + la politique a appliquer.
$intParId = intval($_REQUEST['par_id'] ?? 0);
$strNoBib = trim((string)($_REQUEST['no_bib'] ?? ''));
$blnTeam = (trim((string)($_REQUEST['type'] ?? '')) === 'no_equipe');
$intEveId = fxEveAccesResolveEveIdFromParId($intParId);
if ($intEveId <= 0 || !fxInscrGestionCanDo($intComId, $intEveId, 'inscriptions_gestion.bib_edit')) {
$tabRetour = array('state' => 'error', 'message' => 'unauthorized');
break;
}
$strPolicy = fxInscrGestionBibDuplicatePolicy($intEveId);
if ($strNoBib === '') {
$tabRetour = array('state' => 'ok', 'duplicate' => false, 'policy' => $strPolicy, 'who' => array());
break;
}
// Commande courante a exclure du controle (un participant ne se "double" pas lui-meme).
$intPecOrig = intval($objDatabase->fxGetVar(
'SELECT e.pec_id_original FROM resultats_epreuves_commandees e, resultats_participants p'
. ' WHERE p.par_id = ' . $intParId . ' AND p.pec_id = e.pec_id_original LIMIT 1'
));
$intCurrentEprId = (int)$objDatabase->fxGetVar(
'SELECT epr_id FROM resultats_participants WHERE par_id = ' . $intParId . ' LIMIT 1'
);
$arrDup = fxInscrGestionFindBibDuplicates($intEveId, $strNoBib, $intPecOrig, $blnTeam, $intCurrentEprId, $intParId);
$arrWho = array();
foreach ($arrDup as $rowDup) {
$arrWho[] = array(
'name' => fxInscrGestionEsc(fxUnescape($rowDup['par_nom']) . ', ' . fxUnescape($rowDup['par_prenom'])),
'epreuve' => fxInscrGestionEsc(fxInscrGestionEpreuveLabel($rowDup['epr_id'], $strLangue)),
);
}
$tabRetour = array(
'state' => 'ok',
'duplicate' => count($arrWho) > 0,
'policy' => $strPolicy,
'who' => $arrWho,
);
break;
case 'transaction_resume':
// Resume de la transaction (meme rendu que facture.php cote promoteur) + detail des
// remboursements PayPal, affichable a tout moment via le panneau repliable de la barre Gestion.
$strNoPanier = trim((string)($_REQUEST['no_panier'] ?? ''));
if ($strNoPanier === '') {
$tabRetour = array('state' => 'error', 'message' => 'no_panier_manquant');
break;
}
// Commande complete (meme source que refund_form / Super Admin) : alimente fxRefundComputeContext.
$sqlCmd = "SELECT *, IF(a.com_id <> 0, (SELECT c.com_login FROM inscriptions_comptes c WHERE a.com_id = c.com_id), '') AS usager"
. " FROM inscriptions_panier_acheteurs a, inscriptions_panier_statuts s"
. " WHERE s.sta_id = a.sta_id AND a.no_panier = '" . $objDatabase->fxEscape($strNoPanier) . "'";
$recCommandes = $objDatabase->fxGetRow($sqlCmd);
if (!$recCommandes) {
$tabRetour = array('state' => 'error', 'message' => 'commande_introuvable');
break;
}
// L'eve_id fait foi cote serveur (jamais le client). Droit granulaire transaction.
$intEveId = intval($recCommandes['eve_id']);
if ($intEveId <= 0 || !fxInscrGestionCanDo($intComId, $intEveId, 'inscriptions_gestion.transaction')) {
$tabRetour = array('state' => 'error', 'message' => 'unauthorized');
break;
}
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/inc_fx_panier.php';
$strResume = fxShowPanier($strNoPanier, 0, 'web', 1, $strLangue);
// Sommaire des remboursements PayPal (vue compacte, pas la grande table Super Admin).
// N'apparait que s'il existe une capture PayPal exploitable ; sinon rien (les cas
// gratuits/non-PayPal sont deja couverts par la facture ci-dessus).
$strRefunds = '';
require_once $_SERVER['DOCUMENT_ROOT'] . '/superadm/php/inc_fx_paypal.php';
$arrRefundCtx = fxRefundComputeContext($recCommandes);
if (!empty($arrRefundCtx['ok'])) {
// renderPayPalRefundSummaryHtml() renvoie '' s'il n'y a aucun remboursement :
// dans ce cas on n'ajoute meme pas le separateur/encadre (rien a montrer).
$strSummary = renderPayPalRefundSummaryHtml($arrRefundCtx['paypaldetails']);
if (trim($strSummary) !== '') {
$strRefunds = '<div style="margin-top:14px;border-top:1px solid #cfe0ff;padding-top:12px;">'
. $strSummary
. '</div>';
}
}
// Conteneur encadre (sans bascule interne : le bouton de la barre Gestion fait office de toggle).
$strHtml =
'<div class="ms1-transaction-resume"'
. ' style="border:1px solid #2d6cdf;border-radius:10px;background:#f4f7fc;overflow:hidden;box-shadow:0 1px 4px rgba(0,0,0,.12);">'
. '<div style="padding:14px;">' . $strResume . $strRefunds . '</div>'
. '</div>';
$tabRetour = array('state' => 'ok', 'html' => $strHtml);
break;
case 'fiche_audit_history':
// MSIN-4464 — derniers changements d'un champ whitelist (popup « ! » fiche).
require_once 'php/inc_fx_fiche_audit.php';
$intParId = intval($_REQUEST['par_id'] ?? 0);
$strField = preg_replace('/[^a-z0-9_]/i', '', (string)($_REQUEST['field'] ?? ''));
$intEveId = fxEveAccesResolveEveIdFromParId($intParId);
if ($intParId <= 0 || $strField === '' || $intEveId <= 0
|| !fxInscrGestionCanDo($intComId, $intEveId, 'inscriptions_gestion.audit_view')) {
$tabRetour = array('state' => 'error', 'message' => 'unauthorized');
break;
}
$arrItems = fxFicheAuditGetRecentForField($intParId, $strField);
$arrOut = array();
if (is_array($arrItems)) {
foreach ($arrItems as $row) {
if (!is_array($row)) {
continue;
}
$arrOut[] = array(
'when' => (string)($row['created_at'] ?? ''),
'who' => (string)($row['changed_by_label'] ?? ''),
'old' => (string)($row['old_value'] ?? ''),
'new' => (string)($row['new_value'] ?? ''),
'source' => (string)($row['source'] ?? ''),
);
}
}
$tabRetour = array(
'state' => 'success',
'field' => $strField,
'items' => $arrOut,
);
break;
// MSIN-4521 — édition hors inscription en ligne (fiche verte).
case 'hors_tx_profile_save':
$intParId = intval($_REQUEST['par_id'] ?? 0);
$intEveId = fxEveAccesResolveEveIdFromParId($intParId);
if ($intEveId <= 0 || !fxInscrGestionCanDo($intComId, $intEveId, 'inscriptions_gestion.edit')
|| !fxInscrGestionEveIsSansInscriptionEnLigne($intEveId)) {
$tabRetour = array('state' => 'error', 'message' => 'unauthorized');
break;
}
$tabRetour = fxInscrGestionHorsTxSaveProfile($intParId, $intEveId, array(
'par_prenom' => $_REQUEST['par_prenom'] ?? '',
'par_nom' => $_REQUEST['par_nom'] ?? '',
'par_sexe' => $_REQUEST['par_sexe'] ?? '',
'par_naissance' => $_REQUEST['par_naissance'] ?? '',
'par_contact_urgence_nom' => $_REQUEST['par_contact_urgence_nom'] ?? '',
'par_contact_urgence_telephone' => $_REQUEST['par_contact_urgence_telephone'] ?? '',
));
break;
case 'hors_tx_epreuve_save':
$intParId = intval($_REQUEST['par_id'] ?? 0);
$intNewEpr = intval($_REQUEST['epr_id'] ?? 0);
$blnConfirmed = (intval($_REQUEST['confirm'] ?? 0) === 1);
$intEveId = fxEveAccesResolveEveIdFromParId($intParId);
if ($intEveId <= 0 || !fxInscrGestionCanDo($intComId, $intEveId, 'inscriptions_gestion.edit')
|| !fxInscrGestionEveIsSansInscriptionEnLigne($intEveId)) {
$tabRetour = array('state' => 'error', 'message' => 'unauthorized');
break;
}
$tabRetour = fxInscrGestionHorsTxSaveEpreuve($intParId, $intEveId, $intNewEpr, $blnConfirmed);
break;
case 'hors_tx_questions_save':
$intParId = intval($_REQUEST['par_id'] ?? 0);
$intEveId = fxEveAccesResolveEveIdFromParId($intParId);
if ($intEveId <= 0 || !fxInscrGestionCanDo($intComId, $intEveId, 'inscriptions_gestion.edit')
|| !fxInscrGestionEveIsSansInscriptionEnLigne($intEveId)) {
$tabRetour = array('state' => 'error', 'message' => 'unauthorized');
break;
}
$arrAnswers = array();
if (isset($_REQUEST['answers']) && is_array($_REQUEST['answers'])) {
$arrAnswers = $_REQUEST['answers'];
} elseif (!empty($_REQUEST['answers_json'])) {
$mix = json_decode((string)$_REQUEST['answers_json'], true);
if (is_array($mix)) {
$arrAnswers = $mix;
}
}
$tabRetour = fxInscrGestionHorsTxSaveQuestions($intParId, $intEveId, $arrAnswers);
break;
// MSIN-4532 — un seul Enregistrer (identité + épreuve + questions).
case 'hors_tx_save_all':
$intParId = intval($_REQUEST['par_id'] ?? 0);
$intEveId = fxEveAccesResolveEveIdFromParId($intParId);
$blnConfirmed = (intval($_REQUEST['confirm'] ?? 0) === 1);
if ($intEveId <= 0 || !fxInscrGestionCanDo($intComId, $intEveId, 'inscriptions_gestion.edit')
|| !fxInscrGestionEveIsSansInscriptionEnLigne($intEveId)) {
$tabRetour = array('state' => 'error', 'message' => 'unauthorized');
break;
}
$arrAnswers = array();
if (isset($_REQUEST['answers']) && is_array($_REQUEST['answers'])) {
$arrAnswers = $_REQUEST['answers'];
} elseif (!empty($_REQUEST['answers_json'])) {
$mix = json_decode((string)$_REQUEST['answers_json'], true);
if (is_array($mix)) {
$arrAnswers = $mix;
}
}
$tabRetour = fxInscrGestionHorsTxSaveAll(
$intParId,
$intEveId,
array(
'par_prenom' => $_REQUEST['par_prenom'] ?? '',
'par_nom' => $_REQUEST['par_nom'] ?? '',
'par_sexe' => $_REQUEST['par_sexe'] ?? '',
'par_naissance' => $_REQUEST['par_naissance'] ?? '',
'par_contact_urgence_nom' => $_REQUEST['par_contact_urgence_nom'] ?? '',
'par_contact_urgence_telephone' => $_REQUEST['par_contact_urgence_telephone'] ?? '',
'epr_id' => $_REQUEST['epr_id'] ?? 0,
),
$arrAnswers,
$blnConfirmed
);
break;
// MSIN-4532 — saisie rapide : questions selon épreuve (création).
case 'hors_tx_create_questions':
$intEveId = intval($_REQUEST['eve_id'] ?? 0);
$intEprId = intval($_REQUEST['epr_id'] ?? 0);
if ($intEveId <= 0 || !fxInscrGestionCanDo($intComId, $intEveId, 'inscriptions_gestion.edit')
|| !fxInscrGestionEveIsSansInscriptionEnLigne($intEveId)) {
$tabRetour = array('state' => 'error', 'message' => 'unauthorized');
break;
}
$tabQ = fxInscrGestionHorsTxLoadQuestionsForCreate($intEveId, $intEprId, $strLangue);
$tabRetour = array(
'state' => 'success',
'html' => fxInscrGestionHorsTxRenderCreateQuestionsHtml($tabQ),
);
break;
// MSIN-4532 — saisie rapide : créer participant.
case 'hors_tx_create':
$intEveId = intval($_REQUEST['eve_id'] ?? 0);
if ($intEveId <= 0 || !fxInscrGestionCanDo($intComId, $intEveId, 'inscriptions_gestion.edit')
|| !fxInscrGestionEveIsSansInscriptionEnLigne($intEveId)) {
$tabRetour = array('state' => 'error', 'message' => 'unauthorized');
break;
}
$arrAnswers = array();
if (isset($_REQUEST['answers']) && is_array($_REQUEST['answers'])) {
$arrAnswers = $_REQUEST['answers'];
} elseif (!empty($_REQUEST['answers_json'])) {
$mix = json_decode((string)$_REQUEST['answers_json'], true);
if (is_array($mix)) {
$arrAnswers = $mix;
}
}
$tabRetour = fxInscrGestionHorsTxCreate($intEveId, array(
'epr_id' => $_REQUEST['epr_id'] ?? 0,
'par_prenom' => $_REQUEST['par_prenom'] ?? '',
'par_nom' => $_REQUEST['par_nom'] ?? '',
'par_sexe' => $_REQUEST['par_sexe'] ?? '',
'par_naissance' => $_REQUEST['par_naissance'] ?? '',
'par_contact_urgence_nom' => $_REQUEST['par_contact_urgence_nom'] ?? '',
'par_contact_urgence_telephone' => $_REQUEST['par_contact_urgence_telephone'] ?? '',
'no_bib' => $_REQUEST['no_bib'] ?? '',
), $arrAnswers);
break;
default:
$tabRetour = array('state' => 'error', 'message' => 'invalid_action');
break;
}
} catch (\Throwable $e) {
error_log('[ajax_inscr_gestion] ' . $e->getMessage() . ' @ ' . $e->getFile() . ':' . $e->getLine());
$tabRetour = array(
'state' => 'error',
'message' => 'exception',
'detail' => $e->getMessage() . ' @ ' . $e->getFile() . ':' . $e->getLine(),
);
}
$GLOBALS['__inscr_gestion_done'] = true;
echo json_encode($tabRetour);

49
ajax_inscr_gestion_qr.php Normal file
View File

@ -0,0 +1,49 @@
<?php
session_start();
// MSIN-4401 — Traductions toujours au nom de compte.php (jamais ajax_inscr_gestion_qr.php).
define('MS1_INSCR_GESTION_AJAX', true);
$_SERVER['PHP_SELF'] = '/compte.php';
$_SERVER['SCRIPT_NAME'] = '/compte.php';
require_once 'php/inc_fonctions.php';
require_once 'php/inc_fx_inscriptions_gestion.php';
global $vDomaine;
$strLangue = 'fr';
if (!empty($_REQUEST['lang']) && in_array($_REQUEST['lang'], array('fr', 'en'), true)) {
$strLangue = $_REQUEST['lang'];
}
$vPage = 'compte.php';
$vtexte_page = obtenirTextepage($vPage, $strLangue, 2);
fxInscrGestionInitTextes($strLangue);
header('Content-Type: application/json; charset=utf-8');
if (!isset($_SESSION['com_id'])) {
// MSIN-4401 — session morte : pas un faux "QR invalide".
echo json_encode(array('state' => 'error', 'code' => 'session', 'message' => 'session'));
exit;
}
$intEveId = 0;
if (!empty($_REQUEST['promoteur_eve_id'])) {
$intEveId = fxInscrGestionParseEveId($_REQUEST['promoteur_eve_id']);
}
if ($intEveId <= 0 || !fxInscrGestionCanScan($_SESSION['com_id'], $intEveId)) {
echo json_encode(array('state' => 'error', 'message' => fxInscrGestionT('inscr_gestion_qr_invalid')));
exit;
}
$strRaw = '';
if (!empty($_REQUEST['qr'])) {
$strRaw = $_REQUEST['qr'];
} elseif (!empty($_REQUEST['t'])) {
$strRaw = $_REQUEST['t'];
}
echo json_encode(fxInscrGestionResolveQrScan($strRaw, $intEveId, $strLangue));

View File

@ -30,7 +30,7 @@ $strAction = $intEvenement = $intNewNoBib = $intEpreuveCommandee = $intEpreuve =
//$_POST['typedemande']="test sl"; //$_POST['typedemande']="test sl";
//$_POST['summary']="sujet"; //$_POST['summary']="sujet";
$typedemande = (int)($_POST['typedemande'] ?? 4); const JIRA_REQUEST_TYPE_SOUTIEN_TECHNIQUE = '4';
if (isset($_POST['request_type'] )) { if (isset($_POST['request_type'] )) {
$strAction = $_POST['request_type']; $strAction = $_POST['request_type'];
@ -40,82 +40,7 @@ $tabRetour = array();
$tabRetour['success']=false; $tabRetour['success']=false;
switch($strAction) { switch($strAction) {
case 'check_email': case 'check_email':
$result=jira_recherche_email($_POST['email']); $tabRetour = jira_submit_contact_ticket($_POST);
if($result['success']==false){
//cree le user
$result=jira_creer_user($_POST['email'],$_POST['nom']);
if($result['success']==true){
$tabRetour['success']=true;
$tabRetour['accountId']=$result['statut']['accountId'] ;
}else{
$tabRetour['success']=false;
$tabRetour['accountId']="";
};
}else{
//retourne le id
$tabRetour['success']=true;
$tabRetour['accountId']= $result['jira']['accountId'];
}
if($tabRetour['success']==true){
// creer le jira
$data['serviceDeskId'] = 2; // Remplacez par l'ID réel de votre Service Desk
$data['requestTypeId'] = (string)$typedemande; // Remplacez par l'ID réel du type de demande
$data['summary']=$_POST['summary'];
$data['description']=$_POST['description'];
$data['typedemande']=$_POST['typedemande'];
$data['raiseOnBehalfOf']= $_POST['email'] ;
$tabRetourtemp=jira_create_ticket($data);
$tabRetour['success']=true;
}else
{
$tabRetour['success']=false;
$tabRetour['accountId']= "";
}
break;
case 'check_emailold':
$result=jira_recherche_email($_POST['email']);
if($result['success']==false){
//cree le user
$result=jira_creer_user($_POST['email'],$_POST['nom']);
if($result['success']==true){
$tabRetour['success']='success';
$tabRetour['accountId']=$result['statut']['accountId'] ;
}else{
$tabRetour['success']=false;
$tabRetour['accountId']="";
};
}else{
//retourne le id
$tabRetour['success']=true;
$tabRetour['accountId']= $result['jira']['accountId'];
}
if($tabRetour['success']==true){
// creer le jira
$data['serviceDeskId'] = 2; // Remplacez par l'ID réel de votre Service Desk
$data['requestTypeId'] = (string)$typedemande; // Remplacez par l'ID réel du type de demande
$data['summary']=utf8_encode($_POST['summary']);
$data['description']=utf8_encode($_POST['description']);
$data['typedemande']=utf8_encode($_POST['typedemande']);
$data['raiseOnBehalfOf']= $_POST['email'] ;
$tabRetourtemp=jira_create_ticket($data);
$tabRetour['success']=true;
}else
{
$tabRetour['success']=false;
$tabRetour['accountId']= "";
}
break; break;
case 'no_equipe': case 'no_equipe':
case 'no_bib': case 'no_bib':
@ -129,6 +54,149 @@ header('Content-Type: application/json; charset=utf-8');
echo json_encode($tabRetour); echo json_encode($tabRetour);
exit; exit;
function jira_get_type_demande_row(array $post)
{
global $objDatabase;
if (empty($post['typedemande']) || !isset($objDatabase)) {
return null;
}
$sqlCol = "SELECT info_texte, info_option2, info_option3 FROM info WHERE info_actif = 1 AND info_clef = 'type_de_demande' AND info_option2 = '" . $objDatabase->fxEscape($post['typedemande']) . "' LIMIT 1";
$row = $objDatabase->fxGetResults($sqlCol);
return !empty($row) ? reset($row) : null;
}
function jira_resolve_request_type_id(array $post)
{
$row = jira_get_type_demande_row($post);
if (!empty($row['info_texte']) && fxJiraIsAutreDemande($row['info_texte'])) {
return (string)($post['typedemande'] ?? JIRA_REQUEST_TYPE_SOUTIEN_TECHNIQUE);
}
return JIRA_REQUEST_TYPE_SOUTIEN_TECHNIQUE;
}
function jira_resolve_categorie_interne(array $post)
{
$strCategorie = '';
if (isset($post['categorie_interne'])) {
$strCategorie = trim((string)$post['categorie_interne']);
} else {
$row = jira_get_type_demande_row($post);
if (!empty($row['info_option3'])) {
$strCategorie = trim((string)$row['info_option3']);
} elseif (!empty($row['info_texte'])) {
$strCategorie = fxJiraMapCategorieInterne($row['info_texte']);
}
}
// Seul « Vente » est géré pour l'instant ; les autres choix n'envoient pas de catégorie interne.
return ($strCategorie === 'Vente') ? 'Vente' : '';
}
function jira_log_contact_error($context, $detail = '')
{
$line = 'jira contact [' . $context . ']';
if ($detail !== '') {
$line .= ': ' . (is_string($detail) ? $detail : json_encode($detail, JSON_UNESCAPED_UNICODE));
}
error_log($line);
}
function jira_wait_for_account_id($email, $maxAttempts = 4)
{
for ($i = 0; $i < $maxAttempts; $i++) {
if ($i > 0) {
usleep(500000);
}
$result = jira_recherche_email($email);
if (!empty($result['success']) && !empty($result['jira']['accountId'])) {
return $result['jira']['accountId'];
}
}
return '';
}
function jira_submit_contact_ticket(array $post)
{
$tabRetour = array('success' => false, 'accountId' => '');
$email = trim((string)($post['email'] ?? ''));
$nom = trim((string)($post['nom'] ?? ''));
$userJustCreated = false;
$result = jira_recherche_email($email);
$accountId = !empty($result['success']) ? ($result['jira']['accountId'] ?? '') : '';
if ($accountId === '') {
$result = jira_creer_user($email, $nom);
if (empty($result['success'])) {
jira_log_contact_error('user_creation', $result['statut'] ?? '');
return $tabRetour;
}
$userJustCreated = true;
if (is_array($result['statut'])) {
$accountId = $result['statut']['accountId'] ?? '';
}
if ($accountId === '') {
$accountId = jira_wait_for_account_id($email);
}
}
$tabRetour['accountId'] = $accountId;
$data = array(
'serviceDeskId' => 2,
'requestTypeId' => jira_resolve_request_type_id($post),
'summary' => $post['summary'] ?? '',
'description' => $post['description'] ?? '',
'categorie_interne' => jira_resolve_categorie_interne($post),
'raiseOnBehalfOf' => $accountId !== '' ? $accountId : $email,
);
$ticket = jira_create_ticket($data);
if (empty($ticket['success']) && ($userJustCreated || $accountId === '')) {
$accountId = jira_wait_for_account_id($email, 3);
if ($accountId !== '') {
$tabRetour['accountId'] = $accountId;
$data['raiseOnBehalfOf'] = $accountId;
$ticket = jira_create_ticket($data);
}
}
if (empty($ticket['success'])) {
jira_log_contact_error('ticket_creation', $ticket['statut'] ?? '');
return $tabRetour;
}
$categorieInterne = $data['categorie_interne'] ?? '';
if ($categorieInterne === '') {
$tabRetour['success'] = true;
return $tabRetour;
}
$issueKey = '';
if (is_array($ticket['statut']) && !empty($ticket['statut']['issueKey'])) {
$issueKey = $ticket['statut']['issueKey'];
}
if ($issueKey === '') {
jira_log_contact_error('categorie_interne', 'issueKey manquant après création');
return $tabRetour;
}
$update = jira_set_categorie_interne($issueKey, $categorieInterne);
if (empty($update['success'])) {
jira_log_contact_error('categorie_interne', $update['statut'] ?? '');
return $tabRetour;
}
$tabRetour['success'] = true;
return $tabRetour;
}
function jira_recherche_email($param) function jira_recherche_email($param)
{ {
// recherche user // recherche user
@ -263,6 +331,43 @@ function jira_creer_user($email,$displayName)
return($sortie); return($sortie);
} }
function jira_set_categorie_interne($issueKey, $categorieInterne)
{
$jiraDomain = 'https://progiwebinc.atlassian.net';
$userEmail = 'stephan@progiweb.ca';
$apiToken = 'ATATT3xFfGF0Z91qESp8j7uO783qK0E4BWsY8rhPWEmexSa4Rw3c21Spg6hYIkmVgy6Vwzb34RxqFl3sR00Kby_DuY1NdcUJyfPQYZ0_JbeEjAk9ybFgNY8-MBSABMCxzaDk4szTbWupfjxHT8sJxJMvSNxgHPds5FxXaGo2DnZICZu5dXuvWbk=F73E15AE';
$payload = json_encode(array(
'fields' => array(
'customfield_11179' => array(
'value' => $categorieInterne,
),
),
));
$ch = curl_init($jiraDomain . '/rest/api/3/issue/' . rawurlencode($issueKey));
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Authorization: Basic ' . base64_encode($userEmail . ':' . $apiToken),
'Content-Type: application/json',
));
$response = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$sortie = array('success' => false, 'statut' => '');
if ($httpcode === 204) {
$sortie['success'] = true;
return $sortie;
}
$sortie['statut'] = ($response !== false && $response !== '') ? $response : ('HTTP ' . $httpcode);
return $sortie;
}
function jira_create_ticket( $param) function jira_create_ticket( $param)
{ {
@ -278,7 +383,6 @@ function jira_create_ticket( $param)
'requestFieldValues' => [ 'requestFieldValues' => [
'summary' => $param['summary'], 'summary' => $param['summary'],
'description' => $param['description'], 'description' => $param['description'],
'customfield_11143' => $param['typedemande'],
], ],
'raiseOnBehalfOf' => $param['raiseOnBehalfOf'], // Remplacez par l'accountId du client (facultatif) 'raiseOnBehalfOf' => $param['raiseOnBehalfOf'], // Remplacez par l'accountId du client (facultatif)
] ; ] ;

View File

@ -1,13 +1,22 @@
<?php <?php
session_start(); session_start();
// MSIN-4468 — Les libellés AJAX appartiennent toujours au contexte compte.php.
define('MS1_PROMOTEUR_AJAX', true);
$_SERVER['PHP_SELF'] = '/compte.php';
$_SERVER['SCRIPT_NAME'] = '/compte.php';
$vPage = 'compte.php';
require_once "php/inc_fonctions.php"; require_once "php/inc_fonctions.php";
include_once("php/inc_fx_modifierinscriptions.php"); include_once("php/inc_fx_modifierinscriptions.php");
require_once "php/inc_fx_promoteur.php"; require_once "php/inc_fx_promoteur.php";
require_once "php/inc_fx_messages.php"; require_once "php/inc_fx_messages.php";
require_once "php/inc_fx_eve_acces.php";
global $vDomaine; global $vDomaine;
$strAction = $intEvenement = $intNewNoBib = $intEpreuveCommandee = $intEpreuve = $intPartipcipant = $intQuantite = $strLangue = ''; $strAction = $intEvenement = $intNewNoBib = $intEpreuveCommandee = $intEpreuve = $intPartipcipant = $intQuantite = $strStatutCourse = '';
$strLangue = $_SESSION['lang'] ?? 'fr';
$strQuantiteTotale = '';
if (isset($_REQUEST['a'])) { if (isset($_REQUEST['a'])) {
$strAction = $_REQUEST['a']; $strAction = $_REQUEST['a'];
@ -30,23 +39,72 @@ if (isset($_REQUEST['par_id'])) {
if (isset($_REQUEST['qte'])) { if (isset($_REQUEST['qte'])) {
$intQuantite = $_REQUEST['qte']; $intQuantite = $_REQUEST['qte'];
} }
if (isset($_REQUEST['lang'])) { if (isset($_REQUEST['total'])) {
$strQuantiteTotale = trim((string)$_REQUEST['total']);
}
if (isset($_REQUEST['lang']) && in_array($_REQUEST['lang'], array('fr', 'en'), true)) {
$strLangue = $_REQUEST['lang']; $strLangue = $_REQUEST['lang'];
} }
if (isset($_REQUEST['par_statut_course'])) {
$strStatutCourse = $_REQUEST['par_statut_course'];
}
$vPage = 'compte.php';
$vtexte_page = obtenirTextepage($vPage, $strLangue, 2); $vtexte_page = obtenirTextepage($vPage, $strLangue, 2);
$tabRetour = array(); $tabRetour = array();
header('Content-Type: application/json; charset=utf-8');
switch($strAction) { switch($strAction) {
case 'qte': case 'qte':
$tabRetour = fxUpdateQuantites($intEpreuve, $intQuantite, $strLangue); $tabRetour = fxUpdateQuantites($intEpreuve, $intQuantite, $strLangue);
break; break;
case 'qte_total':
// MSIN-4468 — Nouvelle convention V4; l'action legacy qte demeure inchangée.
if (!fxBibRequirePromoteurSession()) {
$tabRetour = array('state' => 'error', 'message' => fxBibMsg('bib_v4_ajax_session_invalid'));
break;
}
if (!fxBibValidateCsrf($_POST['csrf_token'] ?? '')) {
$tabRetour = array('state' => 'error', 'message' => fxBibMsg('bib_v4_ajax_unauthorized'));
break;
}
$intEveId = fxBibResolveEveIdFromRequest();
$blnCanEditQte = !empty($_SESSION['usa_id'])
|| (!empty($_SESSION['com_id'])
&& $intEveId > 0
&& fxEveAccesHasPermission($_SESSION['com_id'], $intEveId, 'epreuves.edit_qte'));
if (!$blnCanEditQte) {
$tabRetour = array('state' => 'error', 'message' => fxBibMsg('bib_v4_ajax_unauthorized'));
break;
}
if ($strQuantiteTotale === ''
|| strlen($strQuantiteTotale) > 7
|| !ctype_digit($strQuantiteTotale)) {
$tabRetour = array('state' => 'error', 'message' => afficheTexte('promoteur_bad_qte_update', 0));
break;
}
$tabRetour = fxUpdateQuantiteTotale($intEpreuve, intval($strQuantiteTotale));
if (($tabRetour['state'] ?? 'error') !== 'success') {
if (($tabRetour['error_code'] ?? '') === 'total_below_registrations') {
$tabRetour['message'] = fxBibMsg(
'bib_v5_qte_total_min',
intval($tabRetour['qte_inscrits'] ?? 0)
);
} else {
$tabRetour['message'] = afficheTexte('promoteur_bad_qte_update', 0);
}
}
break;
case 'no_equipe': case 'no_equipe':
case 'no_bib': case 'no_bib':
$tabRetour = fxSetBibManuel($strAction, $intEvenement, $intNewNoBib, $intEpreuveCommandee, $intEpreuve, $intPartipcipant, $strLangue); $tabRetour = fxSetBibManuel($strAction, $intEvenement, $intNewNoBib, $intEpreuveCommandee, $intEpreuve, $intPartipcipant, $strLangue);
break; break;
case 'par_statut_course':
$tabRetour = fxSetParStatutCourse($intPartipcipant, $strStatutCourse, $strLangue);
break;
case 'teammates': case 'teammates':
$tabRetour = fxShowTeammates($intEvenement, $intEpreuve, $intEpreuveCommandee); $tabRetour = fxShowTeammates($intEvenement, $intEpreuve, $intEpreuveCommandee);
break; break;

71
ajax_promoteur_hub.php Normal file
View File

@ -0,0 +1,71 @@
<?php
session_start();
// Traductions toujours au nom de compte.php (jamais ajax_promoteur_hub.php).
define('MS1_PROMOTEUR_HUB_AJAX', true);
$_SERVER['PHP_SELF'] = '/compte.php';
$_SERVER['SCRIPT_NAME'] = '/compte.php';
require_once __DIR__ . '/php/inc_fonctions.php';
require_once __DIR__ . '/php/inc_fx_promoteur_hub.php';
header('Content-Type: application/json; charset=UTF-8');
$strAction = $_REQUEST['a'] ?? '';
$strLangue = $_REQUEST['lang'] ?? ($_SESSION['lang'] ?? 'fr');
if (!in_array($strLangue, array('fr', 'en'), true)) {
$strLangue = 'fr';
}
$vPage = 'compte.php';
$vtexte_page = obtenirTextepage($vPage, $strLangue, 2);
$intEveId = intval($_REQUEST['eve_id'] ?? 0);
if (empty($_SESSION['com_id'])) {
// MSIN-4401 — Le JS doit renvoyer au login, pas afficher un message mort.
echo json_encode(array(
'ok' => false,
'code' => 'session',
'msg' => fxPromoteurHubTexte('promoteur_hub_ajax_session', 0),
));
exit;
}
$intComId = intval($_SESSION['com_id']);
if (!in_array($strAction, array('event_stats', 'event_links'), true) || $intEveId <= 0) {
echo json_encode(array('ok' => false, 'msg' => fxPromoteurHubTexte('promoteur_hub_ajax_invalid', 0)));
exit;
}
if (!fxPromoteurHubComHasEvent($intComId, $intEveId)) {
echo json_encode(array('ok' => false, 'msg' => fxPromoteurHubTexte('promoteur_hub_ajax_denied', 0)));
exit;
}
if ($strAction === 'event_stats') {
if (!fxPromoteurHubCanViewDashboard($intComId, $intEveId)) {
echo json_encode(array('ok' => false, 'msg' => fxPromoteurHubTexte('promoteur_hub_ajax_denied', 0)));
exit;
}
$arrBundle = fxPromoteurHubRenderEventStatsBundle($intEveId, $strLangue);
echo json_encode(array(
'ok' => true,
'flash' => $arrBundle['flash'],
'html' => $arrBundle['html'],
));
exit;
}
$arrItem = fxPromoteurHubGetEventItemForLinks($intComId, $intEveId);
if ($arrItem === null) {
echo json_encode(array('ok' => false, 'msg' => fxPromoteurHubTexte('promoteur_hub_ajax_not_found', 0)));
exit;
}
echo json_encode(array(
'ok' => true,
'html' => fxPromoteurHubRenderEventPanelContent($arrItem, $strLangue),
));

View File

@ -0,0 +1 @@

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

View File

@ -0,0 +1,27 @@
{
"version": 2,
"page": "8.5x5.5",
"boxes": [
{
"id": "bib",
"field": "no_bib",
"align": "C",
"font_max": 220,
"left": 0.2,
"right": 0.78,
"top": 0.36,
"bottom": 0.66
},
{
"id": "prenom",
"field": "par_prenom",
"align": "L",
"font_max": 28,
"left": 0.32,
"right": 0.68,
"top": 0.72,
"bottom": 0.84
}
],
"updated_at": "2026-07-25T00:00:00+00:00"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

50
auto_chronotrack_sync.php Normal file
View File

@ -0,0 +1,50 @@
<?php
/**
* MSIN-4328 — Cron sync auto ChronoTrack (différentiel).
*
* Même pattern que auto_liste_attente.php :
* /usr/bin/curl -s https://VOTRE-DOMAINE/auto_chronotrack_sync.php > /dev/null 2>&1
*
* Minute=* Hour=* … → le script décide selon fréquence/fin de chaque event.
*/
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', __DIR__ . '/log/php_log.txt');
@set_time_limit(300);
$strLockPath = rtrim(sys_get_temp_dir(), '/\\') . DIRECTORY_SEPARATOR . 'ms1_chronotrack_auto_sync.lock';
$fpLock = @fopen($strLockPath, 'c');
if ($fpLock === false || !flock($fpLock, LOCK_EX | LOCK_NB)) {
if (is_resource($fpLock)) {
fclose($fpLock);
}
header('Content-Type: text/plain; charset=utf-8');
echo 'busy';
exit;
}
session_start();
try {
require_once __DIR__ . '/php/inc_fonctions.php';
require_once __DIR__ . '/php/chronotrack_api/inc_bootstrap.php';
$arrResult = fxChronotrackApiSyncRunAutoCron();
$strLine = 'auto_chronotrack_sync [' . date('Y-m-d H:i:s') . '] '
. ($arrResult['message'] ?? 'done');
// Même style que liste dattente : une ligne dans log/php_log.txt
error_log($strLine);
header('Content-Type: text/plain; charset=utf-8');
echo $strLine . "\n";
} catch (Throwable $ex) {
$strErr = 'auto_chronotrack_sync ERROR [' . date('Y-m-d H:i:s') . '] ' . $ex->getMessage();
error_log($strErr);
header('Content-Type: text/plain; charset=utf-8');
echo $strErr . "\n";
}
flock($fpLock, LOCK_UN);
fclose($fpLock);

59
bib_commande.php Normal file
View File

@ -0,0 +1,59 @@
<?php
/**
* MSIN-4577 — Page ops : préparation commande dossards / puces.
* Chrome compte (Info via compte.php). Pas un onglet du module dossards.
*/
require_once('php/inc_start_time.php');
require_once('php/inc_fonctions.php');
require_once('php/inc_fx_messages.php');
require_once('php/inc_v2_assets.php');
require_once('php/inc_fx_eve_acces.php');
require_once('php/inc_fx_promoteur.php');
global $vDomaine, $vPage, $strLangue;
$_SERVER['PHP_SELF'] = '/compte.php';
$_SERVER['SCRIPT_NAME'] = '/compte.php';
$strLangue = $_GET['lang'] ?? $_SESSION['lang'] ?? 'fr';
if (!in_array($strLangue, ['fr', 'en'], true)) {
$strLangue = 'fr';
}
$_SESSION['lang'] = $strLangue;
$vPage = 'compte.php';
$footer_script = false;
$strCode = 'bib_commande';
$strPage = ($strLangue === 'en') ? 'account' : 'compte';
$vtexte_page = obtenirTextepage($vPage, $strLangue, 2);
$intEveId = 0;
if (!empty($_GET['promoteur_eve_id'])) {
$intEveId = (int)base64_decode(urldecode((string)$_GET['promoteur_eve_id']));
}
$strLienFr = '/bib_commande.php';
$strLienEn = '/bib_commande.php';
$blnHideCompteTitle = true;
if ($intEveId <= 0 || !fxBibOpsUserCanAccessCommande($intEveId)) {
header('Location: ' . $vDomaine . '/' . $strPage);
exit;
}
$strMetaTitle = fxBibTexte('bib_v5_cmd_page_title', 0);
$strMetaDescription = $strMetaTitle;
fxV2RegisterScript('bib-commande');
require_once('inc_header.php');
?>
<div id="page">
<div id="main">
<div class="container bg-white p-3 p-xl-5">
<?php echo fxBibCommandeRenderPage($intEveId, $strLangue); ?>
</div>
</div>
</div>
<?php
require_once('inc_footer.php');

49
bib_dist_print.php Normal file
View File

@ -0,0 +1,49 @@
<?php
// MSIN-4433 — Génération PDF distribution des dossards (POST + session promoteur).
session_start();
$_SERVER['PHP_SELF'] = '/compte.php';
$_SERVER['SCRIPT_NAME'] = '/compte.php';
require_once('php/inc_fonctions.php');
require_once('php/inc_fx_promoteur.php');
$strLangue = $_SESSION['lang'] ?? 'fr';
$reqLang = $_REQUEST['lang'] ?? $_REQUEST['lng'] ?? '';
if (!empty($reqLang) && in_array($reqLang, ['fr', 'en'], true)) {
$strLangue = $reqLang;
}
$vPage = 'compte.php';
$vtexte_page = obtenirTextepage($vPage, $strLangue, 2);
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
exit;
}
$int_eve_id = (int)($_POST['eve_id'] ?? 0);
$_REQUEST['eve_id'] = $int_eve_id;
$_POST['eve_id'] = $int_eve_id;
fxBibRequireDistPrintAccess();
$tabSelections = fxBibParseDistPrintSelectionsFromPost($_POST);
$tabIncludeSans = fxBibParseDistPrintIncludeSansFromPost($_POST);
$strOrient = fxBibDistPrintNormalizeOrient($_POST['dist_print_orient'] ?? 'P');
$tabSave = fxBibSaveDistPrintSelections($int_eve_id, $tabSelections, $strLangue, $strOrient, $tabIncludeSans);
if (!$tabSave['success']) {
http_response_code(400);
header('Content-Type: text/plain; charset=utf-8');
echo $tabSave['message'] ?? 'Invalid selection';
exit;
}
$strPrintedBy = fxBibGetCurrentPromoteurDisplayName();
$intComId = (int)($_SESSION['com_id'] ?? 0);
fxBibSaveDistPrintMeta($int_eve_id, $intComId, $strPrintedBy);
// MSIN-4512 — PDF de CE run lit le POST (indépendant de la colonne BD).
fxBibOutputDistPrintPdf($int_eve_id, $strLangue, $strPrintedBy, $tabIncludeSans, $strOrient);
exit;

53
bib_ops.php Normal file
View File

@ -0,0 +1,53 @@
<?php
/**
* MSIN-4578 — Tableau de bord temporaire gestionnaire de dossards (ops MS1).
* Chrome compte (Info via compte.php). Pas le hub promoteur.
*/
require_once('php/inc_start_time.php');
require_once('php/inc_fonctions.php');
require_once('php/inc_fx_messages.php');
require_once('php/inc_v2_assets.php');
require_once('php/inc_fx_eve_acces.php');
require_once('php/inc_fx_promoteur.php');
global $vDomaine, $vPage, $strLangue;
$_SERVER['PHP_SELF'] = '/compte.php';
$_SERVER['SCRIPT_NAME'] = '/compte.php';
$strLangue = $_GET['lang'] ?? $_SESSION['lang'] ?? 'fr';
if (!in_array($strLangue, ['fr', 'en'], true)) {
$strLangue = 'fr';
}
$_SESSION['lang'] = $strLangue;
$vPage = 'compte.php';
$footer_script = false;
$strCode = 'bib_ops';
$strPage = ($strLangue === 'en') ? 'account' : 'compte';
$vtexte_page = obtenirTextepage($vPage, $strLangue, 2);
$strLienFr = '/bib_ops.php';
$strLienEn = '/bib_ops.php';
$blnHideCompteTitle = true;
if (!fxBibOpsUserCanAccessGlobal()) {
header('Location: ' . $vDomaine . '/' . $strPage);
exit;
}
$strMetaTitle = fxBibTexte('bib_v5_ops_page_title', 0);
$strMetaDescription = $strMetaTitle;
require_once('inc_header.php');
?>
<div id="page">
<div id="main">
<div class="container bg-white p-3 p-xl-5">
<?php echo fxBibOpsRenderDashboard($strLangue); ?>
</div>
</div>
</div>
<?php
require_once('inc_footer.php');

205
bib_production_excel.php Normal file
View File

@ -0,0 +1,205 @@
<?php
// MSIN-4469 — Génération et retéléchargement du fichier officiel de production.
session_start();
$_SERVER['PHP_SELF'] = '/compte.php';
$_SERVER['SCRIPT_NAME'] = '/compte.php';
require_once('php/inc_fonctions.php');
require_once('php/inc_fx_promoteur.php');
require_once('php/inc_fx_eve_acces.php');
$strLangue = $_SESSION['lang'] ?? 'fr';
$strReqLang = $_REQUEST['lang'] ?? '';
if (in_array($strReqLang, ['fr', 'en'], true)) {
$strLangue = $strReqLang;
}
$vPage = 'compte.php';
$vtexte_page = obtenirTextepage($vPage, $strLangue, 2);
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
exit;
}
$intEveId = (int)($_POST['eve_id'] ?? 0);
$strAction = (string)($_POST['action'] ?? '');
$strKind = fxBibProductionNormalizeKind((string)($_POST['kind'] ?? 'dossards'));
$blnReplace = ((string)($_POST['replace'] ?? '') === '1');
fxBibRequireDistPrintAccess();
if (!fxBibOpsUserCanAccessCommande($intEveId)) {
http_response_code(403);
header('Content-Type: text/plain; charset=utf-8');
echo fxBibMsg('bib_v4_ajax_unauthorized');
exit;
}
$strCsrf = (string)($_POST['csrf_token'] ?? '');
if (!fxBibValidateCsrf($strCsrf)) {
http_response_code(403);
header('Content-Type: text/plain; charset=utf-8');
echo fxBibMsg('bib_v4_ajax_unauthorized');
exit;
}
if ($strAction === 'generate') {
// MSIN-4516 — Filet timeout (cPanel 60s) pendant génération Excel imprimeur.
if (function_exists('set_time_limit')) {
@set_time_limit(0);
}
// MSIN-4515 — Mémoriser Face/Endos/Info avant génération (pas si le fichier imprimeur est déjà bloqué).
$tabFieldSelections = fxBibProductionParseFieldsFromPost($_POST);
if (!empty($tabFieldSelections) && !fxBibProductionEventIsLocked($intEveId)) {
$tabSaveFields = fxBibProductionSaveFields($intEveId, $tabFieldSelections, $strLangue);
if (empty($tabSaveFields['success'])) {
http_response_code(400);
header('Content-Type: text/html; charset=utf-8');
$strMsg = (string)($tabSaveFields['message'] ?? fxBibMsg('bib_v5_production_generation_error'));
echo '<!DOCTYPE html><html><head><meta charset="utf-8"><title>Erreur</title></head><body>';
echo '<p>' . htmlspecialchars($strMsg, ENT_QUOTES, 'UTF-8') . '</p>';
echo '<p><a href="javascript:window.close()">Fermer</a></p>';
echo '</body></html>';
exit;
}
}
$tabResult = fxBibProductionCreateOfficialOrder($intEveId, $strLangue, $strKind, $blnReplace);
if (empty($tabResult['success']) || empty($tabResult['order'])) {
http_response_code(400);
header('Content-Type: text/html; charset=utf-8');
$strMsg = (string)($tabResult['message'] ?? fxBibMsg('bib_v5_production_generation_error'));
echo '<!DOCTYPE html><html><head><meta charset="utf-8"><title>Erreur</title></head><body>';
echo '<p>' . htmlspecialchars($strMsg, ENT_QUOTES, 'UTF-8') . '</p>';
echo '<p><a href="javascript:window.close()">Fermer</a></p>';
echo '</body></html>';
exit;
}
$tabOrder = $tabResult['order'];
} elseif ($strAction === 'download') {
if (function_exists('set_time_limit')) {
@set_time_limit(0);
}
$intOrderId = (int)($_POST['order_id'] ?? 0);
$tabOrder = fxBibProductionGetOrder($intOrderId, $intEveId);
if (!$tabOrder || (int)($tabOrder['bpo_active_key'] ?? 0) !== 1) {
http_response_code(404);
header('Content-Type: text/plain; charset=utf-8');
echo fxBibMsg('bib_v5_production_file_missing');
exit;
}
// MSIN-4516 — Si coincé en generating (timeout), reprendre puis servir le fichier.
if ((string)($tabOrder['bpo_status'] ?? '') === 'generating') {
$tabResume = fxBibProductionResumeGeneratingOrder($tabOrder, $strLangue);
if (empty($tabResume['success']) || empty($tabResume['order'])) {
http_response_code(400);
header('Content-Type: text/html; charset=utf-8');
$strMsg = (string)($tabResume['message'] ?? fxBibMsg('bib_v5_production_generation_error'));
echo '<!DOCTYPE html><html><head><meta charset="utf-8"><title>Erreur</title></head><body>';
echo '<p>' . htmlspecialchars($strMsg, ENT_QUOTES, 'UTF-8') . '</p>';
echo '<p><a href="javascript:window.close()">Fermer</a></p>';
echo '</body></html>';
exit;
}
$tabOrder = $tabResume['order'];
} elseif ((string)($tabOrder['bpo_status'] ?? '') === 'ready'
&& !fxBibProductionOrderIsFrozen($tabOrder)
) {
// MSIN-4577 — Brouillon : le fichier suit les interrupteurs actuels.
$tabRebuild = fxBibProductionRebuildActiveOrderWorkbook(
$intEveId,
$strLangue,
$strKind,
$tabOrder,
false
);
if (empty($tabRebuild['success']) || empty($tabRebuild['order'])) {
http_response_code(400);
header('Content-Type: text/html; charset=utf-8');
$strMsg = (string)($tabRebuild['message'] ?? fxBibMsg('bib_v5_production_generation_error'));
echo '<!DOCTYPE html><html><head><meta charset="utf-8"><title>Erreur</title></head><body>';
echo '<p>' . htmlspecialchars($strMsg, ENT_QUOTES, 'UTF-8') . '</p>';
echo '<p><a href="javascript:window.close()">Fermer</a></p>';
echo '</body></html>';
exit;
}
$tabOrder = $tabRebuild['order'];
} elseif (
in_array((string)($tabOrder['bpo_status'] ?? ''), ['ready', 'locked'], true)
&& fxBibProductionOrderNeedsSplitRebuild($tabOrder)
) {
// MSIN-4577 — Ancien fichier mixte : recréer sans changer le cadenas.
$tabRebuild = fxBibProductionRebuildActiveOrderWorkbook(
$intEveId,
$strLangue,
$strKind,
$tabOrder,
false
);
if (empty($tabRebuild['success']) || empty($tabRebuild['order'])) {
http_response_code(400);
header('Content-Type: text/html; charset=utf-8');
$strMsg = (string)($tabRebuild['message'] ?? fxBibMsg('bib_v5_production_generation_error'));
echo '<!DOCTYPE html><html><head><meta charset="utf-8"><title>Erreur</title></head><body>';
echo '<p>' . htmlspecialchars($strMsg, ENT_QUOTES, 'UTF-8') . '</p>';
echo '<p><a href="javascript:window.close()">Fermer</a></p>';
echo '</body></html>';
exit;
}
$tabOrder = $tabRebuild['order'];
}
} elseif ($strAction === 'lock_file') {
$tabResult = fxBibProductionLockOfficialOrder($intEveId, $strKind);
if (empty($tabResult['success'])) {
http_response_code(400);
header('Content-Type: text/html; charset=utf-8');
$strMsg = (string)($tabResult['message'] ?? fxBibMsg('bib_v5_production_generation_error'));
echo '<!DOCTYPE html><html><head><meta charset="utf-8"><title>Erreur</title></head><body>';
echo '<p>' . htmlspecialchars($strMsg, ENT_QUOTES, 'UTF-8') . '</p>';
echo '<p><a href="javascript:window.close()">Fermer</a></p>';
echo '</body></html>';
exit;
}
if (function_exists('fxBibCommandePageUrl')) {
header('Location: ' . fxBibCommandePageUrl($intEveId, $strLangue));
exit;
}
header('Location: ' . (($strLangue === 'en') ? '/account' : '/compte'));
exit;
} elseif ($strAction === 'development_unlock') {
$tabResult = fxBibProductionDevelopmentUnlock($intEveId, $strKind);
if (empty($tabResult['success'])) {
http_response_code(403);
header('Content-Type: text/plain; charset=utf-8');
echo $tabResult['message'] ?? fxBibMsg('bib_v4_ajax_unauthorized');
exit;
}
$strRedirect = (string)($_POST['redirect'] ?? '');
if ($strRedirect === 'commande' && function_exists('fxBibCommandePageUrl')) {
header('Location: ' . fxBibCommandePageUrl($intEveId, $strLangue));
exit;
}
$strAccountPage = $strLangue === 'en' ? '/account' : '/compte';
header(
'Location: ' . $strAccountPage
. '/inc_tableau_gestion_epreuves?promoteur_eve_id='
. urlencode(base64_encode($intEveId))
);
exit;
} else {
http_response_code(400);
exit;
}
if (!fxBibProductionStreamOrder($tabOrder)) {
http_response_code(404);
header('Content-Type: text/plain; charset=utf-8');
echo fxBibMsg('bib_v5_production_file_missing');
exit;
}
exit;

View File

@ -21,6 +21,10 @@ require_once('php/inc_logs_course.php');
require_once('superadm/php/inc_fx_excell.php'); require_once('superadm/php/inc_fx_excell.php');
require_once('php/inc_fx_liste_attente.php'); require_once('php/inc_fx_liste_attente.php');
require_once('php/inc_fx_inscriptions_gestion.php');
require_once('php/inc_v2_assets.php');
require_once('php/inc_fx_eve_acces.php');
require_once('php/inc_fx_promoteur_hub.php');
global $objDatabase, $vPage, $vDomaine; global $objDatabase, $vPage, $vDomaine;
// vérifier les paramètres // vérifier les paramètres
@ -51,15 +55,40 @@ else
if (!empty($_GET['code'])) if (!empty($_GET['code']))
$strCode = 'compte_' . $_GET['code']; $strCode = 'compte_' . $_GET['code'];
else { else {
if (isset($_SESSION['com_id'])) if (isset($_SESSION['com_id'])) {
$strCode = "compte_accueil"; // Compte v2 : atterrissage hub (Mes événements). Sinon accueil participant.
else if (fxPromoteurHubShouldUse($_SESSION['com_id'])) {
$strCode = "compte_login"; $strCode = 'compte_promoteur_hub';
} else {
$strCode = 'compte_accueil';
}
} else {
$strCode = 'compte_login';
}
} }
if ($strCode === 'compte_promoteur_hub' && (!isset($_SESSION['com_id']) || !fxPromoteurHubIsAvailable($_SESSION['com_id']))) {
header('Location: ' . $vDomaine . '/' . ($strLangue === 'fr' ? 'compte' : 'account'));
exit;
}
// Ancien slug renomme (retrait du nom "mobile") : redirection 301 vers le nouveau slug "gestion".
// Conserve les parametres utiles (promoteur_eve_id, pec_id, lng...) pour ne pas casser favoris/QR.
if (($_GET['code'] ?? '') === 'inc_tableau_inscriptions_mobile') {
$strNewPath = ($strLangue === 'en') ? '/account/inc_tableau_inscriptions_gestion' : '/compte/inc_tableau_inscriptions_gestion';
parse_str($_SERVER['QUERY_STRING'] ?? '', $arrOldQs);
unset($arrOldQs['code'], $arrOldQs['lang']);
$strQsClean = http_build_query($arrOldQs);
header('Location: ' . $vDomaine . $strNewPath . ($strQsClean !== '' ? '?' . $strQsClean : ''), true, 301);
exit;
}
$recPage = fxGetPage($strCode); $recPage = fxGetPage($strCode);
if (count($recPage) < 1) { // SM-9493 — affichecarte n'a pas de ligne CMS. Depuis v4.72.707 (ce7c4300, 2026-06-27)
header('Location: index.php'); // fxGetPage([]) + Location: index.php relatif → 404. Ne pas court-circuiter le PDF plus haut.
if (count($recPage) < 1 && $strCode !== 'compte_affichecarte') {
header('Location: ' . $vDomaine . '/' . ($strLangue === 'en' ? 'account' : 'compte'));
exit; exit;
} }
@ -72,16 +101,17 @@ if (!empty($_GET['a']) && $_GET['code'] != 'bib_assignment') {
$strCode = $_GET['a']; $strCode = $_GET['a'];
} }
// empêcher d'accéder à une page où il faut être loggé et qu'on ne l'est pas // Page protégée sans session : sauver la cible puis formulaire de login (MSIN-4339).
if (($strCode != "compte_login" && $strCode != "compte_motdepasse" && $strCode != "compte_nouveau" && $strCode != "compte_reset" && !isset($_SESSION['com_id'])) || (($strCode == "compte_login" || $strCode == "compte_motdepasse" || $strCode == "compte_nouveau" || $strCode == "compte_reset") && isset($_SESSION['com_id']))) { if ($strCode != 'compte_login' && $strCode != 'compte_motdepasse' && $strCode != 'compte_nouveau' && $strCode != 'compte_reset' && !isset($_SESSION['com_id'])) {
/// MSIN-4339 Sauvegarder l'URL demandée
$_SESSION['redirect_after_login'] = $_SERVER['REQUEST_URI']; $_SESSION['redirect_after_login'] = $_SERVER['REQUEST_URI'];
header('Location: ' . $vDomaine . '/' . $strPage); header('Location: ' . $vDomaine . '/' . $strPage);
exit;
}
// Déjà connecté sur login / mot de passe / inscription : accueil compte, sans polluer redirect_after_login.
// MSIN-4401 — compte_reset exclu : géré dans fxShowFormReset (conflit autre session).
if (($strCode == 'compte_login' || $strCode == 'compte_motdepasse' || $strCode == 'compte_nouveau') && isset($_SESSION['com_id'])) {
header('Location: ' . $vDomaine . '/' . $strPage);
exit; exit;
} }
@ -226,7 +256,6 @@ if (!empty($_POST['form_action'])) {
default: default:
switch ($strAction) { switch ($strAction) {
case 'login': case 'login':
fxLoginCompte($_POST, $strLangue); fxLoginCompte($_POST, $strLangue);
@ -308,10 +337,26 @@ if (isset($_GET['promoteur_eve_id'])) {
$intEveId = base64_decode(urldecode($_GET['promoteur_eve_id'])); $intEveId = base64_decode(urldecode($_GET['promoteur_eve_id']));
$intEveIdbase = $intEveId; $intEveIdbase = $intEveId;
$blnIsPromoteur = fxIsPromoteur( $_SESSION['com_id'],$intEveIdbase ); if ($strCode === 'compte_inc_tableau_inscriptions_gestion') {
if (!fxInscrGestionCanAccess($_SESSION['com_id'], $intEveIdbase)) {
header('Location: ' . $vDomaine . '/' . $strPage);
if (!$blnIsPromoteur) { exit;
}
} elseif ($strCode === 'compte_inc_tableau_gestion_epreuves') {
if (empty($_SESSION['usa_id'])
&& !fxEveAccesHasAnyPermission($_SESSION['com_id'] ?? 0, $intEveIdbase, array('dossards.manage', 'epreuves.edit_qte'))) {
header('Location: ' . $vDomaine . '/' . $strPage);
exit;
}
} elseif ($strCode === 'compte_inc_tableau_eve_acces') {
require_once('php/inc_fx_eve_acces.php');
if (!fxEveAccesCanTeamInvite($_SESSION['com_id'] ?? 0, $intEveIdbase)) {
header('Location: ' . $vDomaine . '/' . $strPage);
exit;
}
} elseif (empty($_SESSION['usa_id'])
&& !fxIsPromoteur($_SESSION['com_id'], $intEveIdbase)
&& !fxEveAccesComHasV2ForEvent($_SESSION['com_id'], $intEveIdbase)) {
header('Location: ' . $vDomaine . '/' . $strPage); header('Location: ' . $vDomaine . '/' . $strPage);
exit; exit;
} }
@ -334,6 +379,69 @@ $strMetaTitle = fxRemoveHtml(fxUnescape($recPage['pag_titre_' . $strLangue]));
$strMetaDescription = fxRemoveHtml(fxUnescape($recPage['pag_description_' . $strLangue])); $strMetaDescription = fxRemoveHtml(fxUnescape($recPage['pag_description_' . $strLangue]));
$strMetaKeywords = fxRemoveHtml(fxUnescape($recPage['pag_keywords_' . $strLangue])); $strMetaKeywords = fxRemoveHtml(fxUnescape($recPage['pag_keywords_' . $strLangue]));
$blnBoutonRetour = true; $blnBoutonRetour = true;
$blnHideCompteTitle = false;
// MSIN-4401 — POST invite/revoke AVANT le HTML (sinon header Location → page blanche).
if ($strCode === 'compte_inc_tableau_eve_acces'
&& ($_SERVER['REQUEST_METHOD'] ?? '') === 'POST'
&& isset($_GET['promoteur_eve_id'])) {
require_once('php/inc_fx_eve_acces_promoteur.php');
$intEveIdPost = intval(base64_decode(urldecode($_GET['promoteur_eve_id'])));
$intActorPost = intval($_SESSION['com_id'] ?? 0);
if (!empty($_SESSION['usa_id'])) {
$intActorPost = intval($_SESSION['usa_id']);
}
$strLanguePost = ($strLangue === 'en') ? 'en' : 'fr';
$strBasePathPost = ($strLanguePost === 'en') ? '/account' : '/compte';
$strSelfUrlPost = $vDomaine . $strBasePathPost . '/inc_tableau_eve_acces?promoteur_eve_id='
. urlencode(base64_encode($intEveIdPost)) . '&lng=' . $strLanguePost;
if ($intEveIdPost > 0 && fxEveAccesCanTeamInvite($intActorPost, $intEveIdPost)) {
$strEveAccesAction = (string)($_POST['eve_acces_action'] ?? '');
if ($strEveAccesAction === 'invite') {
$arrEveAccesResult = fxEveAccesPromoteurInvite($intEveIdPost, $intActorPost, $_POST, $strLanguePost);
if (($arrEveAccesResult['state'] ?? '') === 'success') {
header('Location: ' . $strSelfUrlPost . '&ok=' . urlencode($arrEveAccesResult['message']));
exit;
}
header('Location: ' . $strSelfUrlPost . '&err=' . urlencode(
$arrEveAccesResult['message'] ?? fxEveAccesPromoteurT('eve_acces_promoteur_err_generic', $strLanguePost)
));
exit;
}
if ($strEveAccesAction === 'revoke') {
$arrEveAccesResult = fxEveAccesPromoteurRevoke(intval($_POST['ea_id'] ?? 0), $intActorPost, $intEveIdPost);
if (($arrEveAccesResult['state'] ?? '') === 'success') {
header('Location: ' . $strSelfUrlPost . '&ok=' . urlencode(
fxEveAccesPromoteurT('eve_acces_promoteur_ok_revoked', $strLanguePost)
));
exit;
}
header('Location: ' . $strSelfUrlPost . '&err=' . urlencode(
fxEveAccesPromoteurT('eve_acces_promoteur_err_revoke', $strLanguePost)
));
exit;
}
if ($strEveAccesAction === 'resend') {
$arrEveAccesResult = fxEveAccesPromoteurResendMail(
intval($_POST['ea_id'] ?? 0),
$intActorPost,
$intEveIdPost,
$strLanguePost
);
if (($arrEveAccesResult['state'] ?? '') === 'success') {
header('Location: ' . $strSelfUrlPost . '&ok=' . urlencode(
$arrEveAccesResult['message'] ?? fxEveAccesPromoteurT('eve_acces_promoteur_ok_resent', $strLanguePost)
));
exit;
}
header('Location: ' . $strSelfUrlPost . '&err=' . urlencode(
$arrEveAccesResult['message'] ?? fxEveAccesPromoteurT('eve_acces_promoteur_err_resend', $strLanguePost)
));
exit;
}
}
}
require_once("inc_header.php"); require_once("inc_header.php");
@ -343,7 +451,7 @@ require_once("inc_header.php");
<div class="container bg-white p-3 p-xl-5"> <div class="container bg-white p-3 p-xl-5">
<?php <?php
if (isset($recPage['pag_titre_' . $strLangue]) != '') { if (isset($recPage['pag_titre_' . $strLangue]) != '' && empty($blnHideCompteTitle)) {
?> ?>
@ -360,7 +468,7 @@ require_once("inc_header.php");
} }
if (isset($_SESSION['msg'])) { if (isset($_SESSION['msg'])) {
echo fxMessage($_SESSION['msg'], '', $strLangue); echo fxMessage($_SESSION['msg'], '', $strLangue);
} else { } else if (empty($blnHideCompteTitle)) {
?> ?>
<br> <br>
<?php <?php
@ -465,9 +573,13 @@ require_once("inc_header.php");
case 'compte_annuler_inscription': case 'compte_annuler_inscription':
include_once('annuler_inscription.php'); include_once('annuler_inscription.php');
break; break;
case 'compte_promoteur_hub':
$blnBoutonRetour = false;
echo fxShowPromoteurHub($_SESSION['com_id'], $strLangue);
break;
case 'compte_accueil': case 'compte_accueil':
//MSIN-4371 //MSIN-4371
if (isset($_SESSION['usa_id'])) { ?> if (fxEveAccesCanQrTest($_SESSION['com_id'])) { ?>
<a href="qr_test.php" target="_blank" class="btn btn-primary"> <a href="qr_test.php" target="_blank" class="btn btn-primary">
QR Test QR Test
@ -688,19 +800,18 @@ require_once("inc_header.php");
$footer_script = true; $footer_script = true;
include("inc_tableau_rabais.php"); include("inc_tableau_rabais.php");
break; break;
// MSIN-3679
case 'compte_inc_tableau_remboursement':
$blnBoutonRetour = false;
$footer_script = true;
include("inc_tableau_remboursement.php");
break;
case 'compte_inc_tableau_inscriptions': case 'compte_inc_tableau_inscriptions':
$blnBoutonRetour = false; $blnBoutonRetour = false;
$footer_script = true; $footer_script = true;
include("inc_tableau_inscriptions.php"); include("inc_tableau_inscriptions.php");
break; break;
case 'compte_inc_tableau_inscriptions_gestion':
$blnBoutonRetour = false;
$footer_script = true;
$blnHideCompteTitle = true;
fxInscrGestionInitTextes($strLangue);
include("inc_tableau_inscriptions_gestion.php");
break;
case 'compte_inc_tableau_mail_entraineurs': case 'compte_inc_tableau_mail_entraineurs':
$blnBoutonRetour = false; $blnBoutonRetour = false;
$footer_script = true; $footer_script = true;
@ -720,6 +831,17 @@ require_once("inc_header.php");
$blnBoutonRetour = false; $blnBoutonRetour = false;
include("inc_tableau_epreuves.php"); include("inc_tableau_epreuves.php");
break; break;
case 'compte_inc_tableau_gestion_epreuves':
$blnBoutonRetour = false;
include("inc_tableau_gestion_epreuves.php");
break;
case 'compte_inc_tableau_eve_acces':
// MSIN-4401
$blnBoutonRetour = false;
$footer_script = true;
$blnHideCompteTitle = true;
include("inc_tableau_eve_acces.php");
break;
case 'compte_inc_tableau_liste_attente': case 'compte_inc_tableau_liste_attente':

324
css/ms1-doc.css Normal file
View File

@ -0,0 +1,324 @@
body.ms1-doc-open {
overflow: hidden;
}
/**
* MS1 — Module documentation (overlay paginé).
* Le bouton ? réutilise .btn-aide-bib (style.css) — pas de style rond dupliqué ici.
*/
.btn-aide-bib.ms1-doc-trigger {
flex-shrink: 0;
}
/* Overlay plein écran */
.ms1-doc-overlay {
position: fixed;
inset: 0;
z-index: 10050;
display: flex;
align-items: center;
justify-content: center;
padding: 16px;
}
.ms1-doc-overlay[hidden] {
display: none !important;
}
.ms1-doc-backdrop {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.55);
cursor: pointer;
}
/* Panneau central — plus large pour le guide */
.ms1-doc-panel {
position: relative;
z-index: 1;
width: 100%;
max-width: 840px;
max-height: 92vh;
display: flex;
flex-direction: column;
background: #fff;
border-radius: 10px;
border: 1px solid #dee2e6;
overflow: hidden;
}
.ms1-doc-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
padding: 16px 20px;
border-bottom: 1px solid #dee2e6;
background: #f8f9fa;
}
.ms1-doc-header-actions {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.ms1-doc-title {
margin: 0;
font-size: 1.25rem;
font-weight: 600;
color: #212529;
line-height: 1.3;
}
.ms1-doc-subtitle {
margin: 4px 0 0;
font-size: 0.85rem;
color: #6c757d;
line-height: 1.35;
}
.ms1-doc-close {
flex-shrink: 0;
width: 32px;
height: 32px;
padding: 0;
border: none;
border-radius: 4px;
background: transparent;
color: #6c757d;
font-size: 24px;
line-height: 1;
cursor: pointer;
}
.ms1-doc-close:hover {
background: #e9ecef;
color: #212529;
}
.ms1-doc-body {
flex: 1;
overflow: auto;
padding: 20px 22px;
min-height: 160px;
}
.ms1-doc-page[hidden] {
display: none !important;
}
/* Typographie contenu HTML admin */
.ms1-doc-format,
.ms1-doc-content {
font-size: 0.95rem;
line-height: 1.6;
color: #212529;
}
.ms1-doc-format h2,
.ms1-doc-format h3,
.ms1-doc-content h2,
.ms1-doc-content h3 {
margin: 14px 0 8px;
font-size: 0.95rem;
font-weight: 600;
color: #212529;
}
.ms1-doc-format h2:first-child,
.ms1-doc-format h3:first-child,
.ms1-doc-content h2:first-child,
.ms1-doc-content h3:first-child {
margin-top: 0;
}
.ms1-doc-format p,
.ms1-doc-content p {
margin: 0 0 10px;
}
.ms1-doc-format ol,
.ms1-doc-format ul,
.ms1-doc-content ol,
.ms1-doc-content ul {
margin: 0 0 10px;
padding-left: 1.35rem;
}
.ms1-doc-format li,
.ms1-doc-content li {
margin-bottom: 4px;
}
.ms1-doc-format blockquote,
.ms1-doc-content blockquote {
margin: 12px 0;
padding: 10px 12px;
border-left: 3px solid #007bff;
background: #f8f9fa;
font-size: 0.85rem;
}
.ms1-doc-format table,
.ms1-doc-content table {
width: 100%;
border-collapse: collapse;
margin-bottom: 10px;
font-size: 0.85rem;
}
.ms1-doc-format th,
.ms1-doc-format td,
.ms1-doc-content th,
.ms1-doc-content td {
border: 1px solid #dee2e6;
padding: 6px 8px;
text-align: left;
vertical-align: top;
}
.ms1-doc-format th,
.ms1-doc-content th {
background: #f1f3f5;
font-weight: 600;
}
.ms1-doc-format strong,
.ms1-doc-content strong {
font-weight: 600;
}
/* Bloc pédagogique par bouton / contrôle */
.ms1-doc-bouton {
margin: 16px 0;
padding: 12px 14px;
border: 1px solid #e9ecef;
border-radius: 6px;
background: #fafbfc;
}
.ms1-doc-bouton h3,
.ms1-doc-bouton h4 {
margin: 0 0 10px;
font-size: 0.92rem;
font-weight: 600;
color: #2f5fd0;
}
.ms1-doc-bouton dl {
margin: 0;
}
.ms1-doc-bouton dt {
font-size: 0.8rem;
font-weight: 600;
color: #495057;
margin-top: 8px;
}
.ms1-doc-bouton dt:first-child {
margin-top: 0;
}
.ms1-doc-bouton dd {
margin: 2px 0 0;
font-size: 0.88rem;
line-height: 1.5;
color: #212529;
}
/* Pied — navigation */
.ms1-doc-footer {
padding: 10px 14px 12px;
border-top: 1px solid #dee2e6;
background: #f8f9fa;
}
.ms1-doc-nav-dots {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 8px;
margin-bottom: 8px;
}
.ms1-doc-dot {
width: 36px;
height: 36px;
padding: 0;
border: 2px solid #dee2e6;
border-radius: 6px;
background: #fff;
color: #495057;
font-size: 13px;
font-weight: 600;
cursor: pointer;
}
.ms1-doc-dot.is-active,
.ms1-doc-dot:hover {
border-color: #007bff;
background: #e7f1ff;
color: #0056b3;
}
.ms1-doc-nav-arrows {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
}
.ms1-doc-prev,
.ms1-doc-next {
padding: 4px 12px;
border: 1px solid #6c757d;
border-radius: 4px;
background: #fff;
color: #495057;
font-size: 0.8rem;
cursor: pointer;
}
.ms1-doc-prev:hover:not(:disabled),
.ms1-doc-next:hover:not(:disabled) {
background: #e9ecef;
}
.ms1-doc-prev:disabled,
.ms1-doc-next:disabled {
opacity: 0.45;
cursor: not-allowed;
}
.ms1-doc-counter {
font-size: 0.8rem;
color: #6c757d;
min-width: 48px;
text-align: center;
}
/* Métadonnées JSON — jamais affichées */
script.ms1-doc-meta {
display: none !important;
}
@media (max-width: 576px) {
.ms1-doc-overlay {
padding: 8px;
}
.ms1-doc-panel {
max-height: 95vh;
border-radius: 8px;
}
.ms1-doc-dot {
width: 32px;
height: 32px;
font-size: 12px;
}
}

100
css/ms1-loader.css Normal file
View File

@ -0,0 +1,100 @@
/**
* MS1 — Indicateur de chargement (coureur animé).
* Reutilisable en overlay plein ecran (#preloader) ou inline (panneaux AJAX).
*/
.ms1-loader {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.75rem;
text-align: center;
}
.ms1-loader__runner {
width: 72px;
height: 72px;
object-fit: contain;
flex-shrink: 0;
}
.ms1-loader__label {
font-size: 1rem;
font-weight: 600;
color: #333;
line-height: 1.35;
}
.ms1-loader--inline {
padding: 1.25rem 1rem;
min-height: 120px;
}
.ms1-loader--inline .ms1-loader__runner {
width: 64px;
height: 64px;
}
.ms1-loader--inline .ms1-loader__label {
font-size: 0.9rem;
font-weight: 500;
color: #666;
}
#preloader #spinner.ms1-loader--overlay {
background: #fff;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18);
padding: 1.25rem 1.75rem;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
margin: 0;
width: auto;
min-width: 200px;
font-size: inherit;
color: inherit;
border: none;
}
#preloader #spinner.ms1-loader--overlay .ms1-loader__runner {
width: 80px;
height: 80px;
}
#preloader #spinner.ms1-loader--overlay #preloader_text,
#preloader #spinner.ms1-loader--overlay .ms1-loader__label {
font-size: 1.1rem;
font-weight: 600;
}
.ms1-loader__progress {
width: 100%;
min-width: 220px;
margin-top: 0.25rem;
}
.ms1-loader__progress-track {
width: 100%;
height: 8px;
background: #e8e8e8;
border-radius: 4px;
overflow: hidden;
}
.ms1-loader__progress-fill {
height: 100%;
width: 0;
background: linear-gradient(90deg, #2e7d32, #43a047);
border-radius: 4px;
transition: width 0.25s ease;
}
.ms1-loader__progress-pct {
display: block;
margin-top: 0.35rem;
font-size: 0.85rem;
font-weight: 600;
color: #555;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
# ChronoTrack Live — docs de référence (MS1)
**Source officielle** : https://api.chronotrack.com/dev/docs
**Résultats (champs pays / province)** : https://api.chronotrack.com/dev/docs/ctlive-results
**Meta entry** : snapshot local `api-meta-entry.json`
## Fichiers
| Fichier | Contenu |
|---|---|
| `api-dev-docs.md` | Copie Overview `/dev/docs` |
| `api-meta-entry.json` | Colonnes meta `entry` |
| `README.md` | Ce fichier |
## ID unique envoyé à ChronoTrack (MSIN-4482)
| Origine MS1 | `external_id` CT |
|---|---|
| Import fichier / saisie rapide | `par_external_id` (ID du fichier client) |
| Inscription en ligne | `par_id_original` (ID inventé MS1) |
Si lentry CT existe déjà sous lancien ID MS1, le PUT (via `ct_entry_id`) pose lID fichier — pas de doublon.
## Calibrage validé (2026-08-11) — event 93163
Sources : **Sonde lecture pays** (GET entry) + export `confirmedentries` CSV.
Quand Athlete Info UI = Canada + Quebec, CT stocke :
| Champ API / export | Valeur |
|---|---|
| `location_country` / `COUNTRY_CODE` | **`CA`** (ISO-2) |
| `location_region` / `REGION_CODE` | **`QC`** |
| `country_name` / `COUNTRY_NAME` | `Canada` |
| `REGION_NAME` (export) | `Quebec` |
**Pas** `CAN` (ISO-3) dans `location_country`.
Export massif : 1578/1580 sans `COUNTRY_CODE` avant fix — seule Gabrielle-Ann (manuel) + 1 FR avaient un pays.
## Relecture liste entry (vérif contenu)
Lécriture accepte souvent `bib` / `first_name` / `external_id`.
La **liste** meta renvoie plutôt :
| Payload écriture | Clé lecture CT |
|---|---|
| `external_id` | `entry_external_id` |
| `bib` | `entry_bib` |
| `birthdate` | `athlete_birthdate` |
| `first_name` / `last_name` / `sex` | `athlete_first_name` / `athlete_last_name` / `athlete_sex` |
`fx_chronotrack_verify.php` mappe ces alias — sinon faux écarts « CT ∅ ».
**Règle vérif (tous les champs)** :
- Liste CT avec `columns=` (identité, adresse, bib, statut, `entry_check_in`).
- Clé absente de la réponse API → **pas un écart**.
- Questions (`entry_note_*`) : envoyées, jamais comparées (Custom Import UI seulement).
**Écriture pays/région** : seulement `location_country` / `location_region` / `country_name`
(pas `country_code` / `state_code` — collision possible → QC dans le pays).
Si province **canadienne** (ex. `QC`) et pays MS1 ≠ `CA`**province non envoyée** + log `skip_region`
(pas de réparation auto du pays ; pas de faux écart à la vérif).
**Push** (règle produit MSIN-4574 — **correctitude > vitesse**) :
- entry déjà dans CT (`entry_id`) → **PUT** unitaire (écrase pays / région / bib)
- création seulement → **POST** bulk
- **Jamais** sacrifier des champs pour aller plus vite ; optimiser la vitesse **après** 0 écart
- **Vérifier contenu** = validation seulement (pas un filet opérationnel)
## Fichiers code (module)
| Fichier | Rôle |
|---|---|
| `fx_chronotrack_payload.php` | Source de vérité **écriture** entry (pays, bib, sexe, notes) |
| `fx_chronotrack_push.php` | Push HTTP, lots, job, conflits dossard |
| `fx_chronotrack_read.php` | Lecture entries CT, index, comptage |
| `fx_chronotrack_lifecycle.php` | Cancel / restore / WITHDRAWN / orphelins |
| `fx_chronotrack_cron.php` | Auto-push / cron |
| `fx_chronotrack_probe.php` | Sondes Diagnostic (support) |
| `fx_chronotrack_sync.php` | Classify, preview, logs |
| `fx_chronotrack_verify.php` | **Vérifier contenu** — payload MS1 vs entries CT (écarts) |
| UI Opération | Envoyer / Tout renvoyer / **Vérifier contenu** / logs |
| UI Diagnostic | Sondes + **Orphelins CT** (hors éligibles) |
| MS1 BD | Champ CT |
|---|---|
| `pay_id``inscriptions_pays.pay_iso` | `location_country` + `country_code` = **ISO-2** (`CA`) |
| `pay_id``pay_nom_en` | `country_name` (`Canada`) |
| `pro_id``inscriptions_provinces.pro_iso` | `location_region` + `state_code` = **`QC`** |
| `pay_iso3` / map ISO-2→3 | référence / logs seulement (pas Athlete Info) |
## Docs résultats CTLIVE
La page résultats mentionne `country_code` « three character ISO-3166 ».
Pour **Athlete Info / entry / export confirmed**, la vérité mesurée = **ISO-2**. On suit la calibrage entry.

View File

@ -0,0 +1,318 @@
Source URL: https://api.chronotrack.com/dev/docs
Title: ChronoTrack Live - ChronoTrack
* Home
* Projects
* Docs
Sign-In Sign-Up
API Version: **v1.3**
NOTICE This is a non-production environment used for testing. Any changes you make to data will be lost. Please do not use for production services.
# API Docs Test the API Right Here!
* Overview
* Terminology
* Endpoints
* Security
* Formats
* Requests
* Rate Limiting
* Responses
* Character Encoding
* Data Types
* Resources
The API hews as closely as possible to the REST architectural style. The API is organized around a set of `resources` that populate the world of endurance racing, like events, races, race entries, etc. With the API, your application can perform operations on these resources through the HTTP protocol. You can use the documentation below to learn about the API. In particular, you can test out API calls right here on this page to see exactly how API calls work and how to incorporate them into your application.
## Terminology
A `resource` is some object of interest targeted by the API. In the world of endurance racing this includes events, races, athletes, timing devices, and others. We'll refer to a specific resource sometimes as a `resource entity` to distinguish that specific entity from all entities of that `resource type`. A `resource collection` is a number of resource entities of the same type.
The ChronoTrack Live Open Platform API is comprised of `operations` on these resources. All operations are executed by sending HTTP requests to one of our API endpoints. An API `endpoint` is a URI to which a API request is directed.
## Endpoints
All API requests should be sent to one of the following two endpoints:
| Endpoint Type | Endpoint URL |
| ------------- | ------------------------------------ |
| Production | https://api.chronotrack.com/api |
| Testing | https://api-test.chronotrack.com/api |
The production endpoint should only be used in live, production code and never for testing. The production endpoint is backed by the ChronoTrack Live production database, which means all changes to data via the production API will be immediately visible to ChronoTrack Live users.
The testing endpoint is backed by a separate testing database that is refreshed every night with a copy of the production database. This means any API activity that changes data via the testing endpoint will have those changes disappear the next day. Please note the QA environment is not 100% guaranteed up at all times as it is a test environment.
**Note:** Both endpoints are secured by the `https` (TLS/SSL) protocol. Any requests sent via the non-secure `http` protocol will be redirected to the secure endpoint.
## Security
All requests to our production API must be through the secure HTTPS protocol. Any requests that come in on a non-secure port will be redirected to our TLS/SSL secured service.
All requests must also be authenticated. First, a request must originate from an application that is registered and approved by ChronoTrack. Second, the application must authenticate the ChronoTrack Live user on whose behalf it is making requests to our system.
### Supported Authorization Methods
We support four authentication methods, each suited for a different kind of application environment. For applications that have easy access to the user's browser, like a web or mobile application, we support the **OAuth 2.0 Web Flow**. For applications without browser access, like native desktop/laptop applications, we provide two alternatives. If the environment your application operates in has the capacity to securely store your users' ChronoTrack Live account credentials, then you can use **HTTP Basic** authentication. If you can't, or prefer not to, locally store your users' credentials, we support the **OAuth 2.0 Password Flow**.
Finally, for certain testing environments where data security requirements are minimal, we support a **simple authentication scheme** where you can pass a user id and password as parameters to the API call. All our methods are described in detail below.
#### OAuth2 Web Flow
Web applications can acquire an OAuth2 access token for a ChronoTrack Live user by following these steps:
1. Obtain a ChronoTrack Live developer account here. Creating a developer account will also register your client application.
2. Your application will be assigned a `client_id` and a `client_secret`. The client\_id is public information, but you should take care to safeguard your client\_secret. During the process of registering your application with us, you will be asked to provide a `redirect_uri`, that is, a URL within your web application where users will be redirected after granting your application permission to access their ChronoTrack Live resources. An example redirect\_uri is:
https://mywebapp.com/oauth2
3. When the user first invokes a function in your application requiring access to their ChronoTrack Live resources, you should redirect the user to our authorization end-point:
GET /oauth2/authorize?response_type=code
Make sure to add a Basic HTTP `Authorization` header to this request using your `client_id` and `client_secret` as the username and password. This is required to verify the authorization request is coming from your application. See the specific instructions in the HTTP Basic Authentication section for the mechanics of how to construct the `Authorization` header.
This will redirect your user to our authentication service. They will have to login as they normally would to ChronoTrack Live. They then will be presented with a screen that allows them to grant your application permission to access their ChronoTrack Live resources.
4. Upon authorizing your application, the user will be redirected back to your application's specified redirect\_uri:
https://mywebapp.com/oauth2?code=bef10603874b
The `code` parameter appended to your redirect uri is a short-lived (10 minutes), single-use authorization code that your application can convert into a longer-lived access token.
5. To obtain an `access token`, your application should make a GET API call to our token endpoint:
GET /oauth2/token?grant_type=authorization_code&code=bef10603874b
passing in the code parameter returned to your application in the last step and making sure to add an `Authorization` header that provides Basic HTTP authentication for your client app (using your `client_id` as the username and your `client_secret` as the password).
This API token endpoint will return a json formatted object containing an OAuth 2.0 access token that allows your application to access the user's ChronoTrack Live resources for a lengthy period of time (usually 72 hours).
{
"access_token": "2YotnFZFEjr1zCsicMWpAA",
"token_type": "Bearer",
"expires": "1343309220",
"refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA"
}
You also will receive a `refresh token` that you can send to our token endpoint to receive another access token, good for another 72 hours of use. The refresh token is valid for one year or until the user revokes your application's access to their resources.
You send a refresh token request like this:
GET /oauth2/token?grant_type=refresh_token&refresh_token=597262138b23
making sure to again provide Basic HTTP authentication for your client app (using your client\_id as the username and your client\_secret as the password). This will send you another access token that you can use for another 72 hours. It will also return a new refresh token that you can use to extend your access.
6. Finally, with a fresh access token, you can make API requests, like getting a list of events the user has access to:
GET /api/event
making sure to pass in an `Authorization` header with the access token like so:
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA
#### OAuth2 Password Flow
For applications where the OAuth2 Web Flow cannot be used (for example, native applications without a browser layer), an OAuth access token can be acquired by making a single call to our token endpoint and passing the end-users ChronoTrack Live account credentials as the `username` and `password` parameters with a `grant_type` of `password`.
GET /oauth2/token?grant_type=password&username=joesmith%40someplace.com&password=some_password
**Note:** In order for this to work you must pass in an `Authorization` header using HTTP Basic Auth where the username is your `client_id` and the password is your `client_secret`. Follow the instructions under the "HTTP Basic Auth" tab for how to construct this header.
**Note:** that users need only enter their username and password once for the application to authenticate. **Applications using the password flow SHOULD NOT store the user's credentials**.
#### HTTP Basic Authentication
This method involves passing in the resource owner's ChronoTrack Live account username and password directly, according to the HTTP Basic Authentication protocol. You simply construct an `Authorization` header as follows:
1. Obtain the resource owner's ChronoTrack Live account credentials. For example, you could provide a dialog through your application asking for their ChronoTrack Live account username and password requesting their permission to use those credentials for accessing their account data on their behalf.
2. Concatenate their username and password with a colon (:). For example, if their username is "zooba" and their password is "tooba", you would construct the string `zooba:tooba`.
3. Now base64 encode that concatenated string to produce: `em9vYmE6dG9vYmE=`.
4. Finally, add an authorization header to your request like this:
Authorization: Basic em9vYmE6dG9vYmE=
**WARNING!** This method can be less secure than using one of the OAuth2 methods, and as such we do not recommend you use it. However, we still provide support for this authentication method because some applications may find it simpler to use.
There are two risks in using this method. The first risk lies in sending a request over a non-TLS/SSL connection. While we automatically redirect all non-secure requests to our TLS/SSL port, an initial non-secure request risks exposing your user's ChronoTrack Live account credentials to anyone sophisticated enough to be sniffing network packets, allowing man-in-the-middle attacks. This risk can be completely mitigated by sending all requests over TLS/SSL.
A second risk is that your application has to ask for and store the user's ChronoTrack Live credentials. You then become responsible for safeguarding their password and making sure it is not exposed via an insecure storage medium, which adds complexity to your application security aparatus. The only way to mitigate this risk is to not use this method and instead use one of the OAuth2 methods.
#### Test Auth Scheme
In certain situations, like when using our docs here to test the API, security requirements are minimal and using oauth or basic authentication is infeasible. For these special cases we support a simple method of passing in the ChronoTrack Live resource owner credentials via parameters. For instance, if you have a test account for a resource owner called `test-user@myorg.com` with password `12345`, then you could make authenticated requests to our API on behalf of that test user:
GET /api/event?client_id=abcdefgh&user_id=test-user%40myorg.com&user_pass=12345
To prevent exposing the password, you should SHA1-encode the password:
GET /api/event?client_id=abcdefgh&user_id=test-user%40myorg.com&user_pass=8cb2237d0679ca88db6464eac60da96345513964
**WARNING!** We provide this authorization method for convenience and testing. We strongly recommend you do not use this in a production environment. If you do, at least make sure you only do so over TLS/SSL and SHA1-encode the password.
## Formats
The following table describes the formats used for the content of API requests and responses. The first column contains a short `format code` for each format, while the second column contains the standard Internet Assigned Numbers Authority MIME type for the format.
| Format Code | MIME Type | Description |
| ----------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| json | application/json | _Javascript Object Notation_. See <http://www.json.org/> for details. |
| xml | application/xml | _Extensible Markup Language_. See <http://www.w3.org/XML/> for details. |
| yaml | application/yaml | _Yet Another Markup Language_. See <http://www.yaml.org/> for details. |
| csv | text/csv | _Comma Separated Values_. A tabular plain-text format. Columns within each row are delimited by a comma character (","). Columns that contain literal commas are escaped by double quotes. Rows are delimited by a carriage-return/line-feed ("\\r\\n") sequence. See <http://tools.ietf.org/html/rfc4180> for more details. |
| tsv | text/tab-delimited-values | _Tab Separated Values_. A tabular plain-text format. Columns within each row are delimited by a tab character ("\\t"). Rows are delimited by a carriage-return/line-feed ("\\r\\n") sequence. See <http://www.iana.org/assignments/media-types/text/tab-separated-values> for more details. |
## Requests
API requests to our servers are just standard HTTP requests. As with all HTTP requests, a request to our API always contains a method which denotes the operation to be done, always has a URL (specifying the resource to operate on), usually has some parameters you send to qualify your request, and sometimes those parameters are specified in the URL itself, sometimes in the request headers and sometimes in the request body (formatted as JSON, XML, or CSV). We'll look at each of these request components in this section.
### Request Methods
| HTTP Method | Example Requests |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| GET | This method is used to retrieve either a list of resource entities or a single resource entity. GET /api/eventReturn a list of all events the user has access to GET /api/event/123Return a single event specified by its resource ID GET /api/event/123/entryReturn a list of all entries (registrants) in event 123 |
| PUT | This method is used to update a single existing resource entity. PUT /api/entry/123456Update an existing entry (body is a single entry entity) |
| POST | This method is used to create or update one or more resource entities. POST /api/event/123/entryCreate new and/or update existing entries in event 123 (body is one or more entry entities) |
| DELETE | This method is used to delete an existing resource entity. DELETE /api/entry/123456Delete an existing entry by specifying its resource ID |
### Request URL
The URL path specifies the resource or resource collection the request operates on.
/api/{resource-name}.{format}/{resource-id}/{sub-resource-name}?{url-params}
| URL Component | Required | Meaning |
| ----------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| /api/ | Yes | All API request URLs begin with this prefix. |
| resource-name | Yes | All API request URLs must contain the name of the resource type to operate on. |
| format | No | The format of the response that you prefer. May be one of format codes specified in the [formats section](#formats). |
| resource-id | No | The identifier for a specific resource. If you are interested in getting or setting the properties of a specific resource or getting a collection of sub-resources belonging to a specific resource, you must specify this URL component in your request to identify the resource. |
| sub-resource-name | No | For requests where you want the collection of resources that belong to a specific resource (like all the entries in a given event, for instance), you use this URL component to name the type of sub-resource you want. |
| url-params | No | Ampersand (&) separated list of request parameters, URL-encoded as per [RFC 1738](http://www.ietf.org/rfc/rfc1738.txt). |
### Request Parameters
Most API requests will have one or more parameters that define some context for the request. For instance, all requests must specify the `client_id` parameter, which identifies the application the request originates from. The request parameters may be specified in several different places, including the URL path, the URL query, the request header and the request body. We'll consider each in turn.
#### URL Path Parameters
Two optional, but significant, parameters are commonly specified in the path of a URL request. First, the `resource_id` of the resource being operated on. Second, the desired `format` of the response. The following example illustrates the use of both of the parameters to request a particular event's properties in xml format:
GET /api/event.xml/123
#### URL Query Parameters
The query portion of a URL is the list of parameters that comes after the question mark following the URL path. If more than one parameter is specified, it must be separated from the others by an ampersand (&). The following URL requests the details for event 123 in xml format for the client application with id `abcdef`:
GET /api/event.xml/123?client_id=abcdef
Instead of specifying the format of the request in the URL path, you could instead specify it as a query parameter:
GET /api/event/123?client_id=abcdef&format=xml
#### Header Parameters
Request headers can also be used to specify context for a request. The following table provides a list of headers you can use with API requests.
| Header | Meaning |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Accept | This is an alternative to specifying the desired format of the response in the URL path or query. The value should be set to one of the MIME types specified in the [formats section](#formats). Defaults to application/json. |
| Accept-Encoding | If you have the capacity to uncompress gzipped or deflated content, set this header to either "gzip" or "default" to trigger automatic compression of the response content to save network bandwidth. |
| Accept-Language | The languages you'd prefer the response to be sent back using. See [RFC 1766](http://www.ietf.org/rfc/rfc1766.txt) for more details. |
| Authorization | Authorization for the request. The value here depends on what kind of authorization method you are using. See the [security section](#security) for details on the setting. |
| X-Http-Method-Override | Specify this header to override the value of the HTTP method for a request. This should only be used if your application wants to submit a PUT or DELETE request but your HTTP library only supports GET and POST. You can tell your library to use the POST method (instead of PUT) or the GET method (instead of DELETE) and set then this header to PUT or DELETE. |
| Content-Type | The MIME type of the content body of your request. Must be set to one of the MIME types listed in the [formats section](#formats). Defaults to application/json. |
| Content-Length | The size (in bytes, not characters) of the request body. Usually this will be set by your HTTP library and does not need to be set manually. |
| Date | The [RFC 822](http://www.ietf.org/rfc/rfc0822.txt) date-time on the client when the request is sent. This should be set by your HTTP library. |
#### Body Parameters
| Method | Expected Content |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| GET | No content sent with request. |
| POST | One or more properly encoded entities containing the fields of each new resource to create or existing resource to update. In the case of updating resources, you must provide either the resource id or some other defined identifier to allow our server to find the existing resource. |
| PUT | A properly encoded (possibly partial) entity containing the data items of the specified resource to be updated. You only need to provide those data items that require updating. The id of the resource being updated is specified as part of the URL path of the request. |
| DELETE | No content sent with request. |
When sending a body in your request that is not formatted as json, remember to set the `Content-Type` header appropriately so our API server can decode it.
#### Common API Request Parameters
The following table enumerates some commonly used request parameters and where they are usually included in the request.
| Parameter | Placement | Comment |
| ---------------- | ----------- | ------- |
| format | Path, Query | |
| client\_id | Query | |
| page | Query | |
| last\_id | Query | |
| modified\_after | Query | |
| modified\_before | Query | |
| columns | Query | |
| user\_id | Query | |
| user\_pass | Query | |
## Rate Limiting
When 1500 concurrent connections, from the same IP address, are reached within a 60 second time frame, the user will be blacklisted for the remaining duration of that time frame and will receive the 503 over rate error.
## Responses
An API response from our servers has several salient characteristics worth drawing attention to. First, each response has a numeric HTTP code that indicates success or failure. Second, a response usually contains a body formatted as JSON, XML, or CSV that contains the data you requested. Third, for many requests that return multiple entities (like a list of all events your user has access to, for instance), there are headers in the response that contain useful information on the size of the total result set and what portion of that total result set the current body of the request contains. We'll elaborate on each of these in turn.
### Response Codes
| Code | Meaning |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Success | |
| 200 | Success With Content. The request succeeded and content was returned. The content of the response body depends on the type of request. A GET on a single resource will return the entity corresponding to that resource. A GET on a resource collection will return one or more entities. A POST will return a data structure for each requested entity indicating either that the resource was created or updated. The responses will correspond one for one with the order of entities in the request. |
| 202 | Accepted. The request has been accepted for processing, but the processing has not been completed. The response to the initial request provides a url that can be used to get further information about the status of processing. This allows our server to accept a request that might take some time to complete without requiring your connection persist until that process is done. |
| Client Errors | |
| 400 | Bad Request. The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications. Details of the error are provided in the body of the response. |
| 401 | Unauthenticated. The request requires authentication (of the ChronoTrack Live user) and either the credentials provided are not a match, or no credentials were provided. Details of the error are provided in the body of the response. |
| 403 | Forbidden. The server understood the request, but is refusing to fulfill it. Authorization will not help and the request SHOULD NOT be repeated. More details are in the body of the response. Usually this is caused by going over usage limits for a request type by an application. |
| 404 | Not Found. The server could not find any resources requested by the client. |
| Server Errors | |
| 500 | Internal Server Error. The server encountered an unexpected condition which prevented it from fulfilling the request. |
| 501 | Not Implemented. The server does not support the functionality required to fulfill the request. |
| 503 | Server Unavailable. The server is currently unable to handle the request due to a temporary overloading or maintenance of the server. The implication is that this is a temporary condition which will be alleviated after some delay. If known, the length of the delay may be indicated in a Retry-After header. If no Retry-After is given, the client should handle the response as it would for a 500 response. |
| 503 over rate | This 'IP', 'IP range' or 'email address' has been blacklisted, please contact esupport@chronotrack.com or call (812) 759-7877 to work with our support staff to resolve this issue. |
### Response Bodies
For responses that return content (basically all responses except PUT and DELETE) the format of the response body can be set to one of several alternatives.
You may specify the response format you want in one of two ways. First, you can add an `Accept` header to your request with the appropriate MIME type for your preferred format. Second, you can add an extension to the resource name in the URL (for example, to request a list of all accessible events in XML format, you could use `GET /api/event.xml`. If neither of these methods is used, the response format defaults to JSON. The proper extension is just the lower-case form of the name in the table above.
### Response Headers
| Header | Meaning |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Standard HTTP Headers | |
| Content-Type | The MIME type of the response body. |
| Content-Length | The size (in bytes, not characters) of the response body. |
| Content-Encoding | If the request indicates it can accept compression via the Accept-Encoding header, our API server will compress the response body to save network bandwidth. In this case, the Content-Encoding response header contains the compression method used, either "gzip" (see [RFC 1952](http://www.ietf.org/rfc/rfc1952.txt)) or "deflate" (see [RFC 1951](http://www.ietf.org/rfc/rfc1951.txt)) depending on which algorithm was requested. |
| Transfer-Encoding | If set, it will be set to "chunked". The chunked encoding modifies the response body in order to transfer it as a series of chunks, each with its own size indicator, followed by an optional trailer containing entity-header fields. This allows dynamically produced content to be transferred along with the information necessary for the recipient to verify that it has received the full message. |
| Content-Language | The language used in the response represented as in [RFC 1766](http://www.ietf.org/rfc/rfc1766.txt) using ISO-639 language abbreviations and, optionally, ISO-3166 country codes to construct the language-tag. |
| Date | The date and time on the server when the response was sent. This is in [RFC 822](http://www.w3.org/Protocols/rfc822/#z28) date-time format, for example: "Tue, 04 Dec 2012 05:44:04 GMT." |
| WWW-Authenticate | The WWW-Authenticate response header field is included only in 401 (Unauthorized) response messages. The field value consists of at least one challenge that indicates the authentication scheme and parameters applicable to the request URI. For more details see [RFC 2617](http://tools.ietf.org/html/rfc2617). |
| Link | For resource collection requests, provides a list of links to first, last, previous and next pages for use in pagination interfaces in the client application. This various relation types (first, prev, next, last) are defined in [RFC 5988](http://tools.ietf.org/html/rfc5988). |
| Custom ChronoTrack Live Headers | |
| X-Ctlive-Api-Version | Provides the current version number of the API. |
| X-Ctlive-Row-Count | For resource collection requests, provides the total number of entities in the collection (of which only a portion may be returned in the current response). |
| X-Ctlive-Current-Rows | For resource collection requests, provides the current range of rows contained in the response body in the format min-max (so on page 1 of a response with a page size of 10, this header's value would be "1-10", page 2 would be "11-20", and so on). |
| X-Ctlive-Page-Count | For resource collection requests, provides the total number of pages, given the current page size. |
| X-Ctlive-Page-Size | For resource collection requests, provides the number of entities returned in a single "page" or results. |
| X-Ctlive-Current-Page | For resource collection requests, provides the current page of results within the total number of pages contained in the collection, given the page size setting. |
## Character Encoding
We use **UTF-8 encoding** through and through to guarantee proper encoding of all international character sets. All data you send to an API endpoint is assumed to be, and therefore must be, encoded in UTF-8\. All data transmitted from an API endpoint is encoded in UTF-8.
## Data Types
The API uses a pretty standard collection of types for all data that it processes, as described below.
| Type | Description |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| resource\_id | A non-semantic (numeric) identifier associated with a particular resource. This identifier is guaranteed to be unique within the collection of all resources of that resource type. Resource identifiers are assigned by ChronoTrack Live and will not change over the life cycle of a resource. Resources may also have one or more semantically significant identifiers assigned by event and timer related organizations. |
| string | A UTF-8 encoded character string without any line breaks, and rarely longer than 250 characters. |
| text | A UTF-8 encoded character string that may contain line breaks and is likely to be longer than 250 characters. |
| boolean | May take either the value 0, meaning false, or 1, meaning true. |
| integer | An integral number (a number without any fractional part). |
| float | A floating point number (a number that may have a fractional part). |
| enum | Several data items in the API may take on a value drawn from a small enumeration of possible strings. All enumerated values are represented as strings. |
| date | Dates (without a time) are represented in ISO-8601 standard format, namely YYYY-MM-DD. |
| epoch\_time | Datetimes are represented as unix epoch times, namely the number of seconds since midnight January 1, 1970 UTC. Information on how to convert unix epoch times to other date/time formats can be found at <http://epochconverter.com>. |
## Resources (Live Testing Sandbox)
This section describes the the list of available resource types as well as the operations available for each. Click on the resource names below to expand the documentation for each. Each operation can be tested live right here on this web page to get a better sense of how the API works.
For each API operation below, you must provide a valid application `client_id` as well as administrative credentials from a valid ChronoTrack Live account that has access to at least one event. If you sign-in with your developer credentials and revisit this page, these values will be filled in automatically for you. Otherwise, common test credentials will be used that have limited access to a few test events.
Loading resources...
* ©2009-2026 ChronoTrack. All rights reserved.
* License

File diff suppressed because one or more lines are too long

49
dossard_print.php Normal file
View File

@ -0,0 +1,49 @@
<?php
/**
* MSIN-4485 — Endpoint PDF dossard (1 participant).
* Isolé du tunnel / panier ; droit inscriptions_gestion.dossard_print.
*/
session_start();
$_SERVER['PHP_SELF'] = '/compte.php';
$_SERVER['SCRIPT_NAME'] = '/compte.php';
require_once __DIR__ . '/php/inc_fonctions.php';
require_once __DIR__ . '/php/inc_fx_dossard_print.php';
$strLangue = $_SESSION['lang'] ?? 'fr';
$reqLang = $_REQUEST['lang'] ?? $_REQUEST['lng'] ?? '';
if (!empty($reqLang) && in_array($reqLang, array('fr', 'en'), true)) {
$strLangue = $reqLang;
}
$vPage = 'compte.php';
$vtexte_page = obtenirTextepage($vPage, $strLangue, 2);
$intParId = intval($_GET['par_id'] ?? $_POST['par_id'] ?? 0);
$intEveId = intval($_GET['eve_id'] ?? $_POST['eve_id'] ?? 0);
$intComId = intval($_SESSION['com_id'] ?? 0);
if ($intComId <= 0 || $intParId <= 0 || $intEveId <= 0) {
http_response_code(403);
header('Content-Type: text/plain; charset=utf-8');
echo 'Accès refusé.';
exit;
}
if (!fxDossardPrintCan($intComId, $intEveId)) {
http_response_code(403);
header('Content-Type: text/plain; charset=utf-8');
echo 'Droit insuffisant.';
exit;
}
$arrRow = fxDossardPrintLoadParticipant($intParId, $intEveId);
if ($arrRow === null) {
http_response_code(404);
header('Content-Type: text/plain; charset=utf-8');
echo 'Participant introuvable.';
exit;
}
fxDossardPrintOutputPdf($arrRow);

21
fpdi2/src/FpdfTpl.php Normal file
View File

@ -0,0 +1,21 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi;
/**
* Class FpdfTpl
*
* This class adds a templating feature to FPDF.
*/
class FpdfTpl extends \FPDF
{
use FpdfTplTrait;
}

473
fpdi2/src/FpdfTplTrait.php Normal file
View File

@ -0,0 +1,473 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi;
/**
* Trait FpdfTplTrait
*
* This trait adds a templating feature to FPDF and tFPDF.
*/
trait FpdfTplTrait
{
/**
* Data of all created templates.
*
* @var array
*/
protected $templates = [];
/**
* The template id for the currently created template.
*
* @var null|int
*/
protected $currentTemplateId;
/**
* A counter for template ids.
*
* @var int
*/
protected $templateId = 0;
/**
* Set the page format of the current page.
*
* @param array $size An array with two values defining the size.
* @param string $orientation "L" for landscape, "P" for portrait.
* @throws \BadMethodCallException
*/
public function setPageFormat($size, $orientation)
{
if ($this->currentTemplateId !== null) {
throw new \BadMethodCallException('The page format cannot be changed when writing to a template.');
}
if (!\in_array($orientation, ['P', 'L'], true)) {
throw new \InvalidArgumentException(\sprintf(
'Invalid page orientation "%s"! Only "P" and "L" are allowed!',
$orientation
));
}
$size = $this->_getpagesize($size);
if (
$orientation != $this->CurOrientation
|| $size[0] != $this->CurPageSize[0]
|| $size[1] != $this->CurPageSize[1]
) {
// New size or orientation
if ($orientation === 'P') {
$this->w = $size[0];
$this->h = $size[1];
} else {
$this->w = $size[1];
$this->h = $size[0];
}
$this->wPt = $this->w * $this->k;
$this->hPt = $this->h * $this->k;
$this->PageBreakTrigger = $this->h - $this->bMargin;
$this->CurOrientation = $orientation;
$this->CurPageSize = $size;
$this->PageInfo[$this->page]['size'] = array($this->wPt, $this->hPt);
}
}
/**
* Draws a template onto the page or another template.
*
* Give only one of the size parameters (width, height) to calculate the other one automatically in view to the
* aspect ratio.
*
* @param mixed $tpl The template id
* @param array|float|int $x The abscissa of upper-left corner. Alternatively you could use an assoc array
* with the keys "x", "y", "width", "height", "adjustPageSize".
* @param float|int $y The ordinate of upper-left corner.
* @param float|int|null $width The width.
* @param float|int|null $height The height.
* @param bool $adjustPageSize
* @return array The size
* @see FpdfTplTrait::getTemplateSize()
*/
public function useTemplate($tpl, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false)
{
if (!isset($this->templates[$tpl])) {
throw new \InvalidArgumentException('Template does not exist!');
}
if (\is_array($x)) {
unset($x['tpl']);
\extract($x, EXTR_IF_EXISTS);
/** @noinspection NotOptimalIfConditionsInspection */
/** @phpstan-ignore function.alreadyNarrowedType */
if (\is_array($x)) {
$x = 0;
}
}
$template = $this->templates[$tpl];
$originalSize = $this->getTemplateSize($tpl);
$newSize = $this->getTemplateSize($tpl, $width, $height);
if ($adjustPageSize) {
$this->setPageFormat($newSize, $newSize['orientation']);
}
$this->_out(
// reset standard values, translate and scale
\sprintf(
'q 0 J 1 w 0 j 0 G 0 g %.4F 0 0 %.4F %.4F %.4F cm /%s Do Q',
($newSize['width'] / $originalSize['width']),
($newSize['height'] / $originalSize['height']),
$x * $this->k,
($this->h - $y - $newSize['height']) * $this->k,
$template['id']
)
);
return $newSize;
}
/**
* Get the size of a template.
*
* Give only one of the size parameters (width, height) to calculate the other one automatically in view to the
* aspect ratio.
*
* @param mixed $tpl The template id
* @param float|int|null $width The width.
* @param float|int|null $height The height.
* @return array|bool An array with following keys: width, height, 0 (=width), 1 (=height), orientation (L or P)
*/
public function getTemplateSize($tpl, $width = null, $height = null)
{
if (!isset($this->templates[$tpl])) {
return false;
}
if ($width === null && $height === null) {
$width = $this->templates[$tpl]['width'];
$height = $this->templates[$tpl]['height'];
} elseif ($width === null) {
$width = $height * $this->templates[$tpl]['width'] / $this->templates[$tpl]['height'];
}
if ($height === null) {
$height = $width * $this->templates[$tpl]['height'] / $this->templates[$tpl]['width'];
}
if ($height <= 0. || $width <= 0.) {
throw new \InvalidArgumentException('Width or height parameter needs to be larger than zero.');
}
return [
'width' => $width,
'height' => $height,
0 => $width,
1 => $height,
'orientation' => $width > $height ? 'L' : 'P'
];
}
/**
* Begins a new template.
*
* @param float|int|null $width The width of the template. If null, the current page width is used.
* @param float|int|null $height The height of the template. If null, the current page height is used.
* @param bool $groupXObject Define the form XObject as a group XObject to support transparency (if used).
* @return int A template identifier.
*/
public function beginTemplate($width = null, $height = null, $groupXObject = false)
{
if ($width === null) {
$width = $this->w;
}
if ($height === null) {
$height = $this->h;
}
$templateId = $this->getNextTemplateId();
// initiate buffer with current state of FPDF
$buffer = "2 J\n"
. \sprintf('%.2F w', $this->LineWidth * $this->k) . "\n";
if ($this->FontFamily) {
$buffer .= \sprintf("BT /F%d %.2F Tf ET\n", $this->CurrentFont['i'], $this->FontSizePt);
}
if ($this->DrawColor !== '0 G') {
$buffer .= $this->DrawColor . "\n";
}
if ($this->FillColor !== '0 g') {
$buffer .= $this->FillColor . "\n";
}
if ($groupXObject && \version_compare('1.4', $this->PDFVersion, '>')) {
$this->PDFVersion = '1.4';
}
$this->templates[$templateId] = [
'objectNumber' => null,
'id' => 'TPL' . $templateId,
'buffer' => $buffer,
'width' => $width,
'height' => $height,
'groupXObject' => $groupXObject,
'state' => [
'x' => $this->x,
'y' => $this->y,
'AutoPageBreak' => $this->AutoPageBreak,
'bMargin' => $this->bMargin,
'tMargin' => $this->tMargin,
'lMargin' => $this->lMargin,
'rMargin' => $this->rMargin,
'h' => $this->h,
'hPt' => $this->hPt,
'w' => $this->w,
'wPt' => $this->wPt,
'FontFamily' => $this->FontFamily,
'FontStyle' => $this->FontStyle,
'FontSizePt' => $this->FontSizePt,
'FontSize' => $this->FontSize,
'underline' => $this->underline,
'TextColor' => $this->TextColor,
'DrawColor' => $this->DrawColor,
'FillColor' => $this->FillColor,
'ColorFlag' => $this->ColorFlag
]
];
$this->SetAutoPageBreak(false);
$this->currentTemplateId = $templateId;
$this->h = $height;
$this->hPt = $height * $this->k;
$this->w = $width;
$this->wPt = $width * $this->k;
$this->SetXY($this->lMargin, $this->tMargin);
$this->SetRightMargin($this->w - $width + $this->rMargin);
return $templateId;
}
/**
* Ends a template.
*
* @return bool|int|null A template identifier.
*/
public function endTemplate()
{
if ($this->currentTemplateId === null) {
return false;
}
$templateId = $this->currentTemplateId;
$template = $this->templates[$templateId];
$state = $template['state'];
$this->SetXY($state['x'], $state['y']);
$this->tMargin = $state['tMargin'];
$this->lMargin = $state['lMargin'];
$this->rMargin = $state['rMargin'];
$this->h = $state['h'];
$this->hPt = $state['hPt'];
$this->w = $state['w'];
$this->wPt = $state['wPt'];
$this->SetAutoPageBreak($state['AutoPageBreak'], $state['bMargin']);
$this->FontFamily = $state['FontFamily'];
$this->FontStyle = $state['FontStyle'];
$this->FontSizePt = $state['FontSizePt'];
$this->FontSize = $state['FontSize'];
$this->TextColor = $state['TextColor'];
$this->DrawColor = $state['DrawColor'];
$this->FillColor = $state['FillColor'];
$this->ColorFlag = $state['ColorFlag'];
$this->underline = $state['underline'];
$fontKey = $this->FontFamily . $this->FontStyle;
if ($fontKey) {
$this->CurrentFont = $this->fonts[$fontKey];
} else {
unset($this->CurrentFont);
}
$this->currentTemplateId = null;
return $templateId;
}
/**
* Get the next template id.
*
* @return int
*/
protected function getNextTemplateId()
{
return $this->templateId++;
}
/* overwritten FPDF methods: */
/**
* @inheritdoc
*/
public function AddPage($orientation = '', $size = '', $rotation = 0)
{
if ($this->currentTemplateId !== null) {
throw new \BadMethodCallException('Pages cannot be added when writing to a template.');
}
parent::AddPage($orientation, $size, $rotation);
}
/**
* @inheritdoc
*/
public function Link($x, $y, $w, $h, $link)
{
if ($this->currentTemplateId !== null) {
throw new \BadMethodCallException('Links cannot be set when writing to a template.');
}
parent::Link($x, $y, $w, $h, $link);
}
/**
* @inheritdoc
*/
public function SetLink($link, $y = 0, $page = -1)
{
if ($this->currentTemplateId !== null) {
throw new \BadMethodCallException('Links cannot be set when writing to a template.');
}
return parent::SetLink($link, $y, $page);
}
/**
* @inheritdoc
*/
public function SetDrawColor($r, $g = null, $b = null)
{
parent::SetDrawColor($r, $g, $b);
if ($this->page === 0 && $this->currentTemplateId !== null) {
$this->_out($this->DrawColor);
}
}
/**
* @inheritdoc
*/
public function SetFillColor($r, $g = null, $b = null)
{
parent::SetFillColor($r, $g, $b);
if ($this->page === 0 && $this->currentTemplateId !== null) {
$this->_out($this->FillColor);
}
}
/**
* @inheritdoc
*/
public function SetLineWidth($width)
{
parent::SetLineWidth($width);
if ($this->page === 0 && $this->currentTemplateId !== null) {
$this->_out(\sprintf('%.2F w', $width * $this->k));
}
}
/**
* @inheritdoc
*/
public function SetFont($family, $style = '', $size = 0)
{
parent::SetFont($family, $style, $size);
if ($this->page === 0 && $this->currentTemplateId !== null) {
$this->_out(\sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
}
}
/**
* @inheritdoc
*/
public function SetFontSize($size)
{
parent::SetFontSize($size);
if ($this->page === 0 && $this->currentTemplateId !== null) {
$this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
}
}
protected function _putimages()
{
parent::_putimages();
foreach ($this->templates as $key => $template) {
$this->_newobj();
$this->templates[$key]['objectNumber'] = $this->n;
$this->_put('<</Type /XObject /Subtype /Form /FormType 1');
$this->_put(\sprintf(
'/BBox[0 0 %.2F %.2F]',
$template['width'] * $this->k,
$template['height'] * $this->k
));
$this->_put('/Resources 2 0 R'); // default resources dictionary of FPDF
if ($this->compress) {
$buffer = \gzcompress($template['buffer']);
$this->_put('/Filter/FlateDecode');
} else {
$buffer = $template['buffer'];
}
$this->_put('/Length ' . \strlen($buffer));
if ($template['groupXObject']) {
$this->_put('/Group <</Type/Group/S/Transparency>>');
}
$this->_put('>>');
$this->_putstream($buffer);
$this->_put('endobj');
}
}
/**
* @inheritdoc
*/
protected function _putxobjectdict()
{
foreach ($this->templates as $key => $template) {
$this->_put('/' . $template['id'] . ' ' . $template['objectNumber'] . ' 0 R');
}
parent::_putxobjectdict();
}
/**
* @inheritdoc
*/
public function _out($s)
{
if ($this->currentTemplateId !== null) {
$this->templates[$this->currentTemplateId]['buffer'] .= $s . "\n";
} else {
parent::_out($s);
}
}
}

193
fpdi2/src/FpdfTrait.php Normal file
View File

@ -0,0 +1,193 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi;
use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException;
use setasign\Fpdi\PdfParser\PdfParserException;
use setasign\Fpdi\PdfParser\Type\PdfIndirectObject;
use setasign\Fpdi\PdfParser\Type\PdfNull;
use setasign\Fpdi\PdfParser\Type\PdfType;
/**
* This trait is used for the implementation of FPDI in FPDF and tFPDF.
*/
trait FpdfTrait
{
protected function _enddoc()
{
parent::_enddoc();
$this->cleanUp();
}
/**
* Draws an imported page or a template onto the page or another template.
*
* Give only one of the size parameters (width, height) to calculate the other one automatically in view to the
* aspect ratio.
*
* @param mixed $tpl The template id
* @param float|int|array $x The abscissa of upper-left corner. Alternatively you could use an assoc array
* with the keys "x", "y", "width", "height", "adjustPageSize".
* @param float|int $y The ordinate of upper-left corner.
* @param float|int|null $width The width.
* @param float|int|null $height The height.
* @param bool $adjustPageSize
* @return array The size
* @see Fpdi::getTemplateSize()
*/
public function useTemplate($tpl, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false)
{
if (isset($this->importedPages[$tpl])) {
$size = $this->useImportedPage($tpl, $x, $y, $width, $height, $adjustPageSize);
if ($this->currentTemplateId !== null) {
$this->templates[$this->currentTemplateId]['resources']['templates']['importedPages'][$tpl] = $tpl;
}
return $size;
}
return parent::useTemplate($tpl, $x, $y, $width, $height, $adjustPageSize);
}
/**
* Get the size of an imported page or template.
*
* Give only one of the size parameters (width, height) to calculate the other one automatically in view to the
* aspect ratio.
*
* @param mixed $tpl The template id
* @param float|int|null $width The width.
* @param float|int|null $height The height.
* @return array|bool An array with following keys: width, height, 0 (=width), 1 (=height), orientation (L or P)
*/
public function getTemplateSize($tpl, $width = null, $height = null)
{
$size = parent::getTemplateSize($tpl, $width, $height);
if ($size === false) {
return $this->getImportedPageSize($tpl, $width, $height);
}
return $size;
}
/**
* @throws CrossReferenceException
* @throws PdfParserException
*/
protected function _putimages()
{
$this->currentReaderId = null;
parent::_putimages();
foreach ($this->importedPages as $key => $pageData) {
$this->_newobj();
$this->importedPages[$key]['objectNumber'] = $this->n;
$this->currentReaderId = $pageData['readerId'];
$this->writePdfType($pageData['stream']);
$this->_put('endobj');
}
foreach (\array_keys($this->readers) as $readerId) {
$parser = $this->getPdfReader($readerId)->getParser();
$this->currentReaderId = $readerId;
while (($objectNumber = \array_pop($this->objectsToCopy[$readerId])) !== null) {
try {
$object = $parser->getIndirectObject($objectNumber);
} catch (CrossReferenceException $e) {
if ($e->getCode() === CrossReferenceException::OBJECT_NOT_FOUND) {
$object = PdfIndirectObject::create($objectNumber, 0, new PdfNull());
} else {
throw $e;
}
}
$this->writePdfType($object);
}
}
$this->currentReaderId = null;
}
/**
* @inheritdoc
*/
protected function _putxobjectdict()
{
foreach ($this->importedPages as $pageData) {
$this->_put('/' . $pageData['id'] . ' ' . $pageData['objectNumber'] . ' 0 R');
}
parent::_putxobjectdict();
}
/**
* @param int $n
* @return void
* @throws PdfParser\Type\PdfTypeException
*/
protected function _putlinks($n)
{
foreach ($this->PageLinks[$n] as $pl) {
$this->_newobj();
$rect = \sprintf('%.2F %.2F %.2F %.2F', $pl[0], $pl[1], $pl[0] + $pl[2], $pl[1] - $pl[3]);
$this->_put('<</Type /Annot /Subtype /Link /Rect [' . $rect . ']', false);
if (is_string($pl[4])) {
if (isset($pl['importedLink'])) {
$this->_put('/A <</S /URI /URI (' . $this->_escape($pl[4]) . ')>>');
$values = $pl['importedLink']['pdfObject']->value;
foreach ($values as $name => $entry) {
$this->_put('/' . $name . ' ', false);
$this->writePdfType($entry);
}
if (isset($pl['quadPoints'])) {
$s = '/QuadPoints[';
foreach ($pl['quadPoints'] as $value) {
$s .= \sprintf('%.2F ', $value);
}
$s .= ']';
$this->_put($s);
}
} else {
$this->_put('/A <</S /URI /URI ' . $this->_textstring($pl[4]) . '>>');
$this->_put('/Border [0 0 0]', false);
}
$this->_put('>>');
} else {
$this->_put('/Border [0 0 0] ', false);
$l = $this->links[$pl[4]];
if (isset($this->PageInfo[$l[0]]['size'])) {
$h = $this->PageInfo[$l[0]]['size'][1];
} else {
$h = ($this->DefOrientation === 'P')
? $this->DefPageSize[1] * $this->k
: $this->DefPageSize[0] * $this->k;
}
$this->_put(\sprintf(
'/Dest [%d 0 R /XYZ 0 %.2F null]>>',
$this->PageInfo[$l[0]]['n'],
$h - $l[1] * $this->k
));
}
$this->_put('endobj');
}
}
protected function _put($s, $newLine = true)
{
if ($newLine) {
$this->buffer .= $s . "\n";
} else {
$this->buffer .= $s;
}
}
}

34
fpdi2/src/Fpdi.php Normal file
View File

@ -0,0 +1,34 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi;
use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException;
use setasign\Fpdi\PdfParser\PdfParserException;
use setasign\Fpdi\PdfParser\Type\PdfIndirectObject;
use setasign\Fpdi\PdfParser\Type\PdfNull;
/**
* Class Fpdi
*
* This class let you import pages of existing PDF documents into a reusable structure for FPDF.
*/
class Fpdi extends FpdfTpl
{
use FpdiTrait;
use FpdfTrait;
/**
* FPDI version
*
* @string
*/
const VERSION = '2.6.8';
}

View File

@ -0,0 +1,18 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi;
/**
* Base exception class for the FPDI package.
*/
class FpdiException extends \Exception
{
}

665
fpdi2/src/FpdiTrait.php Normal file
View File

@ -0,0 +1,665 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi;
use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException;
use setasign\Fpdi\PdfParser\Filter\FilterException;
use setasign\Fpdi\PdfParser\PdfParser;
use setasign\Fpdi\PdfParser\PdfParserException;
use setasign\Fpdi\PdfParser\StreamReader;
use setasign\Fpdi\PdfParser\Type\PdfArray;
use setasign\Fpdi\PdfParser\Type\PdfBoolean;
use setasign\Fpdi\PdfParser\Type\PdfDictionary;
use setasign\Fpdi\PdfParser\Type\PdfHexString;
use setasign\Fpdi\PdfParser\Type\PdfIndirectObject;
use setasign\Fpdi\PdfParser\Type\PdfIndirectObjectReference;
use setasign\Fpdi\PdfParser\Type\PdfName;
use setasign\Fpdi\PdfParser\Type\PdfNull;
use setasign\Fpdi\PdfParser\Type\PdfNumeric;
use setasign\Fpdi\PdfParser\Type\PdfStream;
use setasign\Fpdi\PdfParser\Type\PdfString;
use setasign\Fpdi\PdfParser\Type\PdfToken;
use setasign\Fpdi\PdfParser\Type\PdfType;
use setasign\Fpdi\PdfParser\Type\PdfTypeException;
use setasign\Fpdi\PdfReader\DataStructure\Rectangle;
use setasign\Fpdi\PdfReader\PageBoundaries;
use setasign\Fpdi\PdfReader\PdfReader;
use setasign\Fpdi\PdfReader\PdfReaderException;
use /* This namespace/class is used by the commercial FPDI PDF-Parser add-on. */
/** @noinspection PhpUndefinedClassInspection */
/** @noinspection PhpUndefinedNamespaceInspection */
setasign\FpdiPdfParser\PdfParser\PdfParser as FpdiPdfParser;
/**
* The FpdiTrait
*
* This trait offers the core functionalities of FPDI. By passing them to a trait we can reuse it with e.g. TCPDF in a
* very easy way.
*/
trait FpdiTrait
{
/**
* The pdf reader instances.
*
* @var PdfReader[]
*/
protected $readers = [];
/**
* Instances created internally.
*
* @var array
*/
protected $createdReaders = [];
/**
* The current reader id.
*
* @var string|null
*/
protected $currentReaderId;
/**
* Data of all imported pages.
*
* @var array
*/
protected $importedPages = [];
/**
* A map from object numbers of imported objects to new assigned object numbers by FPDF.
*
* @var array
*/
protected $objectMap = [];
/**
* An array with information about objects, which needs to be copied to the resulting document.
*
* @var array
*/
protected $objectsToCopy = [];
/**
* Release resources and file handles.
*
* This method is called internally when the document is created successfully. By default it only cleans up
* stream reader instances which were created internally.
*
* @param bool $allReaders
*/
public function cleanUp($allReaders = false)
{
$readers = $allReaders ? array_keys($this->readers) : $this->createdReaders;
foreach ($readers as $id) {
$this->readers[$id]->getParser()->getStreamReader()->cleanUp();
unset($this->readers[$id]);
}
$this->createdReaders = [];
}
/**
* Set the minimal PDF version.
*
* @param string $pdfVersion
*/
protected function setMinPdfVersion($pdfVersion)
{
if (\version_compare($pdfVersion, $this->PDFVersion, '>')) {
$this->PDFVersion = $pdfVersion;
}
}
/** @noinspection PhpUndefinedClassInspection */
/**
* Get a new pdf parser instance.
*
* @param StreamReader $streamReader
* @param array $parserParams Individual parameters passed to the parser instance.
* @return PdfParser|FpdiPdfParser
*/
protected function getPdfParserInstance(
StreamReader $streamReader,
#[\SensitiveParameter]
array $parserParams = []
) {
// note: if you get an exception here - turn off errors/warnings on not found classes for your autoloader.
// psr-4 (https://www.php-fig.org/psr/psr-4/) says: Autoloader implementations MUST NOT throw
// exceptions, MUST NOT raise errors of any level, and SHOULD NOT return a value.
/** @noinspection PhpUndefinedClassInspection */
if (\class_exists(FpdiPdfParser::class)) {
/** @noinspection PhpUndefinedClassInspection */
return new FpdiPdfParser($streamReader, $parserParams);
}
return new PdfParser($streamReader);
}
/**
* Get an unique reader id by the $file parameter.
*
* @param string|resource|PdfReader|StreamReader $file An open file descriptor, a path to a file, a PdfReader
* instance or a StreamReader instance.
* @param array $parserParams Individual parameters passed to the parser instance.
* @return string
*/
protected function getPdfReaderId(
$file,
#[\SensitiveParameter]
array $parserParams = []
) {
if (\is_resource($file)) {
$id = (string) $file;
} elseif (\is_string($file)) {
$id = \realpath($file);
if ($id === false) {
$id = $file;
}
} elseif (\is_object($file)) {
$id = \spl_object_hash($file);
} else {
throw new \InvalidArgumentException(
\sprintf('Invalid type in $file parameter (%s)', \gettype($file))
);
}
$id = \md5($id . '|' . \print_r($parserParams, true));
if (isset($this->readers[$id])) {
return $id;
}
if (\is_resource($file)) {
$streamReader = new StreamReader($file);
} elseif (\is_string($file)) {
$streamReader = StreamReader::createByFile($file);
$this->createdReaders[] = $id;
} else {
$streamReader = $file;
}
$reader = new PdfReader($this->getPdfParserInstance($streamReader, $parserParams));
$this->readers[$id] = $reader;
return $id;
}
/**
* Get a pdf reader instance by its id.
*
* @param string $id
* @return PdfReader
*/
protected function getPdfReader($id)
{
if (isset($this->readers[$id])) {
return $this->readers[$id];
}
throw new \InvalidArgumentException(
\sprintf('No pdf reader with the given id (%s) exists.', $id)
);
}
/**
* Set the source PDF file.
*
* @param string|resource|StreamReader $file Path to the file or a stream resource or a StreamReader instance.
* @return int The page count of the PDF document.
* @throws PdfParserException
*/
public function setSourceFile($file)
{
return $this->setSourceFileWithParserParams($file);
}
/**
* Set the source PDF file with parameters which are passed to the parser instance.
*
* This method allows us to pass e.g. authentication information to the parser instance.
*
* @param string|resource|StreamReader $file Path to the file or a stream resource or a StreamReader instance.
* @param array $parserParams Individual parameters passed to the parser instance.
* @return int The page count of the PDF document.
* @throws CrossReferenceException
* @throws PdfParserException
* @throws PdfTypeException
*/
public function setSourceFileWithParserParams(
$file,
#[\SensitiveParameter]
array $parserParams = []
) {
$this->currentReaderId = $this->getPdfReaderId($file, $parserParams);
$this->objectsToCopy[$this->currentReaderId] = [];
$reader = $this->getPdfReader($this->currentReaderId);
$this->setMinPdfVersion($reader->getPdfVersion());
return $reader->getPageCount();
}
/**
* Imports a page.
*
* @param int $pageNumber The page number.
* @param string $box The page boundary to import. Default set to PageBoundaries::CROP_BOX.
* @param bool $groupXObject Define the form XObject as a group XObject to support transparency (if used).
* @param bool $importExternalLinks Define whether external links are imported or not.
* @return string A unique string identifying the imported page.
* @throws CrossReferenceException
* @throws FilterException
* @throws PdfParserException
* @throws PdfTypeException
* @throws PdfReaderException
* @see PageBoundaries
*/
public function importPage(
$pageNumber,
$box = PageBoundaries::CROP_BOX,
$groupXObject = true,
$importExternalLinks = false
) {
if ($this->currentReaderId === null) {
throw new \BadMethodCallException('No reader initiated. Call setSourceFile() first.');
}
$pageId = $this->currentReaderId;
$pageNumber = (int)$pageNumber;
$pageId .= '|' . $pageNumber . '|' . ($groupXObject ? '1' : '0') . '|' . ($importExternalLinks ? '1' : '0');
// for backwards compatibility with FPDI 1
$box = \ltrim($box, '/');
if (!PageBoundaries::isValidName($box)) {
throw new \InvalidArgumentException(
\sprintf('Box name is invalid: "%s"', $box)
);
}
$pageId .= '|' . $box;
if (isset($this->importedPages[$pageId])) {
return $pageId;
}
$reader = $this->getPdfReader($this->currentReaderId);
$page = $reader->getPage($pageNumber);
$bbox = $page->getBoundary($box);
if ($bbox === false) {
throw new PdfReaderException(
\sprintf("Page doesn't have a boundary box (%s).", $box),
PdfReaderException::MISSING_DATA
);
}
$dict = new PdfDictionary();
$dict->value['Type'] = PdfName::create('XObject');
$dict->value['Subtype'] = PdfName::create('Form');
$dict->value['FormType'] = PdfNumeric::create(1);
$dict->value['BBox'] = $bbox->toPdfArray();
if ($groupXObject) {
$this->setMinPdfVersion('1.4');
$dict->value['Group'] = PdfDictionary::create([
'Type' => PdfName::create('Group'),
'S' => PdfName::create('Transparency')
]);
}
$resources = $page->getAttribute('Resources');
if ($resources !== null) {
$dict->value['Resources'] = $resources;
}
[$width, $height] = $page->getWidthAndHeight($box);
$a = 1;
$b = 0;
$c = 0;
$d = 1;
$e = -$bbox->getLlx();
$f = -$bbox->getLly();
$rotation = $page->getRotation();
if ($rotation !== 0) {
$rotation *= -1;
$angle = $rotation * M_PI / 180;
$a = \cos($angle);
$b = \sin($angle);
$c = -$b;
$d = $a;
switch ($rotation) {
case -90:
$e = -$bbox->getLly();
$f = $bbox->getUrx();
break;
case -180:
$e = $bbox->getUrx();
$f = $bbox->getUry();
break;
case -270:
$e = $bbox->getUry();
$f = -$bbox->getLlx();
break;
}
}
// we need to rotate/translate
if ($a != 1 || $b != 0 || $c != 0 || $d != 1 || $e != 0 || $f != 0) {
$dict->value['Matrix'] = PdfArray::create([
PdfNumeric::create($a), PdfNumeric::create($b), PdfNumeric::create($c),
PdfNumeric::create($d), PdfNumeric::create($e), PdfNumeric::create($f)
]);
}
// try to use the existing content stream
$pageDict = $page->getPageDictionary();
try {
$contentsObject = PdfType::resolve(PdfDictionary::get($pageDict, 'Contents'), $reader->getParser(), true);
$contents = PdfType::resolve($contentsObject, $reader->getParser());
// just copy the stream reference if it is only a single stream
if (
($contentsIsStream = ($contents instanceof PdfStream))
|| ($contents instanceof PdfArray && \count($contents->value) === 1)
) {
if ($contentsIsStream) {
/**
* @var PdfIndirectObject $contentsObject
*/
$stream = $contents;
} else {
$stream = PdfType::resolve($contents->value[0], $reader->getParser());
}
$filter = PdfDictionary::get($stream->value, 'Filter');
if (!$filter instanceof PdfNull) {
$dict->value['Filter'] = $filter;
}
$length = PdfType::resolve(PdfDictionary::get($stream->value, 'Length'), $reader->getParser());
$dict->value['Length'] = $length;
$stream->value = $dict;
// otherwise extract it from the array and re-compress the whole stream
} else {
$streamContent = $this->compress
? \gzcompress($page->getContentStream())
: $page->getContentStream();
$dict->value['Length'] = PdfNumeric::create(\strlen($streamContent));
if ($this->compress) {
$dict->value['Filter'] = PdfName::create('FlateDecode');
}
$stream = PdfStream::create($dict, $streamContent);
}
// Catch faulty pages and use an empty content stream
} catch (FpdiException $e) {
$dict->value['Length'] = PdfNumeric::create(0);
$stream = PdfStream::create($dict, '');
}
$externalLinks = [];
if ($importExternalLinks) {
$externalLinks = $page->getExternalLinks($box);
}
$this->importedPages[$pageId] = [
'objectNumber' => null,
'readerId' => $this->currentReaderId,
'id' => 'TPL' . $this->getNextTemplateId(),
'width' => $width / $this->k,
'height' => $height / $this->k,
'stream' => $stream,
'externalLinks' => $externalLinks
];
return $pageId;
}
/**
* Draws an imported page onto the page.
*
* Give only one of the size parameters (width, height) to calculate the other one automatically in view to the
* aspect ratio.
*
* @param mixed $pageId The page id
* @param float|int|array $x The abscissa of upper-left corner. Alternatively you could use an assoc array
* with the keys "x", "y", "width", "height", "adjustPageSize".
* @param float|int $y The ordinate of upper-left corner.
* @param float|int|null $width The width.
* @param float|int|null $height The height.
* @param bool $adjustPageSize
* @return array The size.
* @see Fpdi::getTemplateSize()
*/
public function useImportedPage($pageId, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false)
{
if (\is_array($x)) {
/** @noinspection OffsetOperationsInspection */
unset($x['pageId']);
\extract($x, EXTR_IF_EXISTS);
/** @noinspection NotOptimalIfConditionsInspection */
/** @phpstan-ignore function.alreadyNarrowedType */
if (\is_array($x)) {
$x = 0;
}
}
if (!isset($this->importedPages[$pageId])) {
throw new \InvalidArgumentException('Imported page does not exist!');
}
$importedPage = $this->importedPages[$pageId];
$originalSize = $this->getTemplateSize($pageId);
$newSize = $this->getTemplateSize($pageId, $width, $height);
if ($adjustPageSize) {
$this->setPageFormat($newSize, $newSize['orientation']);
}
$scaleX = ($newSize['width'] / $originalSize['width']);
$scaleY = ($newSize['height'] / $originalSize['height']);
$xPt = $x * $this->k;
$yPt = $y * $this->k;
$newHeightPt = $newSize['height'] * $this->k;
$this->_out(
// reset standard values, translate and scale
\sprintf(
'q 0 J 1 w 0 j 0 G 0 g %.4F 0 0 %.4F %.4F %.4F cm /%s Do Q',
$scaleX,
$scaleY,
$xPt,
$this->hPt - $yPt - $newHeightPt,
$importedPage['id']
)
);
if (count($importedPage['externalLinks']) > 0) {
foreach ($importedPage['externalLinks'] as $externalLink) {
// mPDF uses also 'externalLinks' but doesn't come with a rect-value
if (!isset($externalLink['rect'])) {
continue;
}
/** @var Rectangle $rect */
$rect = $externalLink['rect'];
$this->Link(
$x + $rect->getLlx() / $this->k * $scaleX,
$y + $newSize['height'] - ($rect->getLly() + $rect->getHeight()) / $this->k * $scaleY,
$rect->getWidth() / $this->k * $scaleX,
$rect->getHeight() / $this->k * $scaleY,
$externalLink['uri']
);
$this->adjustLastLink($externalLink, $xPt, $scaleX, $yPt, $newHeightPt, $scaleY, $importedPage);
}
}
return $newSize;
}
/**
* This method will add additional data to the last created link/annotation.
*
* It is separated because TCPDF uses its own logic to handle link annotations.
* This method is overwritten in the TCPDF implementation.
*
* @param array $externalLink
* @param float|int $xPt
* @param float|int $scaleX
* @param float|int $yPt
* @param float|int $newHeightPt
* @param float|int $scaleY
* @param array $importedPage
* @return void
*/
protected function adjustLastLink($externalLink, $xPt, $scaleX, $yPt, $newHeightPt, $scaleY, $importedPage)
{
// let's create a relation of the newly created link to the data of the external link
$lastLink = \count($this->PageLinks[$this->page]);
$this->PageLinks[$this->page][$lastLink - 1]['importedLink'] = $externalLink;
if (\count($externalLink['quadPoints']) > 0) {
$quadPoints = [];
for ($i = 0, $n = \count($externalLink['quadPoints']); $i < $n; $i += 2) {
$quadPoints[] = $xPt + $externalLink['quadPoints'][$i] * $scaleX;
$quadPoints[] = $this->hPt - $yPt - $newHeightPt + $externalLink['quadPoints'][$i + 1] * $scaleY;
}
$this->PageLinks[$this->page][$lastLink - 1]['quadPoints'] = $quadPoints;
}
}
/**
* Get the size of an imported page.
*
* Give only one of the size parameters (width, height) to calculate the other one automatically in view to the
* aspect ratio.
*
* @param mixed $tpl The template id
* @param float|int|null $width The width.
* @param float|int|null $height The height.
* @return array|bool An array with following keys: width, height, 0 (=width), 1 (=height), orientation (L or P)
*/
public function getImportedPageSize($tpl, $width = null, $height = null)
{
if (isset($this->importedPages[$tpl])) {
$importedPage = $this->importedPages[$tpl];
if ($width === null && $height === null) {
$width = $importedPage['width'];
$height = $importedPage['height'];
} elseif ($width === null) {
$width = $height * $importedPage['width'] / $importedPage['height'];
}
if ($height === null) {
$height = $width * $importedPage['height'] / $importedPage['width'];
}
if ($height <= 0. || $width <= 0.) {
throw new \InvalidArgumentException('Width or height parameter needs to be larger than zero.');
}
return [
'width' => $width,
'height' => $height,
0 => $width,
1 => $height,
'orientation' => $width > $height ? 'L' : 'P'
];
}
return false;
}
/**
* Writes a PdfType object to the resulting buffer.
*
* @param PdfType $value
* @throws PdfTypeException
*/
protected function writePdfType(PdfType $value)
{
if ($value instanceof PdfNumeric) {
if (\is_int($value->value)) {
$this->_put($value->value . ' ', false);
} else {
$this->_put(\rtrim(\rtrim(\sprintf('%.5F', $value->value), '0'), '.') . ' ', false);
}
} elseif ($value instanceof PdfName) {
$this->_put('/' . $value->value . ' ', false);
} elseif ($value instanceof PdfString) {
$this->_put('(' . $value->value . ')', false);
} elseif ($value instanceof PdfHexString) {
$this->_put('<' . $value->value . '>', false);
} elseif ($value instanceof PdfBoolean) {
$this->_put($value->value ? 'true ' : 'false ', false);
} elseif ($value instanceof PdfArray) {
$this->_put('[', false);
foreach ($value->value as $entry) {
$this->writePdfType($entry);
}
$this->_put(']');
} elseif ($value instanceof PdfDictionary) {
$this->_put('<<', false);
foreach ($value->value as $name => $entry) {
$this->_put('/' . $name . ' ', false);
$this->writePdfType($entry);
}
$this->_put('>>');
} elseif ($value instanceof PdfToken) {
$this->_put($value->value);
} elseif ($value instanceof PdfNull) {
$this->_put('null ', false);
} elseif ($value instanceof PdfStream) {
$this->writePdfType($value->value);
$this->_put('stream');
$this->_put($value->getStream());
$this->_put('endstream');
} elseif ($value instanceof PdfIndirectObjectReference) {
if (!isset($this->objectMap[$this->currentReaderId])) {
$this->objectMap[$this->currentReaderId] = [];
}
if (!isset($this->objectMap[$this->currentReaderId][$value->value])) {
$this->objectMap[$this->currentReaderId][$value->value] = ++$this->n;
$this->objectsToCopy[$this->currentReaderId][] = $value->value;
}
$this->_put($this->objectMap[$this->currentReaderId][$value->value] . ' 0 R ', false);
} elseif ($value instanceof PdfIndirectObject) {
$n = $this->objectMap[$this->currentReaderId][$value->objectNumber];
$this->_newobj($n);
$this->writePdfType($value->value);
// add newline before "endobj" for all objects in view to PDF/A conformance
if (
!(
($value->value instanceof PdfArray) ||
($value->value instanceof PdfDictionary) ||
($value->value instanceof PdfToken) ||
($value->value instanceof PdfStream)
)
) {
$this->_put("\n", false);
}
$this->_put('endobj');
}
}
}

View File

@ -0,0 +1,97 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi;
use setasign\Fpdi\Math\Matrix;
use setasign\Fpdi\Math\Vector;
/**
* A simple graphic state class which holds the current transformation matrix.
*/
class GraphicsState
{
/**
* @var Matrix
*/
protected $ctm;
/**
* @param Matrix|null $ctm
*/
public function __construct(?Matrix $ctm = null)
{
if ($ctm === null) {
$ctm = new Matrix();
}
$this->ctm = $ctm;
}
/**
* @param Matrix $matrix
* @return $this
*/
public function add(Matrix $matrix)
{
$this->ctm = $matrix->multiply($this->ctm);
return $this;
}
/**
* @param int|float $x
* @param int|float $y
* @param int|float $angle
* @return $this
*/
public function rotate($x, $y, $angle)
{
if (\abs($angle) < 1e-5) {
return $this;
}
$angle = \deg2rad($angle);
$c = \cos($angle);
$s = \sin($angle);
$this->add(new Matrix($c, $s, -$s, $c, $x, $y));
return $this->translate(-$x, -$y);
}
/**
* @param int|float $shiftX
* @param int|float $shiftY
* @return $this
*/
public function translate($shiftX, $shiftY)
{
return $this->add(new Matrix(1, 0, 0, 1, $shiftX, $shiftY));
}
/**
* @param int|float $scaleX
* @param int|float $scaleY
* @return $this
*/
public function scale($scaleX, $scaleY)
{
return $this->add(new Matrix($scaleX, 0, 0, $scaleY, 0, 0));
}
/**
* @param Vector $vector
* @return Vector
*/
public function toUserSpace(Vector $vector)
{
return $vector->multiplyWithMatrix($this->ctm);
}
}

116
fpdi2/src/Math/Matrix.php Normal file
View File

@ -0,0 +1,116 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\Math;
/**
* A simple 2D-Matrix class
*/
class Matrix
{
/**
* @var float
*/
protected $a;
/**
* @var float
*/
protected $b;
/**
* @var float
*/
protected $c;
/**
* @var float
*/
protected $d;
/**
* @var float
*/
protected $e;
/**
* @var float
*/
protected $f;
/**
* @param int|float $a
* @param int|float $b
* @param int|float $c
* @param int|float $d
* @param int|float $e
* @param int|float $f
*/
public function __construct($a = 1, $b = 0, $c = 0, $d = 1, $e = 0, $f = 0)
{
$this->a = (float)$a;
$this->b = (float)$b;
$this->c = (float)$c;
$this->d = (float)$d;
$this->e = (float)$e;
$this->f = (float)$f;
}
/**
* @return float[]
*/
public function getValues()
{
return [$this->a, $this->b, $this->c, $this->d, $this->e, $this->f];
}
/**
* @param Matrix $by
* @return Matrix
*/
public function multiply(self $by)
{
$a =
$this->a * $by->a
+ $this->b * $by->c
//+ 0 * $by->e
;
$b =
$this->a * $by->b
+ $this->b * $by->d
//+ 0 * $by->f
;
$c =
$this->c * $by->a
+ $this->d * $by->c
//+ 0 * $by->e
;
$d =
$this->c * $by->b
+ $this->d * $by->d
//+ 0 * $by->f
;
$e =
$this->e * $by->a
+ $this->f * $by->c
+ /*1 * */$by->e;
$f =
$this->e * $by->b
+ $this->f * $by->d
+ /*1 * */$by->f;
return new self($a, $b, $c, $d, $e, $f);
}
}

66
fpdi2/src/Math/Vector.php Normal file
View File

@ -0,0 +1,66 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\Math;
/**
* A simple 2D-Vector class
*/
class Vector
{
/**
* @var float
*/
protected $x;
/**
* @var float
*/
protected $y;
/**
* @param int|float $x
* @param int|float $y
*/
public function __construct($x = .0, $y = .0)
{
$this->x = (float)$x;
$this->y = (float)$y;
}
/**
* @return float
*/
public function getX()
{
return $this->x;
}
/**
* @return float
*/
public function getY()
{
return $this->y;
}
/**
* @param Matrix $matrix
* @return Vector
*/
public function multiplyWithMatrix(Matrix $matrix)
{
[$a, $b, $c, $d, $e, $f] = $matrix->getValues();
$x = $a * $this->x + $c * $this->y + $e;
$y = $b * $this->x + $d * $this->y + $f;
return new self($x, $y);
}
}

View File

@ -0,0 +1,93 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\CrossReference;
use setasign\Fpdi\PdfParser\PdfParser;
use setasign\Fpdi\PdfParser\Type\PdfDictionary;
use setasign\Fpdi\PdfParser\Type\PdfToken;
use setasign\Fpdi\PdfParser\Type\PdfTypeException;
/**
* Abstract class for cross-reference reader classes.
*/
abstract class AbstractReader
{
/**
* @var PdfParser
*/
protected $parser;
/**
* @var PdfDictionary
*/
protected $trailer;
/**
* AbstractReader constructor.
*
* @param PdfParser $parser
* @throws CrossReferenceException
*/
public function __construct(PdfParser $parser)
{
$this->parser = $parser;
$this->readTrailer();
}
/**
* Get the trailer dictionary.
*
* @return PdfDictionary
*/
public function getTrailer()
{
return $this->trailer;
}
/**
* Read the trailer dictionary.
*
* @throws CrossReferenceException
*/
protected function readTrailer()
{
try {
$trailerKeyword = $this->parser->readValue(null, PdfToken::class);
if ($trailerKeyword->value !== 'trailer') {
throw new CrossReferenceException(
\sprintf(
'Unexpected end of cross reference. "trailer"-keyword expected, got: %s.',
$trailerKeyword->value
),
CrossReferenceException::UNEXPECTED_END
);
}
} catch (PdfTypeException $e) {
throw new CrossReferenceException(
'Unexpected end of cross reference. "trailer"-keyword expected, got an invalid object type.',
CrossReferenceException::UNEXPECTED_END,
$e
);
}
try {
$trailer = $this->parser->readValue(null, PdfDictionary::class);
} catch (PdfTypeException $e) {
throw new CrossReferenceException(
'Unexpected end of cross reference. Trailer not found.',
CrossReferenceException::UNEXPECTED_END,
$e
);
}
$this->trailer = $trailer;
}
}

View File

@ -0,0 +1,337 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\CrossReference;
use setasign\Fpdi\PdfParser\PdfParser;
use setasign\Fpdi\PdfParser\Type\PdfDictionary;
use setasign\Fpdi\PdfParser\Type\PdfIndirectObject;
use setasign\Fpdi\PdfParser\Type\PdfNumeric;
use setasign\Fpdi\PdfParser\Type\PdfStream;
use setasign\Fpdi\PdfParser\Type\PdfToken;
use setasign\Fpdi\PdfParser\Type\PdfTypeException;
/**
* Class CrossReference
*
* This class processes the standard cross reference of a PDF document.
*/
class CrossReference
{
/**
* The byte length in which the "startxref" keyword should be searched.
*
* @var int
*/
public static $trailerSearchLength = 5500;
/**
* @var int
*/
protected $fileHeaderOffset = 0;
/**
* @var PdfParser
*/
protected $parser;
/**
* @var ReaderInterface[]
*/
protected $readers = [];
/**
* CrossReference constructor.
*
* @param PdfParser $parser
* @throws CrossReferenceException
* @throws PdfTypeException
*/
public function __construct(PdfParser $parser, $fileHeaderOffset = 0)
{
// clear the token stack, if the parser instance is re-used
$parser->getTokenizer()->clearStack();
$this->parser = $parser;
$this->fileHeaderOffset = $fileHeaderOffset;
$offset = $this->findStartXref();
$reader = null;
$offsets = [$offset];
/** @noinspection TypeUnsafeComparisonInspection */
while ($offset != false) { // By doing an unsafe comparsion we ignore faulty references to byte offset 0
try {
$reader = $this->readXref($offset + $this->fileHeaderOffset);
} catch (CrossReferenceException $e) {
// sometimes the file header offset is part of the byte offsets, so let's retry by resetting it to zero.
if ($e->getCode() === CrossReferenceException::INVALID_DATA && $this->fileHeaderOffset !== 0) {
$this->fileHeaderOffset = 0;
$reader = $this->readXref($offset);
} else {
throw $e;
}
}
$trailer = $reader->getTrailer();
$this->checkForEncryption($trailer);
$this->readers[] = $reader;
if (isset($trailer->value['Prev'])) {
$offset = $trailer->value['Prev']->value;
if (\in_array($offset, $offsets, true)) {
throw new CrossReferenceException(
'Cross-references includes cyclic structure.',
CrossReferenceException::CYCLIC_STRUCTURE
);
}
$offsets[] = $offset;
} else {
break;
}
}
// fix faulty sub-section header
if ($reader instanceof FixedReader) {
/**
* @var FixedReader $reader
*/
$reader->fixFaultySubSectionShift();
}
if ($reader === null) {
throw new CrossReferenceException('No cross-reference found.', CrossReferenceException::NO_XREF_FOUND);
}
}
/**
* Get the size of the cross reference.
*
* @return integer
*/
public function getSize()
{
return $this->getTrailer()->value['Size']->value;
}
/**
* Get the trailer dictionary.
*
* @return PdfDictionary
*/
public function getTrailer()
{
return $this->readers[0]->getTrailer();
}
/**
* Get the cross reference readser instances.
*
* @return ReaderInterface[]
*/
public function getReaders()
{
return $this->readers;
}
/**
* Get the offset by an object number.
*
* @param int $objectNumber
* @return integer|bool
*/
public function getOffsetFor($objectNumber)
{
foreach ($this->getReaders() as $reader) {
$offset = $reader->getOffsetFor($objectNumber);
if ($offset !== false) {
return $offset;
}
}
return false;
}
/**
* Get an indirect object by its object number.
*
* @param int $objectNumber
* @return PdfIndirectObject
* @throws CrossReferenceException
*/
public function getIndirectObject($objectNumber)
{
$offset = $this->getOffsetFor($objectNumber);
if ($offset === false) {
throw new CrossReferenceException(
\sprintf('Object (id:%s) not found.', $objectNumber),
CrossReferenceException::OBJECT_NOT_FOUND
);
}
$parser = $this->parser;
$parser->getTokenizer()->clearStack();
$parser->getStreamReader()->reset($offset + $this->fileHeaderOffset);
try {
/** @var PdfIndirectObject $object */
$object = $parser->readValue(null, PdfIndirectObject::class);
} catch (PdfTypeException $e) {
throw new CrossReferenceException(
\sprintf('Object (id:%s) not found at location (%s).', $objectNumber, $offset),
CrossReferenceException::OBJECT_NOT_FOUND,
$e
);
}
if ($object->objectNumber !== $objectNumber) {
throw new CrossReferenceException(
\sprintf('Wrong object found, got %s while %s was expected.', $object->objectNumber, $objectNumber),
CrossReferenceException::OBJECT_NOT_FOUND
);
}
return $object;
}
/**
* Read the cross-reference table at a given offset.
*
* Internally the method will try to evaluate the best reader for this cross-reference.
*
* @param int $offset
* @return ReaderInterface
* @throws CrossReferenceException
* @throws PdfTypeException
*/
protected function readXref($offset)
{
$this->parser->getStreamReader()->reset($offset);
$this->parser->getTokenizer()->clearStack();
$initValue = $this->parser->readValue();
return $this->initReaderInstance($initValue);
}
/**
* Get a cross-reference reader instance.
*
* @param PdfToken|PdfIndirectObject $initValue
* @return ReaderInterface|bool
* @throws CrossReferenceException
* @throws PdfTypeException
*/
protected function initReaderInstance($initValue)
{
$position = $this->parser->getStreamReader()->getPosition()
+ $this->parser->getStreamReader()->getOffset() + $this->fileHeaderOffset;
if ($initValue instanceof PdfToken && $initValue->value === 'xref') {
try {
return new FixedReader($this->parser);
} catch (CrossReferenceException $e) {
$this->parser->getStreamReader()->reset($position);
$this->parser->getTokenizer()->clearStack();
return new LineReader($this->parser);
}
}
if ($initValue instanceof PdfIndirectObject) {
try {
$stream = PdfStream::ensure($initValue->value);
} catch (PdfTypeException $e) {
throw new CrossReferenceException(
'Invalid object type at xref reference offset.',
CrossReferenceException::INVALID_DATA,
$e
);
}
$type = PdfDictionary::get($stream->value, 'Type');
if ($type->value !== 'XRef') {
throw new CrossReferenceException(
'The xref position points to an incorrect object type.',
CrossReferenceException::INVALID_DATA
);
}
$this->checkForEncryption($stream->value);
throw new CrossReferenceException(
'This PDF document probably uses a compression technique which is not supported by the ' .
'free parser shipped with FPDI. (See https://www.setasign.com/fpdi-pdf-parser for more details)',
CrossReferenceException::COMPRESSED_XREF
);
}
throw new CrossReferenceException(
'The xref position points to an incorrect object type.',
CrossReferenceException::INVALID_DATA
);
}
/**
* Check for encryption.
*
* @param PdfDictionary $dictionary
* @throws CrossReferenceException
*/
protected function checkForEncryption(PdfDictionary $dictionary)
{
if (isset($dictionary->value['Encrypt'])) {
throw new CrossReferenceException(
'This PDF document is encrypted and cannot be processed with FPDI.',
CrossReferenceException::ENCRYPTED
);
}
}
/**
* Find the start position for the first cross-reference.
*
* @return int The byte-offset position of the first cross-reference.
* @throws CrossReferenceException
*/
protected function findStartXref()
{
$reader = $this->parser->getStreamReader();
$reader->reset(-self::$trailerSearchLength, self::$trailerSearchLength);
$buffer = $reader->getBuffer(false);
$pos = \strrpos($buffer, 'startxref');
$addOffset = 9;
if ($pos === false) {
// Some corrupted documents uses startref, instead of startxref
$pos = \strrpos($buffer, 'startref');
if ($pos === false) {
throw new CrossReferenceException(
'Unable to find pointer to xref table',
CrossReferenceException::NO_STARTXREF_FOUND
);
}
$addOffset = 8;
}
$reader->setOffset($pos + $addOffset);
try {
$value = $this->parser->readValue(null, PdfNumeric::class);
} catch (PdfTypeException $e) {
throw new CrossReferenceException(
'Invalid data after startxref keyword.',
CrossReferenceException::INVALID_DATA,
$e
);
}
return $value->value;
}
}

View File

@ -0,0 +1,84 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\CrossReference;
use setasign\Fpdi\PdfParser\PdfParserException;
/**
* Exception used by the CrossReference and Reader classes.
*/
class CrossReferenceException extends PdfParserException
{
/**
* @var int
*/
const INVALID_DATA = 0x0101;
/**
* @var int
*/
const XREF_MISSING = 0x0102;
/**
* @var int
*/
const ENTRIES_TOO_LARGE = 0x0103;
/**
* @var int
*/
const ENTRIES_TOO_SHORT = 0x0104;
/**
* @var int
*/
const NO_ENTRIES = 0x0105;
/**
* @var int
*/
const NO_TRAILER_FOUND = 0x0106;
/**
* @var int
*/
const NO_STARTXREF_FOUND = 0x0107;
/**
* @var int
*/
const NO_XREF_FOUND = 0x0108;
/**
* @var int
*/
const UNEXPECTED_END = 0x0109;
/**
* @var int
*/
const OBJECT_NOT_FOUND = 0x010A;
/**
* @var int
*/
const COMPRESSED_XREF = 0x010B;
/**
* @var int
*/
const ENCRYPTED = 0x010C;
/**
* @var int
*/
const CYCLIC_STRUCTURE = 0x010D;
}

View File

@ -0,0 +1,200 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\CrossReference;
use setasign\Fpdi\PdfParser\PdfParser;
use setasign\Fpdi\PdfParser\StreamReader;
/**
* Class FixedReader
*
* This reader allows a very less overhead parsing of single entries of the cross-reference, because the main entries
* are only read when needed and not in a single run.
*/
class FixedReader extends AbstractReader implements ReaderInterface
{
/**
* @var StreamReader
*/
protected $reader;
/**
* Data of subsections.
*
* @var array
*/
protected $subSections;
/**
* FixedReader constructor.
*
* @param PdfParser $parser
* @throws CrossReferenceException
*/
public function __construct(PdfParser $parser)
{
$this->reader = $parser->getStreamReader();
$this->read();
parent::__construct($parser);
}
/**
* Get all subsection data.
*
* @return array
*/
public function getSubSections()
{
return $this->subSections;
}
/**
* @inheritdoc
* @return int|false
*/
public function getOffsetFor($objectNumber)
{
foreach ($this->subSections as $offset => [$startObject, $objectCount]) {
/**
* @var int $startObject
* @var int $objectCount
*/
if ($objectNumber >= $startObject && $objectNumber < ($startObject + $objectCount)) {
$position = $offset + 20 * ($objectNumber - $startObject);
$this->reader->ensure($position, 20);
$line = $this->reader->readBytes(20);
if ($line === false || $line[17] === 'f') {
return false;
}
return (int) \substr($line, 0, 10);
}
}
return false;
}
/**
* Read the cross-reference.
*
* This reader will only read the subsections in this method. The offsets were resolved individually by this
* information.
*
* @throws CrossReferenceException
*/
protected function read()
{
$subSections = [];
$startObject = $entryCount = $lastLineStart = null;
$validityChecked = false;
while (($line = $this->reader->readLine(20)) !== false) {
if (\strpos($line, 'trailer') !== false) {
$this->reader->reset($lastLineStart);
break;
}
// jump over if line content doesn't match the expected string
if (\sscanf($line, '%d %d', $startObject, $entryCount) !== 2) {
continue;
}
$oldPosition = $this->reader->getPosition();
$position = $oldPosition + $this->reader->getOffset();
if (!$validityChecked && $entryCount > 0) {
$nextLine = $this->reader->readBytes(21);
/* Check the next line for maximum of 20 bytes and not longer
* By catching 21 bytes and trimming the length should be still 21.
*/
if (\strlen(\trim($nextLine)) !== 21) {
throw new CrossReferenceException(
'Cross-reference entries are larger than 20 bytes.',
CrossReferenceException::ENTRIES_TOO_LARGE
);
}
/* Check for less than 20 bytes: cut the line to 20 bytes and trim; have to result in exactly 18 bytes.
* If it would have less bytes the substring would get the first bytes of the next line which would
* evaluate to a 20 bytes long string after trimming.
*/
if (\strlen(\trim(\substr($nextLine, 0, 20))) !== 18) {
throw new CrossReferenceException(
'Cross-reference entries are less than 20 bytes.',
CrossReferenceException::ENTRIES_TOO_SHORT
);
}
$validityChecked = true;
}
$subSections[$position] = [$startObject, $entryCount];
$lastLineStart = $position + $entryCount * 20;
$this->reader->reset($lastLineStart);
}
// reset after the last correct parsed line
$this->reader->reset($lastLineStart);
if (\count($subSections) === 0) {
throw new CrossReferenceException(
'No entries found in cross-reference.',
CrossReferenceException::NO_ENTRIES
);
}
$this->subSections = $subSections;
}
/**
* Fixes an invalid object number shift.
*
* This method can be used to repair documents with an invalid subsection header:
*
* <code>
* xref
* 1 7
* 0000000000 65535 f
* 0000000009 00000 n
* 0000412075 00000 n
* 0000412172 00000 n
* 0000412359 00000 n
* 0000412417 00000 n
* 0000412468 00000 n
* </code>
*
* It shall only be called on the first table.
*
* @return bool
*/
public function fixFaultySubSectionShift()
{
$subSections = $this->getSubSections();
if (\count($subSections) > 1) {
return false;
}
$subSection = \current($subSections);
if ($subSection[0] != 1) {
return false;
}
if ($this->getOffsetFor(1) === false) {
foreach ($subSections as $offset => [$startObject, $objectCount]) {
$this->subSections[$offset] = [$startObject - 1, $objectCount];
}
return true;
}
return false;
}
}

View File

@ -0,0 +1,168 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\CrossReference;
use setasign\Fpdi\PdfParser\PdfParser;
use setasign\Fpdi\PdfParser\StreamReader;
/**
* Class LineReader
*
* This reader class read all cross-reference entries in a single run.
* It supports reading cross-references with e.g. invalid data (e.g. entries with a length < or > 20 bytes).
*/
class LineReader extends AbstractReader implements ReaderInterface
{
/**
* The object offsets.
*
* @var array
*/
protected $offsets;
/**
* LineReader constructor.
*
* @param PdfParser $parser
* @throws CrossReferenceException
*/
public function __construct(PdfParser $parser)
{
$this->read($this->extract($parser->getStreamReader()));
parent::__construct($parser);
}
/**
* @inheritdoc
* @return int|false
*/
public function getOffsetFor($objectNumber)
{
if (isset($this->offsets[$objectNumber])) {
return $this->offsets[$objectNumber][0];
}
return false;
}
/**
* Get all found offsets.
*
* @return array
*/
public function getOffsets()
{
return $this->offsets;
}
/**
* Extracts the cross reference data from the stream reader.
*
* @param StreamReader $reader
* @return string
* @throws CrossReferenceException
*/
protected function extract(StreamReader $reader)
{
$bytesPerCycle = 100;
$reader->reset(null, $bytesPerCycle);
$cycles = 0;
do {
// 6 = length of "trailer" - 1
$pos = \max(($bytesPerCycle * $cycles) - 6, 0);
$trailerPos = \strpos($reader->getBuffer(false), 'trailer', $pos);
$cycles++;
} while ($trailerPos === false && $reader->increaseLength($bytesPerCycle) !== false);
if ($trailerPos === false) {
throw new CrossReferenceException(
'Unexpected end of cross reference. "trailer"-keyword not found.',
CrossReferenceException::NO_TRAILER_FOUND
);
}
$xrefContent = \substr($reader->getBuffer(false), 0, $trailerPos);
$reader->reset($reader->getPosition() + $trailerPos);
return $xrefContent;
}
/**
* Read the cross-reference entries.
*
* @param string $xrefContent
* @throws CrossReferenceException
*/
protected function read($xrefContent)
{
// get eol markers in the first 100 bytes
\preg_match_all("/(\r\n|\n|\r)/", \substr($xrefContent, 0, 100), $m);
if (\count($m[0]) === 0) {
throw new CrossReferenceException(
'No data found in cross-reference.',
CrossReferenceException::INVALID_DATA
);
}
// count(array_count_values()) is faster then count(array_unique())
// @see https://github.com/symfony/symfony/pull/23731
// can be reverted for php7.2
$differentLineEndings = \count(\array_count_values($m[0]));
if ($differentLineEndings > 1) {
$lines = \preg_split("/(\r\n|\n|\r)/", $xrefContent, -1, PREG_SPLIT_NO_EMPTY);
} else {
$lines = \explode($m[0][0], $xrefContent);
}
unset($differentLineEndings, $m);
if (!\is_array($lines)) {
$this->offsets = [];
return;
}
$start = 0;
$offsets = [];
// trim all lines and remove empty lines
$lines = \array_filter(\array_map('\trim', $lines));
foreach ($lines as $line) {
$pieces = \explode(' ', $line);
switch (\count($pieces)) {
case 2:
$start = (int) $pieces[0];
break;
case 3:
switch ($pieces[2]) {
case 'n':
$offsets[$start] = [(int) $pieces[0], (int) $pieces[1]];
$start++;
break 2;
case 'f':
$start++;
break 2;
}
// fall through if pieces doesn't match
default:
throw new CrossReferenceException(
\sprintf('Unexpected data in xref table (%s)', \implode(' ', $pieces)),
CrossReferenceException::INVALID_DATA
);
}
}
$this->offsets = $offsets;
}
}

View File

@ -0,0 +1,34 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\CrossReference;
use setasign\Fpdi\PdfParser\Type\PdfDictionary;
/**
* ReaderInterface for cross-reference readers.
*/
interface ReaderInterface
{
/**
* Get an offset by an object number.
*
* @param int $objectNumber
* @return int|bool False if the offset was not found.
*/
public function getOffsetFor($objectNumber);
/**
* Get the trailer related to this cross reference.
*
* @return PdfDictionary
*/
public function getTrailer();
}

View File

@ -0,0 +1,102 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Filter;
/**
* Class for handling ASCII base-85 encoded data
*/
class Ascii85 implements FilterInterface
{
/**
* Decode ASCII85 encoded string.
*
* @param string $data The input string
* @return string
* @throws Ascii85Exception
*/
public function decode($data)
{
$out = '';
$state = 0;
$chn = null;
$data = \preg_replace('/\s/', '', $data);
$l = \strlen($data);
/** @noinspection ForeachInvariantsInspection */
for ($k = 0; $k < $l; ++$k) {
$ch = \ord($data[$k]) & 0xff;
//Start <~
if ($k === 0 && $ch === 60 && isset($data[$k + 1]) && (\ord($data[$k + 1]) & 0xFF) === 126) {
$k++;
continue;
}
//End ~>
if ($ch === 126 && isset($data[$k + 1]) && (\ord($data[$k + 1]) & 0xFF) === 62) {
break;
}
if ($ch === 122 /* z */ && $state === 0) {
$out .= "\x00\x00\x00\x00";
continue;
}
if ($ch < 33 /* ! */ || $ch > 117 /* u */) {
throw new Ascii85Exception(
'Illegal character found while ASCII85 decode.',
Ascii85Exception::ILLEGAL_CHAR_FOUND
);
}
$chn[$state] = $ch - 33;/* ! */
$state++;
if ($state === 5) {
$state = 0;
$r = 0;
for ($j = 0; $j < 5; ++$j) {
/** @noinspection UnnecessaryCastingInspection */
$r = (int)($r * 85 + $chn[$j]);
}
$out .= \chr(($r >> 24) & 255)
. \chr(($r >> 16) & 255)
. \chr(($r >> 8) & 255)
. \chr($r & 255);
}
}
if ($state === 1) {
throw new Ascii85Exception(
'Illegal length while ASCII85 decode.',
Ascii85Exception::ILLEGAL_LENGTH
);
}
if ($state === 2) {
$r = $chn[0] * 85 * 85 * 85 * 85 + ($chn[1] + 1) * 85 * 85 * 85;
$out .= \chr(($r >> 24) & 255);
} elseif ($state === 3) {
$r = $chn[0] * 85 * 85 * 85 * 85 + $chn[1] * 85 * 85 * 85 + ($chn[2] + 1) * 85 * 85;
$out .= \chr(($r >> 24) & 255);
$out .= \chr(($r >> 16) & 255);
} elseif ($state === 4) {
$r = $chn[0] * 85 * 85 * 85 * 85 + $chn[1] * 85 * 85 * 85 + $chn[2] * 85 * 85 + ($chn[3] + 1) * 85;
$out .= \chr(($r >> 24) & 255);
$out .= \chr(($r >> 16) & 255);
$out .= \chr(($r >> 8) & 255);
}
return $out;
}
}

View File

@ -0,0 +1,27 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Filter;
/**
* Exception for Ascii85 filter class
*/
class Ascii85Exception extends FilterException
{
/**
* @var integer
*/
const ILLEGAL_CHAR_FOUND = 0x0301;
/**
* @var integer
*/
const ILLEGAL_LENGTH = 0x0302;
}

View File

@ -0,0 +1,47 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Filter;
/**
* Class for handling ASCII hexadecimal encoded data
*/
class AsciiHex implements FilterInterface
{
/**
* Converts an ASCII hexadecimal encoded string into its binary representation.
*
* @param string $data The input string
* @return string
*/
public function decode($data)
{
$data = \preg_replace('/[^0-9A-Fa-f]/', '', \rtrim($data, '>'));
if ((\strlen($data) % 2) === 1) {
$data .= '0';
}
return \pack('H*', $data);
}
/**
* Converts a string into ASCII hexadecimal representation.
*
* @param string $data The input string
* @param boolean $leaveEOD
* @return string
*/
public function encode($data, $leaveEOD = false)
{
$t = \unpack('H*', $data);
return \current($t)
. ($leaveEOD ? '' : '>');
}
}

View File

@ -0,0 +1,23 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Filter;
use setasign\Fpdi\PdfParser\PdfParserException;
/**
* Exception for filters
*/
class FilterException extends PdfParserException
{
const UNSUPPORTED_FILTER = 0x0201;
const NOT_IMPLEMENTED = 0x0202;
}

View File

@ -0,0 +1,25 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Filter;
/**
* Interface for filters
*/
interface FilterInterface
{
/**
* Decode a string.
*
* @param string $data The input string
* @return string
*/
public function decode($data);
}

View File

@ -0,0 +1,77 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Filter;
/**
* Class for handling zlib/deflate encoded data
*/
class Flate implements FilterInterface
{
/**
* Checks whether the zlib extension is loaded.
*
* Used for testing purpose.
*
* @return boolean
* @internal
*/
protected function extensionLoaded()
{
return \extension_loaded('zlib');
}
/**
* Decodes a flate compressed string.
*
* @param string|false $data The input string
* @return string
* @throws FlateException
*/
public function decode($data)
{
if ($this->extensionLoaded()) {
$oData = $data;
$data = (($data !== '') ? @\gzuncompress($data) : '');
if ($data === false) {
// let's try if the checksum is CRC32
$fh = fopen('php://temp', 'w+b');
fwrite($fh, "\x1f\x8b\x08\x00\x00\x00\x00\x00" . $oData);
// "window" == 31 -> 16 + (8 to 15): Uses the low 4 bits of the value as the window size logarithm.
// The input must include a gzip header and trailer (via 16).
stream_filter_append($fh, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 31]);
fseek($fh, 0);
$data = @stream_get_contents($fh);
fclose($fh);
if ($data) {
return $data;
}
// Try this fallback (remove the zlib stream header)
$data = @(gzinflate(substr($oData, 2)));
if ($data === false) {
throw new FlateException(
'Error while decompressing stream.',
FlateException::DECOMPRESS_ERROR
);
}
}
} else {
throw new FlateException(
'To handle FlateDecode filter, enable zlib support in PHP.',
FlateException::NO_ZLIB
);
}
return $data;
}
}

View File

@ -0,0 +1,27 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Filter;
/**
* Exception for flate filter class
*/
class FlateException extends FilterException
{
/**
* @var integer
*/
const NO_ZLIB = 0x0401;
/**
* @var integer
*/
const DECOMPRESS_ERROR = 0x0402;
}

View File

@ -0,0 +1,178 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Filter;
/**
* Class for handling LZW encoded data
*/
class Lzw implements FilterInterface
{
/**
* @var null|string
*/
protected $data;
/**
* @var array
*/
protected $sTable = [];
/**
* @var int
*/
protected $dataLength = 0;
/**
* @var int
*/
protected $tIdx;
/**
* @var int
*/
protected $bitsToGet = 9;
/**
* @var int
*/
protected $bytePointer;
/**
* @var int
*/
protected $nextData = 0;
/**
* @var int
*/
protected $nextBits = 0;
/**
* @var array
*/
protected $andTable = [511, 1023, 2047, 4095];
/**
* Method to decode LZW compressed data.
*
* @param string $data The compressed data
* @return string The uncompressed data
* @throws LzwException
*/
public function decode($data)
{
if ($data[0] === "\x00" && $data[1] === "\x01") {
throw new LzwException(
'LZW flavour not supported.',
LzwException::LZW_FLAVOUR_NOT_SUPPORTED
);
}
$this->initsTable();
$this->data = $data;
$this->dataLength = \strlen($data);
// Initialize pointers
$this->bytePointer = 0;
$this->nextData = 0;
$this->nextBits = 0;
$prevCode = 0;
$uncompData = '';
while (($code = $this->getNextCode()) !== 257) {
if ($code === 256) {
$this->initsTable();
} elseif ($prevCode === 256) {
$uncompData .= $this->sTable[$code];
} elseif ($code < $this->tIdx) {
$string = $this->sTable[$code];
$uncompData .= $string;
$this->addStringToTable($this->sTable[$prevCode], $string[0]);
} else {
$string = $this->sTable[$prevCode];
$string .= $string[0];
$uncompData .= $string;
$this->addStringToTable($string);
}
$prevCode = $code;
}
return $uncompData;
}
/**
* Initialize the string table.
*/
protected function initsTable()
{
$this->sTable = [];
for ($i = 0; $i < 256; $i++) {
$this->sTable[$i] = \chr($i);
}
$this->tIdx = 258;
$this->bitsToGet = 9;
}
/**
* Add a new string to the string table.
*
* @param string $oldString
* @param string $newString
*/
protected function addStringToTable($oldString, $newString = '')
{
$string = $oldString . $newString;
// Add this new String to the table
$this->sTable[$this->tIdx++] = $string;
if ($this->tIdx === 511) {
$this->bitsToGet = 10;
} elseif ($this->tIdx === 1023) {
$this->bitsToGet = 11;
} elseif ($this->tIdx === 2047) {
$this->bitsToGet = 12;
}
}
/**
* Returns the next 9, 10, 11 or 12 bits.
*
* @return int
*/
protected function getNextCode()
{
if ($this->bytePointer === $this->dataLength) {
return 257;
}
$this->nextData = ($this->nextData << 8) | (\ord($this->data[$this->bytePointer++]) & 0xff);
$this->nextBits += 8;
if ($this->nextBits < $this->bitsToGet) {
$this->nextData = ($this->nextData << 8) | (\ord($this->data[$this->bytePointer++]) & 0xff);
$this->nextBits += 8;
}
$code = ($this->nextData >> ($this->nextBits - $this->bitsToGet)) & $this->andTable[$this->bitsToGet - 9];
$this->nextBits -= $this->bitsToGet;
return $code;
}
}

View File

@ -0,0 +1,22 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Filter;
/**
* Exception for LZW filter class
*/
class LzwException extends FilterException
{
/**
* @var integer
*/
const LZW_FLAVOUR_NOT_SUPPORTED = 0x0501;
}

View File

@ -0,0 +1,436 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser;
use setasign\Fpdi\PdfParser\CrossReference\CrossReference;
use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException;
use setasign\Fpdi\PdfParser\Type\PdfArray;
use setasign\Fpdi\PdfParser\Type\PdfBoolean;
use setasign\Fpdi\PdfParser\Type\PdfDictionary;
use setasign\Fpdi\PdfParser\Type\PdfHexString;
use setasign\Fpdi\PdfParser\Type\PdfIndirectObject;
use setasign\Fpdi\PdfParser\Type\PdfIndirectObjectReference;
use setasign\Fpdi\PdfParser\Type\PdfName;
use setasign\Fpdi\PdfParser\Type\PdfNull;
use setasign\Fpdi\PdfParser\Type\PdfNumeric;
use setasign\Fpdi\PdfParser\Type\PdfStream;
use setasign\Fpdi\PdfParser\Type\PdfString;
use setasign\Fpdi\PdfParser\Type\PdfToken;
use setasign\Fpdi\PdfParser\Type\PdfType;
use setasign\Fpdi\PdfParser\Type\PdfTypeException;
/**
* A PDF parser class
*/
class PdfParser
{
/**
* @var StreamReader
*/
protected $streamReader;
/**
* @var Tokenizer
*/
protected $tokenizer;
/**
* The file header.
*
* @var string
*/
protected $fileHeader;
/**
* The offset to the file header.
*
* @var int
*/
protected $fileHeaderOffset;
/**
* @var CrossReference|null
*/
protected $xref;
/**
* All read objects.
*
* @var array
*/
protected $objects = [];
/**
* PdfParser constructor.
*
* @param StreamReader $streamReader
*/
public function __construct(StreamReader $streamReader)
{
$this->streamReader = $streamReader;
$this->tokenizer = new Tokenizer($streamReader);
}
/**
* Removes cycled references.
*
* @internal
*/
public function cleanUp()
{
$this->xref = null;
}
/**
* Get the stream reader instance.
*
* @return StreamReader
*/
public function getStreamReader()
{
return $this->streamReader;
}
/**
* Get the tokenizer instance.
*
* @return Tokenizer
*/
public function getTokenizer()
{
return $this->tokenizer;
}
/**
* Resolves the file header.
*
* @throws PdfParserException
* @return int
*/
protected function resolveFileHeader()
{
if ($this->fileHeader) {
return $this->fileHeaderOffset;
}
$this->streamReader->reset(0);
$maxIterations = 1000;
while (true) {
$buffer = $this->streamReader->getBuffer(false);
$offset = \strpos($buffer, '%PDF-');
if ($offset === false) {
if (!$this->streamReader->increaseLength(100) || (--$maxIterations === 0)) {
throw new PdfParserException(
'Unable to find PDF file header.',
PdfParserException::FILE_HEADER_NOT_FOUND
);
}
continue;
}
break;
}
$this->fileHeaderOffset = $offset;
$this->streamReader->setOffset($offset);
$this->fileHeader = \trim($this->streamReader->readLine());
return $this->fileHeaderOffset;
}
/**
* Get the cross-reference instance.
*
* @return CrossReference
* @throws CrossReferenceException
* @throws PdfParserException
*/
public function getCrossReference()
{
if ($this->xref === null) {
$this->xref = new CrossReference($this, $this->resolveFileHeader());
}
return $this->xref;
}
/**
* Get the PDF version.
*
* @return int[] An array of major and minor version.
* @throws PdfParserException
*/
public function getPdfVersion()
{
$this->resolveFileHeader();
if (\preg_match('/%PDF-(\d)\.(\d)/', $this->fileHeader, $result) === 0) {
throw new PdfParserException(
'Unable to extract PDF version from file header.',
PdfParserException::PDF_VERSION_NOT_FOUND
);
}
[, $major, $minor] = $result;
$catalog = $this->getCatalog();
if (isset($catalog->value['Version'])) {
$versionParts = \explode(
'.',
PdfName::unescape(PdfType::resolve($catalog->value['Version'], $this)->value)
);
if (count($versionParts) === 2) {
[$major, $minor] = $versionParts;
}
}
return [(int) $major, (int) $minor];
}
/**
* Get the catalog dictionary.
*
* @return PdfDictionary
* @throws Type\PdfTypeException
* @throws CrossReferenceException
* @throws PdfParserException
*/
public function getCatalog()
{
$trailer = $this->getCrossReference()->getTrailer();
$catalog = PdfType::resolve(PdfDictionary::get($trailer, 'Root'), $this);
return PdfDictionary::ensure($catalog);
}
/**
* Get an indirect object by its object number.
*
* @param int $objectNumber
* @param bool $cache
* @return PdfIndirectObject
* @throws CrossReferenceException
* @throws PdfParserException
*/
public function getIndirectObject($objectNumber, $cache = false)
{
$objectNumber = (int) $objectNumber;
if (isset($this->objects[$objectNumber])) {
return $this->objects[$objectNumber];
}
$object = $this->getCrossReference()->getIndirectObject($objectNumber);
if ($cache) {
$this->objects[$objectNumber] = $object;
}
return $object;
}
/**
* Read a PDF value.
*
* @param null|bool|string $token
* @param null|string $expectedType
* @return false|PdfArray|PdfBoolean|PdfDictionary|PdfHexString|PdfIndirectObject|PdfIndirectObjectReference|PdfName|PdfNull|PdfNumeric|PdfStream|PdfString|PdfToken
* @throws Type\PdfTypeException
*/
public function readValue($token = null, $expectedType = null)
{
if ($token === null) {
$token = $this->tokenizer->getNextToken();
}
if ($token === false) {
if ($expectedType !== null) {
throw new Type\PdfTypeException('Got unexpected token type.', Type\PdfTypeException::INVALID_DATA_TYPE);
}
return false;
}
switch ($token) {
case '(':
$this->ensureExpectedType($token, $expectedType);
return $this->parsePdfString();
case '<':
if ($this->streamReader->getByte() === '<') {
$this->ensureExpectedType('<<', $expectedType);
$this->streamReader->addOffset(1);
return $this->parsePdfDictionary();
}
$this->ensureExpectedType($token, $expectedType);
return $this->parsePdfHexString();
case '/':
$this->ensureExpectedType($token, $expectedType);
return $this->parsePdfName();
case '[':
$this->ensureExpectedType($token, $expectedType);
return $this->parsePdfArray();
default:
if (\is_numeric($token)) {
$token2 = $this->tokenizer->getNextToken();
if ($token2 !== false) {
if (\is_numeric($token2)) {
$token3 = $this->tokenizer->getNextToken();
if ($token3 === 'obj') {
if ($expectedType !== null && $expectedType !== PdfIndirectObject::class) {
throw new Type\PdfTypeException(
'Got unexpected token type.',
Type\PdfTypeException::INVALID_DATA_TYPE
);
}
return $this->parsePdfIndirectObject((int) $token, (int) $token2);
} elseif ($token3 === 'R') {
if (
$expectedType !== null &&
$expectedType !== PdfIndirectObjectReference::class
) {
throw new Type\PdfTypeException(
'Got unexpected token type.',
Type\PdfTypeException::INVALID_DATA_TYPE
);
}
return PdfIndirectObjectReference::create((int) $token, (int) $token2);
} elseif ($token3 !== false) {
$this->tokenizer->pushStack($token3);
}
}
$this->tokenizer->pushStack($token2);
}
if ($expectedType !== null && $expectedType !== PdfNumeric::class) {
throw new Type\PdfTypeException(
'Got unexpected token type.',
Type\PdfTypeException::INVALID_DATA_TYPE
);
}
return PdfNumeric::create($token + 0);
}
if ($token === 'true' || $token === 'false') {
$this->ensureExpectedType($token, $expectedType);
return PdfBoolean::create($token === 'true');
}
if ($token === 'null') {
$this->ensureExpectedType($token, $expectedType);
return new PdfNull();
}
if ($expectedType !== null && $expectedType !== PdfToken::class) {
throw new Type\PdfTypeException(
'Got unexpected token type.',
Type\PdfTypeException::INVALID_DATA_TYPE
);
}
$v = new PdfToken();
$v->value = $token;
return $v;
}
}
/**
* @return PdfString
*/
protected function parsePdfString()
{
return PdfString::parse($this->streamReader);
}
/**
* @return false|PdfHexString
*/
protected function parsePdfHexString()
{
return PdfHexString::parse($this->streamReader);
}
/**
* @return bool|PdfDictionary
* @throws PdfTypeException
*/
protected function parsePdfDictionary()
{
return PdfDictionary::parse($this->tokenizer, $this->streamReader, $this);
}
/**
* @return PdfName
*/
protected function parsePdfName()
{
return PdfName::parse($this->tokenizer, $this->streamReader);
}
/**
* @return false|PdfArray
* @throws PdfTypeException
*/
protected function parsePdfArray()
{
return PdfArray::parse($this->tokenizer, $this);
}
/**
* @param int $objectNumber
* @param int $generationNumber
* @return false|PdfIndirectObject
* @throws Type\PdfTypeException
*/
protected function parsePdfIndirectObject($objectNumber, $generationNumber)
{
return PdfIndirectObject::parse(
$objectNumber,
$generationNumber,
$this,
$this->tokenizer,
$this->streamReader
);
}
/**
* Ensures that the token will evaluate to an expected object type (or not).
*
* @param string $token
* @param string|null $expectedType
* @return bool
* @throws Type\PdfTypeException
*/
protected function ensureExpectedType($token, $expectedType)
{
static $mapping = [
'(' => PdfString::class,
'<' => PdfHexString::class,
'<<' => PdfDictionary::class,
'/' => PdfName::class,
'[' => PdfArray::class,
'true' => PdfBoolean::class,
'false' => PdfBoolean::class,
'null' => PdfNull::class
];
if ($expectedType === null || $mapping[$token] === $expectedType) {
return true;
}
throw new Type\PdfTypeException('Got unexpected token type.', Type\PdfTypeException::INVALID_DATA_TYPE);
}
}

View File

@ -0,0 +1,49 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser;
use setasign\Fpdi\FpdiException;
/**
* Exception for the pdf parser class
*/
class PdfParserException extends FpdiException
{
/**
* @var int
*/
const NOT_IMPLEMENTED = 0x0001;
/**
* @var int
*/
const IMPLEMENTED_IN_FPDI_PDF_PARSER = 0x0002;
/**
* @var int
*/
const INVALID_DATA_TYPE = 0x0003;
/**
* @var int
*/
const FILE_HEADER_NOT_FOUND = 0x0004;
/**
* @var int
*/
const PDF_VERSION_NOT_FOUND = 0x0005;
/**
* @var int
*/
const INVALID_DATA_SIZE = 0x0006;
}

View File

@ -0,0 +1,481 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser;
/**
* A stream reader class
*/
class StreamReader
{
/**
* Creates a stream reader instance by a string value.
*
* @param string $content
* @param int $maxMemory
* @return StreamReader
*/
public static function createByString($content, $maxMemory = 2097152)
{
$h = \fopen('php://temp/maxmemory:' . ((int) $maxMemory), 'r+b');
\fwrite($h, $content);
\rewind($h);
return new self($h, true);
}
/**
* Creates a stream reader instance by a filename.
*
* @param string $filename
* @return StreamReader
*/
public static function createByFile($filename)
{
$h = \fopen($filename, 'rb');
return new self($h, true);
}
/**
* Defines whether the stream should be closed when the stream reader instance is deconstructed or not.
*
* @var bool
*/
protected $closeStream;
/**
* The stream resource.
*
* @var resource
*/
protected $stream;
/**
* The byte-offset position in the stream.
*
* @var int
*/
protected $position;
/**
* The byte-offset position in the buffer.
*
* @var int
*/
protected $offset;
/**
* The buffer length.
*
* @var int
*/
protected $bufferLength;
/**
* The total length of the stream.
*
* @var int
*/
protected $totalLength;
/**
* The buffer.
*
* @var string
*/
protected $buffer;
/**
* StreamReader constructor.
*
* @param resource $stream
* @param bool $closeStream Defines whether to close the stream resource if the instance is destructed or not.
*/
public function __construct($stream, $closeStream = false)
{
if (!\is_resource($stream)) {
throw new \InvalidArgumentException(
'No stream given.'
);
}
$metaData = \stream_get_meta_data($stream);
if (!$metaData['seekable']) {
throw new \InvalidArgumentException(
'Given stream is not seekable!'
);
}
if (fseek($stream, 0) === -1) {
throw new \InvalidArgumentException(
'Given stream is not seekable!'
);
}
$this->stream = $stream;
$this->closeStream = $closeStream;
$this->reset();
}
/**
* The destructor.
*/
public function __destruct()
{
$this->cleanUp();
}
/**
* Closes the file handle.
*/
public function cleanUp()
{
if ($this->closeStream && is_resource($this->stream)) {
\fclose($this->stream);
}
}
/**
* Returns the byte length of the buffer.
*
* @param bool $atOffset
* @return int
*/
public function getBufferLength($atOffset = false)
{
if ($atOffset === false) {
return $this->bufferLength;
}
return $this->bufferLength - $this->offset;
}
/**
* Get the current position in the stream.
*
* @return int
*/
public function getPosition()
{
return $this->position;
}
/**
* Returns the current buffer.
*
* @param bool $atOffset
* @return string
*/
public function getBuffer($atOffset = true)
{
if ($atOffset === false) {
return $this->buffer;
}
$string = \substr($this->buffer, $this->offset);
return (string) $string;
}
/**
* Gets a byte at a specific position in the buffer.
*
* If the position is invalid the method will return false.
*
* If the $position parameter is set to null the value of $this->offset will be used.
*
* @param int|null $position
* @return string|bool
*/
public function getByte($position = null)
{
$position = (int) ($position !== null ? $position : $this->offset);
if (
$position >= $this->bufferLength
&& (!$this->increaseLength() || $position >= $this->bufferLength)
) {
return false;
}
return $this->buffer[$position];
}
/**
* Returns a byte at a specific position, and set the offset to the next byte position.
*
* If the position is invalid the method will return false.
*
* If the $position parameter is set to null the value of $this->offset will be used.
*
* @param int|null $position
* @return string|bool
*/
public function readByte($position = null)
{
if ($position !== null) {
$position = (int) $position;
// check if needed bytes are available in the current buffer
if (!($position >= $this->position && $position < $this->position + $this->bufferLength)) {
$this->reset($position);
$offset = $this->offset;
} else {
$offset = $position - $this->position;
}
} else {
$offset = $this->offset;
}
if (
$offset >= $this->bufferLength
&& ((!$this->increaseLength()) || $offset >= $this->bufferLength)
) {
return false;
}
$this->offset = $offset + 1;
return $this->buffer[$offset];
}
/**
* Read bytes from the current or a specific offset position and set the internal pointer to the next byte.
*
* If the position is invalid the method will return false.
*
* If the $position parameter is set to null the value of $this->offset will be used.
*
* @param int $length
* @param int|null $position
* @return string|false
*/
public function readBytes($length, $position = null)
{
$length = (int) $length;
if ($position !== null) {
// check if needed bytes are available in the current buffer
if (!($position >= $this->position && $position < $this->position + $this->bufferLength)) {
$this->reset($position, $length);
$offset = $this->offset;
} else {
$offset = $position - $this->position;
}
} else {
$offset = $this->offset;
}
if (
($offset + $length) > $this->bufferLength
&& ((!$this->increaseLength($length)) || ($offset + $length) > $this->bufferLength)
) {
return false;
}
$bytes = \substr($this->buffer, $offset, $length);
$this->offset = $offset + $length;
return $bytes;
}
/**
* Read a line from the current position.
*
* @param int $length
* @return string|bool
*/
public function readLine($length = 1024)
{
if ($this->ensureContent() === false) {
return false;
}
$line = '';
while ($this->ensureContent()) {
$char = $this->readByte();
if ($char === "\n") {
break;
}
if ($char === "\r") {
if ($this->getByte() === "\n") {
$this->addOffset(1);
}
break;
}
$line .= $char;
if (\strlen($line) >= $length) {
break;
}
}
return $line;
}
/**
* Set the offset position in the current buffer.
*
* @param int $offset
*/
public function setOffset($offset)
{
if ($offset > $this->bufferLength || $offset < 0) {
throw new \OutOfRangeException(
\sprintf('Offset (%s) out of range (length: %s)', $offset, $this->bufferLength)
);
}
$this->offset = (int) $offset;
}
/**
* Returns the current offset in the current buffer.
*
* @return int
*/
public function getOffset()
{
return $this->offset;
}
/**
* Add an offset to the current offset.
*
* @param int $offset
*/
public function addOffset($offset)
{
$this->setOffset($this->offset + $offset);
}
/**
* Make sure that there is at least one character beyond the current offset in the buffer.
*
* @return bool
*/
public function ensureContent()
{
while ($this->offset >= $this->bufferLength) {
if (!$this->increaseLength()) {
return false;
}
}
return true;
}
/**
* Returns the stream.
*
* @return resource
*/
public function getStream()
{
return $this->stream;
}
/**
* Gets the total available length.
*
* @return int
*/
public function getTotalLength()
{
if ($this->totalLength === null) {
$stat = \fstat($this->stream);
$this->totalLength = $stat['size'];
}
return $this->totalLength;
}
/**
* Resets the buffer to a position and re-read the buffer with the given length.
*
* If the $pos parameter is negative the start buffer position will be the $pos'th position from
* the end of the file.
*
* If the $pos parameter is negative and the absolute value is bigger then the totalLength of
* the file $pos will set to zero.
*
* @param int|null $pos Start position of the new buffer
* @param int $length Length of the new buffer. Mustn't be negative
*/
public function reset($pos = 0, $length = 200)
{
if ($pos === null) {
$pos = $this->position + $this->offset;
} elseif ($pos < 0) {
$pos = \max(0, $this->getTotalLength() + $pos);
}
\fseek($this->stream, $pos);
$this->position = $pos;
$this->offset = 0;
if ($length > 0) {
$this->buffer = (string) \fread($this->stream, $length);
} else {
$this->buffer = '';
}
$this->bufferLength = \strlen($this->buffer);
// If a stream wrapper is in use it is possible that
// length values > 8096 will be ignored, so use the
// increaseLength()-method to correct that behavior
if ($this->bufferLength < $length && $this->increaseLength($length - $this->bufferLength)) {
// increaseLength parameter is $minLength, so cut to have only the required bytes in the buffer
$this->buffer = (string) \substr($this->buffer, 0, $length);
$this->bufferLength = \strlen($this->buffer);
}
}
/**
* Ensures bytes in the buffer with a specific length and location in the file.
*
* @param int $pos
* @param int $length
* @see reset()
*/
public function ensure($pos, $length)
{
if (
$pos >= $this->position
&& $pos < ($this->position + $this->bufferLength)
&& ($this->position + $this->bufferLength) >= ($pos + $length)
) {
$this->offset = $pos - $this->position;
} else {
$this->reset($pos, $length);
}
}
/**
* Forcefully read more data into the buffer.
*
* @param int $minLength
* @return bool Returns false if the stream reaches the end
*/
public function increaseLength($minLength = 100)
{
$length = \max($minLength, 100);
if (\feof($this->stream) || $this->getTotalLength() === $this->position + $this->bufferLength) {
return false;
}
$newLength = $this->bufferLength + $length;
do {
$this->buffer .= \fread($this->stream, $newLength - $this->bufferLength);
$this->bufferLength = \strlen($this->buffer);
} while (($this->bufferLength !== $newLength) && !\feof($this->stream));
return true;
}
}

View File

@ -0,0 +1,151 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser;
/**
* A tokenizer class.
*/
class Tokenizer
{
/**
* @var StreamReader
*/
protected $streamReader;
/**
* A token stack.
*
* @var string[]
*/
protected $stack = [];
/**
* Tokenizer constructor.
*
* @param StreamReader $streamReader
*/
public function __construct(StreamReader $streamReader)
{
$this->streamReader = $streamReader;
}
/**
* Get the stream reader instance.
*
* @return StreamReader
*/
public function getStreamReader()
{
return $this->streamReader;
}
/**
* Clear the token stack.
*/
public function clearStack()
{
$this->stack = [];
}
/**
* Push a token onto the stack.
*
* @param string $token
*/
public function pushStack($token)
{
$this->stack[] = $token;
}
/**
* Get next token.
*
* @return false|string
*/
public function getNextToken()
{
$token = \array_pop($this->stack);
if ($token !== null) {
return $token;
}
if (($byte = $this->streamReader->readByte()) === false) {
return false;
}
if (\in_array($byte, ["\x20", "\x0A", "\x0D", "\x0C", "\x09", "\x00"], true)) {
if ($this->leapWhiteSpaces() === false) {
return false;
}
$byte = $this->streamReader->readByte();
}
switch ($byte) {
case '/':
case '[':
case ']':
case '(':
case ')':
case '{':
case '}':
case '<':
case '>':
return $byte;
case '%':
$this->streamReader->readLine();
return $this->getNextToken();
}
/* This way is faster than checking single bytes.
*/
$bufferOffset = $this->streamReader->getOffset();
do {
$lastBuffer = $this->streamReader->getBuffer(false);
$pos = \strcspn(
$lastBuffer,
"\x00\x09\x0A\x0C\x0D\x20()<>[]{}/%",
$bufferOffset
);
} while (
// Break the loop if a delimiter or white space char is matched
// in the current buffer or increase the buffers length
$bufferOffset + $pos === \strlen($lastBuffer)
&& $this->streamReader->increaseLength()
);
$result = \substr($lastBuffer, $bufferOffset - 1, $pos + 1);
$this->streamReader->setOffset($bufferOffset + $pos);
return $result;
}
/**
* Leap white spaces.
*
* @return boolean
*/
public function leapWhiteSpaces()
{
do {
if (!$this->streamReader->ensureContent()) {
return false;
}
$buffer = $this->streamReader->getBuffer(false);
$matches = \strspn($buffer, "\x20\x0A\x0C\x0D\x09\x00", $this->streamReader->getOffset());
if ($matches > 0) {
$this->streamReader->addOffset($matches);
}
} while ($this->streamReader->getOffset() >= $this->streamReader->getBufferLength());
return true;
}
}

View File

@ -0,0 +1,85 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Type;
use setasign\Fpdi\PdfParser\PdfParser;
use setasign\Fpdi\PdfParser\Tokenizer;
/**
* Class representing a PDF array object
*
* @property array $value The value of the PDF type.
*/
class PdfArray extends PdfType
{
/**
* Parses an array of the passed tokenizer and parser.
*
* @param Tokenizer $tokenizer
* @param PdfParser $parser
* @return false|self
* @throws PdfTypeException
*/
public static function parse(Tokenizer $tokenizer, PdfParser $parser)
{
$result = [];
// Recurse into this function until we reach the end of the array.
while (($token = $tokenizer->getNextToken()) !== ']') {
if ($token === false || ($value = $parser->readValue($token)) === false) {
return false;
}
$result[] = $value;
}
$v = new self();
$v->value = $result;
return $v;
}
/**
* Helper method to create an instance.
*
* @param PdfType[] $values
* @return self
*/
public static function create(array $values = [])
{
$v = new self();
$v->value = $values;
return $v;
}
/**
* Ensures that the passed array is a PdfArray instance with a (optional) specific size.
*
* @param mixed $array
* @param null|int $size
* @return self
* @throws PdfTypeException
*/
public static function ensure($array, $size = null)
{
$result = PdfType::ensureType(self::class, $array, 'Array value expected.');
if ($size !== null && \count($array->value) !== $size) {
throw new PdfTypeException(
\sprintf('Array with %s entries expected.', $size),
PdfTypeException::INVALID_DATA_SIZE
);
}
return $result;
}
}

View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Type;
/**
* Class representing a boolean PDF object
*/
class PdfBoolean extends PdfType
{
/**
* Helper method to create an instance.
*
* @param bool $value
* @return self
*/
public static function create($value)
{
$v = new self();
$v->value = (bool) $value;
return $v;
}
/**
* Ensures that the passed value is a PdfBoolean instance.
*
* @param mixed $value
* @return self
* @throws PdfTypeException
*/
public static function ensure($value)
{
return PdfType::ensureType(self::class, $value, 'Boolean value expected.');
}
}

View File

@ -0,0 +1,131 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Type;
use setasign\Fpdi\PdfParser\PdfParser;
use setasign\Fpdi\PdfParser\StreamReader;
use setasign\Fpdi\PdfParser\Tokenizer;
/**
* Class representing a PDF dictionary object
*/
class PdfDictionary extends PdfType
{
/**
* Parses a dictionary of the passed tokenizer, stream-reader and parser.
*
* @param Tokenizer $tokenizer
* @param StreamReader $streamReader
* @param PdfParser $parser
* @return bool|self
* @throws PdfTypeException
*/
public static function parse(Tokenizer $tokenizer, StreamReader $streamReader, PdfParser $parser)
{
$entries = [];
while (true) {
$token = $tokenizer->getNextToken();
if ($token === '>' && $streamReader->getByte() === '>') {
$streamReader->addOffset(1);
break;
}
$key = $parser->readValue($token);
if ($key === false) {
return false;
}
// ensure the first value to be a Name object
if (!($key instanceof PdfName)) {
$lastToken = null;
// ignore all other entries and search for the closing brackets
while (($token = $tokenizer->getNextToken()) !== '>' || $lastToken !== '>') {
if ($token === false) {
return false;
}
$lastToken = $token;
}
break;
}
$value = $parser->readValue();
if ($value === false) {
return false;
}
if ($value instanceof PdfNull) {
continue;
}
// catch missing value
if ($value instanceof PdfToken && $value->value === '>' && $streamReader->getByte() === '>') {
$streamReader->addOffset(1);
break;
}
$entries[$key->value] = $value;
}
$v = new self();
$v->value = $entries;
return $v;
}
/**
* Helper method to create an instance.
*
* @param PdfType[] $entries The keys are the name entries of the dictionary.
* @return self
*/
public static function create(array $entries = [])
{
$v = new self();
$v->value = $entries;
return $v;
}
/**
* Get a value by its key from a dictionary or a default value.
*
* @param mixed $dictionary
* @param string $key
* @param PdfType|null $default
* @return PdfNull|PdfType
* @throws PdfTypeException
*/
public static function get($dictionary, $key, ?PdfType $default = null)
{
$dictionary = self::ensure($dictionary);
if (isset($dictionary->value[$key])) {
return $dictionary->value[$key];
}
return $default ?? new PdfNull();
}
/**
* Ensures that the passed value is a PdfDictionary instance.
*
* @param mixed $dictionary
* @return self
* @throws PdfTypeException
*/
public static function ensure($dictionary)
{
return PdfType::ensureType(self::class, $dictionary, 'Dictionary value expected.');
}
}

View File

@ -0,0 +1,77 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Type;
use setasign\Fpdi\PdfParser\StreamReader;
/**
* Class representing a hexadecimal encoded PDF string object
*/
class PdfHexString extends PdfType
{
/**
* Parses a hexadecimal string object from the stream reader.
*
* @param StreamReader $streamReader
* @return false|self
*/
public static function parse(StreamReader $streamReader)
{
$bufferOffset = $streamReader->getOffset();
while (true) {
$buffer = $streamReader->getBuffer(false);
$pos = \strpos($buffer, '>', $bufferOffset);
if ($pos === false) {
if (!$streamReader->increaseLength()) {
return false;
}
continue;
}
break;
}
$result = \substr($buffer, $bufferOffset, $pos - $bufferOffset);
$streamReader->setOffset($pos + 1);
$v = new self();
$v->value = $result;
return $v;
}
/**
* Helper method to create an instance.
*
* @param string $string The hex encoded string.
* @return self
*/
public static function create($string)
{
$v = new self();
$v->value = $string;
return $v;
}
/**
* Ensures that the passed value is a PdfHexString instance.
*
* @param mixed $hexString
* @return self
* @throws PdfTypeException
*/
public static function ensure($hexString)
{
return PdfType::ensureType(self::class, $hexString, 'Hex string value expected.');
}
}

View File

@ -0,0 +1,103 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Type;
use setasign\Fpdi\PdfParser\PdfParser;
use setasign\Fpdi\PdfParser\StreamReader;
use setasign\Fpdi\PdfParser\Tokenizer;
/**
* Class representing an indirect object
*/
class PdfIndirectObject extends PdfType
{
/**
* Parses an indirect object from a tokenizer, parser and stream-reader.
*
* @param int $objectNumber
* @param int $objectGenerationNumber
* @param PdfParser $parser
* @param Tokenizer $tokenizer
* @param StreamReader $reader
* @return self|false
* @throws PdfTypeException
*/
public static function parse(
$objectNumber,
$objectGenerationNumber,
PdfParser $parser,
Tokenizer $tokenizer,
StreamReader $reader
) {
$value = $parser->readValue();
if ($value === false) {
return false;
}
$nextToken = $tokenizer->getNextToken();
if ($nextToken === 'stream') {
$value = PdfStream::parse($value, $reader, $parser);
} elseif ($nextToken !== false) {
$tokenizer->pushStack($nextToken);
}
$v = new self();
$v->objectNumber = (int) $objectNumber;
$v->generationNumber = (int) $objectGenerationNumber;
$v->value = $value;
return $v;
}
/**
* Helper method to create an instance.
*
* @param int $objectNumber
* @param int $generationNumber
* @param PdfType $value
* @return self
*/
public static function create($objectNumber, $generationNumber, PdfType $value)
{
$v = new self();
$v->objectNumber = (int) $objectNumber;
$v->generationNumber = (int) $generationNumber;
$v->value = $value;
return $v;
}
/**
* Ensures that the passed value is a PdfIndirectObject instance.
*
* @param mixed $indirectObject
* @return self
* @throws PdfTypeException
*/
public static function ensure($indirectObject)
{
return PdfType::ensureType(self::class, $indirectObject, 'Indirect object expected.');
}
/**
* The object number.
*
* @var int
*/
public $objectNumber;
/**
* The generation number.
*
* @var int
*/
public $generationNumber;
}

View File

@ -0,0 +1,52 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Type;
/**
* Class representing an indirect object reference
*/
class PdfIndirectObjectReference extends PdfType
{
/**
* Helper method to create an instance.
*
* @param int $objectNumber
* @param int $generationNumber
* @return self
*/
public static function create($objectNumber, $generationNumber)
{
$v = new self();
$v->value = (int) $objectNumber;
$v->generationNumber = (int) $generationNumber;
return $v;
}
/**
* Ensures that the passed value is a PdfIndirectObject instance.
*
* @param mixed $value
* @return self
* @throws PdfTypeException
*/
public static function ensure($value)
{
return PdfType::ensureType(self::class, $value, 'Indirect reference value expected.');
}
/**
* The generation number.
*
* @var int
*/
public $generationNumber;
}

View File

@ -0,0 +1,82 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Type;
use setasign\Fpdi\PdfParser\StreamReader;
use setasign\Fpdi\PdfParser\Tokenizer;
/**
* Class representing a PDF name object
*/
class PdfName extends PdfType
{
/**
* Parses a name object from the passed tokenizer and stream-reader.
*
* @param Tokenizer $tokenizer
* @param StreamReader $streamReader
* @return self
*/
public static function parse(Tokenizer $tokenizer, StreamReader $streamReader)
{
$v = new self();
if (\strspn($streamReader->getByte(), "\x00\x09\x0A\x0C\x0D\x20()<>[]{}/%") === 0) {
$v->value = (string) $tokenizer->getNextToken();
return $v;
}
$v->value = '';
return $v;
}
/**
* Unescapes a name string.
*
* @param string $value
* @return string
*/
public static function unescape($value)
{
if (\strpos($value, '#') === false) {
return $value;
}
return \preg_replace_callback('/#([a-fA-F\d]{2})/', function ($matches) {
return \chr(\hexdec($matches[1]));
}, $value);
}
/**
* Helper method to create an instance.
*
* @param string $string
* @return self
*/
public static function create($string)
{
$v = new self();
$v->value = $string;
return $v;
}
/**
* Ensures that the passed value is a PdfName instance.
*
* @param mixed $name
* @return self
* @throws PdfTypeException
*/
public static function ensure($name)
{
return PdfType::ensureType(self::class, $name, 'Name value expected.');
}
}

View File

@ -0,0 +1,19 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Type;
/**
* Class representing a PDF null object
*/
class PdfNull extends PdfType
{
// empty body
}

View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Type;
/**
* Class representing a numeric PDF object
*/
class PdfNumeric extends PdfType
{
/**
* Helper method to create an instance.
*
* @param int|float $value
* @return PdfNumeric
*/
public static function create($value)
{
$v = new self();
$v->value = $value + 0;
return $v;
}
/**
* Ensures that the passed value is a PdfNumeric instance.
*
* @param mixed $value
* @return self
* @throws PdfTypeException
*/
public static function ensure($value)
{
return PdfType::ensureType(self::class, $value, 'Numeric value expected.');
}
}

View File

@ -0,0 +1,352 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Type;
use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException;
use setasign\Fpdi\PdfParser\Filter\Ascii85;
use setasign\Fpdi\PdfParser\Filter\AsciiHex;
use setasign\Fpdi\PdfParser\Filter\FilterException;
use setasign\Fpdi\PdfParser\Filter\Flate;
use setasign\Fpdi\PdfParser\Filter\Lzw;
use setasign\Fpdi\PdfParser\PdfParser;
use setasign\Fpdi\PdfParser\PdfParserException;
use setasign\Fpdi\PdfParser\StreamReader;
use setasign\FpdiPdfParser\PdfParser\Filter\Predictor;
/**
* Class representing a PDF stream object
*/
class PdfStream extends PdfType
{
/**
* Parses a stream from a stream reader.
*
* @param PdfDictionary $dictionary
* @param StreamReader $reader
* @param PdfParser|null $parser Optional to keep backwards compatibility
* @return self
* @throws PdfTypeException
*/
public static function parse(PdfDictionary $dictionary, StreamReader $reader, ?PdfParser $parser = null)
{
$v = new self();
$v->value = $dictionary;
$v->reader = $reader;
$v->parser = $parser;
$offset = $reader->getOffset();
// Find the first "newline"
while (($firstByte = $reader->getByte($offset)) !== false) {
$offset++;
if ($firstByte === "\n" || $firstByte === "\r") {
break;
}
}
if ($firstByte === false) {
throw new PdfTypeException(
'Unable to parse stream data. No newline after the stream keyword found.',
PdfTypeException::NO_NEWLINE_AFTER_STREAM_KEYWORD
);
}
$sndByte = $reader->getByte($offset);
if ($sndByte === "\n" && $firstByte !== "\n") {
$offset++;
}
$reader->setOffset($offset);
// let's only save the byte-offset and read the stream only when needed
$v->stream = $reader->getPosition() + $reader->getOffset();
return $v;
}
/**
* Helper method to create an instance.
*
* @param PdfDictionary $dictionary
* @param string $stream
* @return self
*/
public static function create(PdfDictionary $dictionary, $stream)
{
$v = new self();
$v->value = $dictionary;
$v->stream = (string) $stream;
return $v;
}
/**
* Ensures that the passed value is a PdfStream instance.
*
* @param mixed $stream
* @return self
* @throws PdfTypeException
*/
public static function ensure($stream)
{
return PdfType::ensureType(self::class, $stream, 'Stream value expected.');
}
/**
* The stream or its byte-offset position.
*
* @var int|string
*/
protected $stream;
/**
* The stream reader instance.
*
* @var StreamReader|null
*/
protected $reader;
/**
* The PDF parser instance.
*
* @var PdfParser
*/
protected $parser;
/**
* Get the stream data.
*
* @param bool $cache Whether cache the stream data or not.
* @return bool|string
* @throws PdfTypeException
* @throws CrossReferenceException
* @throws PdfParserException
*/
public function getStream($cache = false)
{
if (\is_int($this->stream)) {
$length = PdfDictionary::get($this->value, 'Length');
if ($this->parser !== null) {
$length = PdfType::resolve($length, $this->parser);
}
if (!($length instanceof PdfNumeric) || $length->value === 0) {
$this->reader->reset($this->stream, 100000);
$buffer = $this->extractStream();
} else {
$this->reader->reset($this->stream, $length->value);
$buffer = $this->reader->getBuffer(false);
if ($this->parser !== null) {
$this->reader->reset($this->stream + strlen($buffer));
$this->parser->getTokenizer()->clearStack();
$token = $this->parser->readValue();
if (!($token instanceof PdfToken) || $token->value !== 'endstream') {
$this->reader->reset($this->stream, 100000);
$buffer = $this->extractStream();
$this->reader->reset($this->stream + strlen($buffer));
}
}
}
if ($cache === false) {
return $buffer;
}
$this->stream = $buffer;
$this->reader = null;
}
return $this->stream;
}
/**
* Extract the stream "manually".
*
* @return string
* @throws PdfTypeException
*/
protected function extractStream()
{
while (true) {
$buffer = $this->reader->getBuffer(false);
$length = \strpos($buffer, 'endstream');
if ($length === false) {
if (!$this->reader->increaseLength(100000)) {
throw new PdfTypeException('Cannot extract stream.');
}
continue;
}
break;
}
$buffer = \substr($buffer, 0, $length);
$lastByte = \substr($buffer, -1);
/* Check for EOL marker =
* CARRIAGE RETURN (\r) and a LINE FEED (\n) or just a LINE FEED (\n},
* and not by a CARRIAGE RETURN (\r) alone
*/
if ($lastByte === "\n") {
$buffer = \substr($buffer, 0, -1);
$lastByte = \substr($buffer, -1);
if ($lastByte === "\r") {
$buffer = \substr($buffer, 0, -1);
}
}
// There are streams in the wild, which have only white signs in them but need to be parsed manually due
// to a problem encountered before (e.g. Length === 0). We should set them to empty streams to avoid problems
// in further processing (e.g. applying of filters).
if (\trim($buffer) === '') {
$buffer = '';
}
return $buffer;
}
/**
* Get all filters defined for this stream.
*
* @return PdfType[]
* @throws PdfTypeException
*/
public function getFilters()
{
$filters = PdfDictionary::get($this->value, 'Filter');
if ($filters instanceof PdfNull) {
return [];
}
if ($filters instanceof PdfArray) {
$filters = $filters->value;
} else {
$filters = [$filters];
}
return $filters;
}
/**
* Get the unfiltered stream data.
*
* @return string
* @throws FilterException
* @throws PdfParserException
*/
public function getUnfilteredStream()
{
$stream = $this->getStream();
$filters = $this->getFilters();
if ($filters === []) {
return $stream;
}
$decodeParams = PdfDictionary::get($this->value, 'DecodeParms');
if ($decodeParams instanceof PdfArray) {
$decodeParams = $decodeParams->value;
} else {
$decodeParams = [$decodeParams];
}
foreach ($filters as $key => $filter) {
if (!($filter instanceof PdfName)) {
continue;
}
$decodeParam = null;
if (isset($decodeParams[$key])) {
$decodeParam = ($decodeParams[$key] instanceof PdfDictionary ? $decodeParams[$key] : null);
}
switch ($filter->value) {
case 'FlateDecode':
case 'Fl':
case 'LZWDecode':
case 'LZW':
if (\strpos($filter->value, 'LZW') === 0) {
$filterObject = new Lzw();
} else {
$filterObject = new Flate();
}
$stream = $filterObject->decode($stream);
if ($decodeParam instanceof PdfDictionary) {
$predictor = PdfDictionary::get($decodeParam, 'Predictor', PdfNumeric::create(1));
if ($predictor->value !== 1) {
if (!\class_exists(Predictor::class)) {
throw new PdfParserException(
'This PDF document makes use of features which are only implemented in the ' .
'commercial "FPDI PDF-Parser" add-on (see https://www.setasign.com/fpdi-pdf-' .
'parser).',
PdfParserException::IMPLEMENTED_IN_FPDI_PDF_PARSER
);
}
$colors = PdfDictionary::get($decodeParam, 'Colors', PdfNumeric::create(1));
$bitsPerComponent = PdfDictionary::get(
$decodeParam,
'BitsPerComponent',
PdfNumeric::create(8)
);
$columns = PdfDictionary::get($decodeParam, 'Columns', PdfNumeric::create(1));
$filterObject = new Predictor(
$predictor->value,
$colors->value,
$bitsPerComponent->value,
$columns->value
);
$stream = $filterObject->decode($stream);
}
}
break;
case 'ASCII85Decode':
case 'A85':
$filterObject = new Ascii85();
$stream = $filterObject->decode($stream);
break;
case 'ASCIIHexDecode':
case 'AHx':
$filterObject = new AsciiHex();
$stream = $filterObject->decode($stream);
break;
case 'Crypt':
if (!$decodeParam instanceof PdfDictionary) {
break;
}
// Filter is "Identity"
$name = PdfDictionary::get($decodeParam, 'Name');
if (!$name instanceof PdfName || $name->value !== 'Identity') {
break;
}
throw new FilterException(
'Support for Crypt filters other than "Identity" is not implemented.',
FilterException::UNSUPPORTED_FILTER
);
default:
throw new FilterException(
\sprintf('Unsupported filter "%s".', $filter->value),
FilterException::UNSUPPORTED_FILTER
);
}
}
return $stream;
}
}

View File

@ -0,0 +1,202 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Type;
use setasign\Fpdi\PdfParser\StreamReader;
/**
* Class representing a PDF string object
*/
class PdfString extends PdfType
{
/**
* Parses a string object from the stream reader.
*
* @param StreamReader $streamReader
* @return self
*/
public static function parse(StreamReader $streamReader)
{
$pos = $startPos = $streamReader->getOffset();
$openBrackets = 1;
do {
$buffer = $streamReader->getBuffer(false);
for ($length = \strlen($buffer); $openBrackets !== 0 && $pos < $length; $pos++) {
switch ($buffer[$pos]) {
case '(':
$openBrackets++;
break;
case ')':
$openBrackets--;
break;
case '\\':
$pos++;
}
}
} while ($openBrackets !== 0 && $streamReader->increaseLength());
$result = \substr($buffer, $startPos, $openBrackets + $pos - $startPos - 1);
$streamReader->setOffset($pos);
$v = new self();
$v->value = $result;
return $v;
}
/**
* Helper method to create an instance.
*
* @param string $value The string needs to be escaped accordingly.
* @return self
*/
public static function create($value)
{
$v = new self();
$v->value = $value;
return $v;
}
/**
* Ensures that the passed value is a PdfString instance.
*
* @param mixed $string
* @return self
* @throws PdfTypeException
*/
public static function ensure($string)
{
return PdfType::ensureType(self::class, $string, 'String value expected.');
}
/**
* Escapes sequences in a string according to the PDF specification.
*
* @param string $s
* @return string
*/
public static function escape($s)
{
// Still a bit faster, than direct replacing
if (
\strpos($s, '\\') !== false ||
\strpos($s, ')') !== false ||
\strpos($s, '(') !== false ||
\strpos($s, "\x0D") !== false ||
\strpos($s, "\x0A") !== false ||
\strpos($s, "\x09") !== false ||
\strpos($s, "\x08") !== false ||
\strpos($s, "\x0C") !== false
) {
// is faster than strtr(...)
return \str_replace(
['\\', ')', '(', "\x0D", "\x0A", "\x09", "\x08", "\x0C"],
['\\\\', '\\)', '\\(', '\r', '\n', '\t', '\b', '\f'],
$s
);
}
return $s;
}
/**
* Unescapes escaped sequences in a PDF string according to the PDF specification.
*
* @param string $s
* @return string
*/
public static function unescape($s)
{
$out = '';
/** @noinspection ForeachInvariantsInspection */
for ($count = 0, $n = \strlen($s); $count < $n; $count++) {
if ($s[$count] !== '\\') {
$out .= $s[$count];
} else {
// A backslash at the end of the string - ignore it
if ($count === ($n - 1)) {
break;
}
switch ($s[++$count]) {
case ')':
case '(':
case '\\':
$out .= $s[$count];
break;
case 'f':
$out .= "\x0C";
break;
case 'b':
$out .= "\x08";
break;
case 't':
$out .= "\x09";
break;
case 'r':
$out .= "\x0D";
break;
case 'n':
$out .= "\x0A";
break;
case "\r":
if ($count !== $n - 1 && $s[$count + 1] === "\n") {
$count++;
}
break;
case "\n":
break;
default:
$actualChar = \ord($s[$count]);
// ascii 48 = number 0
// ascii 57 = number 9
if ($actualChar >= 48 && $actualChar <= 57) {
$oct = '' . $s[$count];
/** @noinspection NotOptimalIfConditionsInspection */
if (
$count + 1 < $n
&& \ord($s[$count + 1]) >= 48
&& \ord($s[$count + 1]) <= 57
) {
$count++;
$oct .= $s[$count];
/** @noinspection NotOptimalIfConditionsInspection */
if (
$count + 1 < $n
&& \ord($s[$count + 1]) >= 48
&& \ord($s[$count + 1]) <= 57
) {
$oct .= $s[++$count];
}
}
$out .= \chr(\octdec($oct));
} else {
// If the character is not one of those defined, the backslash is ignored
$out .= $s[$count];
}
}
}
}
return $out;
}
}

View File

@ -0,0 +1,43 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Type;
/**
* Class representing PDF token object
*/
class PdfToken extends PdfType
{
/**
* Helper method to create an instance.
*
* @param string $token
* @return self
*/
public static function create($token)
{
$v = new self();
$v->value = $token;
return $v;
}
/**
* Ensures that the passed value is a PdfToken instance.
*
* @param mixed $token
* @return self
* @throws PdfTypeException
*/
public static function ensure($token)
{
return PdfType::ensureType(self::class, $token, 'Token value expected.');
}
}

View File

@ -0,0 +1,117 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Type;
use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException;
use setasign\Fpdi\PdfParser\PdfParser;
use setasign\Fpdi\PdfParser\PdfParserException;
/**
* A class defining a PDF data type
*/
class PdfType
{
/**
* Resolves a PdfType value to its value.
*
* This method is used to evaluate indirect and direct object references until a final value is reached.
*
* @param PdfType $value
* @param PdfParser $parser
* @param bool $stopAtIndirectObject
* @param array $ensuredObjectsList A list of all ensured indirect objects to prevent recursion
* @return PdfType
* @throws CrossReferenceException
* @throws PdfParserException
*/
public static function resolve(
PdfType $value,
PdfParser $parser,
$stopAtIndirectObject = false,
array &$ensuredObjectsList = []
) {
if ($value instanceof PdfIndirectObjectReference) {
$value = $parser->getIndirectObject($value->value);
}
if ($value instanceof PdfIndirectObject) {
if ($stopAtIndirectObject === true) {
return $value;
}
if (\in_array($value->objectNumber, $ensuredObjectsList, true)) {
throw new PdfParserException(
\sprintf('Indirect reference recursion detected (%s).', $value->objectNumber)
);
}
$ensuredObjectsList[] = $value->objectNumber;
return self::resolve($value->value, $parser, $stopAtIndirectObject, $ensuredObjectsList);
}
return $value;
}
/**
* Ensure that a value is an instance of a specific PDF type.
*
* @param string $type
* @param PdfType $value
* @param string $errorMessage
* @return mixed
* @throws PdfTypeException
*/
protected static function ensureType($type, $value, $errorMessage)
{
if (!($value instanceof $type)) {
throw new PdfTypeException(
$errorMessage,
PdfTypeException::INVALID_DATA_TYPE
);
}
return $value;
}
/**
* Flatten indirect object references to direct objects.
*
* @param PdfType $value
* @param PdfParser $parser
* @return PdfType
* @throws CrossReferenceException
* @throws PdfParserException
*/
public static function flatten(PdfType $value, PdfParser $parser)
{
if ($value instanceof PdfIndirectObjectReference) {
return self::flatten(self::resolve($value, $parser), $parser);
}
if ($value instanceof PdfDictionary || $value instanceof PdfArray) {
foreach ($value->value as $key => $_value) {
$value->value[$key] = self::flatten($_value, $parser);
}
}
if ($value instanceof PdfStream) {
throw new PdfTypeException('There is a stream object found which cannot be flattened to a direct object.');
}
return $value;
}
/**
* The value of the PDF type.
*
* @var mixed
*/
public $value;
}

View File

@ -0,0 +1,24 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfParser\Type;
use setasign\Fpdi\PdfParser\PdfParserException;
/**
* Exception class for pdf type classes
*/
class PdfTypeException extends PdfParserException
{
/**
* @var int
*/
const NO_NEWLINE_AFTER_STREAM_KEYWORD = 0x0601;
}

View File

@ -0,0 +1,179 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfReader\DataStructure;
use setasign\Fpdi\Math\Vector;
use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException;
use setasign\Fpdi\PdfParser\PdfParser;
use setasign\Fpdi\PdfParser\PdfParserException;
use setasign\Fpdi\PdfParser\Type\PdfArray;
use setasign\Fpdi\PdfParser\Type\PdfNumeric;
use setasign\Fpdi\PdfParser\Type\PdfType;
use setasign\Fpdi\PdfParser\Type\PdfTypeException;
/**
* Class representing a rectangle
*/
class Rectangle
{
/**
* @var int|float
*/
protected $llx;
/**
* @var int|float
*/
protected $lly;
/**
* @var int|float
*/
protected $urx;
/**
* @var int|float
*/
protected $ury;
/**
* Create a rectangle instance by a PdfArray.
*
* @param PdfArray|mixed $array
* @param PdfParser $parser
* @return Rectangle
* @throws PdfTypeException
* @throws CrossReferenceException
* @throws PdfParserException
*/
public static function byPdfArray($array, PdfParser $parser)
{
$array = PdfArray::ensure(PdfType::resolve($array, $parser), 4)->value;
$ax = PdfNumeric::ensure(PdfType::resolve($array[0], $parser))->value;
$ay = PdfNumeric::ensure(PdfType::resolve($array[1], $parser))->value;
$bx = PdfNumeric::ensure(PdfType::resolve($array[2], $parser))->value;
$by = PdfNumeric::ensure(PdfType::resolve($array[3], $parser))->value;
return new self($ax, $ay, $bx, $by);
}
public static function byVectors(Vector $ll, Vector $ur)
{
return new self($ll->getX(), $ll->getY(), $ur->getX(), $ur->getY());
}
/**
* Rectangle constructor.
*
* @param float|int $ax
* @param float|int $ay
* @param float|int $bx
* @param float|int $by
*/
public function __construct($ax, $ay, $bx, $by)
{
$this->llx = \min($ax, $bx);
$this->lly = \min($ay, $by);
$this->urx = \max($ax, $bx);
$this->ury = \max($ay, $by);
}
/**
* Get the width of the rectangle.
*
* @return float|int
*/
public function getWidth()
{
return $this->urx - $this->llx;
}
/**
* Get the height of the rectangle.
*
* @return float|int
*/
public function getHeight()
{
return $this->ury - $this->lly;
}
/**
* Get the lower left abscissa.
*
* @return float|int
*/
public function getLlx()
{
return $this->llx;
}
/**
* Get the lower left ordinate.
*
* @return float|int
*/
public function getLly()
{
return $this->lly;
}
/**
* Get the upper right abscissa.
*
* @return float|int
*/
public function getUrx()
{
return $this->urx;
}
/**
* Get the upper right ordinate.
*
* @return float|int
*/
public function getUry()
{
return $this->ury;
}
/**
* Get the rectangle as an array.
*
* @return array
*/
public function toArray()
{
return [
$this->llx,
$this->lly,
$this->urx,
$this->ury
];
}
/**
* Get the rectangle as a PdfArray.
*
* @return PdfArray
*/
public function toPdfArray()
{
$array = new PdfArray();
$array->value[] = PdfNumeric::create($this->llx);
$array->value[] = PdfNumeric::create($this->lly);
$array->value[] = PdfNumeric::create($this->urx);
$array->value[] = PdfNumeric::create($this->ury);
return $array;
}
}

View File

@ -0,0 +1,443 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfReader;
use setasign\Fpdi\FpdiException;
use setasign\Fpdi\GraphicsState;
use setasign\Fpdi\Math\Vector;
use setasign\Fpdi\PdfParser\Filter\FilterException;
use setasign\Fpdi\PdfParser\PdfParser;
use setasign\Fpdi\PdfParser\PdfParserException;
use setasign\Fpdi\PdfParser\Type\PdfArray;
use setasign\Fpdi\PdfParser\Type\PdfDictionary;
use setasign\Fpdi\PdfParser\Type\PdfHexString;
use setasign\Fpdi\PdfParser\Type\PdfIndirectObject;
use setasign\Fpdi\PdfParser\Type\PdfName;
use setasign\Fpdi\PdfParser\Type\PdfNull;
use setasign\Fpdi\PdfParser\Type\PdfNumeric;
use setasign\Fpdi\PdfParser\Type\PdfStream;
use setasign\Fpdi\PdfParser\Type\PdfString;
use setasign\Fpdi\PdfParser\Type\PdfType;
use setasign\Fpdi\PdfParser\Type\PdfTypeException;
use setasign\Fpdi\PdfReader\DataStructure\Rectangle;
use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException;
/**
* Class representing a page of a PDF document
*/
class Page
{
/**
* @var PdfIndirectObject
*/
protected $pageObject;
/**
* @var PdfDictionary
*/
protected $pageDictionary;
/**
* @var PdfParser
*/
protected $parser;
/**
* Inherited attributes
*
* @var null|array
*/
protected $inheritedAttributes;
/**
* Page constructor.
*
* @param PdfIndirectObject $page
* @param PdfParser $parser
*/
public function __construct(PdfIndirectObject $page, PdfParser $parser)
{
$this->pageObject = $page;
$this->parser = $parser;
}
/**
* Get the indirect object of this page.
*
* @return PdfIndirectObject
*/
public function getPageObject()
{
return $this->pageObject;
}
/**
* Get the dictionary of this page.
*
* @return PdfDictionary
* @throws PdfParserException
* @throws PdfTypeException
* @throws CrossReferenceException
*/
public function getPageDictionary()
{
if ($this->pageDictionary === null) {
$this->pageDictionary = PdfDictionary::ensure(PdfType::resolve($this->getPageObject(), $this->parser));
}
return $this->pageDictionary;
}
/**
* Get a page attribute.
*
* @param string $name
* @param bool $inherited
* @return PdfType|null
* @throws PdfParserException
* @throws PdfTypeException
* @throws CrossReferenceException
*/
public function getAttribute($name, $inherited = true)
{
$dict = $this->getPageDictionary();
if (isset($dict->value[$name])) {
return $dict->value[$name];
}
$inheritedKeys = ['Resources', 'MediaBox', 'CropBox', 'Rotate'];
if ($inherited && \in_array($name, $inheritedKeys, true)) {
if ($this->inheritedAttributes === null) {
$this->inheritedAttributes = [];
$inheritedKeys = \array_filter($inheritedKeys, static function ($key) use ($dict) {
return !isset($dict->value[$key]);
});
if (\count($inheritedKeys) > 0) {
$ensuredObjectList = [];
$parentDict = PdfType::resolve(
PdfDictionary::get($dict, 'Parent'),
$this->parser,
false,
$ensuredObjectList
);
while ($parentDict instanceof PdfDictionary) {
foreach ($inheritedKeys as $index => $key) {
if (isset($parentDict->value[$key])) {
$this->inheritedAttributes[$key] = $parentDict->value[$key];
unset($inheritedKeys[$index]);
}
}
/** @noinspection NotOptimalIfConditionsInspection */
if (isset($parentDict->value['Parent']) && \count($inheritedKeys) > 0) {
$parentDict = PdfType::resolve(
PdfDictionary::get($parentDict, 'Parent'),
$this->parser,
false,
$ensuredObjectList
);
} else {
break;
}
}
}
}
if (isset($this->inheritedAttributes[$name])) {
return $this->inheritedAttributes[$name];
}
}
return null;
}
/**
* Get the rotation value.
*
* @return int
* @throws PdfParserException
* @throws PdfTypeException
* @throws CrossReferenceException
*/
public function getRotation()
{
$rotation = $this->getAttribute('Rotate');
if ($rotation === null) {
return 0;
}
$rotation = PdfNumeric::ensure(PdfType::resolve($rotation, $this->parser))->value % 360;
if ($rotation < 0) {
$rotation += 360;
}
return $rotation;
}
/**
* Get a boundary of this page.
*
* @param string $box
* @param bool $fallback
* @return bool|Rectangle
* @throws PdfParserException
* @throws PdfTypeException
* @throws CrossReferenceException
* @see PageBoundaries
*/
public function getBoundary($box = PageBoundaries::CROP_BOX, $fallback = true)
{
$value = $this->getAttribute($box);
if ($value !== null) {
return Rectangle::byPdfArray($value, $this->parser);
}
if ($fallback === false) {
return false;
}
switch ($box) {
case PageBoundaries::BLEED_BOX:
case PageBoundaries::TRIM_BOX:
case PageBoundaries::ART_BOX:
return $this->getBoundary(PageBoundaries::CROP_BOX, true);
case PageBoundaries::CROP_BOX:
return $this->getBoundary(PageBoundaries::MEDIA_BOX, true);
}
return false;
}
/**
* Get the width and height of this page.
*
* @param string $box
* @param bool $fallback
* @return array|bool
* @throws PdfParserException
* @throws PdfTypeException
* @throws CrossReferenceException
*/
public function getWidthAndHeight($box = PageBoundaries::CROP_BOX, $fallback = true)
{
$boundary = $this->getBoundary($box, $fallback);
if ($boundary === false) {
return false;
}
$rotation = $this->getRotation();
$interchange = ($rotation / 90) % 2;
return [
$interchange ? $boundary->getHeight() : $boundary->getWidth(),
$interchange ? $boundary->getWidth() : $boundary->getHeight()
];
}
/**
* Get the raw content stream.
*
* @return string
* @throws PdfReaderException
* @throws PdfTypeException
* @throws FilterException
* @throws PdfParserException
*/
public function getContentStream()
{
$dict = $this->getPageDictionary();
$contents = PdfType::resolve(PdfDictionary::get($dict, 'Contents'), $this->parser);
if ($contents instanceof PdfNull) {
return '';
}
if ($contents instanceof PdfArray) {
$result = [];
foreach ($contents->value as $content) {
$content = PdfType::resolve($content, $this->parser);
if (!($content instanceof PdfStream)) {
continue;
}
try {
$result[] = $content->getUnfilteredStream();
} catch (FilterException $e) {
// ignore streams that cannot be unfiltered
}
}
return \implode("\n", $result);
}
if ($contents instanceof PdfStream) {
try {
return $contents->getUnfilteredStream();
} catch (FilterException $e) {
// ignore streams that cannot be unfiltered
return '';
}
}
throw new PdfReaderException(
'Array or stream expected.',
PdfReaderException::UNEXPECTED_DATA_TYPE
);
}
/**
* Get information of all external links on this page.
*
* All coordinates are normalized in view to rotation and translation of the boundary-box, so that their
* origin is lower-left.
*
* The URI is the binary value of the PDF string object. It can be in PdfDocEncoding or in UTF-16BE encoding.
*
* @return array
*/
public function getExternalLinks($box = PageBoundaries::CROP_BOX)
{
try {
$dict = $this->getPageDictionary();
$annotations = PdfType::resolve(PdfDictionary::get($dict, 'Annots'), $this->parser);
} catch (FpdiException $e) {
return [];
}
if (!$annotations instanceof PdfArray) {
return [];
}
$links = [];
foreach ($annotations->value as $entry) {
try {
$annotation = PdfType::resolve($entry, $this->parser);
$value = PdfType::resolve(PdfDictionary::get($annotation, 'Subtype'), $this->parser);
if (!$value instanceof PdfName || $value->value !== 'Link') {
continue;
}
$dest = PdfType::resolve(PdfDictionary::get($annotation, 'Dest'), $this->parser);
if (!$dest instanceof PdfNull) {
continue;
}
$action = PdfType::resolve(PdfDictionary::get($annotation, 'A'), $this->parser);
if (!$action instanceof PdfDictionary) {
continue;
}
$actionType = PdfType::resolve(PdfDictionary::get($action, 'S'), $this->parser);
if (!$actionType instanceof PdfName || $actionType->value !== 'URI') {
continue;
}
$uri = PdfType::resolve(PdfDictionary::get($action, 'URI'), $this->parser);
if ($uri instanceof PdfString) {
$uriValue = PdfString::unescape($uri->value);
} elseif ($uri instanceof PdfHexString) {
$uriValue = \hex2bin($uri->value);
} else {
continue;
}
$rect = PdfType::resolve(PdfDictionary::get($annotation, 'Rect'), $this->parser);
if (!$rect instanceof PdfArray || \count($rect->value) !== 4) {
continue;
}
$rect = Rectangle::byPdfArray($rect, $this->parser);
if ($rect->getWidth() === 0 || $rect->getHeight() === 0) {
continue;
}
$bbox = $this->getBoundary($box);
$rotation = $this->getRotation();
$gs = new GraphicsState();
$gs->translate(-$bbox->getLlx(), -$bbox->getLly());
$gs->rotate($bbox->getLlx(), $bbox->getLly(), -$rotation);
switch ($rotation) {
case 90:
$gs->translate(-$bbox->getWidth(), 0);
break;
case 180:
$gs->translate(-$bbox->getWidth(), -$bbox->getHeight());
break;
case 270:
$gs->translate(0, -$bbox->getHeight());
break;
}
$normalizedRect = Rectangle::byVectors(
$gs->toUserSpace(new Vector($rect->getLlx(), $rect->getLly())),
$gs->toUserSpace(new Vector($rect->getUrx(), $rect->getUry()))
);
$quadPoints = PdfType::resolve(PdfDictionary::get($annotation, 'QuadPoints'), $this->parser);
$normalizedQuadPoints = [];
if ($quadPoints instanceof PdfArray) {
$quadPointsCount = \count($quadPoints->value);
if ($quadPointsCount % 8 === 0) {
for ($i = 0; ($i + 1) < $quadPointsCount; $i += 2) {
$x = PdfNumeric::ensure(PdfType::resolve($quadPoints->value[$i], $this->parser));
$y = PdfNumeric::ensure(PdfType::resolve($quadPoints->value[$i + 1], $this->parser));
$v = $gs->toUserSpace(new Vector($x->value, $y->value));
$normalizedQuadPoints[] = $v->getX();
$normalizedQuadPoints[] = $v->getY();
}
}
}
// we remove unsupported/unneeded values here
unset(
$annotation->value['P'],
$annotation->value['NM'],
$annotation->value['AP'],
$annotation->value['AS'],
$annotation->value['Type'],
$annotation->value['Subtype'],
$annotation->value['Rect'],
$annotation->value['A'],
$annotation->value['QuadPoints'],
$annotation->value['Rotate'],
$annotation->value['M'],
$annotation->value['StructParent'],
$annotation->value['OC']
);
// ...and flatten the PDF object to eliminate any indirect references.
// Indirect references are a problem when writing the output in FPDF
// because FPDF uses pre-calculated object numbers while FPDI creates
// them at runtime.
$annotation = PdfType::flatten($annotation, $this->parser);
$links[] = [
'rect' => $normalizedRect,
'quadPoints' => $normalizedQuadPoints,
'uri' => $uriValue,
'pdfObject' => $annotation
];
} catch (FpdiException $e) {
continue;
}
}
return $links;
}
}

View File

@ -0,0 +1,94 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfReader;
/**
* An abstract class for page boundary constants and some helper methods
*/
abstract class PageBoundaries
{
/**
* MediaBox
*
* The media box defines the boundaries of the physical medium on which the page is to be printed.
*
* @see PDF 32000-1:2008 - 14.11.2 Page Boundaries
* @var string
*/
const MEDIA_BOX = 'MediaBox';
/**
* CropBox
*
* The crop box defines the region to which the contents of the page shall be clipped (cropped) when displayed or
* printed.
*
* @see PDF 32000-1:2008 - 14.11.2 Page Boundaries
* @var string
*/
const CROP_BOX = 'CropBox';
/**
* BleedBox
*
* The bleed box defines the region to which the contents of the page shall be clipped when output in a
* production environment.
*
* @see PDF 32000-1:2008 - 14.11.2 Page Boundaries
* @var string
*/
const BLEED_BOX = 'BleedBox';
/**
* TrimBox
*
* The trim box defines the intended dimensions of the finished page after trimming.
*
* @see PDF 32000-1:2008 - 14.11.2 Page Boundaries
* @var string
*/
const TRIM_BOX = 'TrimBox';
/**
* ArtBox
*
* The art box defines the extent of the pages meaningful content (including potential white space) as intended
* by the pages creator.
*
* @see PDF 32000-1:2008 - 14.11.2 Page Boundaries
* @var string
*/
const ART_BOX = 'ArtBox';
/**
* All page boundaries
*
* @var array
*/
public static $all = array(
self::MEDIA_BOX,
self::CROP_BOX,
self::BLEED_BOX,
self::TRIM_BOX,
self::ART_BOX
);
/**
* Checks if a name is a valid page boundary name.
*
* @param string $name The boundary name
* @return boolean A boolean value whether the name is valid or not.
*/
public static function isValidName($name)
{
return \in_array($name, self::$all, true);
}
}

View File

@ -0,0 +1,259 @@
<?php
/**
* This file is part of FPDI
*
* @package setasign\Fpdi
* @copyright Copyright (c) 2026 Setasign GmbH & Co. KG (https://www.setasign.com)
* @license http://opensource.org/licenses/mit-license The MIT License
*/
namespace setasign\Fpdi\PdfReader;
use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException;
use setasign\Fpdi\PdfParser\PdfParser;
use setasign\Fpdi\PdfParser\PdfParserException;
use setasign\Fpdi\PdfParser\Type\PdfArray;
use setasign\Fpdi\PdfParser\Type\PdfDictionary;
use setasign\Fpdi\PdfParser\Type\PdfIndirectObject;
use setasign\Fpdi\PdfParser\Type\PdfIndirectObjectReference;
use setasign\Fpdi\PdfParser\Type\PdfNumeric;
use setasign\Fpdi\PdfParser\Type\PdfType;
use setasign\Fpdi\PdfParser\Type\PdfTypeException;
/**
* A PDF reader class
*/
class PdfReader
{
/**
* @var PdfParser
*/
protected $parser;
/**
* @var int
*/
protected $pageCount;
/**
* Indirect objects of resolved pages.
*
* @var PdfIndirectObjectReference[]|PdfIndirectObject[]
*/
protected $pages = [];
/**
* PdfReader constructor.
*
* @param PdfParser $parser
*/
public function __construct(PdfParser $parser)
{
$this->parser = $parser;
}
/**
* PdfReader destructor.
*/
public function __destruct()
{
if ($this->parser !== null) {
$this->parser->cleanUp();
}
}
/**
* Get the pdf parser instance.
*
* @return PdfParser
*/
public function getParser()
{
return $this->parser;
}
/**
* Get the PDF version.
*
* @return string
* @throws PdfParserException
*/
public function getPdfVersion()
{
return \implode('.', $this->parser->getPdfVersion());
}
/**
* Get the page count.
*
* @return int
* @throws PdfTypeException
* @throws CrossReferenceException
* @throws PdfParserException
*/
public function getPageCount()
{
if ($this->pageCount === null) {
$catalog = $this->parser->getCatalog();
$pages = PdfType::resolve(PdfDictionary::get($catalog, 'Pages'), $this->parser);
$count = PdfType::resolve(PdfDictionary::get($pages, 'Count'), $this->parser);
$this->pageCount = PdfNumeric::ensure($count)->value;
}
return $this->pageCount;
}
/**
* Get a page instance.
*
* @param int|numeric-string $pageNumber
* @return Page
* @throws PdfTypeException
* @throws CrossReferenceException
* @throws PdfParserException
* @throws \InvalidArgumentException
*/
public function getPage($pageNumber)
{
if (!\is_numeric($pageNumber)) {
throw new \InvalidArgumentException(
'Page number needs to be a number.'
);
}
$pageNumber = (int)$pageNumber;
if ($pageNumber < 1 || $pageNumber > $this->getPageCount()) {
throw new \InvalidArgumentException(
\sprintf(
'Page number "%s" out of available page range (1 - %s)',
$pageNumber,
$this->getPageCount()
)
);
}
$this->readPages();
$page = $this->pages[$pageNumber - 1];
if ($page instanceof PdfIndirectObjectReference) {
$alreadyReadKids = [];
$readPages = function ($kids) use (&$readPages, &$alreadyReadKids) {
$kids = PdfArray::ensure($kids);
/** @noinspection LoopWhichDoesNotLoopInspection */
foreach ($kids->value as $reference) {
$reference = PdfIndirectObjectReference::ensure($reference);
if (\in_array($reference->value, $alreadyReadKids, true)) {
throw new PdfReaderException('Recursive pages dictionary detected.');
}
$alreadyReadKids[] = $reference->value;
$object = $this->parser->getIndirectObject($reference->value);
$type = PdfDictionary::get($object->value, 'Type');
if ($type->value === 'Pages') {
return $readPages(PdfDictionary::get($object->value, 'Kids'));
}
return $object;
}
throw new PdfReaderException(
'Kids array cannot be empty.',
PdfReaderException::KIDS_EMPTY
);
};
$page = $this->parser->getIndirectObject($page->value);
$dict = PdfType::resolve($page, $this->parser);
$type = PdfDictionary::get($dict, 'Type');
if ($type->value === 'Pages') {
$kids = PdfType::resolve(PdfDictionary::get($dict, 'Kids'), $this->parser);
try {
$alreadyReadKids[] = $page->objectNumber;
$page = $this->pages[$pageNumber - 1] = $readPages($kids);
} catch (PdfReaderException $e) {
if ($e->getCode() !== PdfReaderException::KIDS_EMPTY) {
throw $e;
}
// let's reset the pages array and read all page objects
$this->pages = [];
$this->readPages(true);
// @phpstan-ignore-next-line
$page = $this->pages[$pageNumber - 1];
}
} else {
$this->pages[$pageNumber - 1] = $page;
}
}
return new Page($page, $this->parser);
}
/**
* Walk the page tree and resolve all indirect objects of all pages.
*
* @param bool $readAll
* @throws CrossReferenceException
* @throws PdfParserException
* @throws PdfTypeException
*/
protected function readPages($readAll = false)
{
if (\count($this->pages) > 0) {
return;
}
$expectedPageCount = $this->getPageCount();
$alreadyReadKids = [];
$readPages = function ($kids, $count) use (&$readPages, &$alreadyReadKids, $readAll, $expectedPageCount) {
$kids = PdfArray::ensure($kids);
$isLeaf = ($count->value === \count($kids->value));
foreach ($kids->value as $reference) {
$reference = PdfIndirectObjectReference::ensure($reference);
if (!$readAll && $isLeaf) {
$this->pages[] = $reference;
continue;
}
if (\in_array($reference->value, $alreadyReadKids, true)) {
throw new PdfReaderException('Recursive pages dictionary detected.');
}
$alreadyReadKids[] = $reference->value;
$object = $this->parser->getIndirectObject($reference->value);
$type = PdfDictionary::get($object->value, 'Type');
if ($type->value === 'Pages') {
$readPages(
PdfType::resolve(PdfDictionary::get($object->value, 'Kids'), $this->parser),
PdfType::resolve(PdfDictionary::get($object->value, 'Count'), $this->parser)
);
} else {
$this->pages[] = $object;
}
// stop if all pages are read - faulty documents exists with additional entries with invalid data.
if (count($this->pages) === $expectedPageCount) {
break;
}
}
};
$catalog = $this->parser->getCatalog();
$pages = PdfType::resolve(PdfDictionary::get($catalog, 'Pages'), $this->parser);
$count = PdfType::resolve(PdfDictionary::get($pages, 'Count'), $this->parser);
$kids = PdfType::resolve(PdfDictionary::get($pages, 'Kids'), $this->parser);
$readPages($kids, $count);
}
}

Some files were not shown because too many files have changed in this diff Show More