Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
"require-dev": {
"prestashop/php-dev-tools": "^3.4"
},
"autoload": {
"psr-4": {
"PrestaShop\\Module\\DashActivity\\": "src/"
}
},
"config": {
"preferred-install": "dist",
"classmap-authoritative": true,
Expand Down
8 changes: 4 additions & 4 deletions config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
<module>
<name>dashactivity</name>
<displayName><![CDATA[Dashboard Activity]]></displayName>
<version><![CDATA[2.1.2]]></version>
<description><![CDATA[]]></description>
<version><![CDATA[3.0.0]]></version>
<description><![CDATA[Check at a glance what is happening on your store with a list of KPIs on your dashboard.]]></description>
<author><![CDATA[PrestaShop]]></author>
<tab><![CDATA[dashboard]]></tab>
<is_configurable>0</is_configurable>
<tab><![CDATA[administration]]></tab>
<is_configurable>1</is_configurable>
<need_instance>1</need_instance>
<limited_countries></limited_countries>
</module>
34 changes: 34 additions & 0 deletions config/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');

header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');

header('Location: ../');
exit;
7 changes: 7 additions & 0 deletions config/routes.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dashactivity_configuration:
path: /dashactivity/configuration
methods: [GET, POST]
defaults:
_controller: PrestaShop\Module\DashActivity\Controller\ConfigurationController::indexAction
_legacy_controller: AdminDashactivityConfiguration
_legacy_link: AdminDashactivityConfiguration
10 changes: 10 additions & 0 deletions config/services.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
services:
PrestaShop\Module\DashActivity\Controller\ConfigurationController:
public: true
autowire: true
autoconfigure: true

PrestaShop\Module\DashActivity\Type\ConfigurationType:
autowire: true
autoconfigure: true
tags: ['form.type']
157 changes: 150 additions & 7 deletions dashactivity.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
use Twig\Environment;

if (!defined('_PS_VERSION_')) {
exit;
}
Expand All @@ -35,7 +38,7 @@ public function __construct()
{
$this->name = 'dashactivity';
$this->tab = 'administration';
$this->version = '2.1.2';
$this->version = '3.0.0';
$this->author = 'PrestaShop';

parent::__construct();
Expand All @@ -47,17 +50,61 @@ public function __construct()
public function install()
{
Configuration::updateValue('DASHACTIVITY_CART_ACTIVE', 30);
Configuration::updateValue('DASHACTIVITY_CART_ABANDONED_MIN', 24);
Configuration::updateValue('DASHACTIVITY_CART_ABANDONED_MAX', 48);
// In minutes (see hookDashboardData()'s strtotime('- X MIN')), not hours: 1440/2880 is 24h/48h.
Configuration::updateValue('DASHACTIVITY_CART_ABANDONED_MIN', 1440);
Configuration::updateValue('DASHACTIVITY_CART_ABANDONED_MAX', 2880);
Configuration::updateValue('DASHACTIVITY_VISITOR_ONLINE', 30);

return parent::install()
&& $this->createConfigurationTab()
&& $this->registerHook('dashboardZoneOne')
&& $this->registerHook('dashboardData')
&& $this->registerHook('actionAdminControllerSetMedia')
// Modern counterpart of dashboardZoneOne, registered alongside it (#41971).
&& $this->registerHook('displayAdminDashboardZoneOne')
;
}

public function uninstall()
{
$idTab = (int) Tab::getIdFromClassName('AdminDashactivityConfiguration');
if ($idTab) {
(new Tab($idTab))->delete();
}

return parent::uninstall();
}

/**
* Hidden tab (id_parent -1): only used to back the settings route's ACL. Shared between
* install() and the 3.0.0 upgrade script so shops upgrading from an earlier version get it too.
*/
public function createConfigurationTab(): bool
{
if (Tab::getIdFromClassName('AdminDashactivityConfiguration')) {
return true;
}

$tab = new Tab();
$tab->active = true;
$tab->class_name = 'AdminDashactivityConfiguration';
$tab->name = [];
foreach (Language::getLanguages(true) as $lang) {
$tab->name[$lang['id_lang']] = 'Dashactivity configuration';
}
$tab->id_parent = -1;
$tab->module = $this->name;

return $tab->add();
}

public function getContent()
{
Tools::redirectAdmin(
SymfonyContainer::getInstance()->get('router')->generate('dashactivity_configuration')
);
}

public function hookActionAdminControllerSetMedia()
{
if (get_class($this->context->controller) == 'AdminDashboardController') {
Expand All @@ -80,6 +127,102 @@ public function hookDashboardZoneOne($params)
return $this->display(__FILE__, 'dashboard_zone_one.tpl');
}

/**
* Modern counterpart of hookDashboardZoneOne(). Reuses hookDashboardData() as-is.
*/
public function hookDisplayAdminDashboardZoneOne(array $params)
{
$data = $this->hookDashboardData($params);

return $this->render('zone_one.html.twig', [
'kpiTitle' => $this->trans('Activity overview', [], 'Modules.Dashactivity.Admin'),
'kpiItems' => $this->getKpiList($data['data_value']),
'configUrl' => $this->getConfigUrl(),
'chartTitle' => $this->trans('Traffic Sources', [], 'Modules.Dashactivity.Admin'),
'chartId' => 'dashactivity-traffic-sources',
'chartConfig' => $this->getTrafficSourcesChartConfig($data['data_chart']['dash_trends_chart1'] ?? []),
]);
}

private function render(string $template, array $params = []): string
{
$twig = $this->get('twig');
if (!$twig instanceof Environment) {
return '';
}

return $twig->render('@Modules/dashactivity/views/templates/admin/' . $template, $params);
}

/**
* Null (no "Configure" link shown) when the current employee can't configure this module.
*/
private function getConfigUrl(): ?string
{
if (!Tab::checkTabRights((int) Tab::getIdFromClassName('AdminDashactivityConfiguration'))) {
return null;
}

return SymfonyContainer::getInstance()->get('router')->generate('dashactivity_configuration');
}

/**
* Plain label/value pairs — no JS contract needed, this is server-rendered HTML.
*/
private function getKpiList(array $dataValue): array
{
$labels = [
'pending_orders' => $this->trans('Orders', [], 'Admin.Global'),
'return_exchanges' => $this->trans('Return/Exchanges', [], 'Modules.Dashactivity.Admin'),
'abandoned_cart' => $this->trans('Abandoned Carts', [], 'Admin.Global'),
'products_out_of_stock' => $this->trans('Out of Stock Products', [], 'Modules.Dashactivity.Admin'),
'new_messages' => $this->trans('New Messages', [], 'Modules.Dashactivity.Admin'),
'product_reviews' => $this->trans('Product Reviews', [], 'Modules.Dashactivity.Admin'),
'new_customers' => $this->trans('New Customers', [], 'Modules.Dashactivity.Admin'),
'online_visitor' => $this->trans('Online Visitors', [], 'Modules.Dashactivity.Admin'),
'active_shopping_cart' => $this->trans('Active Shopping Carts', [], 'Modules.Dashactivity.Admin'),
'new_registrations' => $this->trans('New Subscriptions', [], 'Modules.Dashactivity.Admin'),
'total_suscribers' => $this->trans('Total Subscribers', [], 'Modules.Dashactivity.Admin'),
'visits' => $this->trans('Visits', [], 'Modules.Dashactivity.Admin'),
'unique_visitors' => $this->trans('Unique Visitors', [], 'Modules.Dashactivity.Admin'),
];

$kpis = [];
foreach ($labels as $key => $label) {
if (array_key_exists($key, $dataValue)) {
$kpis[] = ['label' => $label, 'value' => $dataValue[$key]];
}
}

return $kpis;
}

/**
* Plain Chart.js config, no dataset color (auto-applied by the core psColors plugin).
*/
private function getTrafficSourcesChartConfig(array $nvd3Chart): array
{
$labels = [];
$values = [];
foreach ($nvd3Chart['data'] ?? [] as $point) {
$labels[] = $point['key'];
$values[] = $point['y'];
}

return [
'type' => 'doughnut',
'data' => [
'labels' => $labels,
'datasets' => [
['data' => $values],
],
],
'options' => [
'plugins' => ['legend' => ['position' => 'bottom']],
],
];
}

public function hookDashboardData($params)
{
if (Tools::strlen($params['date_from']) == 10) {
Expand Down Expand Up @@ -398,17 +541,17 @@ public function renderConfigForm()
];
$fields_form['form']['input'][] = [
'label' => $this->trans('Abandoned cart (min)', [], 'Modules.Dashactivity.Admin'),
'hint' => $this->trans('How long (in hours) after the last action a cart is to be considered as abandoned (default: 24 hrs).', [], 'Modules.Dashactivity.Admin'),
'hint' => $this->trans('How long (in minutes) after the last action a cart is to be considered as abandoned (default: 1440 min, 24h).', [], 'Modules.Dashactivity.Admin'),
'name' => 'DASHACTIVITY_CART_ABANDONED_MIN',
'type' => 'text',
'suffix' => $this->trans('hrs', [], 'Modules.Dashactivity.Admin'),
'suffix' => $this->trans('min', [], 'Modules.Dashactivity.Admin'),
];
$fields_form['form']['input'][] = [
'label' => $this->trans('Abandoned cart (max)', [], 'Modules.Dashactivity.Admin'),
'hint' => $this->trans('How long (in hours) after the last action a cart is no longer to be considered as abandoned (default: 24 hrs).', [], 'Modules.Dashactivity.Admin'),
'hint' => $this->trans('How long (in minutes) after the last action a cart is no longer to be considered as abandoned (default: 2880 min, 48h).', [], 'Modules.Dashactivity.Admin'),
'name' => 'DASHACTIVITY_CART_ABANDONED_MAX',
'type' => 'text',
'suffix' => $this->trans('hrs', [], 'Modules.Dashactivity.Admin'),
'suffix' => $this->trans('min', [], 'Modules.Dashactivity.Admin'),
];

$helper = new HelperForm();
Expand Down
92 changes: 92 additions & 0 deletions src/Controller/ConfigurationController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php
/**
* 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)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/

declare(strict_types=1);

namespace PrestaShop\Module\DashActivity\Controller;

use Configuration;
use PrestaShop\Module\DashActivity\Type\ConfigurationType;
// FrameworkBundleAdminController is deprecated since 9.0 in favor of PrestaShopAdminController,
// which doesn't exist on PS 8.2 — kept for the module's declared 8.2+ compatibility.
use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController;
use PrestaShopBundle\Security\Annotation\AdminSecurity;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class ConfigurationController extends FrameworkBundleAdminController
{
private const FIELDS = [
'DASHACTIVITY_CART_ACTIVE',
'DASHACTIVITY_VISITOR_ONLINE',
'DASHACTIVITY_CART_ABANDONED_MIN',
'DASHACTIVITY_CART_ABANDONED_MAX',
];

// Every install() before 3.0.0 set these two exact values, meant as hours (the free-text
// field's own former label) though always read back as minutes — never anything else, since
// no UI let a merchant change them before this settings page existed.
private const LEGACY_ABANDONED_CART_MIN_DEFAULT = 24;
private const LEGACY_ABANDONED_CART_MAX_DEFAULT = 48;

/**
* @AdminSecurity("is_granted('read', request.get('_legacy_controller'))")
*/
public function indexAction(Request $request): Response
Comment thread
mattgoud marked this conversation as resolved.
Comment thread
mattgoud marked this conversation as resolved.
{
$data = [];
foreach (self::FIELDS as $field) {
$data[$field] = (int) Configuration::get($field);
}

// Correct the known pre-3.0.0 default to what it always meant (1440 min = 24h, 2880 min
// = 48h) for display only: nothing is written back unless the merchant saves the form.
if (self::LEGACY_ABANDONED_CART_MIN_DEFAULT === $data['DASHACTIVITY_CART_ABANDONED_MIN']) {
$data['DASHACTIVITY_CART_ABANDONED_MIN'] = 1440;
}
if (self::LEGACY_ABANDONED_CART_MAX_DEFAULT === $data['DASHACTIVITY_CART_ABANDONED_MAX']) {
$data['DASHACTIVITY_CART_ABANDONED_MAX'] = 2880;
}

$form = $this->createForm(ConfigurationType::class, $data);
$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.

Same comment than here : PrestaShop/dashproducts#78 (comment)


// No @DemoRestricted attribute here: its listener only recognizes the PHP 8
// attribute form (PrestaShopBundle\Security\Attribute\DemoRestricted), which
// doesn't exist on PS 8.2 — this module's declared minimum version.
if ($this->isDemoModeEnabled()) {
$this->addFlash('error', $this->trans('This functionality has been disabled.', 'Admin.Notifications.Error'));

return $this->redirectToRoute('dashactivity_configuration');
}

foreach ($form->getData() as $field => $value) {
Configuration::updateValue($field, (int) $value);
}
$this->addFlash('success', $this->trans('Successful update.', 'Admin.Notifications.Success'));

return $this->redirectToRoute('dashactivity_configuration');
}

return $this->render('@Modules/dashactivity/views/templates/admin/configuration.html.twig', [
'configurationForm' => $form->createView(),
'enableSidebar' => true,
'help_link' => $this->generateSidebarLink('AdminDashactivityConfiguration'),
]);
}
}
Loading
Loading