From b9cdcd178f5b95cb9e5ae0966d9c2eb605855a73 Mon Sep 17 00:00:00 2001 From: Mark Shust Date: Tue, 6 Oct 2026 11:07:41 -0400 Subject: [PATCH] fix!: wire up or remove config keys nothing reads - admin-panel.page_title: DashboardController now injects AdminPanelConfigInterface and passes getPageTitle() to the template as pageTitle, which the latte/twig base layouts already render in . - admin-panel.items_per_page / getItemsPerPage(): removed. The panel has no listing to paginate. - layout.components / layout.layouts: removed along with config/layout.php. Layouts and components are discovered from attributes. - mail.sendmail: removed. No sendmail driver ships; the driver comment now names the drivers that do (smtp, log). The matching ConfigReadersTest allowlist entries are gone, and the docs and tutorial no longer mention the removed keys. Fixes #518 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KyjR4cJTfro54iFZ9CWkv1 --- packages/admin-panel/config/admin-panel.php | 1 - .../src/Config/AdminPanelConfig.php | 5 --- .../src/Config/AdminPanelConfigInterface.php | 2 -- .../src/Controller/DashboardController.php | 3 ++ .../Unit/Config/AdminPanelConfigTest.php | 31 +++---------------- .../Controller/DashboardControllerTest.php | 19 ++++++++++++ .../docs/packages/admin-panel.md | 11 ++++++- .../docs/tutorials/build-an-admin-panel.md | 3 +- packages/layout/config/layout.php | 8 ----- .../layout/tests/PackageStructureTest.php | 10 ++---- packages/mail/config/mail.php | 16 ++-------- tests/config-readers-allowlist.php | 5 --- 12 files changed, 41 insertions(+), 73 deletions(-) delete mode 100644 packages/layout/config/layout.php diff --git a/packages/admin-panel/config/admin-panel.php b/packages/admin-panel/config/admin-panel.php index d6f40c3fb..2456066c3 100644 --- a/packages/admin-panel/config/admin-panel.php +++ b/packages/admin-panel/config/admin-panel.php @@ -4,5 +4,4 @@ return [ 'page_title' => 'Marko Admin', - 'items_per_page' => 20, ]; diff --git a/packages/admin-panel/src/Config/AdminPanelConfig.php b/packages/admin-panel/src/Config/AdminPanelConfig.php index 8b73741b4..53a4ad174 100644 --- a/packages/admin-panel/src/Config/AdminPanelConfig.php +++ b/packages/admin-panel/src/Config/AdminPanelConfig.php @@ -16,9 +16,4 @@ public function getPageTitle(): string { return $this->config->getString('admin-panel.page_title'); } - - public function getItemsPerPage(): int - { - return $this->config->getInt('admin-panel.items_per_page'); - } } diff --git a/packages/admin-panel/src/Config/AdminPanelConfigInterface.php b/packages/admin-panel/src/Config/AdminPanelConfigInterface.php index 757ad2768..3285251e3 100644 --- a/packages/admin-panel/src/Config/AdminPanelConfigInterface.php +++ b/packages/admin-panel/src/Config/AdminPanelConfigInterface.php @@ -7,6 +7,4 @@ interface AdminPanelConfigInterface { public function getPageTitle(): string; - - public function getItemsPerPage(): int; } diff --git a/packages/admin-panel/src/Controller/DashboardController.php b/packages/admin-panel/src/Controller/DashboardController.php index bfc057243..46f544918 100644 --- a/packages/admin-panel/src/Controller/DashboardController.php +++ b/packages/admin-panel/src/Controller/DashboardController.php @@ -7,6 +7,7 @@ use Marko\AdminAuth\AdminGuardResolver; use Marko\AdminAuth\Entity\AdminUserInterface; use Marko\AdminAuth\Middleware\AdminAuthMiddleware; +use Marko\AdminPanel\Config\AdminPanelConfigInterface; use Marko\AdminPanel\Menu\AdminMenuBuilderInterface; use Marko\Routing\Attributes\Get; use Marko\Routing\Attributes\Middleware; @@ -20,6 +21,7 @@ public function __construct( private readonly ViewInterface $view, private readonly AdminMenuBuilderInterface $menuBuilder, private readonly AdminGuardResolver $adminGuard, + private readonly AdminPanelConfigInterface $config, ) {} /** @@ -41,6 +43,7 @@ public function index( return $this->view->render('admin-panel::dashboard/index', [ 'sections' => $sections, 'currentUser' => $user, + 'pageTitle' => $this->config->getPageTitle(), ]); } } diff --git a/packages/admin-panel/tests/Unit/Config/AdminPanelConfigTest.php b/packages/admin-panel/tests/Unit/Config/AdminPanelConfigTest.php index 29785da7c..f5b7c118a 100644 --- a/packages/admin-panel/tests/Unit/Config/AdminPanelConfigTest.php +++ b/packages/admin-panel/tests/Unit/Config/AdminPanelConfigTest.php @@ -6,45 +6,22 @@ use Marko\AdminPanel\Config\AdminPanelConfigInterface; use Marko\Testing\Fake\FakeConfigRepository; -it('creates AdminPanelConfig with pageTitle and itemsPerPage settings', function (): void { +it('reads the page title from admin-panel.page_title', function (): void { $config = new AdminPanelConfig(new FakeConfigRepository([ 'admin-panel.page_title' => 'My Custom Admin', - 'admin-panel.items_per_page' => 50, ])); expect($config)->toBeInstanceOf(AdminPanelConfigInterface::class) - ->and($config->getPageTitle())->toBe('My Custom Admin') - ->and($config->getItemsPerPage())->toBe(50); + ->and($config->getPageTitle())->toBe('My Custom Admin'); }); -it('provides default page title of Marko Admin', function (): void { - $config = new AdminPanelConfig(new FakeConfigRepository([ - 'admin-panel.page_title' => 'Marko Admin', - 'admin-panel.items_per_page' => 20, - ])); - - expect($config->getPageTitle())->toBe('Marko Admin'); -}); - -it('provides default items per page of 20', function (): void { - $config = new AdminPanelConfig(new FakeConfigRepository([ - 'admin-panel.page_title' => 'Marko Admin', - 'admin-panel.items_per_page' => 20, - ])); - - expect($config->getItemsPerPage())->toBe(20); -}); - -it('has valid config/admin-panel.php with default values', function (): void { +it('ships config/admin-panel.php with only the page title default', function (): void { $configPath = dirname(__DIR__, 3) . '/config/admin-panel.php'; $configData = require $configPath; expect(file_exists($configPath))->toBeTrue() ->and($configData)->toBeArray() - ->and($configData)->toHaveKey('page_title') - ->and($configData)->toHaveKey('items_per_page') - ->and($configData['page_title'])->toBe('Marko Admin') - ->and($configData['items_per_page'])->toBe(20); + ->and($configData)->toBe(['page_title' => 'Marko Admin']); }); it('binds AdminPanelConfigInterface to AdminPanelConfig in module.php', function (): void { diff --git a/packages/admin-panel/tests/Unit/Controller/DashboardControllerTest.php b/packages/admin-panel/tests/Unit/Controller/DashboardControllerTest.php index 2d9038ee4..4bbe26f7a 100644 --- a/packages/admin-panel/tests/Unit/Controller/DashboardControllerTest.php +++ b/packages/admin-panel/tests/Unit/Controller/DashboardControllerTest.php @@ -13,6 +13,7 @@ use Marko\AdminAuth\Entity\AdminUser; use Marko\AdminAuth\Entity\Role; use Marko\AdminAuth\Middleware\AdminAuthMiddleware; +use Marko\AdminPanel\Config\AdminPanelConfig; use Marko\AdminPanel\Controller\DashboardController; use Marko\AdminPanel\Menu\AdminMenuBuilder; use Marko\AdminPanel\Tests\Fixtures\FixedAdminGuardResolver; @@ -20,6 +21,7 @@ use Marko\Routing\Http\Request; use Marko\Routing\Http\Response; use Marko\Testing\Fake\FakeAuthenticatable; +use Marko\Testing\Fake\FakeConfigRepository; use Marko\Testing\Fake\FakeGuard; use Marko\View\ViewInterface; use ReflectionMethod; @@ -167,11 +169,15 @@ function createDashboardController( StubView $view, StubSectionRegistry $registry, FakeGuard $guard, + string $pageTitle = 'Marko Admin', ): DashboardController { return new DashboardController( view: $view, menuBuilder: new AdminMenuBuilder($registry), adminGuard: new FixedAdminGuardResolver($guard), + config: new AdminPanelConfig(new FakeConfigRepository([ + 'admin-panel.page_title' => $pageTitle, + ])), ); } @@ -269,3 +275,16 @@ function createDashboardController( ->and($view->lastData['currentUser'])->toBe($user) ->and($view->lastData['currentUser']->getAuthIdentifier())->toBe(5); }); + +it('passes the configured admin-panel.page_title to the template as pageTitle', function (): void { + $view = new StubView(); + $registry = new StubSectionRegistry(); + $guard = new FakeGuard(name: 'admin', attemptResult: false); + + $guard->setUser(createDashboardAdminUser(superAdmin: true)); + + createDashboardController($view, $registry, $guard, pageTitle: 'Acme Back Office')->index(new Request()); + + expect($view->lastData)->toHaveKey('pageTitle') + ->and($view->lastData['pageTitle'])->toBe('Acme Back Office'); +}); diff --git a/packages/docs-markdown/docs/packages/admin-panel.md b/packages/docs-markdown/docs/packages/admin-panel.md index bbbd3d501..7863783d6 100644 --- a/packages/docs-markdown/docs/packages/admin-panel.md +++ b/packages/docs-markdown/docs/packages/admin-panel.md @@ -88,6 +88,16 @@ mymodule/ index.latte # Overrides the default dashboard ``` +### Configuration + +`config/admin-panel.php` sets the browser title of the dashboard. `DashboardController` passes it to the template as `pageTitle`: + +```php title="config/admin-panel.php" +return [ + 'page_title' => 'Marko Admin', +]; +``` + ## Customization Replace the menu builder via Preferences to customize navigation behavior: @@ -153,6 +163,5 @@ use Marko\AdminAuth\Middleware\AdminAuthMiddleware; interface AdminPanelConfigInterface { public function getPageTitle(): string; - public function getItemsPerPage(): int; } ``` diff --git a/packages/docs-markdown/docs/tutorials/build-an-admin-panel.md b/packages/docs-markdown/docs/tutorials/build-an-admin-panel.md index 688f335fa..f5446aff1 100644 --- a/packages/docs-markdown/docs/tutorials/build-an-admin-panel.md +++ b/packages/docs-markdown/docs/tutorials/build-an-admin-panel.md @@ -50,7 +50,7 @@ return [ ]; ``` -Create the admin panel configuration: +Set the title the dashboard shows in the browser tab: ```php title="config/admin-panel.php" <?php @@ -59,7 +59,6 @@ declare(strict_types=1); return [ 'page_title' => 'My App Admin', - 'items_per_page' => 25, ]; ``` diff --git a/packages/layout/config/layout.php b/packages/layout/config/layout.php deleted file mode 100644 index 243332328..000000000 --- a/packages/layout/config/layout.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php - -declare(strict_types=1); - -return [ - 'components' => [], - 'layouts' => [], -]; diff --git a/packages/layout/tests/PackageStructureTest.php b/packages/layout/tests/PackageStructureTest.php index 21989c1dc..e9bef8feb 100644 --- a/packages/layout/tests/PackageStructureTest.php +++ b/packages/layout/tests/PackageStructureTest.php @@ -56,14 +56,8 @@ ->and($module['bindings'][ComponentCollectorInterface::class])->toBe(DiscoveringComponentCollector::class); }); -it('has a config/layout.php that returns a configuration array', function (): void { - $configPath = dirname(__DIR__) . '/config/layout.php'; - - expect(file_exists($configPath))->toBeTrue(); - - $config = require $configPath; - - expect($config)->toBeArray(); +it('ships no config file, since layouts and components are discovered from attributes', function (): void { + expect(is_dir(dirname(__DIR__) . '/config'))->toBeFalse(); }); it('module.php declares LayoutMiddleware as globalMiddleware', function (): void { diff --git a/packages/mail/config/mail.php b/packages/mail/config/mail.php index 70ff0cf20..d1575b143 100644 --- a/packages/mail/config/mail.php +++ b/packages/mail/config/mail.php @@ -8,8 +8,8 @@ | Mail Driver |-------------------------------------------------------------------------- | - | The default mail driver used for sending emails. Supported drivers - | include: "smtp", "sendmail", "log", "array" + | The default mail driver used for sending emails. Drivers ship as + | separate packages: "smtp" (marko/mail-smtp), "log" (marko/mail-log). | */ 'driver' => 'smtp', @@ -44,16 +44,4 @@ 'timeout' => 30, 'auth_mode' => 'login', ], - - /* - |-------------------------------------------------------------------------- - | Sendmail Configuration - |-------------------------------------------------------------------------- - | - | Configuration for the sendmail driver. - | - */ - 'sendmail' => [ - 'path' => '/usr/sbin/sendmail -bs', - ], ]; diff --git a/tests/config-readers-allowlist.php b/tests/config-readers-allowlist.php index 4613d466c..19f51029d 100644 --- a/tests/config-readers-allowlist.php +++ b/tests/config-readers-allowlist.php @@ -19,13 +19,8 @@ 'discovery.cache_path' => 'Mirrors the boot gate for introspection; boot reads the env through DiscoveryEnvironment (marko/core cannot depend on marko/config).', 'http-guzzle.timeout' => 'Read through the composed key "http-guzzle.$name" in GuzzleHttpClient::timeoutFromConfig().', 'http-guzzle.connect_timeout' => 'Read through the composed key "http-guzzle.$name" in GuzzleHttpClient::timeoutFromConfig().', - 'layout.components' => 'Pre-existing: no reader yet; components are discovered from #[Component] attributes. Not security-relevant.', - 'layout.layouts' => 'Pre-existing: no reader yet; layouts are discovered from attributes. Not security-relevant.', - 'mail.sendmail' => 'Pre-existing: no sendmail driver ships yet. Not security-relevant.', ], 'accessors' => [ - 'Marko\AdminPanel\Config\AdminPanelConfig::getPageTitle' => 'Pre-existing: documented accessor for app templates; the panel itself does not read it yet. Not security-relevant.', - 'Marko\AdminPanel\Config\AdminPanelConfig::getItemsPerPage' => 'Pre-existing: documented accessor for app code; the panel itself does not paginate yet. Not security-relevant.', 'Marko\Authentication\Config\AuthConfig::providers' => 'Whole-section accessor for app code; the framework reads providersOrNull().', 'Marko\Authentication\Config\AuthConfig::passwordConfig' => 'Whole-section accessor for app code; the framework reads bcryptCost().', 'Marko\Authentication\Config\AuthConfig::rememberConfig' => 'Whole-section accessor for app code; the framework reads the remember* accessors.',