diff --git a/src/Kunstmaan/RedirectBundle/AdminList/RedirectAdminListConfigurator.php b/src/Kunstmaan/RedirectBundle/AdminList/RedirectAdminListConfigurator.php index da4bea083f..814102ae9e 100644 --- a/src/Kunstmaan/RedirectBundle/AdminList/RedirectAdminListConfigurator.php +++ b/src/Kunstmaan/RedirectBundle/AdminList/RedirectAdminListConfigurator.php @@ -21,9 +21,15 @@ class RedirectAdminListConfigurator extends AbstractDoctrineORMAdminListConfigur * @param AclHelper $aclHelper The acl helper * @param DomainConfigurationInterface $domainConfiguration */ - public function __construct(EntityManager $em, AclHelper $aclHelper = null, DomainConfigurationInterface $domainConfiguration) - { - parent::__construct($em, $aclHelper); + public function __construct( + EntityManager $em, + AclHelper $aclHelper = null, + DomainConfigurationInterface $domainConfiguration + ) { + parent::__construct( + $em, + $aclHelper + ); $this->domainConfiguration = $domainConfiguration; @@ -34,32 +40,90 @@ public function __construct(EntityManager $em, AclHelper $aclHelper = null, Doma /** * Configure the visible columns */ - public function buildFields() + public function buildFields(): void { if ($this->domainConfiguration->isMultiDomainHost()) { - $this->addField('domain', 'redirect.adminlist.header.domain', true); + $this->addField( + 'domain', + 'redirect.adminlist.header.domain', + true + ); } - $this->addField('origin', 'redirect.adminlist.header.origin', true); - $this->addField('target', 'redirect.adminlist.header.target', true); - $this->addField('permanent', 'redirect.adminlist.header.permanent', true); - $this->addField('note', 'redirect.adminlist.header.note', true); + $this->addField( + 'origin', + 'redirect.adminlist.header.origin', + true + ); + $this->addField( + 'target', + 'redirect.adminlist.header.target', + true + ); + $this->addField( + 'permanent', + 'redirect.adminlist.header.permanent', + true + ); + $this->addField( + 'note', + 'redirect.adminlist.header.note', + true + ); + $this->addField( + 'isAutoRedirect', + 'redirect.adminlist.header.is_auto_redirect', + true + ); } /** * Build filters for admin list */ - public function buildFilters() + public function buildFilters(): void { if ($this->domainConfiguration->isMultiDomainHost()) { $hosts = $this->domainConfiguration->getHosts(); - $domains = array_combine($hosts, $hosts); - $domains = array_merge(array('' => 'redirect.all'), $domains); - $this->addFilter('domain', new ORM\EnumerationFilterType('domain'), 'redirect.adminlist.filter.domain', $domains); + $domains = array_combine( + $hosts, + $hosts + ); + $domains = array_merge( + ['' => 'redirect.all'], + $domains + ); + $this->addFilter( + 'domain', + new ORM\EnumerationFilterType('domain'), + 'redirect.adminlist.filter.domain', + $domains + ); } - $this->addFilter('origin', new ORM\StringFilterType('origin'), 'redirect.adminlist.filter.origin'); - $this->addFilter('target', new ORM\StringFilterType('target'), 'redirect.adminlist.filter.target'); - $this->addFilter('permanent', new ORM\BooleanFilterType('permanent'), 'redirect.adminlist.filter.permanent'); - $this->addFilter('note', new ORM\StringFilterType('note'), 'redirect.adminlist.filter.note'); + + $this->addFilter( + 'origin', + new ORM\StringFilterType('origin'), + 'redirect.adminlist.filter.origin' + ); + $this->addFilter( + 'target', + new ORM\StringFilterType('target'), + 'redirect.adminlist.filter.target' + ); + $this->addFilter( + 'permanent', + new ORM\BooleanFilterType('permanent'), + 'redirect.adminlist.filter.permanent' + ); + $this->addFilter( + 'note', + new ORM\StringFilterType('note'), + 'redirect.adminlist.filter.note' + ); + $this->addFilter( + 'isAutoRedirect', + new ORM\BooleanFilterType('isAutoRedirect'), + 'redirect.adminlist.filter.is_auto_redirect' + ); } /** @@ -68,31 +132,30 @@ public function buildFilters() * * @return string */ - public function getValue($item, $columnName) - { - if ($columnName == 'domain' && !$item->getDomain()) { + public function getValue( + $item, + $columnName + ) { + if ($columnName === 'domain' && !$item->getDomain()) { return 'All domains'; } - return parent::getValue($item, $columnName); + if ($columnName === 'isAutoRedirect' && $item->isAutoRedirect() === null) { + return false; + } + + return parent::getValue( + $item, + $columnName + ); } - /** - * Get bundle name - * - * @return string - */ - public function getBundleName() + public function getBundleName(): string { return 'KunstmaanRedirectBundle'; } - /** - * Get entity name - * - * @return string - */ - public function getEntityName() + public function getEntityName(): string { return 'Redirect'; } diff --git a/src/Kunstmaan/RedirectBundle/Entity/AutoRedirectInterface.php b/src/Kunstmaan/RedirectBundle/Entity/AutoRedirectInterface.php new file mode 100644 index 0000000000..9b0a7b810d --- /dev/null +++ b/src/Kunstmaan/RedirectBundle/Entity/AutoRedirectInterface.php @@ -0,0 +1,7 @@ +domain; } - /** - * Set domain - * - * @param string $domain - * - * @return Redirect - */ - public function setDomain($domain) + public function setDomain(?string $domain): Redirect { $this->domain = $domain; return $this; } - /** - * Set origin - * - * @param string $origin - * - * @return Redirect - */ - public function setOrigin($origin) + public function getOrigin(): ?string + { + return $this->origin; + } + + public function setOrigin(?string $origin): Redirect { $this->origin = $origin; return $this; } - /** - * Get origin - * - * @return string - */ - public function getOrigin() + public function getNote(): ?string { - return $this->origin; + return $this->note; } - /** - * Set target - * - * @param string $target - * - * @return Redirect - */ - public function setTarget($target) + public function setNote(?string $note): Redirect { - $this->target = $target; + $this->note = $note; return $this; } - /** - * Get target - * - * @return string - */ - public function getTarget() + public function getTarget(): ?string { return $this->target; } - /** - * Set permanent - * - * @param bool $permanent - * - * @return Redirect - */ - public function setPermanent($permanent) + public function setTarget(?string $target): Redirect { - $this->permanent = $permanent; + $this->target = $target; return $this; } - /** - * Get permanent - * - * @return bool - */ - public function isPermanent() + public function isPermanent(): bool { return $this->permanent; } - /** - * @return string - */ - public function getNote() + public function setPermanent(bool $permanent): Redirect { - return $this->note; + $this->permanent = $permanent; + + return $this; } - /** - * @param string $note - * - * @return Redirect - */ - public function setNote($note) + public function isAutoRedirect(): ?bool { - $this->note = $note; + return $this->isAutoRedirect; + } + + public function setIsAutoRedirect(?bool $isAutoRedirect): Redirect + { + $this->isAutoRedirect = $isAutoRedirect; + + return $this; } /** @@ -176,7 +141,7 @@ public function setNote($note) * * @param ExecutionContextInterface $context */ - public function validate(ExecutionContextInterface $context) + public function validate(ExecutionContextInterface $context): void { if ($this->getOrigin() === $this->getTarget()) { $context->buildViolation('errors.redirect.origin_same_as_target') diff --git a/src/Kunstmaan/RedirectBundle/EventSubscriber/RedirectSubscriber.php b/src/Kunstmaan/RedirectBundle/EventSubscriber/RedirectSubscriber.php new file mode 100644 index 0000000000..49e8df6212 --- /dev/null +++ b/src/Kunstmaan/RedirectBundle/EventSubscriber/RedirectSubscriber.php @@ -0,0 +1,226 @@ + + */ + private $redirects = []; + + public function __construct(RequestStack $requestStack) + { + $this->requestStack = $requestStack; + } + + public function getSubscribedEvents(): array + { + return [ + Events::onFlush, + ]; + } + + public function onFlush(OnFlushEventArgs $onFlushEventArgs): void + { + $entityManager = $onFlushEventArgs->getEntityManager(); + $unitOfWork = $entityManager->getUnitOfWork(); + + if (!$entityManager instanceof EntityManager) { + return; + } + + foreach ($unitOfWork->getScheduledEntityUpdates() as $entity) { + if (!$entity instanceof NodeTranslation) { + continue; + } + + $this->createAutoRedirect( + $entity, + $entityManager, + $unitOfWork + ); + } + + $unitOfWork->computeChangeSets(); + } + + private function createAutoRedirect( + NodeTranslation $nodeTranslation, + EntityManager $entityManager, + UnitOfWork $unitOfWork + ): void { + $changeSet = $unitOfWork->getEntityChangeSet($nodeTranslation); + + if (!isset($changeSet['slug'])) { + return; + } + + $page = $nodeTranslation->getRef($entityManager); + + if (!$page instanceof AutoRedirectInterface) { + return; + } + + $this->processRedirect( + $entityManager, + $nodeTranslation->getUrl(), + $this->getNewUrl($nodeTranslation) + ); + } + + private function getNewUrl(NodeTranslation $nodeTranslation): string + { + $newUrl = $nodeTranslation->getFullSlug(); + + if (!is_string($newUrl)) { + return '/'; + } + + return $this->startWithSlash($newUrl); + } + + private function startWithSlash(string $url): string + { + $firstCharacter = $url[0] ?? ''; + + if ($firstCharacter !== '/') { + return '/' . $url; + } + + return $url; + } + + private function removeSlashAtStart(string $url): string + { + $firstCharacter = $url[0] ?? ''; + + if ($firstCharacter !== '/') { + return $url; + } + + return substr($url, 1); + } + + private function processRedirect( + EntityManager $entityManager, + ?string $oldUrl, + string $newUrl + ): void { + $this->removeOriginRedirects( + $newUrl, + $entityManager + ); + + if (!is_string($oldUrl)) { + return; + } + + $this->updateTargetRedirects( + $oldUrl, + $newUrl, + $entityManager + ); + + $this->createRedirect( + $entityManager, + $oldUrl, + $newUrl + ); + } + + private function removeOriginRedirects( + string $newUrl, + EntityManager $entityManager + ): void { + $origin = $this->removeSlashAtStart($newUrl); + + $redirects = $entityManager->getRepository(Redirect::class)->findBy( + [ + 'origin' => $origin, + ] + ); + + /** @var Redirect $redirect */ + foreach ($redirects as $redirect) { + $entityManager->remove($redirect); + } + + if (isset($this->redirects[$origin])) { + foreach ($this->redirects[$origin] as $redirect) { + $entityManager->remove($redirect); + } + } + } + + private function updateTargetRedirects( + string $oldUrl, + string $newUrl, + EntityManager $entityManager + ): void { + $redirects = $entityManager->getRepository(Redirect::class)->findBy( + [ + 'target' => $this->startWithSlash($oldUrl), + ] + ); + + /** @var Redirect $redirect */ + foreach ($redirects as $redirect) { + $redirect->setTarget($newUrl); + $redirect->setIsAutoRedirect(true); + } + } + + private function createRedirect( + EntityManager $entityManager, + string $oldUrl, + string $newUrl + ): void { + $redirect = new Redirect(); + $redirect->setOrigin($oldUrl); + $redirect->setTarget($newUrl); + $redirect->setPermanent(true); + $redirect->setDomain($this->getDomain()); + $redirect->setIsAutoRedirect(true); + + $entityManager->persist($redirect); + + $entityManager->getUnitOfWork()->computeChangeSet( + $entityManager->getClassMetadata(Redirect::class), + $redirect + ); + + if (!isset($this->redirects[$oldUrl])) { + $this->redirects[$oldUrl] = []; + } + + $this->redirects[$oldUrl][] = $redirect; + } + + private function getDomain(): ?string + { + $request = $this->requestStack->getCurrentRequest(); + + if (!$request instanceof Request) { + return null; + } + + return $request->getHost(); + } +} diff --git a/src/Kunstmaan/RedirectBundle/Resources/config/services.yml b/src/Kunstmaan/RedirectBundle/Resources/config/services.yml index a0a6fe4699..2e27087090 100644 --- a/src/Kunstmaan/RedirectBundle/Resources/config/services.yml +++ b/src/Kunstmaan/RedirectBundle/Resources/config/services.yml @@ -25,3 +25,9 @@ services: arguments: ['@kunstmaan_admin.domain_configuration'] tags: - { name: form.type, alias: kunstmaan_redirect_form_type } + + Kunstmaan\RedirectBundle\EventSubscriber\RedirectSubscriber: + arguments: + $requestStack: '@request_stack' + tags: + - { name: doctrine.event_subscriber, connection: default } diff --git a/src/Kunstmaan/RedirectBundle/Resources/translations/messages.en.yml b/src/Kunstmaan/RedirectBundle/Resources/translations/messages.en.yml index 6ee5eb6036..baaaeebd14 100644 --- a/src/Kunstmaan/RedirectBundle/Resources/translations/messages.en.yml +++ b/src/Kunstmaan/RedirectBundle/Resources/translations/messages.en.yml @@ -19,14 +19,16 @@ redirect: adminlist: header: - domain: Domain - origin: Origin - target: Target - permanent: Permanent? - note: Note + domain: Domain + origin: Origin + target: Target + permanent: Permanent? + note: Note + is_auto_redirect: Automatische redirect? filter: - domain: Domain - origin: Origin - target: Target - permanent: Permanent - note: Note + domain: Domain + origin: Origin + target: Target + permanent: Permanent + note: Note + is_auto_redirect: Automatische redirect? diff --git a/src/Kunstmaan/RedirectBundle/Resources/translations/messages.nl.yml b/src/Kunstmaan/RedirectBundle/Resources/translations/messages.nl.yml index 3fc39cf382..726dc59069 100644 --- a/src/Kunstmaan/RedirectBundle/Resources/translations/messages.nl.yml +++ b/src/Kunstmaan/RedirectBundle/Resources/translations/messages.nl.yml @@ -23,6 +23,7 @@ redirect: target: Bestemming permanent: Permanent? note: Notitie + is_auto_redirect: Automatische redirect? filter: domain: Domein origin: Herkomst diff --git a/src/Kunstmaan/RedirectBundle/Tests/unit/AdminList/RedirectAdminListConfiguratorTest.php b/src/Kunstmaan/RedirectBundle/Tests/unit/AdminList/RedirectAdminListConfiguratorTest.php index 0f5b47e9f6..12d09bc0a1 100644 --- a/src/Kunstmaan/RedirectBundle/Tests/unit/AdminList/RedirectAdminListConfiguratorTest.php +++ b/src/Kunstmaan/RedirectBundle/Tests/unit/AdminList/RedirectAdminListConfiguratorTest.php @@ -1,6 +1,6 @@ object = new RedirectAdminListConfigurator($this->em, $this->aclHelper, $domainConfiguration); } - public function testBuildFields() + public function testBuildFields(): void { $this->object->buildFields(); $fields = $this->object->getFields(); - $this->assertCount(4, $fields); + $this->assertCount(5, $fields); $fieldNames = array_map( function (Field $field) { return $field->getName(); }, $fields ); - $this->assertEquals(array('origin', 'target', 'permanent', 'note'), $fieldNames); + $this->assertEquals(['origin', 'target', 'permanent', 'note', 'isAutoRedirect'], $fieldNames); } - public function testBuildFilters() + public function testBuildFilters(): void { $filterBuilder = $this->createMock('Kunstmaan\AdminListBundle\AdminList\FilterBuilder'); $filterBuilder @@ -74,16 +74,20 @@ public function testBuildFilters() ->expects($this->at(3)) ->method('add') ->with('note'); + $filterBuilder + ->expects($this->at(4)) + ->method('add') + ->with('isAutoRedirect'); $this->object->setFilterBuilder($filterBuilder); $this->object->buildFilters(); } - public function testGetBundleName() + public function testGetBundleName(): void { $this->assertEquals('KunstmaanRedirectBundle', $this->object->getBundleName()); } - public function testGetEntityName() + public function testGetEntityName(): void { $this->assertEquals('Redirect', $this->object->getEntityName()); }