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:
2026-02-14 09:33:48 +00:00
parent 99a8f5bf9b
commit 5b3374e2d5
3 changed files with 219 additions and 240 deletions

View File

@@ -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,46 +377,121 @@ 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) { } catch (\Exception $e) {
// Ignore detection errors // Detection failed — use safe default
} }
// Default to Font Awesome (safe for 95% of themes)
return self::FA; 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) {
// Detection failed
}
return false;
}
/** /**
* Check if icon exists * Check if icon exists
*/ */
@@ -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();
}
} }

View File

@@ -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;
}
} }

View File

@@ -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),
],
];
} }
} }