diff --git a/CHANGELOG.md b/CHANGELOG.md index 83e1bd0703f..aeb5399510e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ - Fixed a bug where addresses couldn’t be saved from an element editor slideout. ([#19792](https://github.com/craftcms/cms/pull/19792)) - Fixed a bug where element actions couldn’t find a user’s addresses. ([#19792](https://github.com/craftcms/cms/pull/19792)) - Fixed a bug where users’ breadcrumb chips weren’t getting hyperlinked. +- Fixed a bug where plugins’ control panel nav items weren’t showing their icons. ([#19794](https://github.com/craftcms/cms/pull/19794)) - Fixed a bug where `craft:up` could fail on installs that didn’t have a migrations table yet. ([#19796](https://github.com/craftcms/cms/pull/19796)) - Fixed a bug where subsequent embedded index requests lost configuration supplied by non-Matrix nested element managers. ([#19788](https://github.com/craftcms/cms/pull/19788)) diff --git a/src/Cp/Navigation.php b/src/Cp/Navigation.php index a2cbbb9e933..c48ac74373f 100644 --- a/src/Cp/Navigation.php +++ b/src/Cp/Navigation.php @@ -533,8 +533,8 @@ private function settingNavItem(string $handle, array $setting): NavItem } /** - * Gives an item its id and an absolute URL, and does the same for anything - * beneath it. + * Gives an item its id and an absolute URL, renders an icon it can't name, + * and does the same for anything beneath it. */ private function normalize(NavItem $item): NavItem { @@ -544,6 +544,8 @@ private function normalize(NavItem $item): NavItem $item->href = Url::url($item->href); } + $this->resolveIcon($item); + if (is_array($item->subnav)) { $item->subnav = array_map( $this->normalize(...), @@ -554,6 +556,46 @@ private function normalize(NavItem $item): NavItem return $item; } + /** + * Renders an icon the control panel can't look up by name. + * + * The nav draws a named icon through `craft-nav-item`'s `icon` attribute + * and anything else as inline markup, so those are the only two shapes it + * understands. A plugin supplies neither: `cpNavIconPath()` returns the + * path to its own `icon-mask.svg`, which went into `icon` and was then + * looked up as though it were a system icon's name — so no plugin's icon + * ever appeared. + * + * Rendering it here covers plugins written for Craft 6 and those coming + * through the Yii adapter alike, since both arrive as a path in the same + * field, and neither has to change. + * + * Deliberately narrow: only a value naming an SVG file is rendered, so the + * named icons the rest of the nav uses stay names. Inlining every icon + * would weigh down a tree that is cached and shared across every control + * panel page. + */ + private function resolveIcon(NavItem $item): void + { + if ($item->iconSvg !== null || $item->icon === null) { + return; + } + + if (! str_ends_with(strtolower($item->icon), '.svg')) { + return; + } + + $svg = Icons::svg($item->icon); + + // `Icons::svg()` logs and returns an empty string for anything it + // can't read, so a missing or unreadable file leaves the item as it + // was rather than giving it a blank icon. + if ($svg !== null && $svg !== '') { + $item->iconSvg = $svg; + $item->icon = null; + } + } + /** The stable id an item is known by, in the tree and in the badge map. */ private function itemId(string $href): string {