From b75eb1454f0315610057de15b9333e0e269e4bd8 Mon Sep 17 00:00:00 2001 From: myprestarocks Date: Sat, 14 Feb 2026 09:56:49 +0000 Subject: [PATCH] Fix icon detection: check theme.css content for Material Icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PS 1.7+ classic theme bundles Material Icons inside theme.css via webpack — no separate material-icons.css file exists. The old detection only scanned filenames, so it wrongly defaulted to FA. Now reads the first 50KB of theme.css to check for 'Material Icons' or 'FontAwesome' font-face declarations. Falls back to version-based default: Material for PS 1.7+, FA for PS 1.6. Also fixes themeHasIconFont() to use the same CSS content check, so self-hosted icons are correctly skipped when the theme provides them. Co-Authored-By: Claude Opus 4.6 --- src/MprIcons.php | 107 +++++++++++++++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 36 deletions(-) diff --git a/src/MprIcons.php b/src/MprIcons.php index 97d3724..0804a14 100644 --- a/src/MprIcons.php +++ b/src/MprIcons.php @@ -379,9 +379,12 @@ class MprIcons /** * Detect which icon set the current theme uses * - * Checks theme config and assets for Material Icons indicators. - * Returns 'fontawesome' for classic and most themes, - * 'material' for Hummingbird and Material-based themes. + * PS 1.6 themes use Font Awesome. + * PS 1.7+ themes (classic, hummingbird, etc.) bundle Material Icons + * inside theme.css — no separate CSS file to scan for. + * + * Strategy: read the first ~50KB of theme.css and check for + * 'Material Icons' font-face. Fast, reliable, works across all versions. */ public static function detectThemeIcons(): string { @@ -390,19 +393,32 @@ class MprIcons } try { - // PS 1.7+ — check theme.yml - $themeYml = _PS_THEME_DIR_ . 'config/theme.yml'; - if (file_exists($themeYml)) { - $content = file_get_contents($themeYml); - if (stripos($content, 'hummingbird') !== false) { - return self::MATERIAL; + // Check compiled theme CSS for Material Icons font-face + // PS 1.7+ bundles icons into theme.css via webpack + $cssFiles = [ + _PS_THEME_DIR_ . 'assets/css/theme.css', + _PS_THEME_DIR_ . 'css/theme.css', + ]; + + foreach ($cssFiles as $cssFile) { + if (!file_exists($cssFile)) { + continue; } - if (stripos($content, 'material-icons') !== false || stripos($content, 'material_icons') !== false) { - return self::MATERIAL; + // Read first 50KB — font-face is always near the top + $handle = fopen($cssFile, 'r'); + if ($handle) { + $chunk = fread($handle, 51200); + fclose($handle); + if (stripos($chunk, 'Material Icons') !== false) { + return self::MATERIAL; + } + if (stripos($chunk, 'FontAwesome') !== false) { + return self::FA; + } } } - // Check theme assets for material-icons CSS + // Fallback: check for separate icon CSS files $dirs = [ _PS_THEME_DIR_ . 'assets/css/', _PS_THEME_DIR_ . 'css/', @@ -416,20 +432,26 @@ class MprIcons if (stripos($file, 'material') !== false) { return self::MATERIAL; } + if (stripos($file, 'fontawesome') !== false || stripos($file, 'font-awesome') !== false) { + return self::FA; + } } } - // PS 1.6 — check for parent theme name - if (defined('_THEME_NAME_')) { - $themeName = _THEME_NAME_; - if (stripos($themeName, 'hummingbird') !== false) { - return self::MATERIAL; - } + // PS 1.6 — check for font-awesome directory + $faDir = _PS_THEME_DIR_ . 'css/font-awesome/'; + if (is_dir($faDir)) { + return self::FA; } } catch (\Exception $e) { // Detection failed — use safe default } + // Safe default: FA for PS 1.6, Material for 1.7+ + if (defined('_PS_VERSION_') && version_compare(_PS_VERSION_, '1.7.0.0', '>=')) { + return self::MATERIAL; + } + return self::FA; } @@ -444,8 +466,30 @@ class MprIcons } $iconSet = self::getIconSet(); + $needle = ($iconSet === self::MATERIAL) ? 'Material Icons' : 'FontAwesome'; try { + // Check compiled theme CSS — icons are typically bundled here + $cssFiles = [ + _PS_THEME_DIR_ . 'assets/css/theme.css', + _PS_THEME_DIR_ . 'css/theme.css', + ]; + + foreach ($cssFiles as $cssFile) { + if (!file_exists($cssFile)) { + continue; + } + $handle = fopen($cssFile, 'r'); + if ($handle) { + $chunk = fread($handle, 51200); + fclose($handle); + if (stripos($chunk, $needle) !== false) { + return true; + } + } + } + + // Check for separate icon CSS files $dirs = [ _PS_THEME_DIR_ . 'assets/css/', _PS_THEME_DIR_ . 'css/', @@ -455,34 +499,25 @@ class MprIcons 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; - } + if ($iconSet === self::MATERIAL && stripos($file, 'material') !== false) { + return true; + } + if ($iconSet === self::FA && (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 + // PS 1.6 — check for font-awesome directory 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; - } + $faDir = _PS_THEME_DIR_ . 'css/font-awesome/'; + if (is_dir($faDir)) { + return true; } } } catch (\Exception $e) {