From 5523d001d15994d91d215b1b993ed5fff79f389f Mon Sep 17 00:00:00 2001 From: "Eric Richer eric.richer@vistoconsulting.com" Date: Wed, 4 Sep 2024 15:32:34 -0400 Subject: [PATCH] Added test suites. Update view template. Added listeners, module options, etc. --- LICENSE | 2 +- README.md | 25 +- composer.json | 9 +- composer.lock | 287 +++++++++++++++++- ...in.global.php => lmcadmin.global.php.dist} | 28 +- src/ConfigProvider.php | 125 ++++---- src/Controller/AdminController.php | 53 +--- src/Listener/LayoutTemplateSelectListener.php | 45 +++ .../LayoutTemplateSelectListenerFactory.php | 19 ++ src/Module.php | 125 +------- .../Service/AdminNavigationFactory.php | 45 +-- src/Options/ModuleOptions.php | 39 +++ src/Options/ModuleOptionsFactory.php | 20 ++ test/Assets/TestController.php | 24 ++ test/ConfigProviderTest.php | 10 +- test/Controller/AdminControllerTest.php | 51 ++++ test/Controller/testing.config.php | 13 + ...ayoutTemplateSelectListenerFactoryTest.php | 22 ++ .../LayoutTemplateSelectListenerTest.php | 105 +++++++ test/Listener/testing.config.php | 51 ++++ test/ModuleTest.php | 16 + test/Options/ModuleOptionsFactoryTest.php | 38 +++ test/Options/ModuleOptionsTest.php | 35 +++ test/test.application.config.php | 17 ++ test/testing.config.php | 11 + test/view/home-index.phtml | 1 + test/view/testlayout.phtml | 8 + view/lmc-admin/admin/index.phtml | 3 - view/lmc/admin/index.phtml | 5 + view/{ => lmc}/layout/admin.phtml | 2 +- 30 files changed, 924 insertions(+), 310 deletions(-) rename config/{lmcadmin.global.php => lmcadmin.global.php.dist} (77%) create mode 100644 src/Listener/LayoutTemplateSelectListener.php create mode 100644 src/Listener/LayoutTemplateSelectListenerFactory.php create mode 100644 src/Options/ModuleOptions.php create mode 100644 src/Options/ModuleOptionsFactory.php create mode 100644 test/Assets/TestController.php create mode 100644 test/Controller/AdminControllerTest.php create mode 100644 test/Controller/testing.config.php create mode 100644 test/Listener/LayoutTemplateSelectListenerFactoryTest.php create mode 100644 test/Listener/LayoutTemplateSelectListenerTest.php create mode 100644 test/Listener/testing.config.php create mode 100644 test/ModuleTest.php create mode 100644 test/Options/ModuleOptionsFactoryTest.php create mode 100644 test/Options/ModuleOptionsTest.php create mode 100644 test/test.application.config.php create mode 100644 test/testing.config.php create mode 100644 test/view/home-index.phtml create mode 100644 test/view/testlayout.phtml delete mode 100644 view/lmc-admin/admin/index.phtml create mode 100644 view/lmc/admin/index.phtml rename view/{ => lmc}/layout/admin.phtml (94%) diff --git a/LICENSE b/LICENSE index 7519bc3..0f799ea 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013, ZF-Commons Contributors +Copyright (c) 2024, LM-Commons Contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, diff --git a/README.md b/README.md index 54e2006..db7c520 100644 --- a/README.md +++ b/README.md @@ -4,23 +4,32 @@ [![PHP Version Require](http://poser.pugx.org/lm-commons/lmc-admin/require/php)](https://packagist.org/packages/lm-commons/lmc-admin) # LmcAdmin Module for Laminas Framework -Created by [Jurian Sluiman](http://juriansluiman.nl) and [Martin Shwalbe](https://github.com/Hounddog). +Originally created by [Jurian Sluiman](http://juriansluiman.nl) and [Martin Shwalbe](https://github.com/Hounddog). ## Introduction LmcAdmin is a minimal admin interface for generic administrative purposes. It is a common screen with navigation that hides behind authentication and authorization. +## Requirements + +- PHP 8.1 or higher + ## Installation -LmcAdmin is enabled to be installed via composer. Load `lm-commons/lmc-admin` in your `composer.json` file. You can specify its version (currently only 1.0.0 is recommended) or use `dev-master` to load the latest version from master. Enable LmcAdmin in your `module.config.php` configuration file. +LmcAdmin is enabled to be installed via composer. -If you do not want to use composer, clone this project (either as a git submodule or not) into `./vendor/` directory. +```bash +$ composer require lm-commons/lmc-admin +``` +Enable the module by adding `Lmc\Admin` key to your `application.config.php` or `modules.config.php` file. -## Usage -LmcAdmin allows you to create routes under a single parent "admin" route. You can also use it to enable navigation in your admin layout. Furthermore integration of [LmcRbacMvc](https://github.com/LM-Commons/LmcRbacMvc) is provided. +Customize the module by copy-pasting the `config/lmcadmin.global.php.dist` file to your `config/autoload` folder. -The complete configuration is flexible, so you can update the zfcadmin parent route, its children, the navigation and all default provided view scripts. Read more in the [documentation](docs-old/1.Introduction.md) about usage and customization of LmcAdmin. +## Usage +LmcAdmin allows you to create routes under a single parent "lmcadmin" route. You can also use it to enable navigation in +your admin layout. Furthermore, integration of [LmcRbacMvc](https://github.com/LM-Commons/LmcRbacMvc) is provided. -## Development -LmcAdmin is currently under development. The authors feel LmcAdmin is stable enough for production versions and you can always fix your LmcAdmin version to a specific tag. +The complete configuration is flexible, so you can update the lmc_admin parent route, its children, the navigation +and all default provided view scripts. Read more in the [documentation](https://lm-commons.github.io/LmcAdmin) about +usage and customization of LmcAdmin. ## Support diff --git a/composer.json b/composer.json index bf0c0a3..b82c495 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ }, "autoload": { "psr-4": { - "LmcAdmin\\": "src/" + "Lmc\\Admin\\": "src/" } }, "autoload-dev": { @@ -35,8 +35,8 @@ } }, "extra": { - "lm": { - "component": "LmcAdmin", + "laminas": { + "component": "Lmc\\Admin", "config-provider": [ "LmcAdmin\\ConfigProvider" ] @@ -44,7 +44,8 @@ }, "require-dev": { "laminas/laminas-coding-standard": "^2.5", - "phpunit/phpunit": "^10.5.30", + "laminas/laminas-test": "^4.10", + "phpunit/phpunit": "^10.5.30 || ^11.3.0", "psalm/plugin-phpunit": "^0.19.0", "vimeo/psalm": "^5.25" }, diff --git a/composer.lock b/composer.lock index 113437c..aabe86d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cf5d1de5b6c5590eb53a9d93c79e2c78", + "content-hash": "7005981cd9937043384c737e3b7c8f02", "packages": [ { "name": "brick/varexporter", @@ -2138,6 +2138,147 @@ ], "time": "2023-01-05T15:53:40+00:00" }, + { + "name": "laminas/laminas-test", + "version": "4.10.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-test.git", + "reference": "607c55ccb98bf3ade42820a1cf14e9af1f4d1cc8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-test/zipball/607c55ccb98bf3ade42820a1cf14e9af1f4d1cc8", + "reference": "607c55ccb98bf3ade42820a1cf14e9af1f4d1cc8", + "shasum": "" + }, + "require": { + "laminas/laminas-eventmanager": "^3.0", + "laminas/laminas-http": "^2.15.0", + "laminas/laminas-mvc": "^3.3.0", + "laminas/laminas-servicemanager": "^3.0.3", + "laminas/laminas-uri": "^2.5", + "laminas/laminas-view": "^2.13.1", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "phpunit/phpunit": "^10.4", + "symfony/css-selector": "^6.0 || ^7.0", + "symfony/dom-crawler": "^6.0 || ^7.0" + }, + "conflict": { + "zendframework/zend-test": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "^2.4.0", + "laminas/laminas-i18n": "^2.21", + "laminas/laminas-modulemanager": "^2.14.0", + "laminas/laminas-mvc-plugin-flashmessenger": "^1.9.0", + "laminas/laminas-serializer": "^2.14.0", + "laminas/laminas-session": "^2.16", + "laminas/laminas-stdlib": "^3.16.1", + "laminas/laminas-validator": "^2.28", + "mikey179/vfsstream": "^1.6.11", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Test\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Tools to facilitate integration testing of laminas-mvc applications", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "test" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-test/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-test/issues", + "rss": "https://github.com/laminas/laminas-test/releases.atom", + "source": "https://github.com/laminas/laminas-test" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2023-12-02T14:06:29+00:00" + }, + { + "name": "masterminds/html5", + "version": "2.9.0", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", + "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.9.0" + }, + "time": "2024-03-31T07:05:07+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.12.0", @@ -2899,16 +3040,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.31", + "version": "10.5.32", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "43e7c3e6a484e538453f89dfa6a6f308c32792da" + "reference": "f069f46840445d37a4e6f0de8c5879598f9c4327" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/43e7c3e6a484e538453f89dfa6a6f308c32792da", - "reference": "43e7c3e6a484e538453f89dfa6a6f308c32792da", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f069f46840445d37a4e6f0de8c5879598f9c4327", + "reference": "f069f46840445d37a4e6f0de8c5879598f9c4327", "shasum": "" }, "require": { @@ -2980,7 +3121,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.31" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.32" }, "funding": [ { @@ -2996,7 +3137,7 @@ "type": "tidelift" } ], - "time": "2024-09-03T11:57:55+00:00" + "time": "2024-09-04T13:33:39+00:00" }, { "name": "psalm/plugin-phpunit", @@ -4326,6 +4467,71 @@ ], "time": "2024-08-15T22:48:53+00:00" }, + { + "name": "symfony/css-selector", + "version": "v7.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "1c7cee86c6f812896af54434f8ce29c8d94f9ff4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/1c7cee86c6f812896af54434f8ce29c8d94f9ff4", + "reference": "1c7cee86c6f812896af54434f8ce29c8d94f9ff4", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v7.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T14:57:53+00:00" + }, { "name": "symfony/deprecation-contracts", "version": "v3.5.0", @@ -4393,6 +4599,73 @@ ], "time": "2024-04-18T09:32:20+00:00" }, + { + "name": "symfony/dom-crawler", + "version": "v7.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "01ce8174447f1f1dd33a5854b01beef79061d9fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/01ce8174447f1f1dd33a5854b01beef79061d9fa", + "reference": "01ce8174447f1f1dd33a5854b01beef79061d9fa", + "shasum": "" + }, + "require": { + "masterminds/html5": "^2.6", + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/css-selector": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases DOM navigation for HTML and XML documents", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dom-crawler/tree/v7.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T14:57:53+00:00" + }, { "name": "symfony/filesystem", "version": "v7.1.2", diff --git a/config/lmcadmin.global.php b/config/lmcadmin.global.php.dist similarity index 77% rename from config/lmcadmin.global.php rename to config/lmcadmin.global.php.dist index 14f6140..a31a4b3 100644 --- a/config/lmcadmin.global.php +++ b/config/lmcadmin.global.php.dist @@ -1,10 +1,14 @@ true, + * */ - //'use_admin_layout' => true, /** * Layout template for LmcAdmin @@ -24,30 +28,26 @@ * When use_admin_layout is set to true, this value will be used as template * name for the admin layout. Default is 'layout/admin' * + * 'admin_layout_template' => 'layout/admin', + * * Accepted is a string that resolves to a view script */ - //'admin_layout_template' => 'layout/admin', - - /** - * End of LmcAdmin configuration - */ - ]; - /** * You do not need to edit below this line */ return [ - 'lmcadmin' => $settings, - + 'lmc_admin' => $settings, /** - * Default LmcRbacMvc configuration for RBAC + * Suggested LmcRbacMvc configuration for RBAC */ + /* 'lmc_rbac' => [ 'guards' => [ - 'LmcRbacMvc\Guard\RouteGuard' => [ + 'Lmc\Rbac\Mvc\Guard\RouteGuard' => [ 'lmcadmin*' => ['admin'], - ] + ], ], ], + */ ]; diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index 088c14f..a6100ef 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -1,99 +1,98 @@ - * @copyright 2012 Jurian Sluiman. - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link https://github.com/LM-Commons - */ +declare(strict_types = 1); -namespace LmcAdmin; +namespace Lmc\Admin; + +use Laminas\ServiceManager\Factory\InvokableFactory; +use Lmc\Admin\Listener\LayoutTemplateSelectListener; +use Lmc\Admin\Listener\LayoutTemplateSelectListenerFactory; +use Lmc\Admin\Options\ModuleOptions; +use Lmc\Admin\Options\ModuleOptionsFactory; /** * Class ConfigProvider. */ class ConfigProvider { - /** - * Provide dependency configuration for an application integrating i18n. - * - * @return array - */ - public function __invoke() + public function __invoke(): array { return [ 'dependencies' => $this->getDependencyConfig(), 'view_manager' => $this->getViewManagerConfig(), - 'lmcadmin' => $this->getModuleConfig(), + 'lmc_admin' => $this->getModuleConfig(), + 'controllers' => $this->getControllerConfig(), + 'router' => $this->getRouterConfig(), ]; } - /** - * Provide dependency configuration for an application. - * - * @return array - */ - public function getDependencyConfig() + public function getDependencyConfig(): array { return [ 'factories' => [ 'admin_navigation' => Navigation\Service\AdminNavigationFactory::class, + ModuleOptions::class => ModuleOptionsFactory::class, + LayoutTemplateSelectListener::class => LayoutTemplateSelectListenerFactory::class, ], ]; } - /** - * @return array - */ - public function getViewManagerConfig() + public function getViewManagerConfig(): array { return [ 'template_path_stack' => [ __DIR__ . '/../view', ], + 'template_map' => [ + 'lmc-admin/admin/index' => __DIR__ . '/../view/lmc/admin/index.phtml', + 'layout/lmcadmin' => __DIR__ . '/../view/lmc/layout/admin.phtml', + ], ]; } - /** - * @return array - */ - public function getModuleConfig() + public function getModuleConfig(): array + { + return []; + } + + public function getNavigationConfig(): array { return [ - 'use_admin_layout' => true, - 'admin_layout_template' => 'layout/admin', + 'admin' => [], + ]; + } + + public function getControllerConfig(): array + { + return [ + 'factories' => [ + Controller\AdminController::class => InvokableFactory::class, + ], + ]; + } + + public function getListenerConfig(): array + { + return [ + LayoutTemplateSelectListener::class, + ]; + } + + public function getRouterConfig(): array + { + return [ + 'routes' => [ + 'lmcadmin' => [ + 'type' => 'literal', + 'options' => [ + 'route' => '/admin', + 'defaults' => [ + 'controller' => Controller\AdminController::class, + 'action' => 'index', + ], + ], + 'may_terminate' => true, + ], + ], ]; } } diff --git a/src/Controller/AdminController.php b/src/Controller/AdminController.php index b035a21..53f99a6 100644 --- a/src/Controller/AdminController.php +++ b/src/Controller/AdminController.php @@ -1,48 +1,10 @@ - * @copyright 2012 Jurian Sluiman. - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link https://github.com/LM-Commons - */ +declare(strict_types = 1); -namespace LmcAdmin\Controller; +namespace Lmc\Admin\Controller; use Laminas\Mvc\Controller\AbstractActionController; +use Laminas\View\Model\ViewModel; /** * Placeholder controller @@ -71,10 +33,13 @@ * ), * ); * - * - * @package LmcAdmin - * @subpackage Controller */ class AdminController extends AbstractActionController { + public function indexAction() + { + $view = new ViewModel(); + $view->setTemplate('lmc-admin/admin/index'); + return $view; + } } diff --git a/src/Listener/LayoutTemplateSelectListener.php b/src/Listener/LayoutTemplateSelectListener.php new file mode 100644 index 0000000..acf4ce8 --- /dev/null +++ b/src/Listener/LayoutTemplateSelectListener.php @@ -0,0 +1,45 @@ +listeners[] = $events->attach(MvcEvent::EVENT_DISPATCH, [$this, 'selectLayoutBasedOnRoute'], $priority); + } + + public function selectLayoutBasedOnRoute(MvcEvent $event): void + { + if (!$this->getModuleOptions()->getUseAdminLayout()) { + return; + } + + $routeMatch = $event->getRouteMatch(); + $controller = $event->getTarget(); + if (!str_starts_with($routeMatch->getMatchedRouteName(), 'lmcadmin') + || $controller->getEvent()->getResult()->terminate() + ) { + return; + } + $controller->layout($this->getModuleOptions()->getAdminLayoutTemplate()); + } + + private function getModuleOptions(): ModuleOptions + { + return $this->moduleOptions; + } +} diff --git a/src/Listener/LayoutTemplateSelectListenerFactory.php b/src/Listener/LayoutTemplateSelectListenerFactory.php new file mode 100644 index 0000000..d0c24e9 --- /dev/null +++ b/src/Listener/LayoutTemplateSelectListenerFactory.php @@ -0,0 +1,19 @@ +get(ModuleOptions::class)); + } +} diff --git a/src/Module.php b/src/Module.php index ec29ca3..cde12c9 100644 --- a/src/Module.php +++ b/src/Module.php @@ -1,135 +1,26 @@ - * @copyright 2012 Jurian Sluiman. - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link https://github.com/LM-Commons - */ +declare(strict_types = 1); -namespace LmcAdmin; +namespace Lmc\Admin; use Laminas\EventManager\EventInterface; use Laminas\Mvc\MvcEvent; use Laminas\Router\RouteMatch as V3RouteMatch; -use Laminas\ServiceManager\Factory\InvokableFactory; -/** - * Module class for LmcAdmin - * - * @package LmcAdmin - */ class Module { - - /** - * @{inheritdoc} - */ - public function getConfig() + public function getConfig(): array { $provider = new ConfigProvider(); return [ 'service_manager' => $provider->getDependencyConfig(), 'view_manager' => $provider->getViewManagerConfig(), - 'lmcadmin' => $provider->getModuleConfig(), - 'controllers' => [ - 'factories' => [ - Controller\AdminController::class => InvokableFactory::class, - ], - ], - 'navigation' => [ - 'admin' => [], - ], - 'router' => [ - 'routes' => [ - 'lmcadmin' => [ - 'type' => 'literal', - 'options' => [ - 'route' => '/admin', - 'defaults' => [ - 'controller' => Controller\AdminController::class, - 'action' => 'index', - ], - ], - 'may_terminate' => true, - ], - ], - ], + 'lmc_admin' => $provider->getModuleConfig(), + 'controllers' => $provider->getControllerConfig(), + 'navigation' => $provider->getNavigationConfig(), + 'router' => $provider->getRouterConfig(), + 'listeners' => $provider->getListenerConfig(), ]; } - - /** - * @{inheritdoc} - */ - public function onBootstrap(EventInterface $e) - { - /** @var \Laminas\Mvc\Application $app */ - $app = $e->getParam('application'); - $em = $app->getEventManager(); - - $em->attach(MvcEvent::EVENT_DISPATCH, [$this, 'selectLayoutBasedOnRoute']); - } - - /** - * Select the admin layout based on route name - * - * @param MvcEvent $e - * @return void - */ - public function selectLayoutBasedOnRoute(MvcEvent $e) - { - /** @var \Laminas\Mvc\Application $app */ - $app = $e->getParam('application'); - $sm = $app->getServiceManager(); - $config = $sm->get('config'); - - if (false === $config['lmcadmin']['use_admin_layout']) { - return; - } - - $match = $e->getRouteMatch(); - $controller = $e->getTarget(); - if (!($match instanceof V3RouteMatch) - || 0 !== strpos($match->getMatchedRouteName(), 'lmcadmin') - || $controller->getEvent()->getResult()->terminate() - ) { - return; - } - - $layout = $config['lmcadmin']['admin_layout_template']; - $controller->layout($layout); - } } diff --git a/src/Navigation/Service/AdminNavigationFactory.php b/src/Navigation/Service/AdminNavigationFactory.php index 5adb55a..6bf15ee 100644 --- a/src/Navigation/Service/AdminNavigationFactory.php +++ b/src/Navigation/Service/AdminNavigationFactory.php @@ -1,54 +1,13 @@ - * @copyright 2012 Jurian Sluiman. - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link https://zf-commons.github.com - */ +declare(strict_types = 1); -namespace LmcAdmin\Navigation\Service; +namespace Lmc\Admin\Navigation\Service; use Laminas\Navigation\Service\DefaultNavigationFactory; /** * Factory for the LmcAdmin admin navigation * - * @package LmcAdmin - * @subpackage Navigation\Service */ class AdminNavigationFactory extends DefaultNavigationFactory { diff --git a/src/Options/ModuleOptions.php b/src/Options/ModuleOptions.php new file mode 100644 index 0000000..7f8e3c1 --- /dev/null +++ b/src/Options/ModuleOptions.php @@ -0,0 +1,39 @@ +__strictMode__ = false; + parent::__construct($options); + } + + + public function getUseAdminLayout(): bool + { + return $this->useAdminLayout; + } + + public function setUseAdminLayout(bool $useAdminLayout): void + { + $this->useAdminLayout = $useAdminLayout; + } + + public function getAdminLayoutTemplate(): string + { + return $this->adminLayoutTemplate; + } + + public function setAdminLayoutTemplate(string $adminLayoutTemplate): void + { + $this->adminLayoutTemplate = $adminLayoutTemplate; + } +} diff --git a/src/Options/ModuleOptionsFactory.php b/src/Options/ModuleOptionsFactory.php new file mode 100644 index 0000000..063a224 --- /dev/null +++ b/src/Options/ModuleOptionsFactory.php @@ -0,0 +1,20 @@ +get('config'); + $options = $config['lmc_admin'] ?? []; + return new ModuleOptions($options); + } +} diff --git a/test/Assets/TestController.php b/test/Assets/TestController.php new file mode 100644 index 0000000..289aa5e --- /dev/null +++ b/test/Assets/TestController.php @@ -0,0 +1,24 @@ +setTemplate('home/index'); + return $view; + } + + public function terminateAction() + { + $view = new ViewModel(); + $view->setTerminal(true); + $view->setTemplate('home/index'); + return $view; + } +} diff --git a/test/ConfigProviderTest.php b/test/ConfigProviderTest.php index f8d05f2..8a3cfe4 100644 --- a/test/ConfigProviderTest.php +++ b/test/ConfigProviderTest.php @@ -21,8 +21,7 @@ namespace LmcTest\Admin; -use LmcAdmin\ConfigProvider; -use LmcAdmin\Navigation\Service\AdminNavigationFactory; +use Lmc\Admin\ConfigProvider; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; @@ -34,10 +33,11 @@ public function testProvidesExpectedConfiguration(): void $provider = new ConfigProvider(); $this->assertArrayHasKey('dependencies', $provider()); $this->assertArrayHasKey('view_manager', $provider()); - $this->assertArrayHasKey('lmcadmin', $provider()); + $this->assertArrayHasKey('lmc_admin', $provider()); + $this->assertArrayHasKey('controllers', $provider()); + $this->assertArrayHasKey('router', $provider()); $this->assertArrayHasKey('factories', $provider->getDependencyConfig()); $this->assertArrayHasKey('template_path_stack', $provider->getViewManagerConfig()); - $this->assertArrayHasKey('use_admin_layout', $provider->getModuleConfig()); - $this->assertArrayHasKey('admin_layout_template', $provider->getModuleConfig()); + $this->assertEmpty($provider->getModuleConfig()); } } diff --git a/test/Controller/AdminControllerTest.php b/test/Controller/AdminControllerTest.php new file mode 100644 index 0000000..321b825 --- /dev/null +++ b/test/Controller/AdminControllerTest.php @@ -0,0 +1,51 @@ +setApplicationConfig( + include __DIR__ . '/../test.application.config.php' + ); + parent::setUp(); + } + */ + + public function testIndexAction(): void + { + $this->setApplicationConfig( + include __DIR__ . '/../test.application.config.php' + ); + $this->dispatch('/admin'); + $this->assertResponseStatusCode(200); + $this->assertTemplateName('layout/lmcadmin'); + } + + public function testIndexAction2(): void + { + $this->setApplicationConfig( + [ + 'modules' => [ + 'Laminas\Navigation', + 'Laminas\Router', + 'Lmc\Admin', + ], + 'module_listener_options' => [ + 'config_glob_paths' => [ + __DIR__ . '/testing.config.php', + ], + 'module_paths' => [ + ], + ], + ] + ); + $this->dispatch('/admin'); + $this->assertResponseStatusCode(200); + $this->assertTemplateName('layout/layout'); + } +} diff --git a/test/Controller/testing.config.php b/test/Controller/testing.config.php new file mode 100644 index 0000000..c3b6627 --- /dev/null +++ b/test/Controller/testing.config.php @@ -0,0 +1,13 @@ + [ + 'use_admin_layout' => false, + ], + 'view_manager' => [ + 'template_map' => [ + 'layout/layout' => __DIR__ . '/../view/testlayout.phtml', + ], + ], +]; + diff --git a/test/Listener/LayoutTemplateSelectListenerFactoryTest.php b/test/Listener/LayoutTemplateSelectListenerFactoryTest.php new file mode 100644 index 0000000..b9ea138 --- /dev/null +++ b/test/Listener/LayoutTemplateSelectListenerFactoryTest.php @@ -0,0 +1,22 @@ +createMock(ContainerInterface::class); + $container->expects($this->once())->method('get')->willReturn($options); + $factory = new LayoutTemplateSelectListenerFactory(); + $listener = $factory($container, ''); + $this->assertInstanceOf(LayoutTemplateSelectListener::class, $listener); + } +} diff --git a/test/Listener/LayoutTemplateSelectListenerTest.php b/test/Listener/LayoutTemplateSelectListenerTest.php new file mode 100644 index 0000000..6c19f1f --- /dev/null +++ b/test/Listener/LayoutTemplateSelectListenerTest.php @@ -0,0 +1,105 @@ +createMock('Laminas\EventManager\EventManagerInterface'); + $moduleOptions = new ModuleOptions([]); + $listener = new LayoutTemplateSelectListener($moduleOptions); + $priority = 10; + $events->expects($this->once())->method('attach')->with( + MvcEvent::EVENT_DISPATCH, + [$listener, 'selectLayoutBasedOnRoute'], + $priority); + $listener->attach($events, $priority); + } + + public function testUseAdminLayoutFalse(): void + { + $event = $this->createMock(MvcEvent::class); + $moduleOptions = new ModuleOptions([ + 'use_admin_layout' => false, + ]); + // The listener should return early and not try to get the route + $event->expects($this->never())->method('getRouteMatch'); + $listener = new LayoutTemplateSelectListener($moduleOptions); + $listener->selectLayoutBasedOnRoute($event); + } + + public function testUseAdminLayoutTrue(): void + { + $this->setApplicationConfig( + include __DIR__ . '/../test.application.config.php' + ); + // Listener should set the layout template to layout/admin + $this->dispatch('/admin'); + $this->assertResponseStatusCode(200); + $this->assertTemplateName('layout/lmcadmin'); + } + + /** + * Test that the listener does not change the layout when the route is not an admin route + */ + public function testRouteNotMatched(): void + { + $this->setApplicationConfig( + [ + 'modules' => [ + 'Laminas\Router', + 'Lmc\Admin', + ], + 'module_listener_options' => [ + 'config_glob_paths' => [ + __DIR__ . '/testing.config.php', + ], + ], + ] + ); + // Listener should set the layout template to layout/admin + $this->dispatch('/'); + $this->assertResponseStatusCode(200); + $this->assertNotTemplateName('layout/admin'); + } + + /** Test that layout is not set when an admin controller returns a view set to terminate */ + public function testTerminate(): void + { + $this->setApplicationConfig( + [ + 'modules' => [ + 'Laminas\Router', + 'Lmc\Admin', + ], + 'module_listener_options' => [ + 'config_glob_paths' => [ + __DIR__ . '/testing.config.php', + ], + 'module_paths' => [ + ], + ], + ] + ); + + // Listener should not set the layout template to layout/admin or layout/layout because the view is set to terminate + $this->dispatch('/admin/terminate'); + $this->assertResponseStatusCode(200); + $this->assertNotTemplateName('layout/layout'); + $this->assertNotTemplateName('layout/admin'); + } +} diff --git a/test/Listener/testing.config.php b/test/Listener/testing.config.php new file mode 100644 index 0000000..e7ab44a --- /dev/null +++ b/test/Listener/testing.config.php @@ -0,0 +1,51 @@ + [ + 'use_admin_layout' => true, + ], + 'view_manager' => [ + 'template_map' => [ + 'layout/layout' => __DIR__ . '/../view/testlayout.phtml', + 'home/index' => __DIR__ . '/../view/home-index.phtml', + ], + ], + 'router' => [ + 'routes' => [ + 'home' => [ + 'type' => Literal::class, + 'options' => [ + 'route' => '/', + 'defaults' => [ + 'controller' => TestController::class, + 'action' => 'index', + ], + ], + ], + 'lmcadmin' => [ + 'child_routes' => [ + 'terminate' => [ + 'type' => Literal::class, + 'options' => [ + 'route' => '/terminate', + 'defaults' => [ + 'controller' => TestController::class, + 'action' => 'terminate', + ], + ], + ], + ], + ], + ], + ], + 'controllers' => [ + 'factories' => [ + TestController::class => InvokableFactory::class, + ], + ], +]; + diff --git a/test/ModuleTest.php b/test/ModuleTest.php new file mode 100644 index 0000000..7355171 --- /dev/null +++ b/test/ModuleTest.php @@ -0,0 +1,16 @@ +assertIsArray($module->getConfig()); + } +} diff --git a/test/Options/ModuleOptionsFactoryTest.php b/test/Options/ModuleOptionsFactoryTest.php new file mode 100644 index 0000000..9cafd1c --- /dev/null +++ b/test/Options/ModuleOptionsFactoryTest.php @@ -0,0 +1,38 @@ +createMock(ContainerInterface::class); + $container->expects($this->once())->method('get')->with('config')->willReturn($config); + $factory = new ModuleOptionsFactory(); + $options = $factory($container, ''); + $this->assertInstanceOf(ModuleOptions::class, $options); + } + + public function testFactoryConfig(): void + { + $config = [ + 'lmc_admin' => [ + 'use_admin_layout' => false, + 'admin_layout_template' => 'foo/bar', + ], + ]; + $container = $this->createMock(ContainerInterface::class); + $container->expects($this->once())->method('get')->with('config')->willReturn($config); + $factory = new ModuleOptionsFactory(); + $options = $factory($container, ''); + $this->assertInstanceOf(ModuleOptions::class, $options); + $this->assertFalse($options->getUseAdminLayout()); + $this->assertEquals('foo/bar', $options->getAdminLayoutTemplate()); + } +} diff --git a/test/Options/ModuleOptionsTest.php b/test/Options/ModuleOptionsTest.php new file mode 100644 index 0000000..2943639 --- /dev/null +++ b/test/Options/ModuleOptionsTest.php @@ -0,0 +1,35 @@ +assertEquals(true, $options->getUseAdminLayout()); + $this->assertEquals('layout/lmcadmin', $options->getAdminLayoutTemplate()); + } + + public function testOptions(): void + { + $options = new ModuleOptions([ + 'useAdminLayout' => false, + 'adminLayoutTemplate' => 'foo/bar', + ]); + $this->assertEquals(false, $options->getUseAdminLayout()); + $this->assertEquals('foo/bar', $options->getAdminLayoutTemplate()); + } + + public function testOptionsSetters(): void + { + $options = new ModuleOptions(); + $options->setUseAdminLayout(false); + $options->setAdminLayoutTemplate('foo'); + $this->assertEquals(false, $options->getUseAdminLayout()); + $this->assertEquals('foo', $options->getAdminLayoutTemplate()); + } +} diff --git a/test/test.application.config.php b/test/test.application.config.php new file mode 100644 index 0000000..ff04950 --- /dev/null +++ b/test/test.application.config.php @@ -0,0 +1,17 @@ + [ + 'Laminas\Navigation', + 'Laminas\Router', + 'Lmc\Admin', + ], + 'module_listener_options' => [ + 'config_glob_paths' => [ + __DIR__ . '/testing.config.php', + ], + 'module_paths' => [ + ], + ], +]; + diff --git a/test/testing.config.php b/test/testing.config.php new file mode 100644 index 0000000..db93e88 --- /dev/null +++ b/test/testing.config.php @@ -0,0 +1,11 @@ + [], + 'view_manager' => [ + 'template_map' => [ + + ], + ], +]; + diff --git a/test/view/home-index.phtml b/test/view/home-index.phtml new file mode 100644 index 0000000..a3f6997 --- /dev/null +++ b/test/view/home-index.phtml @@ -0,0 +1 @@ +

Home

diff --git a/test/view/testlayout.phtml b/test/view/testlayout.phtml new file mode 100644 index 0000000..4b0d584 --- /dev/null +++ b/test/view/testlayout.phtml @@ -0,0 +1,8 @@ + + +Test + + +content; ?> + + diff --git a/view/lmc-admin/admin/index.phtml b/view/lmc-admin/admin/index.phtml deleted file mode 100644 index d19139c..0000000 --- a/view/lmc-admin/admin/index.phtml +++ /dev/null @@ -1,3 +0,0 @@ -

LmcAdmin

- -

This is the LmcAdmin interface. Create a view script which resolves to lmc-admin/admin/index to override this text. Create a view script for layout/admin to override this surrounding layout.

\ No newline at end of file diff --git a/view/lmc/admin/index.phtml b/view/lmc/admin/index.phtml new file mode 100644 index 0000000..a3792ad --- /dev/null +++ b/view/lmc/admin/index.phtml @@ -0,0 +1,5 @@ +

LmcAdmin

+ +

This is the LmcAdmin Homepage.

+

Create a view script which resolves to lmc-admin/admin/index to override this text.

+

Create a view script for layout/lmcadmin to override this surrounding layout.

diff --git a/view/layout/admin.phtml b/view/lmc/layout/admin.phtml similarity index 94% rename from view/layout/admin.phtml rename to view/lmc/layout/admin.phtml index 21dc02e..6b508db 100644 --- a/view/layout/admin.phtml +++ b/view/lmc/layout/admin.phtml @@ -50,7 +50,7 @@