Fix icon detection: check theme.css content for Material Icons
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 <noreply@anthropic.com>
This commit is contained in:
@@ -379,9 +379,12 @@ class MprIcons
|
|||||||
/**
|
/**
|
||||||
* Detect which icon set the current theme uses
|
* Detect which icon set the current theme uses
|
||||||
*
|
*
|
||||||
* Checks theme config and assets for Material Icons indicators.
|
* PS 1.6 themes use Font Awesome.
|
||||||
* Returns 'fontawesome' for classic and most themes,
|
* PS 1.7+ themes (classic, hummingbird, etc.) bundle Material Icons
|
||||||
* 'material' for Hummingbird and Material-based themes.
|
* 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
|
public static function detectThemeIcons(): string
|
||||||
{
|
{
|
||||||
@@ -390,19 +393,32 @@ class MprIcons
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// PS 1.7+ — check theme.yml
|
// Check compiled theme CSS for Material Icons font-face
|
||||||
$themeYml = _PS_THEME_DIR_ . 'config/theme.yml';
|
// PS 1.7+ bundles icons into theme.css via webpack
|
||||||
if (file_exists($themeYml)) {
|
$cssFiles = [
|
||||||
$content = file_get_contents($themeYml);
|
_PS_THEME_DIR_ . 'assets/css/theme.css',
|
||||||
if (stripos($content, 'hummingbird') !== false) {
|
_PS_THEME_DIR_ . 'css/theme.css',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($cssFiles as $cssFile) {
|
||||||
|
if (!file_exists($cssFile)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 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;
|
return self::MATERIAL;
|
||||||
}
|
}
|
||||||
if (stripos($content, 'material-icons') !== false || stripos($content, 'material_icons') !== false) {
|
if (stripos($chunk, 'FontAwesome') !== false) {
|
||||||
return self::MATERIAL;
|
return self::FA;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check theme assets for material-icons CSS
|
// Fallback: check for separate icon CSS files
|
||||||
$dirs = [
|
$dirs = [
|
||||||
_PS_THEME_DIR_ . 'assets/css/',
|
_PS_THEME_DIR_ . 'assets/css/',
|
||||||
_PS_THEME_DIR_ . 'css/',
|
_PS_THEME_DIR_ . 'css/',
|
||||||
@@ -416,20 +432,26 @@ class MprIcons
|
|||||||
if (stripos($file, 'material') !== false) {
|
if (stripos($file, 'material') !== false) {
|
||||||
return self::MATERIAL;
|
return self::MATERIAL;
|
||||||
}
|
}
|
||||||
|
if (stripos($file, 'fontawesome') !== false || stripos($file, 'font-awesome') !== false) {
|
||||||
|
return self::FA;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PS 1.6 — check for parent theme name
|
// PS 1.6 — check for font-awesome directory
|
||||||
if (defined('_THEME_NAME_')) {
|
$faDir = _PS_THEME_DIR_ . 'css/font-awesome/';
|
||||||
$themeName = _THEME_NAME_;
|
if (is_dir($faDir)) {
|
||||||
if (stripos($themeName, 'hummingbird') !== false) {
|
return self::FA;
|
||||||
return self::MATERIAL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// Detection failed — use safe default
|
// 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;
|
return self::FA;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,8 +466,30 @@ class MprIcons
|
|||||||
}
|
}
|
||||||
|
|
||||||
$iconSet = self::getIconSet();
|
$iconSet = self::getIconSet();
|
||||||
|
$needle = ($iconSet === self::MATERIAL) ? 'Material Icons' : 'FontAwesome';
|
||||||
|
|
||||||
try {
|
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 = [
|
$dirs = [
|
||||||
_PS_THEME_DIR_ . 'assets/css/',
|
_PS_THEME_DIR_ . 'assets/css/',
|
||||||
_PS_THEME_DIR_ . 'css/',
|
_PS_THEME_DIR_ . 'css/',
|
||||||
@@ -455,36 +499,27 @@ class MprIcons
|
|||||||
if (!is_dir($dir)) {
|
if (!is_dir($dir)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$files = scandir($dir);
|
$files = scandir($dir);
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
if ($file === '.' || $file === '..') {
|
if ($file === '.' || $file === '..') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if ($iconSet === self::MATERIAL && stripos($file, 'material') !== false) {
|
||||||
if ($iconSet === self::MATERIAL) {
|
|
||||||
if (stripos($file, 'material') !== false) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
if ($iconSet === self::FA && (stripos($file, 'fontawesome') !== false || stripos($file, 'font-awesome') !== false)) {
|
||||||
if (stripos($file, 'fontawesome') !== false || stripos($file, 'font-awesome') !== false) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// For Font Awesome — most PS 1.6/1.7 themes include it as a dependency
|
// PS 1.6 — check for font-awesome directory
|
||||||
// Check if theme's package.json or theme.yml lists it
|
|
||||||
if ($iconSet === self::FA) {
|
if ($iconSet === self::FA) {
|
||||||
$themeYml = _PS_THEME_DIR_ . 'config/theme.yml';
|
$faDir = _PS_THEME_DIR_ . 'css/font-awesome/';
|
||||||
if (file_exists($themeYml)) {
|
if (is_dir($faDir)) {
|
||||||
$content = file_get_contents($themeYml);
|
|
||||||
if (stripos($content, 'font-awesome') !== false || stripos($content, 'fontawesome') !== false) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// Detection failed
|
// Detection failed
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user