Add Smarty plugin, auto-detect theme icons, PS 1.6 compat, new icons
- Add {mpr_icon name='x'} Smarty plugin for .tpl templates
- Auto-detect theme icon set (FA vs Material) without config
- Self-host icon fonts automatically if theme doesn't include them
- PS 1.6 compatible (addCSS fallback for registerStylesheet)
- Add 16 new icons: camera, play, video, chart, pie-chart, trending-up/down,
tune, build, swap, touch, inventory, payments, campaign, trophy,
schedule, history, verified, bolt
- Simplify MprIconsAssets: automatic inclusion, no config toggle needed
- Multiple modules calling registerAssets = single CSS load (dedup)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
211
src/MprIcons.php
211
src/MprIcons.php
@@ -5,8 +5,13 @@
|
|||||||
*
|
*
|
||||||
* Simple API: MprIcons::get('account') - that's it.
|
* Simple API: MprIcons::get('account') - that's it.
|
||||||
*
|
*
|
||||||
* - Admin: auto-detects and uses Material Icons
|
* - Auto-detects theme icon set (Font Awesome or Material Icons)
|
||||||
* - Front: reads icon set from ps_mpr_config table
|
* - Admin: always Material Icons
|
||||||
|
* - Front: detects from theme, falls back to Font Awesome
|
||||||
|
* - Self-hosts icon fonts if theme doesn't include them
|
||||||
|
* - Smarty: {mpr_icon name='account'} in .tpl templates
|
||||||
|
*
|
||||||
|
* Compatible with PrestaShop 1.6 through 9.1
|
||||||
*
|
*
|
||||||
* @package myprestarocks/prestashop-icons
|
* @package myprestarocks/prestashop-icons
|
||||||
*/
|
*/
|
||||||
@@ -23,6 +28,7 @@ class MprIcons
|
|||||||
const CONFIG_KEY = 'front_icon_set';
|
const CONFIG_KEY = 'front_icon_set';
|
||||||
|
|
||||||
private static $iconSet = null;
|
private static $iconSet = null;
|
||||||
|
private static $smartyRegistered = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Semantic name => [FA class, Material name]
|
* Semantic name => [FA class, Material name]
|
||||||
@@ -42,6 +48,10 @@ class MprIcons
|
|||||||
'gift' => ['fa fa-gift', 'card_giftcard'],
|
'gift' => ['fa fa-gift', 'card_giftcard'],
|
||||||
'tag' => ['fa fa-tag', 'local_offer'],
|
'tag' => ['fa fa-tag', 'local_offer'],
|
||||||
'percent' => ['fa fa-percent', 'percent'],
|
'percent' => ['fa fa-percent', 'percent'],
|
||||||
|
'inventory' => ['fa fa-cube', 'inventory_2'],
|
||||||
|
'payments' => ['fa fa-money', 'payments'],
|
||||||
|
'campaign' => ['fa fa-bullhorn', 'campaign'],
|
||||||
|
'trophy' => ['fa fa-trophy', 'emoji_events'],
|
||||||
|
|
||||||
// Status
|
// Status
|
||||||
'success' => ['fa fa-check', 'check'],
|
'success' => ['fa fa-check', 'check'],
|
||||||
@@ -60,6 +70,16 @@ class MprIcons
|
|||||||
'save' => ['fa fa-save', 'save'],
|
'save' => ['fa fa-save', 'save'],
|
||||||
'print' => ['fa fa-print', 'print'],
|
'print' => ['fa fa-print', 'print'],
|
||||||
'copy' => ['fa fa-copy', 'content_copy'],
|
'copy' => ['fa fa-copy', 'content_copy'],
|
||||||
|
'tune' => ['fa fa-sliders', 'tune'],
|
||||||
|
'build' => ['fa fa-wrench', 'build'],
|
||||||
|
'swap' => ['fa fa-exchange', 'swap_horiz'],
|
||||||
|
'touch' => ['fa fa-hand-pointer-o', 'touch_app'],
|
||||||
|
|
||||||
|
// Analytics & Charts
|
||||||
|
'chart' => ['fa fa-bar-chart', 'bar_chart'],
|
||||||
|
'pie-chart' => ['fa fa-pie-chart', 'pie_chart'],
|
||||||
|
'trending-up' => ['fa fa-line-chart', 'trending_up'],
|
||||||
|
'trending-down' => ['fa fa-line-chart', 'trending_down'],
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
'close' => ['fa fa-times', 'close'],
|
'close' => ['fa fa-times', 'close'],
|
||||||
@@ -90,6 +110,18 @@ class MprIcons
|
|||||||
'unlock' => ['fa fa-unlock', 'lock_open'],
|
'unlock' => ['fa fa-unlock', 'lock_open'],
|
||||||
'shield' => ['fa fa-shield', 'shield'],
|
'shield' => ['fa fa-shield', 'shield'],
|
||||||
'key' => ['fa fa-key', 'vpn_key'],
|
'key' => ['fa fa-key', 'vpn_key'],
|
||||||
|
'verified' => ['fa fa-check-circle', 'verified_user'],
|
||||||
|
'bolt' => ['fa fa-bolt', 'bolt'],
|
||||||
|
|
||||||
|
// Media
|
||||||
|
'camera' => ['fa fa-camera', 'add_a_photo'],
|
||||||
|
'play' => ['fa fa-play-circle', 'play_circle'],
|
||||||
|
'image' => ['fa fa-image', 'image'],
|
||||||
|
'video' => ['fa fa-video-camera', 'videocam'],
|
||||||
|
|
||||||
|
// Time & History
|
||||||
|
'schedule' => ['fa fa-clock-o', 'schedule'],
|
||||||
|
'history' => ['fa fa-history', 'history'],
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
'home' => ['fa fa-home', 'home'],
|
'home' => ['fa fa-home', 'home'],
|
||||||
@@ -102,7 +134,6 @@ class MprIcons
|
|||||||
'eye-off' => ['fa fa-eye-slash', 'visibility_off'],
|
'eye-off' => ['fa fa-eye-slash', 'visibility_off'],
|
||||||
'file' => ['fa fa-file', 'description'],
|
'file' => ['fa fa-file', 'description'],
|
||||||
'folder' => ['fa fa-folder', 'folder'],
|
'folder' => ['fa fa-folder', 'folder'],
|
||||||
'image' => ['fa fa-image', 'image'],
|
|
||||||
'link' => ['fa fa-link', 'link'],
|
'link' => ['fa fa-link', 'link'],
|
||||||
'external' => ['fa fa-external-link', 'open_in_new'],
|
'external' => ['fa fa-external-link', 'open_in_new'],
|
||||||
'help' => ['fa fa-question-circle', 'help'],
|
'help' => ['fa fa-question-circle', 'help'],
|
||||||
@@ -151,6 +182,34 @@ class MprIcons
|
|||||||
return $set === self::MATERIAL ? $material : $fa;
|
return $set === self::MATERIAL ? $material : $fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Smarty {mpr_icon} function handler
|
||||||
|
*
|
||||||
|
* Usage in .tpl:
|
||||||
|
* {mpr_icon name='account'}
|
||||||
|
* {mpr_icon name='cart' class='text-primary'}
|
||||||
|
*/
|
||||||
|
public static function smartyFunction($params, $smarty): string
|
||||||
|
{
|
||||||
|
return self::get($params['name'] ?? '', $params['class'] ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register {mpr_icon} as a Smarty function plugin
|
||||||
|
* Safe to call multiple times — registers only once
|
||||||
|
*
|
||||||
|
* @param \Smarty|\SmartyBC $smarty Smarty instance
|
||||||
|
*/
|
||||||
|
public static function registerSmartyPlugin($smarty): void
|
||||||
|
{
|
||||||
|
if (self::$smartyRegistered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$smarty->registerPlugin('function', 'mpr_icon', [self::class, 'smartyFunction']);
|
||||||
|
self::$smartyRegistered = true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detect and cache the icon set to use
|
* Detect and cache the icon set to use
|
||||||
*/
|
*/
|
||||||
@@ -160,14 +219,21 @@ class MprIcons
|
|||||||
return self::$iconSet;
|
return self::$iconSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Admin context = Material Icons
|
// Admin context = Material Icons (PS 1.7+ admin always loads them)
|
||||||
if (defined('_PS_ADMIN_DIR_')) {
|
if (defined('_PS_ADMIN_DIR_')) {
|
||||||
self::$iconSet = self::MATERIAL;
|
self::$iconSet = self::MATERIAL;
|
||||||
return self::$iconSet;
|
return self::$iconSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Front office - read from config table
|
// Front office - check config table first, then auto-detect
|
||||||
self::$iconSet = self::readConfigTable() ?: self::FA;
|
$fromConfig = self::readConfigTable();
|
||||||
|
if ($fromConfig !== null) {
|
||||||
|
self::$iconSet = $fromConfig;
|
||||||
|
return self::$iconSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No config stored — auto-detect from theme
|
||||||
|
self::$iconSet = self::detectThemeIcons();
|
||||||
return self::$iconSet;
|
return self::$iconSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +258,7 @@ class MprIcons
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// Table doesn't exist or query failed - use default
|
// Table doesn't exist or query failed
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -222,7 +288,7 @@ class MprIcons
|
|||||||
$result = \Db::getInstance()->execute($sql);
|
$result = \Db::getInstance()->execute($sql);
|
||||||
|
|
||||||
if ($result) {
|
if ($result) {
|
||||||
self::$iconSet = null; // Clear cache
|
self::$iconSet = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
@@ -250,8 +316,8 @@ class MprIcons
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize icon system - call from module install
|
* Initialize icon system — call from module install
|
||||||
* Creates config table if needed, sets default icon set
|
* Creates config table if needed, detects and saves icon set
|
||||||
*/
|
*/
|
||||||
public static function init(): bool
|
public static function init(): bool
|
||||||
{
|
{
|
||||||
@@ -259,7 +325,6 @@ class MprIcons
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no config exists, detect and save default
|
|
||||||
$current = self::readConfigTable();
|
$current = self::readConfigTable();
|
||||||
if ($current === null) {
|
if ($current === null) {
|
||||||
$detected = self::detectThemeIcons();
|
$detected = self::detectThemeIcons();
|
||||||
@@ -281,7 +346,6 @@ class MprIcons
|
|||||||
try {
|
try {
|
||||||
$table = _DB_PREFIX_ . self::CONFIG_TABLE;
|
$table = _DB_PREFIX_ . self::CONFIG_TABLE;
|
||||||
|
|
||||||
// Check if table exists
|
|
||||||
$exists = \Db::getInstance()->getValue(
|
$exists = \Db::getInstance()->getValue(
|
||||||
"SELECT COUNT(*) FROM information_schema.tables
|
"SELECT COUNT(*) FROM information_schema.tables
|
||||||
WHERE table_schema = DATABASE()
|
WHERE table_schema = DATABASE()
|
||||||
@@ -292,7 +356,8 @@ class MprIcons
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create table
|
$engine = defined('_MYSQL_ENGINE_') ? _MYSQL_ENGINE_ : 'InnoDB';
|
||||||
|
|
||||||
$sql = "CREATE TABLE IF NOT EXISTS `{$table}` (
|
$sql = "CREATE TABLE IF NOT EXISTS `{$table}` (
|
||||||
`id_config` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
`id_config` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
`module` VARCHAR(64) NOT NULL,
|
`module` VARCHAR(64) NOT NULL,
|
||||||
@@ -303,7 +368,7 @@ class MprIcons
|
|||||||
PRIMARY KEY (`id_config`),
|
PRIMARY KEY (`id_config`),
|
||||||
UNIQUE KEY `module_key` (`module`, `key`),
|
UNIQUE KEY `module_key` (`module`, `key`),
|
||||||
KEY `module` (`module`)
|
KEY `module` (`module`)
|
||||||
) ENGINE=" . _MYSQL_ENGINE_ . " DEFAULT CHARSET=utf8mb4;";
|
) ENGINE={$engine} DEFAULT CHARSET=utf8mb4;";
|
||||||
|
|
||||||
return \Db::getInstance()->execute($sql);
|
return \Db::getInstance()->execute($sql);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
@@ -312,44 +377,119 @@ class MprIcons
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detect which icon set the current theme likely uses
|
* Detect which icon set the current theme uses
|
||||||
* Returns 'fontawesome' for most PrestaShop themes
|
*
|
||||||
|
* Checks theme config and assets for Material Icons indicators.
|
||||||
|
* Returns 'fontawesome' for classic and most themes,
|
||||||
|
* 'material' for Hummingbird and Material-based themes.
|
||||||
*/
|
*/
|
||||||
public static function detectThemeIcons(): string
|
public static function detectThemeIcons(): string
|
||||||
{
|
{
|
||||||
// Most PrestaShop themes (including classic) use Font Awesome
|
|
||||||
// Only return Material if we can positively detect it
|
|
||||||
|
|
||||||
if (!defined('_PS_THEME_DIR_')) {
|
if (!defined('_PS_THEME_DIR_')) {
|
||||||
return self::FA;
|
return self::FA;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Check theme.yml for icon hints
|
// PS 1.7+ — check theme.yml
|
||||||
$themeYml = _PS_THEME_DIR_ . 'config/theme.yml';
|
$themeYml = _PS_THEME_DIR_ . 'config/theme.yml';
|
||||||
if (file_exists($themeYml)) {
|
if (file_exists($themeYml)) {
|
||||||
$content = file_get_contents($themeYml);
|
$content = file_get_contents($themeYml);
|
||||||
if (strpos($content, 'material') !== false || strpos($content, 'Material') !== false) {
|
if (stripos($content, 'hummingbird') !== false) {
|
||||||
|
return self::MATERIAL;
|
||||||
|
}
|
||||||
|
if (stripos($content, 'material-icons') !== false || stripos($content, 'material_icons') !== false) {
|
||||||
return self::MATERIAL;
|
return self::MATERIAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if theme has material-icons in assets
|
// Check theme assets for material-icons CSS
|
||||||
$assetsDir = _PS_THEME_DIR_ . 'assets/css/';
|
$dirs = [
|
||||||
if (is_dir($assetsDir)) {
|
_PS_THEME_DIR_ . 'assets/css/',
|
||||||
$files = scandir($assetsDir);
|
_PS_THEME_DIR_ . 'css/',
|
||||||
|
];
|
||||||
|
foreach ($dirs as $dir) {
|
||||||
|
if (!is_dir($dir)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$files = scandir($dir);
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
if (strpos($file, 'material') !== false) {
|
if (stripos($file, 'material') !== false) {
|
||||||
return self::MATERIAL;
|
return self::MATERIAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PS 1.6 — check for parent theme name
|
||||||
|
if (defined('_THEME_NAME_')) {
|
||||||
|
$themeName = _THEME_NAME_;
|
||||||
|
if (stripos($themeName, 'hummingbird') !== false) {
|
||||||
|
return self::MATERIAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Detection failed — use safe default
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::FA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the current theme already includes the needed icon font
|
||||||
|
* Used by MprIconsAssets to decide whether to self-host
|
||||||
|
*/
|
||||||
|
public static function themeHasIconFont(): bool
|
||||||
|
{
|
||||||
|
if (!defined('_PS_THEME_DIR_')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$iconSet = self::getIconSet();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$dirs = [
|
||||||
|
_PS_THEME_DIR_ . 'assets/css/',
|
||||||
|
_PS_THEME_DIR_ . 'css/',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($dirs as $dir) {
|
||||||
|
if (!is_dir($dir)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$files = scandir($dir);
|
||||||
|
foreach ($files as $file) {
|
||||||
|
if ($file === '.' || $file === '..') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($iconSet === self::MATERIAL) {
|
||||||
|
if (stripos($file, 'material') !== false) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (stripos($file, 'fontawesome') !== false || stripos($file, 'font-awesome') !== false) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Font Awesome — most PS 1.6/1.7 themes include it as a dependency
|
||||||
|
// Check if theme's package.json or theme.yml lists it
|
||||||
|
if ($iconSet === self::FA) {
|
||||||
|
$themeYml = _PS_THEME_DIR_ . 'config/theme.yml';
|
||||||
|
if (file_exists($themeYml)) {
|
||||||
|
$content = file_get_contents($themeYml);
|
||||||
|
if (stripos($content, 'font-awesome') !== false || stripos($content, 'fontawesome') !== false) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// Ignore detection errors
|
// Detection failed
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to Font Awesome (safe for 95% of themes)
|
return false;
|
||||||
return self::FA;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -369,9 +509,8 @@ class MprIcons
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register icon font CSS assets
|
* Register icon font CSS assets if theme doesn't provide them
|
||||||
* Convenience method that delegates to MprIconsAssets
|
* Delegates to MprIconsAssets. Safe to call from multiple modules.
|
||||||
* Call from hookHeader or setMedia
|
|
||||||
*
|
*
|
||||||
* @param \Module $module The module instance
|
* @param \Module $module The module instance
|
||||||
* @return bool Whether assets were registered
|
* @return bool Whether assets were registered
|
||||||
@@ -380,12 +519,4 @@ class MprIcons
|
|||||||
{
|
{
|
||||||
return MprIconsAssets::registerAssets($module);
|
return MprIconsAssets::registerAssets($module);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if self-hosted icon fonts are enabled
|
|
||||||
*/
|
|
||||||
public static function isSelfHostEnabled(): bool
|
|
||||||
{
|
|
||||||
return MprIconsConfig::isSelfHostEnabled();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MprIconsAssets - CSS injection helper for self-hosted icon fonts
|
* MprIconsAssets - Automatic icon font CSS loader
|
||||||
*
|
*
|
||||||
* Usage in module:
|
* Detects if the theme provides icon fonts. If not, self-hosts them
|
||||||
* // In hookHeader or setMedia:
|
* from the bundled assets. Safe to call from multiple modules —
|
||||||
* MprIconsAssets::registerAssets($this);
|
* CSS is registered with a fixed ID so it loads only once.
|
||||||
|
*
|
||||||
|
* Compatible with PrestaShop 1.6 through 9.1
|
||||||
*
|
*
|
||||||
* @package myprestarocks/prestashop-icons
|
* @package myprestarocks/prestashop-icons
|
||||||
*/
|
*/
|
||||||
@@ -14,9 +16,10 @@ namespace MyPrestaRocks\Icons;
|
|||||||
|
|
||||||
class MprIconsAssets
|
class MprIconsAssets
|
||||||
{
|
{
|
||||||
|
private static $registered = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the base path to this package's assets
|
* Get the base path to this package's assets
|
||||||
* Works regardless of which module includes the package
|
|
||||||
*/
|
*/
|
||||||
public static function getAssetsPath(): string
|
public static function getAssetsPath(): string
|
||||||
{
|
{
|
||||||
@@ -25,39 +28,48 @@ class MprIconsAssets
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the assets URL relative to module
|
* Get the assets URL relative to module
|
||||||
* Requires module context to determine URL path
|
|
||||||
*
|
*
|
||||||
* @param \Module $module The module instance
|
* @param \Module $module The module instance
|
||||||
* @return string URL path to assets
|
* @return string URL path to assets
|
||||||
*/
|
*/
|
||||||
public static function getAssetsUrl(\Module $module): string
|
public static function getAssetsUrl(\Module $module): string
|
||||||
{
|
{
|
||||||
// Find the vendor path relative to module
|
|
||||||
$modulePath = $module->getLocalPath();
|
$modulePath = $module->getLocalPath();
|
||||||
$assetsPath = self::getAssetsPath();
|
$assetsPath = self::getAssetsPath();
|
||||||
|
|
||||||
// Get relative path from module to assets
|
|
||||||
$relativePath = str_replace($modulePath, '', $assetsPath);
|
$relativePath = str_replace($modulePath, '', $assetsPath);
|
||||||
|
|
||||||
return $module->getPathUri() . ltrim($relativePath, '/');
|
return $module->getPathUri() . ltrim($relativePath, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register icon font CSS if self-hosting is enabled
|
* Register icon font CSS — automatic detection
|
||||||
* Call this from your module's hookHeader or setMedia
|
*
|
||||||
|
* 1. Skips in admin context (admin always has Material Icons)
|
||||||
|
* 2. Checks if theme already provides the needed icon font
|
||||||
|
* 3. If not, registers bundled CSS from this package
|
||||||
|
* 4. Uses fixed registration ID — multiple modules calling = one load
|
||||||
|
*
|
||||||
|
* Call from hookActionFrontControllerSetMedia or hookHeader.
|
||||||
*
|
*
|
||||||
* @param \Module $module The module instance
|
* @param \Module $module The module instance
|
||||||
* @return bool Whether assets were registered
|
* @return bool Whether assets were registered (false if skipped or already loaded)
|
||||||
*/
|
*/
|
||||||
public static function registerAssets(\Module $module): bool
|
public static function registerAssets(\Module $module): bool
|
||||||
{
|
{
|
||||||
// Skip in admin context (admin always has Material Icons)
|
// Skip in admin context
|
||||||
if (defined('_PS_ADMIN_DIR_')) {
|
if (defined('_PS_ADMIN_DIR_')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if self-hosting is enabled
|
// Already registered by another module in this request
|
||||||
if (!MprIconsConfig::isSelfHostEnabled()) {
|
if (self::$registered) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theme already includes the needed icon font — skip
|
||||||
|
if (MprIcons::themeHasIconFont()) {
|
||||||
|
self::$registered = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,14 +82,23 @@ class MprIconsAssets
|
|||||||
$cssFile = $assetsUrl . '/fontawesome/fontawesome.min.css';
|
$cssFile = $assetsUrl . '/fontawesome/fontawesome.min.css';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register CSS
|
|
||||||
$controller = \Context::getContext()->controller;
|
$controller = \Context::getContext()->controller;
|
||||||
if ($controller instanceof \FrontController) {
|
|
||||||
|
// PS 1.7+ — use registerStylesheet (deduplicates by ID)
|
||||||
|
if (method_exists($controller, 'registerStylesheet')) {
|
||||||
$controller->registerStylesheet(
|
$controller->registerStylesheet(
|
||||||
'mpr-icons-' . $iconSet,
|
'mpr-icons-' . $iconSet,
|
||||||
$cssFile,
|
$cssFile,
|
||||||
['media' => 'all', 'priority' => 50]
|
['media' => 'all', 'priority' => 50, 'server' => 'remote']
|
||||||
);
|
);
|
||||||
|
self::$registered = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PS 1.6 — use addCSS (deduplicates by URI)
|
||||||
|
if (method_exists($controller, 'addCSS')) {
|
||||||
|
$controller->addCSS($cssFile, 'all');
|
||||||
|
self::$registered = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,17 +106,14 @@ class MprIconsAssets
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get CSS link tag HTML (for manual inclusion)
|
* Get CSS link tag HTML for manual inclusion
|
||||||
|
* Useful for email templates or non-standard contexts
|
||||||
*
|
*
|
||||||
* @param \Module $module The module instance
|
* @param \Module $module The module instance
|
||||||
* @return string HTML link tag or empty string
|
* @return string HTML link tag
|
||||||
*/
|
*/
|
||||||
public static function getCssTag(\Module $module): string
|
public static function getCssTag(\Module $module): string
|
||||||
{
|
{
|
||||||
if (!MprIconsConfig::isSelfHostEnabled()) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$iconSet = MprIcons::getIconSet();
|
$iconSet = MprIcons::getIconSet();
|
||||||
$assetsUrl = self::getAssetsUrl($module);
|
$assetsUrl = self::getAssetsUrl($module);
|
||||||
|
|
||||||
@@ -111,7 +129,7 @@ class MprIconsAssets
|
|||||||
/**
|
/**
|
||||||
* Check if bundled assets exist
|
* Check if bundled assets exist
|
||||||
*
|
*
|
||||||
* @param string $iconSet The icon set to check ('fontawesome' or 'material')
|
* @param string $iconSet The icon set to check
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function assetsExist(string $iconSet): bool
|
public static function assetsExist(string $iconSet): bool
|
||||||
@@ -124,70 +142,4 @@ class MprIconsAssets
|
|||||||
|
|
||||||
return file_exists($assetsPath . '/fontawesome/fontawesome.min.css');
|
return file_exists($assetsPath . '/fontawesome/fontawesome.min.css');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy assets to module's public directory
|
|
||||||
* Use this if your module needs assets in a specific location
|
|
||||||
*
|
|
||||||
* @param string $targetDir Target directory
|
|
||||||
* @param string $iconSet Which icon set to copy
|
|
||||||
* @return bool Success
|
|
||||||
*/
|
|
||||||
public static function copyAssets(string $targetDir, string $iconSet): bool
|
|
||||||
{
|
|
||||||
$assetsPath = self::getAssetsPath();
|
|
||||||
|
|
||||||
if ($iconSet === MprIcons::MATERIAL) {
|
|
||||||
$sourceDir = $assetsPath . '/material-icons';
|
|
||||||
} else {
|
|
||||||
$sourceDir = $assetsPath . '/fontawesome';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_dir($sourceDir)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::recursiveCopy($sourceDir, $targetDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively copy directory
|
|
||||||
*/
|
|
||||||
private static function recursiveCopy(string $source, string $dest): bool
|
|
||||||
{
|
|
||||||
if (!is_dir($dest)) {
|
|
||||||
if (!mkdir($dest, 0755, true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$dir = opendir($source);
|
|
||||||
if (!$dir) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (($file = readdir($dir)) !== false) {
|
|
||||||
if ($file === '.' || $file === '..') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$srcPath = $source . '/' . $file;
|
|
||||||
$destPath = $dest . '/' . $file;
|
|
||||||
|
|
||||||
if (is_dir($srcPath)) {
|
|
||||||
if (!self::recursiveCopy($srcPath, $destPath)) {
|
|
||||||
closedir($dir);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!copy($srcPath, $destPath)) {
|
|
||||||
closedir($dir);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closedir($dir);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MprIconsConfig - Form field generator for icon set configuration
|
* MprIconsConfig - Admin form fields for icon set configuration
|
||||||
*
|
*
|
||||||
* Usage in module admin:
|
* Usage in module admin:
|
||||||
* $fields = array_merge($fields, MprIconsConfig::getFormFields());
|
* $fields = array_merge($fields, MprIconsConfig::getFormFields());
|
||||||
* // Then in postProcess:
|
* // In postProcess:
|
||||||
* MprIconsConfig::processForm();
|
* MprIconsConfig::processForm();
|
||||||
*
|
*
|
||||||
* @package myprestarocks/prestashop-icons
|
* @package myprestarocks/prestashop-icons
|
||||||
@@ -15,12 +15,10 @@ namespace MyPrestaRocks\Icons;
|
|||||||
|
|
||||||
class MprIconsConfig
|
class MprIconsConfig
|
||||||
{
|
{
|
||||||
const CONFIG_KEY_SELFHOST = 'front_icon_selfhost';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get form fields for HelperForm integration
|
* Get form fields for HelperForm integration
|
||||||
*
|
*
|
||||||
* @param string $formName Form name prefix (default: 'MPR_ICONS')
|
* @param string $formName Form name prefix
|
||||||
* @return array HelperForm-compatible field definitions
|
* @return array HelperForm-compatible field definitions
|
||||||
*/
|
*/
|
||||||
public static function getFormFields(string $formName = 'MPR_ICONS'): array
|
public static function getFormFields(string $formName = 'MPR_ICONS'): array
|
||||||
@@ -30,27 +28,16 @@ class MprIconsConfig
|
|||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
'label' => 'Front Office Icon Set',
|
'label' => 'Front Office Icon Set',
|
||||||
'name' => $formName . '_ICON_SET',
|
'name' => $formName . '_ICON_SET',
|
||||||
'desc' => 'Choose which icon font to use on the front office. Most themes include Font Awesome.',
|
'desc' => 'Choose which icon font to use on the front office. Auto-detected from theme on install. Override here if needed.',
|
||||||
'options' => [
|
'options' => [
|
||||||
'query' => [
|
'query' => [
|
||||||
['id' => MprIcons::FA, 'name' => 'Font Awesome (default)'],
|
['id' => MprIcons::FA, 'name' => 'Font Awesome (classic theme)'],
|
||||||
['id' => MprIcons::MATERIAL, 'name' => 'Material Icons'],
|
['id' => MprIcons::MATERIAL, 'name' => 'Material Icons (hummingbird theme)'],
|
||||||
],
|
],
|
||||||
'id' => 'id',
|
'id' => 'id',
|
||||||
'name' => 'name',
|
'name' => 'name',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
|
||||||
'type' => 'switch',
|
|
||||||
'label' => 'Include Icon Fonts from Module',
|
|
||||||
'name' => $formName . '_SELFHOST',
|
|
||||||
'desc' => 'Enable this if your theme does not load icon fonts. The module will include the necessary CSS.',
|
|
||||||
'is_bool' => true,
|
|
||||||
'values' => [
|
|
||||||
['id' => 'active_on', 'value' => 1, 'label' => 'Yes'],
|
|
||||||
['id' => 'active_off', 'value' => 0, 'label' => 'No'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +51,6 @@ class MprIconsConfig
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
$formName . '_ICON_SET' => MprIcons::getIconSet(),
|
$formName . '_ICON_SET' => MprIcons::getIconSet(),
|
||||||
$formName . '_SELFHOST' => self::isSelfHostEnabled() ? 1 : 0,
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,96 +63,6 @@ class MprIconsConfig
|
|||||||
public static function processForm(string $formName = 'MPR_ICONS'): bool
|
public static function processForm(string $formName = 'MPR_ICONS'): bool
|
||||||
{
|
{
|
||||||
$iconSet = \Tools::getValue($formName . '_ICON_SET', MprIcons::FA);
|
$iconSet = \Tools::getValue($formName . '_ICON_SET', MprIcons::FA);
|
||||||
$selfHost = (int) \Tools::getValue($formName . '_SELFHOST', 0);
|
return MprIcons::saveIconSet($iconSet);
|
||||||
|
|
||||||
$success = MprIcons::saveIconSet($iconSet);
|
|
||||||
$success = self::saveSelfHostOption($selfHost) && $success;
|
|
||||||
|
|
||||||
return $success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if self-hosting is enabled
|
|
||||||
*/
|
|
||||||
public static function isSelfHostEnabled(): bool
|
|
||||||
{
|
|
||||||
$value = self::readConfig(self::CONFIG_KEY_SELFHOST);
|
|
||||||
return $value === '1';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save self-host option
|
|
||||||
*/
|
|
||||||
public static function saveSelfHostOption(int $enabled): bool
|
|
||||||
{
|
|
||||||
return self::saveConfig(self::CONFIG_KEY_SELFHOST, $enabled ? '1' : '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read config from mpr_config table
|
|
||||||
*/
|
|
||||||
private static function readConfig(string $key): ?string
|
|
||||||
{
|
|
||||||
if (!defined('_DB_PREFIX_')) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$table = _DB_PREFIX_ . MprIcons::CONFIG_TABLE;
|
|
||||||
$sql = "SELECT `value` FROM `{$table}`
|
|
||||||
WHERE `module` = '" . pSQL(MprIcons::CONFIG_MODULE) . "'
|
|
||||||
AND `key` = '" . pSQL($key) . "'";
|
|
||||||
|
|
||||||
$result = \Db::getInstance()->getValue($sql);
|
|
||||||
return $result !== false ? $result : null;
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save config to mpr_config table
|
|
||||||
*/
|
|
||||||
private static function saveConfig(string $key, string $value): bool
|
|
||||||
{
|
|
||||||
if (!defined('_DB_PREFIX_')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure table exists
|
|
||||||
MprIcons::ensureConfigTable();
|
|
||||||
|
|
||||||
try {
|
|
||||||
$table = _DB_PREFIX_ . MprIcons::CONFIG_TABLE;
|
|
||||||
$now = date('Y-m-d H:i:s');
|
|
||||||
|
|
||||||
$sql = "INSERT INTO `{$table}` (`module`, `key`, `value`, `date_add`, `date_upd`)
|
|
||||||
VALUES ('" . pSQL(MprIcons::CONFIG_MODULE) . "', '" . pSQL($key) . "', '" . pSQL($value) . "', '{$now}', '{$now}')
|
|
||||||
ON DUPLICATE KEY UPDATE `value` = '" . pSQL($value) . "', `date_upd` = '{$now}'";
|
|
||||||
|
|
||||||
return \Db::getInstance()->execute($sql);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get complete form input array for a HelperForm
|
|
||||||
* Includes form wrapper with legend
|
|
||||||
*
|
|
||||||
* @param string $formName Form name prefix
|
|
||||||
* @return array Complete form input structure
|
|
||||||
*/
|
|
||||||
public static function getFormInput(string $formName = 'MPR_ICONS'): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'form' => [
|
|
||||||
'legend' => [
|
|
||||||
'title' => 'Icon Settings',
|
|
||||||
'icon' => 'icon-picture',
|
|
||||||
],
|
|
||||||
'input' => self::getFormFields($formName),
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user