[FA class, Material name] */ private static $map = [ // User 'account' => ['fa fa-user', 'person'], 'user' => ['fa fa-user', 'person'], 'logout' => ['fa fa-sign-out', 'logout'], 'login' => ['fa fa-sign-in', 'login'], // E-commerce 'cart' => ['fa fa-shopping-cart', 'shopping_cart'], 'orders' => ['fa fa-list', 'receipt_long'], 'truck' => ['fa fa-truck', 'local_shipping'], 'credit-card' => ['fa fa-credit-card', 'credit_card'], 'gift' => ['fa fa-gift', 'card_giftcard'], 'tag' => ['fa fa-tag', 'local_offer'], 'percent' => ['fa fa-percent', 'percent'], // Status 'success' => ['fa fa-check', 'check'], 'error' => ['fa fa-times', 'close'], 'warning' => ['fa fa-exclamation-triangle', 'warning'], 'info' => ['fa fa-info-circle', 'info'], // Actions 'edit' => ['fa fa-pencil', 'edit'], 'delete' => ['fa fa-trash', 'delete'], 'search' => ['fa fa-search', 'search'], 'settings' => ['fa fa-cog', 'settings'], 'download' => ['fa fa-download', 'download'], 'upload' => ['fa fa-upload', 'upload'], 'refresh' => ['fa fa-refresh', 'refresh'], 'save' => ['fa fa-save', 'save'], 'print' => ['fa fa-print', 'print'], 'copy' => ['fa fa-copy', 'content_copy'], // UI 'close' => ['fa fa-times', 'close'], 'check' => ['fa fa-check', 'check'], 'plus' => ['fa fa-plus', 'add'], 'minus' => ['fa fa-minus', 'remove'], 'menu' => ['fa fa-bars', 'menu'], 'more' => ['fa fa-ellipsis-v', 'more_vert'], // Arrows 'arrow-left' => ['fa fa-arrow-left', 'arrow_back'], 'arrow-right' => ['fa fa-arrow-right', 'arrow_forward'], 'arrow-up' => ['fa fa-arrow-up', 'arrow_upward'], 'arrow-down' => ['fa fa-arrow-down', 'arrow_downward'], 'chevron-left' => ['fa fa-chevron-left', 'chevron_left'], 'chevron-right' => ['fa fa-chevron-right', 'chevron_right'], 'chevron-up' => ['fa fa-chevron-up', 'expand_less'], 'chevron-down' => ['fa fa-chevron-down', 'expand_more'], // Communication 'email' => ['fa fa-envelope', 'email'], 'phone' => ['fa fa-phone', 'phone'], 'comment' => ['fa fa-comment', 'comment'], 'chat' => ['fa fa-comments', 'chat'], // Security 'lock' => ['fa fa-lock', 'lock'], 'unlock' => ['fa fa-unlock', 'lock_open'], 'shield' => ['fa fa-shield', 'shield'], 'key' => ['fa fa-key', 'vpn_key'], // Misc 'home' => ['fa fa-home', 'home'], 'address' => ['fa fa-map-marker', 'location_on'], 'calendar' => ['fa fa-calendar', 'calendar_today'], 'clock' => ['fa fa-clock-o', 'schedule'], 'heart' => ['fa fa-heart', 'favorite'], 'star' => ['fa fa-star', 'star'], 'eye' => ['fa fa-eye', 'visibility'], 'eye-off' => ['fa fa-eye-slash', 'visibility_off'], 'file' => ['fa fa-file', 'description'], 'folder' => ['fa fa-folder', 'folder'], 'image' => ['fa fa-image', 'image'], 'link' => ['fa fa-link', 'link'], 'external' => ['fa fa-external-link', 'open_in_new'], 'help' => ['fa fa-question-circle', 'help'], // Loading 'spinner' => ['fa fa-spinner fa-spin', 'hourglass_empty'], 'loading' => ['fa fa-circle-o-notch fa-spin', 'autorenew'], ]; /** * Get icon HTML - main method * * @param string $name Semantic icon name * @param string $class Extra CSS classes (optional) * @return string HTML */ public static function get(string $name, string $class = ''): string { if (!isset(self::$map[$name])) { return ''; } $set = self::getIconSet(); [$fa, $material] = self::$map[$name]; $extra = $class ? ' ' . $class : ''; if ($set === self::MATERIAL) { return '' . $material . ''; } return ''; } /** * Get raw class/name without HTML wrapper */ public static function raw(string $name): string { if (!isset(self::$map[$name])) { return ''; } $set = self::getIconSet(); [$fa, $material] = self::$map[$name]; return $set === self::MATERIAL ? $material : $fa; } /** * Detect and cache the icon set to use */ public static function getIconSet(): string { if (self::$iconSet !== null) { return self::$iconSet; } // Admin context = Material Icons if (defined('_PS_ADMIN_DIR_')) { self::$iconSet = self::MATERIAL; return self::$iconSet; } // Front office - read from config table self::$iconSet = self::readConfigTable() ?: self::FA; return self::$iconSet; } /** * Read icon set preference from ps_mpr_config table */ private static function readConfigTable(): ?string { if (!defined('_DB_PREFIX_')) { return null; } try { $table = _DB_PREFIX_ . self::CONFIG_TABLE; $sql = "SELECT `value` FROM `{$table}` WHERE `module` = '" . pSQL(self::CONFIG_MODULE) . "' AND `key` = '" . pSQL(self::CONFIG_KEY) . "'"; $result = \Db::getInstance()->getValue($sql); if ($result && in_array($result, [self::FA, self::MATERIAL])) { return $result; } } catch (\Exception $e) { // Table doesn't exist or query failed - use default } return null; } /** * Save icon set preference to ps_mpr_config table */ public static function saveIconSet(string $set): bool { if (!in_array($set, [self::FA, self::MATERIAL])) { return false; } if (!defined('_DB_PREFIX_')) { return false; } try { $table = _DB_PREFIX_ . self::CONFIG_TABLE; $now = date('Y-m-d H:i:s'); $sql = "INSERT INTO `{$table}` (`module`, `key`, `value`, `date_add`, `date_upd`) VALUES ('" . pSQL(self::CONFIG_MODULE) . "', '" . pSQL(self::CONFIG_KEY) . "', '" . pSQL($set) . "', '{$now}', '{$now}') ON DUPLICATE KEY UPDATE `value` = '" . pSQL($set) . "', `date_upd` = '{$now}'"; $result = \Db::getInstance()->execute($sql); if ($result) { self::$iconSet = null; // Clear cache } return $result; } catch (\Exception $e) { return false; } } /** * Force a specific icon set (for testing or overriding) */ public static function setIconSet(string $set): void { if (in_array($set, [self::FA, self::MATERIAL])) { self::$iconSet = $set; } } /** * Clear cached icon set */ public static function clearCache(): void { self::$iconSet = null; } /** * Initialize icon system - call from module install * Creates config table if needed, sets default icon set */ public static function init(): bool { if (!self::ensureConfigTable()) { return false; } // If no config exists, detect and save default $current = self::readConfigTable(); if ($current === null) { $detected = self::detectThemeIcons(); self::saveIconSet($detected); } return true; } /** * Ensure ps_mpr_config table exists */ public static function ensureConfigTable(): bool { if (!defined('_DB_PREFIX_')) { return false; } try { $table = _DB_PREFIX_ . self::CONFIG_TABLE; // Check if table exists $exists = \Db::getInstance()->getValue( "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = '{$table}'" ); if ((int)$exists > 0) { return true; } // Create table $sql = "CREATE TABLE IF NOT EXISTS `{$table}` ( `id_config` INT UNSIGNED NOT NULL AUTO_INCREMENT, `module` VARCHAR(64) NOT NULL, `key` VARCHAR(128) NOT NULL, `value` TEXT DEFAULT NULL, `date_add` DATETIME NOT NULL, `date_upd` DATETIME NOT NULL, PRIMARY KEY (`id_config`), UNIQUE KEY `module_key` (`module`, `key`), KEY `module` (`module`) ) ENGINE=" . _MYSQL_ENGINE_ . " DEFAULT CHARSET=utf8mb4;"; return \Db::getInstance()->execute($sql); } catch (\Exception $e) { return false; } } /** * Detect which icon set the current theme likely uses * Returns 'fontawesome' for most PrestaShop themes */ 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_')) { return self::FA; } try { // Check theme.yml for icon hints $themeYml = _PS_THEME_DIR_ . 'config/theme.yml'; if (file_exists($themeYml)) { $content = file_get_contents($themeYml); if (strpos($content, 'material') !== false || strpos($content, 'Material') !== false) { return self::MATERIAL; } } // Check if theme has material-icons in assets $assetsDir = _PS_THEME_DIR_ . 'assets/css/'; if (is_dir($assetsDir)) { $files = scandir($assetsDir); foreach ($files as $file) { if (strpos($file, 'material') !== false) { return self::MATERIAL; } } } } catch (\Exception $e) { // Ignore detection errors } // Default to Font Awesome (safe for 95% of themes) return self::FA; } /** * Check if icon exists */ public static function exists(string $name): bool { return isset(self::$map[$name]); } /** * Get all available icon names */ public static function list(): array { return array_keys(self::$map); } /** * Register icon font CSS assets * Convenience method that delegates to MprIconsAssets * Call from hookHeader or setMedia * * @param \Module $module The module instance * @return bool Whether assets were registered */ public static function registerAssets(\Module $module): bool { return MprIconsAssets::registerAssets($module); } /** * Check if self-hosted icon fonts are enabled */ public static function isSelfHostEnabled(): bool { return MprIconsConfig::isSelfHostEnabled(); } }