Skip to content

refactor: replace template override with plugin-based logo injection#4

Open
rhoerr wants to merge 4 commits intoelement119:masterfrom
rhoerr:refactor/plugin-based-logo-override
Open

refactor: replace template override with plugin-based logo injection#4
rhoerr wants to merge 4 commits intoelement119:masterfrom
rhoerr:refactor/plugin-based-logo-override

Conversation

@rhoerr
Copy link
Contributor

@rhoerr rhoerr commented Mar 17, 2026

Summary

Replaces the full template override of Magento_Backend::page/header.phtml with a lightweight plugin on Magento\Backend\Block\Page\Header, eliminating template drift and improving maintainability.

  • Plugin approach: beforeToHtml reads config path and upload directory from layout XML arguments, builds a media URL, and sets logo_image_src on the block. afterGetViewFileUrl passes absolute URLs through unchanged so the core template renders them without static file resolution.
  • Layout-driven context: default.xml passes menu logo config; admin_login.xml overrides with login logo config. No login-page detection logic needed.
  • Defensive coding: show_part guard limits plugin to the logo block instance only; is_string() type guard and basename() path-traversal sanitization on config values; NoSuchEntityException + \Throwable catch with PSR-3 logging ensures the plugin never breaks the admin header.
  • Cleanup: removed redundant _getUploadDir() overrides from backend models (parent resolves from system.xml); fixed system.xml group scope mismatch; added missing composer.json dependencies; fixed LESS license header.

Files removed

  • view/adminhtml/templates/page/header.phtml — template override (the whole point)
  • ViewModel/AdminLogo.php — login-page detection replaced by layout handles
  • Model/AdminLogo.php — URL construction moved to plugin (also fixes FileDriver misuse)
  • Scope/Config.php — config reading moved to plugin via ScopeConfigInterface

Files added

  • Plugin/Backend/Block/Page/HeaderPlugin.php
  • etc/adminhtml/di.xml
  • view/adminhtml/layout/admin_login.xml
  • Test/Unit/Plugin/Backend/Block/Page/HeaderPluginTest.php — 15 unit tests, 20 assertions

Test plan

  • bin/magento setup:upgrade — module.xml sequence changed
  • bin/magento setup:di:compile — new plugin class, deleted classes
  • bin/magento cache:flush
  • No custom logos configured → default Mage-OS/Magento logos render on login and menu
  • Upload login logo only → login page shows custom, menu shows default
  • Upload menu logo only → menu shows custom, login shows default
  • Both logos configured → each page shows correct custom logo
  • Remove logos → fallback to defaults
  • Inspect <img> src: media URL when custom, static view file URL when default
  • Unit tests: vendor/bin/phpunit app/code/Element119/CustomAdminLogo/Test/ — 15 tests, 20 assertions, all pass

🤖 Generated with Claude Code

rhoerr and others added 4 commits March 16, 2026 21:27
Eliminate the fragile full-template override of Magento_Backend::page/header.phtml
by using a plugin on Header::toHtml() and Header::getViewFileUrl() instead.

The core already differentiates login vs. menu logos via layout handles
(default.xml vs admin_login.xml setting logo_image_src). This refactor
leverages that mechanism: layout XML passes config path and upload directory
as block arguments, and the plugin reads config, builds the media URL, and
sets logo_image_src before rendering. A companion afterGetViewFileUrl method
passes full URLs through without asset repository resolution.

Removes 4 files (template, ViewModel, Model, Scope/Config) and adds 3
(plugin class, di.xml, admin_login layout). The module now works with any
theme's default logos and is immune to core template changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cover all branches of beforeToHtml (no config path, no upload dir,
no filename in config, menu logo URL, login logo URL) and
afterGetViewFileUrl (https passthrough, http passthrough, view file
fallback).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Wrap storeManager->getStore() in try-catch for NoSuchEntityException
  to prevent admin lockout from a cosmetic module
- Add is_string() type guard on config value (getValue returns mixed)
- Add basename() sanitization to strip path traversal from filename
- Inject LoggerInterface and log warning on store resolution failure
- Add @param/@return PHPDoc tags for Magento2 PHPCS compliance
- Add class-level docblock explaining the plugin design rationale
- Fix license header: LICENCE.txt → LICENSE across all module files
- Add 4 new test cases: empty string config, non-string config,
  path traversal stripping, NoSuchEntityException handling

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… config

- Add show_part !== 'logo' guard to both plugin methods so they skip
  non-logo Header block instances (e.g. the 'user' block)
- Catch \Throwable in addition to NoSuchEntityException so unexpected
  errors degrade gracefully instead of breaking the admin header
- Fix system.xml group scope to showInWebsite=0/showInStore=0 matching
  the field visibility (prevents empty group at non-default scopes)
- Add missing composer.json dependencies (framework, backend, store)
- Remove redundant _getUploadDir() overrides from backend models
  (parent File class already resolves path from system.xml config)
- Fix LESS license header (LICENCE.txt -> LICENSE)
- Update tests: add show_part guard and \Throwable coverage (15 tests)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@rhoerr
Copy link
Contributor Author

rhoerr commented Mar 17, 2026

I think this will resolve #3, by doing away with the template override. I still need to test it, so leaving as draft for now.

@rhoerr
Copy link
Contributor Author

rhoerr commented Mar 18, 2026

Tested. Works as intended (for me).

@rhoerr rhoerr marked this pull request as ready for review March 18, 2026 02:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant