Commit 84ceebc2 authored by Christian Kuhn's avatar Christian Kuhn Committed by Benjamin Franzke
Browse files

[FEATURE] Add composer package name to backend routes

We have three files that define backend routes:

* Configuration/Backend/Routes.php
* Configuration/Backend/AjaxRoutes.php
* Configuration/Backend/Modules.php

Routes defined by these files now contain the
composer package name ('name' attribute in composer.json)
of the package that defined the route as option.

Backend modules now have a getPackageName()
method.

This is useful as shown with #96962 and will
likely find further usages in the future.

Change-Id: Ifa20f11ee7caa470eb2a1eed9e4ddf3ede088a54
Resolves: #96961
Related: #96962
Related: #96733
Releases: main
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/73191


Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Oliver Bartsch's avatarOliver Bartsch <bo@cedev.de>
Tested-by: Benjamin Franzke's avatarBenjamin Franzke <bfr@qbus.de>
Reviewed-by: Oliver Bartsch's avatarOliver Bartsch <bo@cedev.de>
Reviewed-by: Benjamin Franzke's avatarBenjamin Franzke <bfr@qbus.de>
parent bb2fca15
...@@ -25,6 +25,8 @@ namespace TYPO3\CMS\Backend\Module; ...@@ -25,6 +25,8 @@ namespace TYPO3\CMS\Backend\Module;
abstract class BaseModule abstract class BaseModule
{ {
protected string $identifier; protected string $identifier;
protected string $packageName = '';
protected string $absolutePackagePath = '';
protected string $path = ''; protected string $path = '';
protected string $iconIdentifier = ''; protected string $iconIdentifier = '';
protected string $title = ''; protected string $title = '';
...@@ -199,6 +201,8 @@ abstract class BaseModule ...@@ -199,6 +201,8 @@ abstract class BaseModule
public static function createFromConfiguration(string $identifier, array $configuration): static public static function createFromConfiguration(string $identifier, array $configuration): static
{ {
$obj = new static($identifier); $obj = new static($identifier);
$obj->packageName = (string)($configuration['packageName'] ?? '');
$obj->absolutePackagePath = (string)($configuration['absolutePackagePath'] ?? '');
$obj->path = '/' . ltrim((string)$configuration['path'], '/'); $obj->path = '/' . ltrim((string)$configuration['path'], '/');
$obj->standalone = (bool)($configuration['standalone'] ?? false); $obj->standalone = (bool)($configuration['standalone'] ?? false);
......
...@@ -55,6 +55,8 @@ class ExtbaseModule extends BaseModule implements ModuleInterface ...@@ -55,6 +55,8 @@ class ExtbaseModule extends BaseModule implements ModuleInterface
{ {
return [ return [
'module' => $this, 'module' => $this,
'packageName' => $this->packageName,
'absolutePackagePath' => $this->absolutePackagePath,
'access' => $this->access, 'access' => $this->access,
'target' => Bootstrap::class . '::handleBackendRequest', 'target' => Bootstrap::class . '::handleBackendRequest',
]; ];
......
...@@ -39,6 +39,8 @@ class Module extends BaseModule implements ModuleInterface ...@@ -39,6 +39,8 @@ class Module extends BaseModule implements ModuleInterface
} }
return [ return [
'module' => $this, 'module' => $this,
'packageName' => $this->packageName,
'absolutePackagePath' => $this->absolutePackagePath,
'access' => $this->access, 'access' => $this->access,
'target' => $this->routes['_default']['target'], 'target' => $this->routes['_default']['target'],
]; ];
......
...@@ -51,6 +51,11 @@ class ServiceProvider extends AbstractServiceProvider ...@@ -51,6 +51,11 @@ class ServiceProvider extends AbstractServiceProvider
return __DIR__ . '/../'; return __DIR__ . '/../';
} }
protected static function getPackageName(): string
{
return 'typo3/cms-backend';
}
public function getFactories(): array public function getFactories(): array
{ {
return [ return [
......
...@@ -69,7 +69,10 @@ class BackendModuleValidatorTest extends FunctionalTestCase ...@@ -69,7 +69,10 @@ class BackendModuleValidatorTest extends FunctionalTestCase
{ {
$module = $this->getContainer()->get(ModuleFactory::class)->createModule( $module = $this->getContainer()->get(ModuleFactory::class)->createModule(
'web_layout', 'web_layout',
['path' => '/module/web/layout'] [
'packageName' => 'typo3/cms-testing',
'path' => '/module/web/layout',
]
); );
$response = $this->subject->process( $response = $this->subject->process(
...@@ -88,6 +91,7 @@ class BackendModuleValidatorTest extends FunctionalTestCase ...@@ -88,6 +91,7 @@ class BackendModuleValidatorTest extends FunctionalTestCase
$module = $this->getContainer()->get(ModuleFactory::class)->createModule( $module = $this->getContainer()->get(ModuleFactory::class)->createModule(
'web_layout', 'web_layout',
[ [
'packageName' => 'typo3/cms-testing',
'path' => '/module/web/layout', 'path' => '/module/web/layout',
'moduleData' => [ 'moduleData' => [
'sort' => 'name', 'sort' => 'name',
...@@ -116,7 +120,10 @@ class BackendModuleValidatorTest extends FunctionalTestCase ...@@ -116,7 +120,10 @@ class BackendModuleValidatorTest extends FunctionalTestCase
{ {
$module = $this->getContainer()->get(ModuleFactory::class)->createModule( $module = $this->getContainer()->get(ModuleFactory::class)->createModule(
'some_module', 'some_module',
['path' => '/some/module'] [
'packageName' => 'typo3/cms-testing',
'path' => '/some/module',
]
); );
$this->expectException(\RuntimeException::class); $this->expectException(\RuntimeException::class);
...@@ -138,7 +145,10 @@ class BackendModuleValidatorTest extends FunctionalTestCase ...@@ -138,7 +145,10 @@ class BackendModuleValidatorTest extends FunctionalTestCase
// site_configuration requires admin access // site_configuration requires admin access
$module = $this->getContainer()->get(ModuleFactory::class)->createModule( $module = $this->getContainer()->get(ModuleFactory::class)->createModule(
'site_configuration', 'site_configuration',
['path' => '/module/site/configuration'] [
'packageName' => 'typo3/cms-testing',
'path' => '/module/site/configuration',
]
); );
$this->expectException(\RuntimeException::class); $this->expectException(\RuntimeException::class);
...@@ -157,7 +167,10 @@ class BackendModuleValidatorTest extends FunctionalTestCase ...@@ -157,7 +167,10 @@ class BackendModuleValidatorTest extends FunctionalTestCase
{ {
$module = $this->getContainer()->get(ModuleFactory::class)->createModule( $module = $this->getContainer()->get(ModuleFactory::class)->createModule(
'web_layout', 'web_layout',
['path' => '/module/web/layout'] [
'packageName' => 'typo3/cms-testing',
'path' => '/module/web/layout',
]
); );
$this->expectException(\RuntimeException::class); $this->expectException(\RuntimeException::class);
...@@ -178,7 +191,10 @@ class BackendModuleValidatorTest extends FunctionalTestCase ...@@ -178,7 +191,10 @@ class BackendModuleValidatorTest extends FunctionalTestCase
{ {
$module = $this->getContainer()->get(ModuleFactory::class)->createModule( $module = $this->getContainer()->get(ModuleFactory::class)->createModule(
'web_layout', 'web_layout',
['path' => '/module/web/layout'] [
'packageName' => 'typo3/cms-testing',
'path' => '/module/web/layout',
]
); );
$response = $this->subject->process( $response = $this->subject->process(
......
...@@ -66,6 +66,7 @@ class ModuleDataTest extends FunctionalTestCase ...@@ -66,6 +66,7 @@ class ModuleDataTest extends FunctionalTestCase
$module = $this->getContainer()->get(ModuleFactory::class)->createModule( $module = $this->getContainer()->get(ModuleFactory::class)->createModule(
'my_module', 'my_module',
[ [
'packageName' => 'typo3/cms-testing',
'path' => '/module/my/module', 'path' => '/module/my/module',
'moduleData' => $defaultValues, 'moduleData' => $defaultValues,
] ]
......
...@@ -38,6 +38,13 @@ abstract class AbstractServiceProvider implements ServiceProviderInterface ...@@ -38,6 +38,13 @@ abstract class AbstractServiceProvider implements ServiceProviderInterface
*/ */
abstract protected static function getPackagePath(): string; abstract protected static function getPackagePath(): string;
/**
* Return the composer package name. This is the 'name' attribute in composer.json.
* Note composer.json existence for 'extensions' is still not mandatory
* in non-composer mode, the method returns empty string in this case.
*/
abstract protected static function getPackageName(): string;
/** /**
* @return array * @return array
*/ */
...@@ -60,7 +67,7 @@ abstract class AbstractServiceProvider implements ServiceProviderInterface ...@@ -60,7 +67,7 @@ abstract class AbstractServiceProvider implements ServiceProviderInterface
/** /**
* @param ContainerInterface $container * @param ContainerInterface $container
* @param ArrayObject $middlewares * @param ArrayObject $middlewares
* @param string $path supplied when invoked internally through PseudoServiceProvider * @param string|null $path supplied when invoked internally through PseudoServiceProvider
* @return ArrayObject * @return ArrayObject
*/ */
public static function configureMiddlewares(ContainerInterface $container, ArrayObject $middlewares, string $path = null): ArrayObject public static function configureMiddlewares(ContainerInterface $container, ArrayObject $middlewares, string $path = null): ArrayObject
...@@ -80,15 +87,22 @@ abstract class AbstractServiceProvider implements ServiceProviderInterface ...@@ -80,15 +87,22 @@ abstract class AbstractServiceProvider implements ServiceProviderInterface
* @param ContainerInterface $container * @param ContainerInterface $container
* @param ArrayObject $routes * @param ArrayObject $routes
* @param string|null $path supplied when invoked internally through PseudoServiceProvider * @param string|null $path supplied when invoked internally through PseudoServiceProvider
* @param string|null $packageName supplied when invoked internally through PseudoServiceProvider
* @return ArrayObject * @return ArrayObject
*/ */
public static function configureBackendRoutes(ContainerInterface $container, ArrayObject $routes, string $path = null): ArrayObject public static function configureBackendRoutes(ContainerInterface $container, ArrayObject $routes, string $path = null, string $packageName = null): ArrayObject
{ {
$path = $path ?? static::getPackagePath(); $path = $path ?? static::getPackagePath();
$packageName = $packageName ?? static::getPackageName();
$routesFileNameForPackage = $path . 'Configuration/Backend/Routes.php'; $routesFileNameForPackage = $path . 'Configuration/Backend/Routes.php';
if (file_exists($routesFileNameForPackage)) { if (file_exists($routesFileNameForPackage)) {
$definedRoutesInPackage = require $routesFileNameForPackage; $definedRoutesInPackage = require $routesFileNameForPackage;
if (is_array($definedRoutesInPackage)) { if (is_array($definedRoutesInPackage)) {
array_walk($definedRoutesInPackage, static function (&$options) use ($packageName, $path) {
// Add packageName and absolutePackagePath to all routes
$options['packageName'] = $packageName;
$options['absolutePackagePath'] = $path;
});
$routes->exchangeArray(array_merge($routes->getArrayCopy(), $definedRoutesInPackage)); $routes->exchangeArray(array_merge($routes->getArrayCopy(), $definedRoutesInPackage));
} }
} }
...@@ -99,6 +113,8 @@ abstract class AbstractServiceProvider implements ServiceProviderInterface ...@@ -99,6 +113,8 @@ abstract class AbstractServiceProvider implements ServiceProviderInterface
foreach ($definedRoutesInPackage as $routeIdentifier => $routeOptions) { foreach ($definedRoutesInPackage as $routeIdentifier => $routeOptions) {
// prefix the route with "ajax_" as "namespace" // prefix the route with "ajax_" as "namespace"
$routeOptions['path'] = '/ajax' . $routeOptions['path']; $routeOptions['path'] = '/ajax' . $routeOptions['path'];
$routeOptions['packageName'] = $packageName;
$routeOptions['absolutePackagePath'] = $path;
$routes['ajax_' . $routeIdentifier] = $routeOptions; $routes['ajax_' . $routeIdentifier] = $routeOptions;
$routes['ajax_' . $routeIdentifier]['ajax'] = true; $routes['ajax_' . $routeIdentifier]['ajax'] = true;
} }
...@@ -130,15 +146,22 @@ abstract class AbstractServiceProvider implements ServiceProviderInterface ...@@ -130,15 +146,22 @@ abstract class AbstractServiceProvider implements ServiceProviderInterface
* @param ContainerInterface $container * @param ContainerInterface $container
* @param ArrayObject $modules * @param ArrayObject $modules
* @param string|null $path supplied when invoked internally through PseudoServiceProvider * @param string|null $path supplied when invoked internally through PseudoServiceProvider
* @param string|null $packageName supplied when invoked internally through PseudoServiceProvider
* @return ArrayObject * @return ArrayObject
*/ */
public static function configureBackendModules(ContainerInterface $container, ArrayObject $modules, string $path = null): ArrayObject public static function configureBackendModules(ContainerInterface $container, ArrayObject $modules, string $path = null, string $packageName = null): ArrayObject
{ {
$path = $path ?? static::getPackagePath(); $path = $path ?? static::getPackagePath();
$packageName = $packageName ?? static::getPackageName();
$modulesFileNameForPackage = $path . 'Configuration/Backend/Modules.php'; $modulesFileNameForPackage = $path . 'Configuration/Backend/Modules.php';
if (file_exists($modulesFileNameForPackage)) { if (file_exists($modulesFileNameForPackage)) {
$definedModulesInPackage = require $modulesFileNameForPackage; $definedModulesInPackage = require $modulesFileNameForPackage;
if (is_array($definedModulesInPackage)) { if (is_array($definedModulesInPackage)) {
array_walk($definedModulesInPackage, static function (&$module) use ($packageName, $path) {
// Add packageName and absolutePackagePath to all modules
$module['packageName'] = $packageName;
$module['absolutePackagePath'] = $path;
});
$modules->exchangeArray(array_merge($modules->getArrayCopy(), $definedModulesInPackage)); $modules->exchangeArray(array_merge($modules->getArrayCopy(), $definedModulesInPackage));
} }
} }
......
...@@ -45,6 +45,11 @@ final class PseudoServiceProvider extends AbstractServiceProvider ...@@ -45,6 +45,11 @@ final class PseudoServiceProvider extends AbstractServiceProvider
throw new \BadMethodCallException('PseudoServiceProvider does not support the getPackagePath() method.', 1562354465); throw new \BadMethodCallException('PseudoServiceProvider does not support the getPackagePath() method.', 1562354465);
} }
protected static function getPackageName(): string
{
throw new \BadMethodCallException('PseudoServiceProvider does not support the getPackageName() method.', 1643372902);
}
/** /**
* @return array * @return array
*/ */
...@@ -59,6 +64,9 @@ final class PseudoServiceProvider extends AbstractServiceProvider ...@@ -59,6 +64,9 @@ final class PseudoServiceProvider extends AbstractServiceProvider
public function getExtensions(): array public function getExtensions(): array
{ {
$packagePath = $this->package->getPackagePath(); $packagePath = $this->package->getPackagePath();
// Fallback to empty string if dealing with an extension in non-composer mode
// that still does not provide composer.json.
$packageName = $this->package->getValueFromComposerManifest('name') ?? '';
$extensions = parent::getExtensions(); $extensions = parent::getExtensions();
// The static configure*() methods in AbstractServiceProvider use the // The static configure*() methods in AbstractServiceProvider use the
...@@ -69,9 +77,10 @@ final class PseudoServiceProvider extends AbstractServiceProvider ...@@ -69,9 +77,10 @@ final class PseudoServiceProvider extends AbstractServiceProvider
// AbstractServiceProvider configure methods are aware of this and // AbstractServiceProvider configure methods are aware of this and
// provide an optional third parameter which is forwarded as // provide an optional third parameter which is forwarded as
// dynamic path to getPackagePath(). // dynamic path to getPackagePath().
// Same logic for $packageName.
foreach ($extensions as $serviceName => $previousCallable) { foreach ($extensions as $serviceName => $previousCallable) {
$extensions[$serviceName] = static function (ContainerInterface $container, $value) use ($previousCallable, $packagePath) { $extensions[$serviceName] = static function (ContainerInterface $container, $value) use ($previousCallable, $packagePath, $packageName) {
return ($previousCallable)($container, $value, $packagePath); return ($previousCallable)($container, $value, $packagePath, $packageName);
}; };
} }
......
...@@ -40,6 +40,11 @@ class ServiceProvider extends AbstractServiceProvider ...@@ -40,6 +40,11 @@ class ServiceProvider extends AbstractServiceProvider
return __DIR__ . '/../'; return __DIR__ . '/../';
} }
protected static function getPackageName(): string
{
return 'typo3/cms-core';
}
public function getFactories(): array public function getFactories(): array
{ {
return [ return [
......
.. include:: ../../Includes.txt
==============================================================
Feature: #96961 - Backend routes contain composer package name
==============================================================
See :issue:`96961`
Description
===========
Request objects in the backend already contain the resolved route
object as attribute. These route objects now contain the composer
package name of the package ("extension") that defined the route as option:
.. code-block:: php
/** @var \TYPO3\CMS\Backend\Routing\Route $route */
$route = $request->getAttribute('route');
// Example return: "typo3/cms-backend" when EXT:backend defined that route.
$packageName = $route->getOption('packageName');
Impact
======
The package name can be useful for filesystem lookups
or to bind configuration based on package name to it.
.. index:: Backend, PHP-API, ext:backend
...@@ -166,10 +166,12 @@ class TcaItemsProcessorFunctionsTest extends UnitTestCase ...@@ -166,10 +166,12 @@ class TcaItemsProcessorFunctionsTest extends UnitTestCase
'aModule' => $moduleFactory->createModule('aModule', [ 'aModule' => $moduleFactory->createModule('aModule', [
'iconIdentifier' => 'a-module', 'iconIdentifier' => 'a-module',
'labels' => 'LLL:EXT:a-module/locallang', 'labels' => 'LLL:EXT:a-module/locallang',
'packageName' => 'typo3/cms-testing',
]), ]),
'bModule' => $moduleFactory->createModule('bModule', [ 'bModule' => $moduleFactory->createModule('bModule', [
'iconIdentifier' => 'b-module', 'iconIdentifier' => 'b-module',
'labels' => 'LLL:EXT:b-module/locallang', 'labels' => 'LLL:EXT:b-module/locallang',
'packageName' => 'typo3/cms-testing',
]), ]),
]); ]);
......
...@@ -78,6 +78,7 @@ class AbstractServiceProviderTest extends UnitTestCase ...@@ -78,6 +78,7 @@ class AbstractServiceProviderTest extends UnitTestCase
$package2 = $this->prophesize(Package::class); $package2 = $this->prophesize(Package::class);
$package2->getPackagePath()->willReturn(__DIR__ . '/../Http/Fixtures/Package2/'); $package2->getPackagePath()->willReturn(__DIR__ . '/../Http/Fixtures/Package2/');
$package2->getValueFromComposerManifest('name')->willReturn('typo3/cms-testing');
$package2ServiceProvider = new PseudoServiceProvider($package2->reveal()); $package2ServiceProvider = new PseudoServiceProvider($package2->reveal());
$middlewares = new ArrayObject(); $middlewares = new ArrayObject();
...@@ -113,6 +114,7 @@ class AbstractServiceProviderTest extends UnitTestCase ...@@ -113,6 +114,7 @@ class AbstractServiceProviderTest extends UnitTestCase
$package2 = $this->prophesize(Package::class); $package2 = $this->prophesize(Package::class);
$package2->getPackagePath()->willReturn(__DIR__ . '/../Http/Fixtures/Package2Disables1/'); $package2->getPackagePath()->willReturn(__DIR__ . '/../Http/Fixtures/Package2Disables1/');
$package2->getValueFromComposerManifest('name')->willReturn('typo3/cms-testing');
$package2ServiceProvider = new PseudoServiceProvider($package2->reveal()); $package2ServiceProvider = new PseudoServiceProvider($package2->reveal());
$middlewares = new ArrayObject(); $middlewares = new ArrayObject();
...@@ -149,6 +151,7 @@ class AbstractServiceProviderTest extends UnitTestCase ...@@ -149,6 +151,7 @@ class AbstractServiceProviderTest extends UnitTestCase
$package2 = $this->prophesize(Package::class); $package2 = $this->prophesize(Package::class);
$package2->getPackagePath()->willReturn(__DIR__ . '/../Http/Fixtures/Package2Replaces1/'); $package2->getPackagePath()->willReturn(__DIR__ . '/../Http/Fixtures/Package2Replaces1/');
$package2->getValueFromComposerManifest('name')->willReturn('typo3/cms-testing');
$package2ServiceProvider = new PseudoServiceProvider($package2->reveal()); $package2ServiceProvider = new PseudoServiceProvider($package2->reveal());
$middlewares = new ArrayObject(); $middlewares = new ArrayObject();
......
...@@ -26,6 +26,11 @@ class Package1ServiceProviderMock extends AbstractServiceProvider ...@@ -26,6 +26,11 @@ class Package1ServiceProviderMock extends AbstractServiceProvider
return __DIR__ . '/../../Http/Fixtures/Package1/'; return __DIR__ . '/../../Http/Fixtures/Package1/';
} }
protected static function getPackageName(): string
{
return 'typo3/testing-package1';
}
public function getFactories(): array public function getFactories(): array
{ {
return []; return [];
......
...@@ -26,6 +26,11 @@ class Package2ServiceProviderMock extends AbstractServiceProvider ...@@ -26,6 +26,11 @@ class Package2ServiceProviderMock extends AbstractServiceProvider
return __DIR__ . '/../../Http/Fixtures/Package2/'; return __DIR__ . '/../../Http/Fixtures/Package2/';
} }
protected static function getPackageName(): string
{
return 'typo3/testing-package1';
}
public function getFactories(): array public function getFactories(): array
{ {
return []; return [];
......
...@@ -38,6 +38,11 @@ class ServiceProvider extends AbstractServiceProvider ...@@ -38,6 +38,11 @@ class ServiceProvider extends AbstractServiceProvider
return __DIR__ . '/../'; return __DIR__ . '/../';
} }
protected static function getPackageName(): string
{
return 'typo3/cms-dashboard';
}
public function getFactories(): array public function getFactories(): array
{ {
return [ return [
......
...@@ -34,6 +34,11 @@ class ServiceProvider extends AbstractServiceProvider ...@@ -34,6 +34,11 @@ class ServiceProvider extends AbstractServiceProvider
return __DIR__ . '/../'; return __DIR__ . '/../';
} }
protected static function getPackageName(): string
{
return 'typo3/cms-extbase';
}
public function getFactories(): array public function getFactories(): array
{ {
return [ return [
......
...@@ -44,6 +44,7 @@ class RequestBuilderTest extends FunctionalTestCase ...@@ -44,6 +44,7 @@ class RequestBuilderTest extends FunctionalTestCase
$pluginName = 'blog'; $pluginName = 'blog';
$module = ExtbaseModule::createFromConfiguration($pluginName, [ $module = ExtbaseModule::createFromConfiguration($pluginName, [
'packageName' => 'typo3/cms-blog-example',
'path' => '/blog-example', 'path' => '/blog-example',
'extensionName' => $extensionName, 'extensionName' => $extensionName,
'controllerActions' => [ 'controllerActions' => [
...@@ -76,6 +77,7 @@ class RequestBuilderTest extends FunctionalTestCase ...@@ -76,6 +77,7 @@ class RequestBuilderTest extends FunctionalTestCase
$pluginName = 'blog'; $pluginName = 'blog';
$module = ExtbaseModule::createFromConfiguration($pluginName, [ $module = ExtbaseModule::createFromConfiguration($pluginName, [
'packageName' => 'typo3/cms-blog-example',
'path' => '/blog-example', 'path' => '/blog-example',
'extensionName' => $extensionName, 'extensionName' => $extensionName,
'controllerActions' => [ 'controllerActions' => [
...@@ -109,6 +111,7 @@ class RequestBuilderTest extends FunctionalTestCase ...@@ -109,6 +111,7 @@ class RequestBuilderTest extends FunctionalTestCase
$pluginName = 'blog'; $pluginName = 'blog';
$module = ExtbaseModule::createFromConfiguration($pluginName, [ $module = ExtbaseModule::createFromConfiguration($pluginName, [
'packageName' => 'typo3/cms-blog-example',
'path' => '/blog-example', 'path' => '/blog-example',
'extensionName' => $extensionName, 'extensionName' => $extensionName,
'controllerActions' => [ 'controllerActions' => [
...@@ -181,6 +184,7 @@ class RequestBuilderTest extends FunctionalTestCase ...@@ -181,6 +184,7 @@ class RequestBuilderTest extends FunctionalTestCase
$pluginName = 'blog'; $pluginName = 'blog';