Commit 8acac86e authored by Tino Goratsch's avatar Tino Goratsch

Merge branch 'release/v4.24.0'

parents 0f09a4f5 bf15bf59
Pipeline #18677944 passed with stages
in 10 minutes and 6 seconds
......@@ -16,7 +16,7 @@ interface BootstrapInterface extends HttpKernelInterface
/**
* Contains the current ACP3 version string.
*/
const VERSION = '4.23.0';
const VERSION = '4.24.0';
/**
* Performs some startup checks.
......
......@@ -74,7 +74,7 @@ class Steps
*
* @return array
*/
public function getBreadcrumb()
public function getBreadcrumb(): array
{
if (empty($this->breadcrumbCache)) {
$this->buildBreadcrumbCache();
......@@ -86,7 +86,7 @@ class Steps
/**
* Sets the breadcrumb cache for the current request.
*/
private function buildBreadcrumbCache()
private function buildBreadcrumbCache(): void
{
$this->eventDispatcher->dispatch(
'core.breadcrumb.steps.build_cache',
......@@ -105,14 +105,9 @@ class Steps
/**
* Sets the breadcrumb steps cache for admin panel action requests.
*/
private function buildBreadcrumbCacheForAdmin()
private function buildBreadcrumbCacheForAdmin(): void
{
if (empty($this->steps)) {
$this->eventDispatcher->dispatch(
'core.breadcrumb.steps.build_admin_cache_empty_steps_before',
new StepsBuildCacheEvent($this)
);
$this->append(
$this->translator->t($this->request->getModule(), $this->request->getModule()),
'acp/' . $this->request->getModule()
......@@ -124,17 +119,17 @@ class Steps
$this->translator->t($this->request->getModule(), $this->request->getModule()),
'acp/' . $this->request->getModule()
);
$this->eventDispatcher->dispatch(
'core.breadcrumb.steps.build_admin_cache_not_empty_steps_after',
new StepsBuildCacheEvent($this)
);
}
$this->eventDispatcher->dispatch(
'core.breadcrumb.steps.build_admin_cache_not_empty_steps_after',
new StepsBuildCacheEvent($this)
);
$this->breadcrumbCache = $this->steps;
}
private function appendControllerActionBreadcrumbs()
private function appendControllerActionBreadcrumbs(): void
{
$serviceId = $this->getControllerServiceId();
if ($this->request->getController() !== 'index' && $this->container->has($serviceId)) {
......@@ -154,7 +149,7 @@ class Steps
/**
* @return string
*/
private function getControllerServiceId()
private function getControllerServiceId(): string
{
return $this->request->getModule()
. '.controller.'
......@@ -166,7 +161,7 @@ class Steps
/**
* @return string
*/
private function getControllerActionTitle()
private function getControllerActionTitle(): string
{
return $this->request->getArea() . '_' . $this->request->getController() . '_' . $this->request->getAction();
}
......@@ -174,7 +169,7 @@ class Steps
/**
* @return string
*/
private function getControllerIndexActionTitle()
private function getControllerIndexActionTitle(): string
{
return $this->request->getArea() . '_' . $this->request->getController() . '_index';
}
......@@ -182,7 +177,7 @@ class Steps
/**
* Sets the breadcrumb steps cache for frontend action requests.
*/
protected function buildBreadcrumbCacheForFrontend()
protected function buildBreadcrumbCacheForFrontend(): void
{
if (empty($this->steps)) {
$this->append(
......@@ -193,6 +188,11 @@ class Steps
$this->appendControllerActionBreadcrumbs();
}
$this->eventDispatcher->dispatch(
'core.breadcrumb.steps.build_frontend_cache_after',
new StepsBuildCacheEvent($this)
);
$this->breadcrumbCache = $this->steps;
}
......@@ -205,7 +205,7 @@ class Steps
*
* @return $this
*/
public function replaceAncestor($title, $path = '', $dbSteps = false)
public function replaceAncestor(string $title, string $path = '', bool $dbSteps = false): self
{
if ($dbSteps === false) {
\end($this->steps);
......@@ -221,7 +221,7 @@ class Steps
*
* @return array
*/
protected function buildStepItem($title, $path)
protected function buildStepItem(string $title, string $path): array
{
return [
'title' => $title,
......@@ -237,7 +237,7 @@ class Steps
*
* @return $this
*/
public function append($title, $path = '')
public function append(string $title, string $path = ''): self
{
if (!$this->stepAlreadyExists($path)) {
$this->steps[] = $this->buildStepItem($title, $path);
......@@ -254,7 +254,7 @@ class Steps
*
* @return $this
*/
public function prepend($title, $path)
public function prepend(string $title, string $path): self
{
if (!$this->stepAlreadyExists($path)) {
$step = $this->buildStepItem($title, $path);
......@@ -264,12 +264,31 @@ class Steps
return $this;
}
/**
* @param string $path
*
* @return $this
*/
public function removeByPath(string $path): self
{
$path = $this->router->route($path);
$this->steps = \array_filter(
$this->steps,
function (array $step) use ($path) {
return $step['uri'] !== $path;
}
);
return $this;
}
/**
* @param string $path
*
* @return bool
*/
private function stepAlreadyExists($path)
private function stepAlreadyExists(string $path): bool
{
$route = $this->router->route($path);
foreach ($this->steps as $step) {
......
......@@ -57,22 +57,11 @@ class StepsTest extends \PHPUnit_Framework_TestCase
protected function initializeMockObjects()
{
$this->containerMock = $this->getMockBuilder(Container::class)
->disableOriginalConstructor()
->getMock();
$this->translatorMock = $this->getMockBuilder(Translator::class)
->disableOriginalConstructor()
->getMock();
$this->requestMock = $this->getMockBuilder(Request::class)
->disableOriginalConstructor()
->getMock();
$this->routerMock = $this->getMockBuilder(RouterInterface::class)
->disableOriginalConstructor()
->setMethods(['route'])
->getMock();
$this->eventDispatcherMock = $this->getMockBuilder(EventDispatcher::class)
->disableOriginalConstructor()
->getMock();
$this->containerMock = $this->createMock(Container::class);
$this->translatorMock = $this->createMock(Translator::class);
$this->requestMock = $this->createMock(Request::class);
$this->routerMock = $this->createPartialMock(RouterInterface::class, ['route']);
$this->eventDispatcherMock = $this->createMock(EventDispatcher::class);
}
public function testGetBreadcrumbForAdminControllerIndex()
......
<?php
/**
* Copyright (c) by the ACP3 Developers.
* See the LICENSE file at the top-level module directory for licensing details.
*/
namespace ACP3\Modules\ACP3\Acp\Event\Listener;
use ACP3\Core\Breadcrumb\Event\StepsBuildCacheEvent;
use ACP3\Core\I18n\Translator;
class OnBuildAdminCacheEmptyStepsBeforeListener
{
/**
* @var \ACP3\Core\I18n\Translator
*/
private $translator;
/**
* OnBreadcrumbStepsBuildCacheListener constructor.
*
* @param \ACP3\Core\I18n\Translator $translator
*/
public function __construct(Translator $translator)
{
$this->translator = $translator;
}
/**
* @param \ACP3\Core\Breadcrumb\Event\StepsBuildCacheEvent $event
*/
public function execute(StepsBuildCacheEvent $event)
{
$event->getSteps()->append($this->translator->t('acp', 'acp'), 'acp/acp');
}
}
services:
acp.event.listener.on_build_admin_cache_empty_steps_before_listener:
class: ACP3\Modules\ACP3\Acp\Event\Listener\OnBuildAdminCacheEmptyStepsBeforeListener
arguments:
- '@core.i18n.translator'
tags:
- { name: core.eventListener, event: core.breadcrumb.steps.build_admin_cache_empty_steps_before, method: execute }
acp.event.listener.on_build_admin_cache_not_empty_steps_after_listener:
class: ACP3\Modules\ACP3\Acp\Event\Listener\OnBuildAdminCacheNotEmptyStepsAfterListener
arguments:
- '@core.i18n.translator'
tags:
- { name: core.eventListener, event: core.breadcrumb.steps.build_admin_cache_not_empty_steps_after, method: execute }
- { name: core.eventListener, event: core.breadcrumb.steps.build_frontend_cache_after, method: execute }
acp.event.listener.on_get_site_and_page_title_before_listener:
class: ACP3\Modules\ACP3\Acp\Event\Listener\OnGetSiteAndPageTitleBeforeListener
......
......@@ -17,12 +17,12 @@
"prefer-stable": true,
"require": {
"acp3/composer-installer": "^1.0",
"acp3/core": "^4.23.0",
"acp3/setup": "^4.23.0",
"acp3/module-errors": "^4.23.0",
"acp3/module-permissions": "^4.23.0",
"acp3/module-system": "^4.23.0",
"acp3/module-users": "^4.23.0"
"acp3/core": "^4.24.0",
"acp3/setup": "^4.24.0",
"acp3/module-errors": "^4.24.0",
"acp3/module-permissions": "^4.24.0",
"acp3/module-system": "^4.24.0",
"acp3/module-users": "^4.24.0"
},
"autoload": {
"psr-4": {
......
......@@ -17,12 +17,12 @@
"prefer-stable": true,
"require": {
"acp3/composer-installer": "^1.0",
"acp3/core": "^4.23.0",
"acp3/setup": "^4.23.0",
"acp3/module-errors": "^4.23.0",
"acp3/module-permissions": "^4.23.0",
"acp3/module-system": "^4.23.0",
"acp3/module-users": "^4.23.0"
"acp3/core": "^4.24.0",
"acp3/setup": "^4.24.0",
"acp3/module-errors": "^4.24.0",
"acp3/module-permissions": "^4.24.0",
"acp3/module-system": "^4.24.0",
"acp3/module-users": "^4.24.0"
},
"suggest": {
"acp3/module-seo": "Provides additional SEO capabilities"
......
......@@ -17,12 +17,12 @@
"prefer-stable": true,
"require": {
"acp3/composer-installer": "^1.0",
"acp3/core": "^4.23.0",
"acp3/setup": "^4.23.0",
"acp3/module-errors": "^4.23.0",
"acp3/module-permissions": "^4.23.0",
"acp3/module-system": "^4.23.0",
"acp3/module-users": "^4.23.0"
"acp3/core": "^4.24.0",
"acp3/setup": "^4.24.0",
"acp3/module-errors": "^4.24.0",
"acp3/module-permissions": "^4.24.0",
"acp3/module-system": "^4.24.0",
"acp3/module-users": "^4.24.0"
},
"autoload": {
"psr-4": {
......
......@@ -17,12 +17,12 @@
"prefer-stable": true,
"require": {
"acp3/composer-installer": "^1.0",
"acp3/core": "^4.23.0",
"acp3/setup": "^4.23.0",
"acp3/module-errors": "^4.23.0",
"acp3/module-permissions": "^4.23.0",
"acp3/module-system": "^4.23.0",
"acp3/module-users": "^4.23.0",
"acp3/core": "^4.24.0",
"acp3/setup": "^4.24.0",
"acp3/module-errors": "^4.24.0",
"acp3/module-permissions": "^4.24.0",
"acp3/module-system": "^4.24.0",
"acp3/module-users": "^4.24.0",
"google/recaptcha": "^1.1.0"
},
"autoload": {
......
......@@ -17,12 +17,12 @@
"prefer-stable": true,
"require": {
"acp3/composer-installer": "^1.0",
"acp3/core": "^4.23.0",
"acp3/setup": "^4.23.0",
"acp3/module-errors": "^4.23.0",
"acp3/module-permissions": "^4.23.0",
"acp3/module-system": "^4.23.0",
"acp3/module-users": "^4.23.0"
"acp3/core": "^4.24.0",
"acp3/setup": "^4.24.0",
"acp3/module-errors": "^4.24.0",
"acp3/module-permissions": "^4.24.0",
"acp3/module-system": "^4.24.0",
"acp3/module-users": "^4.24.0"
},
"autoload": {
"psr-4": {
......
......@@ -17,12 +17,12 @@
"prefer-stable": true,
"require": {
"acp3/composer-installer": "^1.0",
"acp3/core": "^4.23.0",
"acp3/setup": "^4.23.0",
"acp3/module-errors": "^4.23.0",
"acp3/module-permissions": "^4.23.0",
"acp3/module-system": "^4.23.0",
"acp3/module-users": "^4.23.0"
"acp3/core": "^4.24.0",
"acp3/setup": "^4.24.0",
"acp3/module-errors": "^4.24.0",
"acp3/module-permissions": "^4.24.0",
"acp3/module-system": "^4.24.0",
"acp3/module-users": "^4.24.0"
},
"suggest": {
"acp3/module-captcha": "Provides basic protection against spam bots."
......
......@@ -17,12 +17,12 @@
"prefer-stable": true,
"require": {
"acp3/composer-installer": "^1.0",
"acp3/core": "^4.23.0",
"acp3/setup": "^4.23.0",
"acp3/module-errors": "^4.23.0",
"acp3/module-permissions": "^4.23.0",
"acp3/module-system": "^4.23.0",
"acp3/module-users": "^4.23.0"
"acp3/core": "^4.24.0",
"acp3/setup": "^4.24.0",
"acp3/module-errors": "^4.24.0",
"acp3/module-permissions": "^4.24.0",
"acp3/module-system": "^4.24.0",
"acp3/module-users": "^4.24.0"
},
"suggest": {
"acp3/module-captcha": "Provides basic protection against spam bots."
......
......@@ -17,12 +17,12 @@
"prefer-stable": true,
"require": {
"acp3/composer-installer": "^1.0",
"acp3/core": "^4.23.0",
"acp3/setup": "^4.23.0",
"acp3/module-errors": "^4.23.0",
"acp3/module-permissions": "^4.23.0",
"acp3/module-system": "^4.23.0",
"acp3/module-users": "^4.23.0"
"acp3/core": "^4.24.0",
"acp3/setup": "^4.24.0",
"acp3/module-errors": "^4.24.0",
"acp3/module-permissions": "^4.24.0",
"acp3/module-system": "^4.24.0",
"acp3/module-users": "^4.24.0"
},
"autoload": {
"psr-4": {
......
......@@ -17,11 +17,11 @@
"prefer-stable": true,
"require": {
"acp3/composer-installer": "^1.0",
"acp3/core": "^4.23.0",
"acp3/setup": "^4.23.0",
"acp3/module-permissions": "^4.23.0",
"acp3/module-system": "^4.23.0",
"acp3/module-users": "^4.23.0"
"acp3/core": "^4.24.0",
"acp3/setup": "^4.24.0",
"acp3/module-permissions": "^4.24.0",
"acp3/module-system": "^4.24.0",
"acp3/module-users": "^4.24.0"
},
"autoload": {
"psr-4": {
......
......@@ -17,12 +17,12 @@
"prefer-stable": true,
"require": {
"acp3/composer-installer": "^1.0",
"acp3/core": "^4.23.0",
"acp3/setup": "^4.23.0",
"acp3/module-errors": "^4.23.0",
"acp3/module-permissions": "^4.23.0",
"acp3/module-system": "^4.23.0",
"acp3/module-users": "^4.23.0",
"acp3/core": "^4.24.0",
"acp3/setup": "^4.24.0",
"acp3/module-errors": "^4.24.0",
"acp3/module-permissions": "^4.24.0",
"acp3/module-system": "^4.24.0",
"acp3/module-users": "^4.24.0",
"mibe/feedwriter": "^1.0"
},
"autoload": {
......
......@@ -17,12 +17,12 @@
"prefer-stable": true,
"require": {
"acp3/composer-installer": "^1.0",
"acp3/core": "^4.23.0",
"acp3/setup": "^4.23.0",
"acp3/module-errors": "^4.23.0",
"acp3/module-permissions": "^4.23.0",
"acp3/module-system": "^4.23.0",
"acp3/module-users": "^4.23.0"
"acp3/core": "^4.24.0",
"acp3/setup": "^4.24.0",
"acp3/module-errors": "^4.24.0",
"acp3/module-permissions": "^4.24.0",
"acp3/module-system": "^4.24.0",
"acp3/module-users": "^4.24.0"
},
"autoload": {
"psr-4": {
......
......@@ -17,13 +17,13 @@
"prefer-stable": true,
"require": {
"acp3/composer-installer": "^1.0",
"acp3/core": "^4.23.0",
"acp3/setup": "^4.23.0",
"acp3/module-errors": "^4.23.0",
"acp3/module-permissions": "^4.23.0",
"acp3/module-system": "^4.23.0",
"acp3/module-users": "^4.23.0",
"acp3/module-categories": "^4.23.0"
"acp3/core": "^4.24.0",
"acp3/setup": "^4.24.0",
"acp3/module-errors": "^4.24.0",
"acp3/module-permissions": "^4.24.0",
"acp3/module-system": "^4.24.0",
"acp3/module-users": "^4.24.0",
"acp3/module-categories": "^4.24.0"
},
"suggest": {
"acp3/module-seo": "Provides additional SEO capabilities"
......
......@@ -17,12 +17,12 @@
"prefer-stable": true,
"require": {
"acp3/composer-installer": "^1.0",
"acp3/core": "^4.23.0",
"acp3/setup": "^4.23.0",
"acp3/module-errors": "^4.23.0",
"acp3/module-permissions": "^4.23.0",
"acp3/module-system": "^4.23.0",
"acp3/module-users": "^4.23.0"
"acp3/core": "^4.24.0",
"acp3/setup": "^4.24.0",
"acp3/module-errors": "^4.24.0",
"acp3/module-permissions": "^4.24.0",
"acp3/module-system": "^4.24.0",
"acp3/module-users": "^4.24.0"
},
"suggest": {
"acp3/module-seo": "Provides additional SEO capabilities"
......
......@@ -17,12 +17,12 @@
"prefer-stable": true,
"require": {
"acp3/composer-installer": "^1.0",
"acp3/core": "^4.23.0",
"acp3/setup": "^4.23.0",
"acp3/module-errors": "^4.23.0",
"acp3/module-permissions": "^4.23.0",
"acp3/module-system": "^4.23.0",
"acp3/module-users": "^4.23.0"
"acp3/core": "^4.24.0",
"acp3/setup": "^4.24.0",
"acp3/module-errors": "^4.24.0",
"acp3/module-permissions": "^4.24.0",
"acp3/module-system": "^4.24.0",
"acp3/module-users": "^4.24.0"
},
"suggest": {
"acp3/module-captcha": "Provides basic protection against spam bots."
......
......@@ -8,6 +8,7 @@
namespace ACP3\Modules\ACP3\Menus\Core\Breadcrumb;
use ACP3\Core;
use ACP3\Core\Breadcrumb\Event\StepsBuildCacheEvent;
use ACP3\Core\Http\RequestInterface;
use ACP3\Modules\ACP3\Menus;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -50,7 +51,7 @@ class Steps extends Core\Breadcrumb\Steps
/**
* {@inheritdoc}
*/
public function replaceAncestor($title, $path = '', $dbSteps = false)
public function replaceAncestor(string $title, string $path = '', bool $dbSteps = false): Core\Breadcrumb\Steps
{
if ($dbSteps === true) {
\end($this->stepsFromDb);
......@@ -63,7 +64,7 @@ class Steps extends Core\Breadcrumb\Steps
/**
* {@inheritdoc}
*/
protected function buildBreadcrumbCacheForFrontend()
protected function buildBreadcrumbCacheForFrontend(): void
{
parent::buildBreadcrumbCacheForFrontend();
......@@ -71,6 +72,11 @@ class Steps extends Core\Breadcrumb\Steps
$this->prePopulate();
}
$this->eventDispatcher->dispatch(
'menus.breadcrumb.steps.build_frontend_cache_after',
new StepsBuildCacheEvent($this)
);
if (!empty($this->stepsFromDb)) {
$offset = $this->findFirstMatchingStep();
......@@ -83,7 +89,7 @@ class Steps extends Core\Breadcrumb\Steps
*
* @throws \Doctrine\DBAL\DBALException
*/
private function prePopulate()
private function prePopulate(): void
{
$items = $this->menuItemRepository->getMenuItemsByUri($this->getPossiblyMatchingRoutes());
......@@ -99,7 +105,10 @@ class Steps extends Core\Breadcrumb\Steps
}
}
private function getPossiblyMatchingRoutes()
/**
* @return array
*/
private function getPossiblyMatchingRoutes(): array
{
return [
$this->request->getQuery(),
......@@ -115,7 +124,7 @@ class Steps extends Core\Breadcrumb\Steps
*
* @return array
*/
private function findRestrictionInRoutes(array $items)
private function findRestrictionInRoutes(array $items): array
{
\rsort($items);
foreach ($items as $index => $item) {
......@@ -138,7 +147,7 @@ class Steps extends Core\Breadcrumb\Steps
*
* @return $this
*/
private function appendFromDB($title, $path = '')
private function appendFromDB(string $title, string $path = ''): self
{
$this->stepsFromDb[] = $this->buildStepItem($title, $path);
......@@ -153,15 +162,47 @@ class Steps extends Core\Breadcrumb\Steps
$steps = \array_reverse($this->steps);
$lastDbStep = \end($this->stepsFromDb);
$matched = false;
$offset = 0;
foreach ($steps as $index => $step) {
if ($step['uri'] === $lastDbStep['uri']) {
$matched = true;
$offset = $index;
break;
}
}
return \count($steps) - $offset;
return $this->hasUseIndex($matched, $lastDbStep['uri']) ? \count($steps) - $offset : 0;
}
/**
* @param bool $matched
* @param string $uri
*
* @return bool
*/
private function hasUseIndex(bool $matched, string $uri): bool
{
return $matched === true || $uri === $this->router->route($this->request->getQuery());
}
/**
* {@inheritdoc}
*/
public function removeByPath(string $path): Core\Breadcrumb\Steps
{
parent::removeByPath($path);
$path = $this->router->route($path);
$this->stepsFromDb = \array_filter(
$this->stepsFromDb,
function (array $step) use ($path) {
return $step['uri'] !== $path;
}
);
return $this;
}
}
......@@ -36,9 +36,7 @@ class StepsTest extends \ACP3\Core\Test\Breadcrumb\StepsTest
{
parent::initializeMockObjects();
$this->menuItemRepositoryMock = $this->getMockBuilder(MenuItemRepository::class)
->disableOriginalConstructor()
->getMock();
$this->menuItemRepositoryMock = $this->createMock(MenuItemRepository::class);
}
protected function setUpMenuItemRepositoryExpectations(array $dbSteps = [])
......
......@@ -17,12 +17,12 @@
"prefer-stable": true,
"require": {
"acp3/composer-installer": "^1.0",
"acp3/core": "^4.23.0",
"acp3/setup": "^4.23.0",
"acp3/module-errors": "^4.23.0",
"acp3/module-permissions": "^4.23.0",
"acp3/module-system": "^4.23.0",
"acp3/module-users": "^4.23.0"
"acp3/core": "^4.24.0",