Skip to content

Feed the Symfony dashboard's Zone Two hook with product tables, add a settings page - #78

Open
nicosomb wants to merge 6 commits into
devfrom
feature/41971-zone-two-poc
Open

nicosomb wants to merge 6 commits into
devfrom
feature/41971-zone-two-poc

Conversation

@nicosomb

Copy link
Copy Markdown
Contributor
Questions Answers
Description? Adds hookDisplayAdminDashboardZoneTwo(), the modern counterpart of hookDashboardZoneTwo(), so this module also feeds the migrated (Symfony) Back Office Dashboard. Reuses hookDashboardData() as-is (recent orders, best sellers, most viewed, top search tables) and renders them as plain server-side Twig — no JSON/JS contract needed for tables, unlike charts. Legacy hooks are untouched. Version bumped 2.2.1 → 2.3.0 (minor, backward compatible with 8.2.0+).
Type? new feature
BC breaks? no
Deprecations? no
Fixed ticket? Fixes PrestaShop/PrestaShop#41971.
How to test? 1. On a shop with PrestaShop/PrestaShop#42431 and the dashboard feature flag enabled, install/reset this module. 2. Open the BO Dashboard: 4 tables render in Zone Two (recent orders, best sellers, most viewed, top search).

Draft PoC for the #41971 spike — the legacy data_table shape is reused almost verbatim, only the transport changes.

@ps-jarvis

ps-jarvis commented Aug 28, 2026 •

Copy link
Copy Markdown

This pull request seems to contain new translation strings. I have summarized them below to ease up review:

  • Modules.Dashproducts.Admin
    • No data
  • Admin.Actions
    • Products dashboard settings

(Note: this is an automated message, but answering it will reach a real human)

@nicosomb
nicosomb force-pushed the feature/41971-zone-two-poc branch from 4977473 to c75d3ce Compare September 3, 2026 09:05
@nicosomb nicosomb changed the title PoC: feed the migrated Symfony dashboard with real product tables Feed the Symfony dashboard's Zone Two hook with product tables, add a settings page Sep 3, 2026
@nicosomb
nicosomb marked this pull request as ready for review September 3, 2026 11:58
@github-project-automation github-project-automation Bot moved this to Ready for review in PR Dashboard Sep 3, 2026

@mattgoud mattgoud left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally against PrestaShop/PrestaShop#42431 with the dashboard flag on. The four tables render in zone two, and the fallback branches work correctly (the "No data" case on Best sellers and Top search, and the raw string body on Most viewed products showing the "Save global page views" notice). Reusing hookDashboardData() rather than duplicating the queries is the right call, and rendering tables server-side instead of pushing them through the JSON contract makes sense.

Two blockers and a few smaller points.

Blocker 1: no upgrade script, so no existing shop gets the hook or the tab

install() only runs on a fresh install. On a shop that already has dashproducts 2.2.1, neither displayAdminDashboardZoneTwo nor the AdminDashproductsConfiguration tab is ever created.

Reproduced locally: old version installed, 2.3.0 files dropped on disk, then the upgrade path replayed (ModuleManager::upgradeMigration() minus the download step):

dashproducts   db=2.2.1   disk=2.3.0   needUpgrade=NO

No hook row, no tab, and Module::upgradeModuleVersion() is never called so ps_module.version stays on 2.2.1 forever.

autoupgrade behaves the same way: ModuleMigration::needMigration() returns false when upgrade/*.php is empty, it logs "Module does not need to be migrated", then calls saveVersionInDb() anyway. After a core upgrade the shop reports 2.3.0 while the hook is still missing, with no way back other than uninstall/reinstall (losing the configuration). autoupgrade cannot fix this on its own: core upgrade SQL never inserts into ps_hook_module, it only cleans orphans.

The module already has the pattern (upgrade/upgrade-2.1.2.php), so it needs an upgrade/upgrade-2.3.0.php that registers the hook and creates the tab, sharing the tab creation with install() rather than duplicating it.

Blocker 2: the settings controller has no ACL

indexAction() carries no #[AdminSecurity]. The admin firewall (app/config/admin/security.yml) only requires IS_AUTHENTICATED, and AdminSecurityListener does nothing when the attribute is absent, so any logged-in employee can open and save these settings regardless of their profile, and the hidden tab created by install() is doing no work today.

90 of the 92 core admin controllers carry the attribute (the two exceptions are Login and Security), and ps_linklist does too:

#[AdminSecurity("is_granted('read', request.get('_legacy_controller'))")]
public function indexAction(Request $request): Response

Once the attribute is there, the id_parent = -1 tab only grants the roles to SuperAdmin by default, so the other profiles will need their permissions initialised.

Two rendering issues visible in the browser

  • The details button in Recent orders renders as an empty square. getTableRecentOrders() builds it with <i class="icon-search"></i>, which is a legacy theme icon class that does not exist in the new BO theme (it uses Material icons). On the legacy dashboard it worked; here it needs <i class="material-icons">search</i>.

    recent-orders-missing-icons
  • dashproducts.php:211 has 'wrapper_end' => '<span>' instead of </span>, which now renders a stray <span></span> in the amount column (€66.80<span></span>). Pre-existing module bug, but the new template surfaces it. Worth fixing while you are here. Side note: the new template reading wrapper_end is correct, and it incidentally fixes a long-standing legacy bug where js/admin/dashboard.js:129 reads wrapper_stop and therefore never closes these wrappers.

No data is a new string in a core catalogue

ps-jarvis flagged it, and it is indeed absent from translations/en-US/AdminGlobal.en-US.xlf. A module cannot add entries to core catalogues, so it will stay untranslated. It belongs in Modules.Dashproducts.Admin.

Smaller points

  • {{ cell.value|raw }} carries unescaped customer input: getTableTop10MostSearch() puts $term['keywords'] straight into value. The legacy dashboard has the same hole (js/admin/dashboard.js:128 injects body.value as HTML), so this is not a regression, but it is exactly the "untyped arrays / raw HTML, XSS surface" pain point listed in PrestaShop/PrestaShop#41971. Since the transport is being redone anyway, splitting a plain-text value from an explicit link would close it instead of carrying it over.
  • FrameworkBundleAdminController is @deprecated since 9.0 in favour of PrestaShopAdminController. Worth deciding explicitly given the announced 8.2 compatibility.
  • The token in getConfigUrl() is dead weight. router is aliased to prestashop.router which already appends _token, and in PS 9 Tools::getAdminToken() ignores its argument and returns that same CSRF token, so the link carries the identical value twice. Verified in the browser on this branch.
  • configUrl only on loop.first. It works, but tying the gear to whichever table happens to come first is a bit arbitrary; worth confirming with UX.
  • config.xml churn. Reindented from tabs to spaces, <limited_countries> dropped, trailing newline gone. Unrelated to the feature.
  • No index.php guard files in the new config/, src/, src/Controller, src/Type directories, while every other directory in the module has one.
  • Form labels are hardcoded strings resolved through translation_domain, so the translation extractor will not pick them up.

The screenshot is attached to this review (GitHub-hosted). An archive copy also lives in mattgoud/qa-assets/dashproducts-78.

Comment thread src/Controller/ConfigurationController.php
Comment thread views/templates/admin/zone_two.html.twig Outdated
@nicosomb
nicosomb force-pushed the feature/41971-zone-two-poc branch from 6b514bc to 813612f Compare September 17, 2026 13:58
Comment thread dashproducts.php Outdated
$this->name = 'dashproducts';
$this->tab = 'administration';
$this->version = '2.2.1';
$this->version = '2.3.0';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$this->version = '2.3.0';
$this->version = '3.0.0';

Comment thread config.xml Outdated
<displayName><![CDATA[Dashboard Products]]></displayName>
<version><![CDATA[2.2.1]]></version>
<description><![CDATA[Adds a block with a table of your latest orders and a ranking of your products]]></description>
<version><![CDATA[2.3.0]]></version>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<version><![CDATA[2.3.0]]></version>
<version><![CDATA[3.0.0]]></version>

@mattgoud mattgoud left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second pass, tested with PrestaShop/PrestaShop#42431 on develop, upgrade replayed from 2.2.1.

Everything from my first review is fixed, and the XSS goes further than I asked: keywords, customer
names and order states are escaped at the source, so the legacy dashboard is fixed too. The four
tables render and survive the AJAX date range refresh.

Left: demo mode (inline), @jolelievre's 3.0.0 suggestion (rename upgrade-2.3.0.php with it), and
the release order after PrestaShop/PrestaShop#42431. Two nits: the category name at
dashproducts.php:344 is the last string cell reaching the |raw output unescaped (merchant data,
pre-existing), and a #[AdminSecurity] attribute next to the annotation would avoid the 9.x
deprecation notice.

Comment thread src/Controller/ConfigurationController.php
@mattgoud

Copy link
Copy Markdown
Contributor

One more point, from a multistore pass I had skipped in my review above: the settings page saves with the static Configuration::updateValue(), the same code as in PrestaShop/dashactivity#63 where I tested it with two shops. Saved from a shop context it creates a shop override, and from All stores the page shows and saves the global value while that override keeps winning for the shop, with nothing on the page saying so.

Not a regression (the legacy getContent() does the same), but the core settings form pattern covers it and is available on 8.2: AbstractMultistoreConfiguration plus multistore_configuration_key on the fields, and the base Core\Form\Handler for the action{HookName}Form / Save hooks (.ai/Component/Forms/SETTINGS.md, .ai/MULTISTORE.md). Details and the DB rows on PrestaShop/dashactivity#63. Here or as a follow up, your call.

@mattgoud mattgoud left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Retested 73c1125, upgrade replayed from 2.2.1: version 3.0.0, displayAdminDashboardZoneTwo registered, configuration tab created. The four tables and the settings page render, demo mode is handled.

Correction on my previous review: upgrade-2.3.0.php does not need renaming for 3.0.0, it ran here.

Approving; ships after PrestaShop/PrestaShop#42431 (Card). The multistore point and the two nits stay optional.

@ps-jarvis ps-jarvis added the Waiting for QA Status: Action required, Waiting for test feedback label Sep 24, 2026
@ps-jarvis ps-jarvis moved this from Ready for review to To be tested in PR Dashboard Sep 24, 2026
{% for row in entry.table.body %}
<tr>
{% for cell in row %}
<td class="{{ cell.class }}">{{ cell.wrapper_start|default('')|raw }}{{ cell.value|raw }}{{ cell.wrapper_end|default('')|raw }}</td>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not in this PR but later it would be better to escape the cell depending its type instead using |raw which could be a security vulnerability.

$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
$this->denyAccessUnlessGranted('update', $request->attributes->get('_legacy_controller'));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not to handle in @adminsecurity ?

Comment thread upgrade/upgrade-2.3.0.php Outdated
exit;
}

function upgrade_module_2_3_0($object)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You upgrade dashactivity to 3.0.0, it may be more consistent to rename the method and the file upgrade_module_3_0_0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Waiting for QA Status: Action required, Waiting for test feedback

Projects

Status: To be tested

Development

Successfully merging this pull request may close these issues.

Dashboard Migration - Dynamic data refacto

5 participants