Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions modules/identity/plugins/Favicon.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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]);
Expand Down
17 changes: 10 additions & 7 deletions tests/Integration/Identity/FaviconPluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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]
Expand Down
Binary file added themes/puma/assets/img/tiger-favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions themes/puma/layouts/scripts/admin.phtml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ $_hasAgent = class_exists('Tiger_Agent') && Tiger_Agent::isAvailable();
<!-- Override playground — loads last so it wins the cascade. See themes/puma/assets/custom.css. -->
<link rel="stylesheet" href="<?= $this->asset($this->themeAssets . '/custom.css') ?>">
<?= $this->codeInject('head') ?>
<?= $this->headLink() ?><?php /* head-registry <link>s — chiefly the site favicon (Identity_Plugin_Favicon) */ ?>
<?= $this->pageHead ?? '' ?><?php /* per-page <link>/styles a view registered via $this->pageStyle() */ ?>
</head>
<body>
Expand Down
1 change: 1 addition & 0 deletions themes/puma/layouts/scripts/auth.phtml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ $_lang = defined('LANG') ? LANG : 'en';
<link rel="stylesheet" href="<?= $this->asset($this->themeAssets . '/auth.css') ?>">
<!-- Override playground — loads last so it wins the cascade. See themes/puma/assets/custom.css. -->
<link rel="stylesheet" href="<?= $this->asset($this->themeAssets . '/custom.css') ?>">
<?= $this->headLink() ?><?php /* head-registry <link>s — chiefly the site favicon (Identity_Plugin_Favicon) */ ?>
<?= $this->pageHead ?? '' ?><?php /* per-page <link>/styles a view registered via $this->pageStyle() */ ?>
</head>
<body class="tiger-auth-body">
Expand Down
Loading