From bb07e61e40fdcd6866a68f779e155b9b01b335e2 Mon Sep 17 00:00:00 2001 From: "Beau Beauchamp, WebTigers" Date: Fri, 7 Aug 2026 05:11:48 -0400 Subject: [PATCH] Default Tiger-paw favicon on every page + admin/auth head registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Identity_Plugin_Favicon now emits a baked-in Tiger paw default (DEFAULT_FAVICON, a puma base-theme asset served at the always-present /_theme base) when Site Identity sets no favicon — so every page has one out of the box; a configured favicon still overrides it. - The admin + auth shells now render $this->headLink(), so the favicon (and any head-registry links) appear there too — previously only the public shell did, which is why the admin panel had no favicon. - Ship the paw at themes/puma/assets/img/tiger-favicon.png (baked into tiger-core). - FaviconPluginTest: unset / unresolvable now assert the default paw, not silence. Co-Authored-By: Claude Opus 4.8 (1M context) --- modules/identity/plugins/Favicon.php | 20 +++++++++++------- .../Identity/FaviconPluginTest.php | 17 +++++++++------ themes/puma/assets/img/tiger-favicon.png | Bin 0 -> 2746 bytes themes/puma/layouts/scripts/admin.phtml | 1 + themes/puma/layouts/scripts/auth.phtml | 1 + 5 files changed, 24 insertions(+), 15 deletions(-) create mode 100644 themes/puma/assets/img/tiger-favicon.png diff --git a/modules/identity/plugins/Favicon.php b/modules/identity/plugins/Favicon.php index fb34747..9475439 100644 --- a/modules/identity/plugins/Favicon.php +++ b/modules/identity/plugins/Favicon.php @@ -6,11 +6,16 @@ * site identity (config `tiger.site.favicon`, a media id), not SEO, but like the SEO head tags it * rides TigerZF's headLink registry so the layout renders it with no theme edit. A single high-res * square source is emitted as both `rel="icon"` (browsers downscale it for every tab size) and - * `rel="apple-touch-icon"` (iOS) — the modern, derivative-free approach. Fail-open: a missing or - * unresolvable favicon simply emits nothing (the browser falls back to /favicon.ico if present). + * `rel="apple-touch-icon"` (iOS) — the modern, derivative-free approach. When Site Identity sets no + * favicon, a baked-in Tiger paw default (DEFAULT_FAVICON — a puma base-theme asset whose `/_theme` + * symlink is always present for both admin and public) is emitted, so EVERY page has a favicon out of + * the box; a configured Site Identity favicon overrides it. Fail-open: any error emits nothing. */ class Identity_Plugin_Favicon extends Zend_Controller_Plugin_Abstract { + /** The stock Tiger paw, shipped in tiger-core (themes/puma/assets/img) + served at the always-present /_theme base. */ + const DEFAULT_FAVICON = '/_theme/img/tiger-favicon.png'; + /** Emit-once latch — the favicon is the same on every dispatch (incl. forwards). */ private static $_done = false; @@ -26,13 +31,12 @@ public function preDispatch(Zend_Controller_Request_Abstract $request) self::$_done = true; try { - $id = self::_config('site.favicon'); - if ($id === '') { - return; - } - $url = self::_mediaUrl($id, $request); + // A configured Site Identity favicon wins; otherwise fall back to the baked-in Tiger paw so + // every page (public / admin / auth) has a favicon by default. + $id = self::_config('site.favicon'); + $url = ($id !== '') ? self::_mediaUrl($id, $request) : ''; if ($url === '') { - return; + $url = self::DEFAULT_FAVICON; } $view = self::_view(); $view->headLink(['rel' => 'icon', 'href' => $url]); diff --git a/tests/Integration/Identity/FaviconPluginTest.php b/tests/Integration/Identity/FaviconPluginTest.php index 782b6f0..ac18211 100644 --- a/tests/Integration/Identity/FaviconPluginTest.php +++ b/tests/Integration/Identity/FaviconPluginTest.php @@ -17,9 +17,10 @@ /** * Identity_Plugin_Favicon — contributes the site favicon (config `tiger.site.favicon`, a media id) to - * the head via TigerZF's `headLink` registry, as both `rel=icon` and `rel=apple-touch-icon`. Fail-open: - * an unset or unresolvable favicon emits nothing. Wave-4 coverage: no config → silent, an unresolvable - * id → silent, and a real media id → two head links pointing at the resolved media URL. + * the head via TigerZF's `headLink` registry, as both `rel=icon` and `rel=apple-touch-icon`. When no + * Site Identity favicon is set (or it's unresolvable), a baked-in Tiger paw default is emitted so every + * page has one; a configured favicon overrides it. Coverage: no config → the default paw, an + * unresolvable id → the default paw, and a real media id → two links pointing at the resolved media URL. * * The plugin has a process-wide emit-once latch (`$_done`); each test resets it via reflection. */ @@ -66,19 +67,21 @@ private function dispatch(): void } #[Test] - public function emits_nothing_when_no_favicon_is_configured(): void + public function emits_the_default_paw_when_no_favicon_is_configured(): void { $this->faviconConfig(''); $this->dispatch(); - $this->assertSame('', trim($this->headLinks()), 'no config → no head links'); + $out = $this->headLinks(); + $this->assertStringContainsString('rel="icon"', $out, 'no config → the baked-in default favicon'); + $this->assertStringContainsString(Identity_Plugin_Favicon::DEFAULT_FAVICON, $out, 'points at the stock Tiger paw'); } #[Test] - public function emits_nothing_for_an_unresolvable_media_id(): void + public function falls_back_to_the_default_paw_for_an_unresolvable_media_id(): void { $this->faviconConfig('deadbeef-0000-7000-8000-000000000000'); // no such media row $this->dispatch(); - $this->assertSame('', trim($this->headLinks()), 'unresolvable id → fail-open, nothing emitted'); + $this->assertStringContainsString(Identity_Plugin_Favicon::DEFAULT_FAVICON, $this->headLinks(), 'unresolvable id → fail-safe to the default paw'); } #[Test] diff --git a/themes/puma/assets/img/tiger-favicon.png b/themes/puma/assets/img/tiger-favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..0d50564ead2244c3b124d610c64b62fbb94eeb8b GIT binary patch literal 2746 zcmV;r3PtsaP)EGix9=ADuTg{jKf6zw!V+Lbld-iqN!rD&)n zP0Et5?#T|H&8;Z~{RG9-1K0IqB{6G8@lrVq>crk@$}UY-dagWe)wz4(???j^M2T zWE3{V-_|YIffa`HG-RX`J7R9AT>zGY7vPii3V{XH8j1#F6Ee>y?E@W&y`dT3eZ$fbMQg#>I_B*Uj|8 zo;4_o;ON?zJY0ATr{159aToLGRO9Bm1q~`#H*yI(`LO!ALZvZkQvFhr|72d<2_sq=TF|J{+SfEIG>t^-^suV)?st!>$#+XaR%DhQY3o$W z00A@_puSpA*Q712qE%H+^h?;6)ADyf<^{y`$MQ5HtqiU#=t_G~_3D7D8oY`R`9GPJ&8ffP>!jTm^OvrG3T-}j(tz40l{j9D5moPY0 z*&QR=L!*G0ZXcsv8lXyh*ml&=0w5k~mkQfA%uFx(i@syvXn*|}{w6H>L0JnjYnNQk zukG^?>CWO&j-i_(_!~tWs!gwa(R0t*&m{f3=yOcMbja!sTGpZ}-H_Gz(f3bk%8ZbP zmNa)2&dI1*@~Esq1q^UuzQ@kdQdXosX;@{QpX#8n;yWh#gf-w8weG{zr;hD&n zHg_Ef{M#?dI)@cZ8b#{_SP#fV4~yUXISEO>zqv5PYChgo4;B^l@zLW#4BVjcw`B(5J+Dork?R#EQv%$=Ak&6(rUDZ4fxqPpB5P`LEi;!MD*-pl z6t`D|S)q6{x3{86fDyo<%mfcktdvieI&!H}*rBV`KvmVIs;F#HSKcgFzqnya*5# z`lYB=8=XDdT&I5cYj048DSvxcQ)%(9E8;3xwQq!Cv({2Q0E%qwr(e;apScv@S5 zSKZ&{E&Pq!VH%P&;5c44YWH_%#nS3NMm#<5Q0%h&s!jq|d=C94<6 za4}=~@#(h5j?8ewv{Bon00!&U9XPQ5;&$snZE0GE1IsVeeW*}re#BZpQKRa)zS}Xz zTP(h{-0Q%mNd6Y9*Ru%1n;LJSP(bulAI^-@b;C1OML0(2J2tDZJqt(H=>D9R&vdP$ zX%%)Qzpicv+PEF#vI_b`LHh+=i8F)jJkzGy9QC$lEH{OyULXQCZOYDZ!d-B)SJy!q z`Lgq{Pr{HNda1DehizG9-|IR+=YaFT^_Mr+X4R)Q$knIxh(ohW6qmPLd~sS^V)6)w zpnt?!AJ7*1WMYs%gm9OBHO+OgwnVsGv-@h(PJPngL*>YW)~^k%oL~xfI`+Qns#p)s zBo>Xre$z@&wFam!7Rb*ho+$pSwsTgGag5wLR=6EUfQl(VFBT}y&pKYXS6gCAlmuBf znzsQCTi#v=T3p~qZQS)!eqEhOqzxHY5s>x}5w zjRC#(q63ZlreLeS>j<@H`WH7UUmUwzoj0fvh^bK$Lp>u^C}uD@X8`k-4MS0W$k_ME1Z68|h}v ziFmzjKjeV%{+nZ*Vh2>%ydZAE5bC>ngCzQK69!aRyqD!Tzutm)3K_%tRajm{qxxO?EhxDPf3yeIx>fd-oTheleU+@MGPkBP*HnVA3Jh^z2TQGJ&SFcN-J4ejf8F7J z-k{9?D7$g{k56l>h?ZlLja)-7jGb z>dyDO4(Y?i$%a`NPRzn^VitxIvoM^Pg#mp0AD`3nFi3iB+yDRo07*qoM6N<$g6y_k A!~g&Q literal 0 HcmV?d00001 diff --git a/themes/puma/layouts/scripts/admin.phtml b/themes/puma/layouts/scripts/admin.phtml index 0de083d..4d0cbf5 100644 --- a/themes/puma/layouts/scripts/admin.phtml +++ b/themes/puma/layouts/scripts/admin.phtml @@ -67,6 +67,7 @@ $_hasAgent = class_exists('Tiger_Agent') && Tiger_Agent::isAvailable(); codeInject('head') ?> +headLink() ?>s — chiefly the site favicon (Identity_Plugin_Favicon) */ ?> pageHead ?? '' ?>/styles a view registered via $this->pageStyle() */ ?> diff --git a/themes/puma/layouts/scripts/auth.phtml b/themes/puma/layouts/scripts/auth.phtml index c71fb9e..8061213 100644 --- a/themes/puma/layouts/scripts/auth.phtml +++ b/themes/puma/layouts/scripts/auth.phtml @@ -33,6 +33,7 @@ $_lang = defined('LANG') ? LANG : 'en'; +headLink() ?>s — chiefly the site favicon (Identity_Plugin_Favicon) */ ?> pageHead ?? '' ?>/styles a view registered via $this->pageStyle() */ ?>