diff --git a/README.md b/README.md index 2bbda46..9cf3017 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Most of these modules have been optimized and made compatible for PrestaShop v9. | Module name | Description | Minimum version | |----------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------| | [api_module](https://github.com/PrestaShop/example-modules/tree/master/api_module) | This example module demonstrates how to modify PrestaShop's new API. | v9.0.0 | +| [dashexample](https://github.com/PrestaShop/example-modules/tree/master/dashexample) | This module demonstrates how to integrate with the migrated Symfony dashboard page through its new dedicated hooks. | v9.2.0 | | [demo_grid](https://github.com/PrestaShop/example-modules/tree/master/demo_grid) | This module demonstrates how to use Grid in PrestaShop | v9.0.0 | | [democonsolecommand](https://github.com/PrestaShop/example-modules/tree/master/democonsolecommand) | Example module showing how to implement a Symfony console command | v9.0.0 | | [democontrollertabs](https://github.com/PrestaShop/example-modules/tree/master/democontrollertabs) | Demo Creating modern Controllers and associate Tabs to them | v9.0.0 | diff --git a/dashexample/.gitignore b/dashexample/.gitignore new file mode 100644 index 0000000..61064cb --- /dev/null +++ b/dashexample/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +.idea diff --git a/dashexample/README.md b/dashexample/README.md new file mode 100644 index 0000000..886876a --- /dev/null +++ b/dashexample/README.md @@ -0,0 +1,88 @@ +# Dashboard example (`dashexample`) + +Demonstration module for the **migrated (Symfony) Back Office Dashboard** and its new dedicated hook family. + +It is both living documentation of the integration contract and a validation vehicle: once installed with the `dashboard` feature flag enabled, it renders content in Zone One, Zone Two and the toolbar area of the new dashboard page. + +## What it demonstrates + +- Registering on the **new** dashboard hooks: `displayAdminDashboardZoneOne`, `displayAdminDashboardZoneTwo`, `displayAdminDashboardToolbar`. +- Rendering hook content through **module Twig templates** (`views/templates/admin/*.html.twig`) — no Smarty, no `HelperForm`, no `Db::getInstance()`. +- Passing and using hook **parameters** (`date_from` / `date_to`, the employee stats date range). +- Loading the module's **own CSS/JS assets from its hook output** (see `toolbar.html.twig`) — no `actionAdminControllerSetMedia`, no `get_class($this->context->controller)` detection. + +## Requirements + +- PrestaShop **9.2.0** or later (the version that introduces the migrated dashboard and its hooks). +- The **`dashboard` feature flag** enabled: *Advanced Parameters > New & Experimental Features > Dashboard page*. + +## Install + +```bash +# from the PrestaShop root +php bin/console prestashop:module install dashexample +``` + +Then enable the `dashboard` feature flag and open the Dashboard. You should see the module's blocks in the two zones and a marker in the toolbar area. + +## New vs legacy hook families + +The migrated dashboard deliberately uses a **new hook family**, distinct from the legacy one. A module knows which architecture it is integrating with purely from **which hook is called** — there is no version detection to do on the module side. + +| New (Symfony dashboard) | Legacy (legacy dashboard) | Area | +|---|---|---| +| `displayAdminDashboardZoneOne` | `dashboardZoneOne` | Left column | +| `displayAdminDashboardZoneTwo` | `dashboardZoneTwo` | Center column | +| `displayAdminDashboardZoneThree` | `dashboardZoneThree` | Right column | +| `displayAdminDashboardTop` | `displayDashboardTop` | Top area | +| `displayAdminDashboardToolbar` | `displayDashboardToolbarTopMenu` | Toolbar | + +The legacy hooks are untouched and keep working on the legacy page (flag off). + +## Supporting both PrestaShop versions in one module + +To render on **both** the legacy and the migrated dashboard from a single module, register on **both** hook families and let each callback render with the matching architecture. The core calls only the hook that belongs to the currently displayed page, so the two never collide. + +```php +public function install(): bool +{ + return parent::install() + && $this->registerHook([ + // Migrated (Symfony) dashboard — Twig, no legacy helpers + 'displayAdminDashboardZoneOne', + 'displayAdminDashboardZoneTwo', + 'displayAdminDashboardToolbar', + // Legacy dashboard — Smarty / HelperForm as before + 'dashboardZoneOne', + 'dashboardZoneTwo', + 'displayDashboardToolbarTopMenu', + ]); +} + +// Called only on the migrated page +public function hookDisplayAdminDashboardZoneOne(array $params): string +{ + return $this->get('twig')->render('@Modules/mymodule/views/templates/admin/zone_one.html.twig', $params); +} + +// Called only on the legacy page +public function hookDashboardZoneOne(array $params): string +{ + // legacy Smarty rendering + $this->smarty->assign($params); + + return $this->display(__FILE__, 'views/templates/hook/zone_one.tpl'); +} +``` + +This module registers **only the new hooks** on purpose, so it also serves as a focused test of the new contract. + +## Files + +| File | Role | +|---|---| +| `dashexample.php` | Module class: hook registration + hook callbacks rendering Twig | +| `views/templates/admin/zone_one.html.twig` | Zone One block | +| `views/templates/admin/zone_two.html.twig` | Zone Two block | +| `views/templates/admin/toolbar.html.twig` | Toolbar block + asset loading | +| `views/css/dashexample.css`, `views/js/dashexample.js` | Module-owned assets | diff --git a/dashexample/composer.json b/dashexample/composer.json new file mode 100644 index 0000000..4bc4ce0 --- /dev/null +++ b/dashexample/composer.json @@ -0,0 +1,17 @@ +{ + "name": "prestashop/dashexample", + "description": "Demonstration of the new dedicated hooks of the migrated Symfony dashboard page.", + "license": "AFL-3.0", + "authors": [ + { + "name": "PrestaShop Core Team" + } + ], + "require": { + "php": ">=8.1" + }, + "config": { + "prepend-autoloader": false + }, + "type": "prestashop-module" +} diff --git a/dashexample/config.xml b/dashexample/config.xml new file mode 100644 index 0000000..759aaae --- /dev/null +++ b/dashexample/config.xml @@ -0,0 +1,11 @@ + + + dashexample + + + + + + 0 + 0 + diff --git a/dashexample/dashexample.php b/dashexample/dashexample.php new file mode 100644 index 0000000..2367822 --- /dev/null +++ b/dashexample/dashexample.php @@ -0,0 +1,111 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +declare(strict_types=1); + +use Twig\Environment; + +if (!defined('_PS_VERSION_')) { + exit; +} + +/** + * Demonstrates how a module integrates with the migrated (Symfony) Back Office Dashboard. + * + * The Symfony dashboard dispatches a dedicated hook family (`displayAdminDashboard*`), distinct + * from the legacy `dashboardZone*` / `displayDashboard*` hooks. A module therefore knows which + * architecture it is integrating with purely from which hook is called — no version detection, + * no `get_class($controller)` check, no Smarty, no `HelperForm`, no `Db::getInstance()`. + * + * See README.md for how to stay compatible with the legacy dashboard at the same time. + */ +class DashExample extends Module +{ + public function __construct() + { + $this->name = 'dashexample'; + $this->author = 'PrestaShop'; + $this->version = '1.0.0'; + $this->ps_versions_compliancy = ['min' => '9.2.0', 'max' => '9.99.99']; + $this->need_instance = 0; + + parent::__construct(); + + $this->displayName = $this->l('Dashboard example'); + $this->description = $this->l('Demonstration of the new dedicated hooks of the migrated Symfony dashboard page.'); + } + + public function install(): bool + { + return parent::install() + && $this->registerHook([ + 'displayAdminDashboardZoneOne', + 'displayAdminDashboardZoneTwo', + 'displayAdminDashboardToolbar', + ]); + } + + /** + * Renders a block in the first (left) column of the Symfony dashboard. + * Receives the employee date range selected on the page. + */ + public function hookDisplayAdminDashboardZoneOne(array $params): string + { + return $this->render('zone_one.html.twig', [ + 'dateFrom' => $params['date_from'] ?? null, + 'dateTo' => $params['date_to'] ?? null, + ]); + } + + /** + * Renders a block in the second (center) column of the Symfony dashboard. + */ + public function hookDisplayAdminDashboardZoneTwo(array $params): string + { + return $this->render('zone_two.html.twig', [ + 'dateFrom' => $params['date_from'] ?? null, + 'dateTo' => $params['date_to'] ?? null, + ]); + } + + /** + * Renders content in the toolbar area of the Symfony dashboard. + * + * This hook is rendered once at the top of the page, so the module loads its own + * assets from here (via its hook output) instead of `actionAdminControllerSetMedia`. + */ + public function hookDisplayAdminDashboardToolbar(array $params): string + { + return $this->render('toolbar.html.twig', [ + 'moduleUri' => $this->getPathUri(), + ]); + } + + /** + * Render one of this module's admin Twig templates. + */ + private function render(string $template, array $params = []): string + { + /** @var Environment $twig */ + $twig = $this->get('twig'); + + return $twig->render(sprintf('@Modules/%s/views/templates/admin/%s', $this->name, $template), $params); + } +} diff --git a/dashexample/views/css/dashexample.css b/dashexample/views/css/dashexample.css new file mode 100644 index 0000000..e51c5f6 --- /dev/null +++ b/dashexample/views/css/dashexample.css @@ -0,0 +1,25 @@ +/** + * Copyright since 2007 PrestaShop SA and Contributors + * PrestaShop is an International Registered Trademark & Property of PrestaShop SA + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0). + * It is also available through the world-wide-web at this URL: https://opensource.org/licenses/AFL-3.0 + */ + +.dashexample-toolbar { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px 14px; + margin-bottom: 15px; + border-radius: 4px; + background-color: #e6f0ff; + color: #25b9d7; + font-weight: 600; +} + +.dashexample-card .panel-heading { + display: flex; + align-items: center; + gap: 6px; +} diff --git a/dashexample/views/js/dashexample.js b/dashexample/views/js/dashexample.js new file mode 100644 index 0000000..cc5fe88 --- /dev/null +++ b/dashexample/views/js/dashexample.js @@ -0,0 +1,13 @@ +/** + * Copyright since 2007 PrestaShop SA and Contributors + * PrestaShop is an International Registered Trademark & Property of PrestaShop SA + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0). + * It is also available through the world-wide-web at this URL: https://opensource.org/licenses/AFL-3.0 + */ + +document.addEventListener('DOMContentLoaded', () => { + // The module manages its own assets: this file is loaded from the module's hook output + // (see views/templates/admin/toolbar.html.twig), not via actionAdminControllerSetMedia. + console.log('[dashexample] Loaded on the migrated Symfony dashboard page.'); +}); diff --git a/dashexample/views/templates/admin/toolbar.html.twig b/dashexample/views/templates/admin/toolbar.html.twig new file mode 100644 index 0000000..615042d --- /dev/null +++ b/dashexample/views/templates/admin/toolbar.html.twig @@ -0,0 +1,19 @@ +{#** + * Copyright since 2007 PrestaShop SA and Contributors + * PrestaShop is an International Registered Trademark & Property of PrestaShop SA + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0). + * It is also available through the world-wide-web at this URL: https://opensource.org/licenses/AFL-3.0 + *#} + +{% trans_default_domain 'Module.Dashexample.Admin' %} + +{# The module loads its own assets from its hook output — no actionAdminControllerSetMedia, + no get_class($controller) detection. #} + + + +
+ extension + {{ 'Dashboard example module is active.'|trans }} +
diff --git a/dashexample/views/templates/admin/zone_one.html.twig b/dashexample/views/templates/admin/zone_one.html.twig new file mode 100644 index 0000000..8d5b854 --- /dev/null +++ b/dashexample/views/templates/admin/zone_one.html.twig @@ -0,0 +1,22 @@ +{#** + * Copyright since 2007 PrestaShop SA and Contributors + * PrestaShop is an International Registered Trademark & Property of PrestaShop SA + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0). + * It is also available through the world-wide-web at this URL: https://opensource.org/licenses/AFL-3.0 + *#} + +{% trans_default_domain 'Module.Dashexample.Admin' %} + +
+

+ widgets + {{ 'Zone one'|trans }} +

+
+

{{ 'This block is rendered by the dashexample module through the displayAdminDashboardZoneOne hook.'|trans }}

+

+ {{ 'Selected range: %from% → %to%'|trans({'%from%': dateFrom, '%to%': dateTo}) }} +

+
+
diff --git a/dashexample/views/templates/admin/zone_two.html.twig b/dashexample/views/templates/admin/zone_two.html.twig new file mode 100644 index 0000000..d18b6b8 --- /dev/null +++ b/dashexample/views/templates/admin/zone_two.html.twig @@ -0,0 +1,24 @@ +{#** + * Copyright since 2007 PrestaShop SA and Contributors + * PrestaShop is an International Registered Trademark & Property of PrestaShop SA + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0). + * It is also available through the world-wide-web at this URL: https://opensource.org/licenses/AFL-3.0 + *#} + +{% trans_default_domain 'Module.Dashexample.Admin' %} + +
+

+ insights + {{ 'Zone two'|trans }} +

+
+

{{ 'This block is rendered by the dashexample module through the displayAdminDashboardZoneTwo hook.'|trans }}

+ +
+