backports murph-skeleton
This commit is contained in:
parent
a1c0fe0564
commit
e5412afff1
154
core/Controller/Redirect/RedirectAdminController.php
Normal file
154
core/Controller/Redirect/RedirectAdminController.php
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core\Controller\Redirect;
|
||||||
|
|
||||||
|
use App\Core\Controller\Admin\Crud\CrudController;
|
||||||
|
use App\Core\Crud\CrudConfiguration;
|
||||||
|
use App\Core\Crud\Field;
|
||||||
|
use App\Core\Entity\EntityInterface;
|
||||||
|
use App\Core\Entity\Redirect as Entity;
|
||||||
|
use App\Core\Factory\RedirectFactory as Factory;
|
||||||
|
use App\Core\Form\Filter\RedirectFilterType as FilterType;
|
||||||
|
use App\Core\Form\RedirectType as Type;
|
||||||
|
use App\Core\Manager\EntityManager;
|
||||||
|
use App\Core\Repository\RedirectRepositoryQuery as RepositoryQuery;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Session;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
|
||||||
|
class RedirectAdminController extends CrudController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Route("/admin/redirect/{page}", name="admin_redirect_index", methods={"GET"}, requirements={"page":"\d+"})
|
||||||
|
*/
|
||||||
|
public function index(RepositoryQuery $query, Request $request, Session $session, int $page = 1): Response
|
||||||
|
{
|
||||||
|
return $this->doIndex($page, $query, $request, $session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/admin/redirect/new", name="admin_redirect_new", methods={"GET", "POST"})
|
||||||
|
*/
|
||||||
|
public function new(Factory $factory, EntityManager $entityManager, Request $request): Response
|
||||||
|
{
|
||||||
|
return $this->doNew($factory->create(), $entityManager, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/admin/redirect/show/{entity}", name="admin_redirect_show", methods={"GET"})
|
||||||
|
*/
|
||||||
|
public function show(Entity $entity): Response
|
||||||
|
{
|
||||||
|
return $this->doShow($entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/admin/redirect/filter", name="admin_redirect_filter", methods={"GET"})
|
||||||
|
*/
|
||||||
|
public function filter(Session $session): Response
|
||||||
|
{
|
||||||
|
return $this->doFilter($session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/admin/redirect/edit/{entity}", name="admin_redirect_edit", methods={"GET", "POST"})
|
||||||
|
*/
|
||||||
|
public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||||
|
{
|
||||||
|
return $this->doEdit($entity, $entityManager, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/admin/redirect/sort/{page}", name="admin_redirect_sort", methods={"POST"}, requirements={"page":"\d+"})
|
||||||
|
*/
|
||||||
|
public function sort(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
|
||||||
|
{
|
||||||
|
return $this->doSort($page, $query, $entityManager, $request, $session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/admin/redirect/batch/{page}", name="admin_redirect_batch", methods={"POST"}, requirements={"page":"\d+"})
|
||||||
|
*/
|
||||||
|
public function batch(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
|
||||||
|
{
|
||||||
|
return $this->doBatch($page, $query, $entityManager, $request, $session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/admin/redirect/delete/{entity}", name="admin_redirect_delete", methods={"DELETE"})
|
||||||
|
*/
|
||||||
|
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||||
|
{
|
||||||
|
return $this->doDelete($entity, $entityManager, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getConfiguration(): CrudConfiguration
|
||||||
|
{
|
||||||
|
return CrudConfiguration::create()
|
||||||
|
->setPageTitle('index', 'Redirects')
|
||||||
|
->setPageTitle('edit', '{label}')
|
||||||
|
->setPageTitle('new', 'New redirect')
|
||||||
|
|
||||||
|
->setPageRoute('index', 'admin_redirect_index')
|
||||||
|
->setPageRoute('new', 'admin_redirect_new')
|
||||||
|
->setPageRoute('edit', 'admin_redirect_edit')
|
||||||
|
->setPageRoute('sort', 'admin_redirect_sort')
|
||||||
|
->setPageRoute('batch', 'admin_redirect_batch')
|
||||||
|
->setPageRoute('delete', 'admin_redirect_delete')
|
||||||
|
->setPageRoute('filter', 'admin_redirect_filter')
|
||||||
|
|
||||||
|
->setForm('edit', Type::class, [])
|
||||||
|
->setForm('new', Type::class)
|
||||||
|
->setForm('filter', FilterType::class)
|
||||||
|
|
||||||
|
->setView('form', '@Core/redirect/redirect_admin/_form.html.twig')
|
||||||
|
->setMaxPerPage('index', 100)
|
||||||
|
->setIsSortableCollection('index', true)
|
||||||
|
|
||||||
|
->setAction('index', 'show', false)
|
||||||
|
->setAction('edit', 'show', false)
|
||||||
|
|
||||||
|
->setField('index', 'Label', Field\TextField::class, [
|
||||||
|
'property' => 'label',
|
||||||
|
'attr' => ['class' => 'col-4'],
|
||||||
|
])
|
||||||
|
->setField('index', 'Rule', Field\TextField::class, [
|
||||||
|
'view' => '@Core/redirect/redirect_admin/field/rule.html.twig',
|
||||||
|
'attr' => ['class' => 'col-4'],
|
||||||
|
])
|
||||||
|
->setField('index', 'Enabled', Field\ButtonField::class, [
|
||||||
|
'property_builder' => function(EntityInterface $entity) {
|
||||||
|
return $entity->getIsEnabled() ? 'Yes' : 'No';
|
||||||
|
},
|
||||||
|
'attr' => ['class' => 'col-2'],
|
||||||
|
'button_attr_builder' => function(EntityInterface $entity) {
|
||||||
|
return ['class' => 'btn btn-sm btn-'.($entity->getIsEnabled() ? 'success' : 'primary')];
|
||||||
|
},
|
||||||
|
])
|
||||||
|
->setField('index', 'Type', Field\ButtonField::class, [
|
||||||
|
'property' => 'redirectCode',
|
||||||
|
'attr' => ['class' => 'col-2'],
|
||||||
|
'button_attr' => ['class' => 'btn btn-sm btn-light border-secondary font-weight-bold'],
|
||||||
|
])
|
||||||
|
->setBatchAction('index', 'enable', 'Enable', function (EntityInterface $entity, EntityManager $manager) {
|
||||||
|
$entity->setIsEnabled(true);
|
||||||
|
|
||||||
|
$manager->update($entity);
|
||||||
|
})
|
||||||
|
->setBatchAction('index', 'disable', 'Disable', function (EntityInterface $entity, EntityManager $manager) {
|
||||||
|
$entity->setIsEnabled(false);
|
||||||
|
|
||||||
|
$manager->update($entity);
|
||||||
|
})
|
||||||
|
->setBatchAction('index', 'delete', 'Delete', function (EntityInterface $entity, EntityManager $manager) {
|
||||||
|
$manager->delete($entity);
|
||||||
|
})
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSection(): string
|
||||||
|
{
|
||||||
|
return 'site_navigation';
|
||||||
|
}
|
||||||
|
}
|
211
core/Entity/Redirect.php
Normal file
211
core/Entity/Redirect.php
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core\Entity;
|
||||||
|
|
||||||
|
use App\Core\Repository\RedirectRepository;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Entity(repositoryClass=RedirectRepository::class)
|
||||||
|
*/
|
||||||
|
class Redirect implements EntityInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @ORM\Id
|
||||||
|
* @ORM\GeneratedValue
|
||||||
|
* @ORM\Column(type="integer")
|
||||||
|
*/
|
||||||
|
protected $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", length=5)
|
||||||
|
*/
|
||||||
|
protected $scheme;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", length=255)
|
||||||
|
*/
|
||||||
|
protected $domain;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", length=6)
|
||||||
|
*/
|
||||||
|
protected $domainType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", length=255)
|
||||||
|
*/
|
||||||
|
protected $rule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", length=6)
|
||||||
|
*/
|
||||||
|
protected $ruleType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", length=255)
|
||||||
|
*/
|
||||||
|
protected $location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="integer")
|
||||||
|
*/
|
||||||
|
protected $redirectCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", length=255)
|
||||||
|
*/
|
||||||
|
protected $label;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="integer", nullable=true)
|
||||||
|
*/
|
||||||
|
protected $sortOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="boolean")
|
||||||
|
*/
|
||||||
|
private $isEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="boolean")
|
||||||
|
*/
|
||||||
|
private $reuseQueryString;
|
||||||
|
|
||||||
|
public function getId(): ?int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getScheme(): ?string
|
||||||
|
{
|
||||||
|
return $this->scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setScheme(string $scheme): self
|
||||||
|
{
|
||||||
|
$this->scheme = $scheme;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDomain(): ?string
|
||||||
|
{
|
||||||
|
return $this->domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDomain(string $domain): self
|
||||||
|
{
|
||||||
|
$this->domain = $domain;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDomainType(): ?string
|
||||||
|
{
|
||||||
|
return $this->domainType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDomainType(string $domainType): self
|
||||||
|
{
|
||||||
|
$this->domainType = $domainType;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRule(): ?string
|
||||||
|
{
|
||||||
|
return $this->rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRule(string $rule): self
|
||||||
|
{
|
||||||
|
$this->rule = $rule;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRuleType(): ?string
|
||||||
|
{
|
||||||
|
return $this->ruleType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRuleType(string $ruleType): self
|
||||||
|
{
|
||||||
|
$this->ruleType = $ruleType;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLocation(): ?string
|
||||||
|
{
|
||||||
|
return $this->location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLocation(string $location): self
|
||||||
|
{
|
||||||
|
$this->location = $location;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRedirectCode(): ?int
|
||||||
|
{
|
||||||
|
return $this->redirectCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRedirectCode(int $redirectCode): self
|
||||||
|
{
|
||||||
|
$this->redirectCode = $redirectCode;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabel(): ?string
|
||||||
|
{
|
||||||
|
return $this->label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLabel(string $label): self
|
||||||
|
{
|
||||||
|
$this->label = $label;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSortOrder(): ?int
|
||||||
|
{
|
||||||
|
return $this->sortOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSortOrder(?int $sortOrder): self
|
||||||
|
{
|
||||||
|
$this->sortOrder = $sortOrder;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIsEnabled(): ?bool
|
||||||
|
{
|
||||||
|
return $this->isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIsEnabled(bool $isEnabled): self
|
||||||
|
{
|
||||||
|
$this->isEnabled = $isEnabled;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReuseQueryString(): ?bool
|
||||||
|
{
|
||||||
|
return $this->reuseQueryString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setReuseQueryString(bool $reuseQueryString): self
|
||||||
|
{
|
||||||
|
$this->reuseQueryString = $reuseQueryString;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
58
core/EventListener/RedirectListener.php
Normal file
58
core/EventListener/RedirectListener.php
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core\EventListener;
|
||||||
|
|
||||||
|
use App\Core\Repository\RedirectRepositoryQuery;
|
||||||
|
use App\Core\Router\RedirectMatcher;
|
||||||
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
|
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class RedirectListener.
|
||||||
|
*
|
||||||
|
* @author Simon Vieille <simon@deblan.fr>
|
||||||
|
*/
|
||||||
|
class RedirectListener
|
||||||
|
{
|
||||||
|
protected RedirectMatcher $matcher;
|
||||||
|
protected RedirectRepositoryQuery $repository;
|
||||||
|
|
||||||
|
public function __construct(RedirectMatcher $matcher, RedirectRepositoryQuery $repository)
|
||||||
|
{
|
||||||
|
$this->matcher = $matcher;
|
||||||
|
$this->repository = $repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onKernelException(ExceptionEvent $event)
|
||||||
|
{
|
||||||
|
$request = $event->getRequest();
|
||||||
|
|
||||||
|
if (!$event->getThrowable() instanceof NotFoundHttpException) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$redirects = $this->repository
|
||||||
|
->orderBy('.sortOrder')
|
||||||
|
->where('.isEnabled=1')
|
||||||
|
->find()
|
||||||
|
;
|
||||||
|
|
||||||
|
$uri = $event->getRequest()->getUri();
|
||||||
|
|
||||||
|
foreach ($redirects as $redirect) {
|
||||||
|
if ($this->matcher->match($redirect, $uri)) {
|
||||||
|
if ($redirect->getReuseQueryString() && count($event->getRequest()->query)) {
|
||||||
|
$query = sprintf('?%s', http_build_query($event->getRequest()->query->all()));
|
||||||
|
} else {
|
||||||
|
$query = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$event->setResponse(new RedirectResponse(
|
||||||
|
$redirect->getLocation().$query,
|
||||||
|
$redirect->getRedirectCode()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
core/Factory/RedirectFactory.php
Normal file
14
core/Factory/RedirectFactory.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core\Factory;
|
||||||
|
|
||||||
|
use App\Core\Factory\FactoryInterface;
|
||||||
|
use App\Core\Entity\Redirect as Entity;
|
||||||
|
|
||||||
|
class RedirectFactory implements FactoryInterface
|
||||||
|
{
|
||||||
|
public function create(): Entity
|
||||||
|
{
|
||||||
|
return new Entity();
|
||||||
|
}
|
||||||
|
}
|
146
core/Form/Filter/RedirectFilterType.php
Normal file
146
core/Form/Filter/RedirectFilterType.php
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core\Form\Filter;
|
||||||
|
|
||||||
|
use App\Core\Entity\Redirect;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
|
||||||
|
class RedirectFilterType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
{
|
||||||
|
$builder->add(
|
||||||
|
'label',
|
||||||
|
TextType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Label',
|
||||||
|
'required' => false,
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'scheme',
|
||||||
|
ChoiceType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Scheme',
|
||||||
|
'required' => false,
|
||||||
|
'choices' => [
|
||||||
|
'http(s)://' => 'all',
|
||||||
|
'http://' => 'http',
|
||||||
|
'https://' => 'https',
|
||||||
|
],
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'domain',
|
||||||
|
TextType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Domain',
|
||||||
|
'required' => false,
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'domainType',
|
||||||
|
ChoiceType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Type',
|
||||||
|
'required' => false,
|
||||||
|
'choices' => [
|
||||||
|
'Domain' => 'domain',
|
||||||
|
'Regular expression' => 'regexp',
|
||||||
|
],
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'rule',
|
||||||
|
TextType::class,
|
||||||
|
[
|
||||||
|
'label' => '',
|
||||||
|
'required' => false,
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'ruleType',
|
||||||
|
ChoiceType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Type',
|
||||||
|
'required' => false,
|
||||||
|
'choices' => [
|
||||||
|
'Path' => 'path',
|
||||||
|
'Regular expression' => 'regexp',
|
||||||
|
],
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'location',
|
||||||
|
TextType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Location',
|
||||||
|
'required' => false,
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'redirectCode',
|
||||||
|
ChoiceType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Code',
|
||||||
|
'required' => false,
|
||||||
|
'choices' => [
|
||||||
|
'301 - Moved Permanently' => 301,
|
||||||
|
'307 - Temporary Redirect' => 307,
|
||||||
|
],
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => null,
|
||||||
|
'csrf_protection' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
182
core/Form/RedirectType.php
Normal file
182
core/Form/RedirectType.php
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core\Form;
|
||||||
|
|
||||||
|
use App\Core\Entity\Redirect;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||||
|
|
||||||
|
class RedirectType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
{
|
||||||
|
$builder->add(
|
||||||
|
'label',
|
||||||
|
TextType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Label',
|
||||||
|
'required' => true,
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'scheme',
|
||||||
|
ChoiceType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Scheme',
|
||||||
|
'required' => true,
|
||||||
|
'choices' => [
|
||||||
|
'http(s)://' => 'all',
|
||||||
|
'http://' => 'http',
|
||||||
|
'https://' => 'https',
|
||||||
|
],
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'domain',
|
||||||
|
TextType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Domain',
|
||||||
|
'required' => true,
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'help' => 'Regular expression: do not add the delimiter',
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'domainType',
|
||||||
|
ChoiceType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Type',
|
||||||
|
'required' => true,
|
||||||
|
'choices' => [
|
||||||
|
'Domain' => 'domain',
|
||||||
|
'Regular expression' => 'regexp',
|
||||||
|
],
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'rule',
|
||||||
|
TextType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Rule',
|
||||||
|
'required' => true,
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'help' => 'Regular expression: do not add the delimiter',
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'ruleType',
|
||||||
|
ChoiceType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Type',
|
||||||
|
'required' => true,
|
||||||
|
'choices' => [
|
||||||
|
'Path' => 'path',
|
||||||
|
'Regular expression' => 'regexp',
|
||||||
|
],
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'location',
|
||||||
|
TextType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Location',
|
||||||
|
'required' => true,
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'redirectCode',
|
||||||
|
ChoiceType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Code',
|
||||||
|
'required' => true,
|
||||||
|
'choices' => [
|
||||||
|
'301 - Moved Permanently' => 301,
|
||||||
|
'307 - Temporary Redirect' => 307,
|
||||||
|
],
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'isEnabled',
|
||||||
|
CheckboxType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Enabled',
|
||||||
|
'required' => false,
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'reuseQueryString',
|
||||||
|
CheckboxType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Reuse the query string',
|
||||||
|
'required' => false,
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => Redirect::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
21
core/Repository/RedirectRepository.php
Normal file
21
core/Repository/RedirectRepository.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core\Repository;
|
||||||
|
|
||||||
|
use App\Core\Entity\Redirect;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method Redirect|null find($id, $lockMode = null, $lockVersion = null)
|
||||||
|
* @method Redirect|null findOneBy(array $criteria, array $orderBy = null)
|
||||||
|
* @method Redirect[] findAll()
|
||||||
|
* @method Redirect[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||||
|
*/
|
||||||
|
class RedirectRepository extends ServiceEntityRepository
|
||||||
|
{
|
||||||
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
{
|
||||||
|
parent::__construct($registry, Redirect::class);
|
||||||
|
}
|
||||||
|
}
|
15
core/Repository/RedirectRepositoryQuery.php
Normal file
15
core/Repository/RedirectRepositoryQuery.php
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core\Repository;
|
||||||
|
|
||||||
|
use App\Core\Repository\RepositoryQuery;
|
||||||
|
use Knp\Component\Pager\PaginatorInterface;
|
||||||
|
use App\Core\Repository\RedirectRepository as Repository;
|
||||||
|
|
||||||
|
class RedirectRepositoryQuery extends RepositoryQuery
|
||||||
|
{
|
||||||
|
public function __construct(Repository $repository, PaginatorInterface $paginator)
|
||||||
|
{
|
||||||
|
parent::__construct($repository, 'r', $paginator);
|
||||||
|
}
|
||||||
|
}
|
45
core/Resources/views/redirect/redirect_admin/_form.html.twig
Normal file
45
core/Resources/views/redirect/redirect_admin/_form.html.twig
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 p-2">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
{{ form_row(form.label) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12">
|
||||||
|
{{ form_row(form.isEnabled) }}
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3">
|
||||||
|
{{ form_row(form.scheme) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3">
|
||||||
|
{{ form_row(form.domainType) }}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-5 pl-md-3">
|
||||||
|
{{ form_row(form.domain) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3">
|
||||||
|
{{ form_row(form.ruleType) }}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-5 pl-md-3">
|
||||||
|
{{ form_row(form.rule) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3">
|
||||||
|
{{ form_row(form.redirectCode) }}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-5 pl-md-3">
|
||||||
|
{{ form_row(form.location) }}
|
||||||
|
{{ form_row(form.reuseQueryString) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,23 @@
|
||||||
|
<div class="btn-group">
|
||||||
|
<span class="btn btn-sm btn-dark border-secondary disabled">
|
||||||
|
{%- if entity.scheme == 'all' -%}
|
||||||
|
http(s)
|
||||||
|
{%- else -%}
|
||||||
|
{{ entity.scheme }}
|
||||||
|
{%- endif -%}://
|
||||||
|
</span>
|
||||||
|
<span class="btn btn-sm btn-info border-secondary disabled">
|
||||||
|
{{ entity.domain }}
|
||||||
|
</span>
|
||||||
|
<span class="btn btn-sm btn-success border-secondary disabled">
|
||||||
|
{{ entity.rule }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
→
|
||||||
|
|
||||||
|
<div class="btn-group">
|
||||||
|
<span class="btn btn-sm btn-warning border-secondary disabled">
|
||||||
|
{{ entity.location }}
|
||||||
|
</span>
|
||||||
|
</div>
|
64
core/Router/RedirectMatcher.php
Normal file
64
core/Router/RedirectMatcher.php
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core\Router;
|
||||||
|
|
||||||
|
use App\Core\Entity\Redirect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class RedirectMatcher.
|
||||||
|
*
|
||||||
|
* @author Simon Vieille <simon@deblan.fr>
|
||||||
|
*/
|
||||||
|
class RedirectMatcher
|
||||||
|
{
|
||||||
|
public function match(Redirect $redirect, $url): bool
|
||||||
|
{
|
||||||
|
$data = $this->parse($url);
|
||||||
|
|
||||||
|
if (!$this->matchSchema($redirect->getScheme(), $data['scheme'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->matchDomain($redirect->getDomain(), $redirect->getDomainType(), $data['host'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->matchRule($redirect->getRule(), $redirect->getRuleType(), $data['path'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function matchSchema(string $redirectScheme, $scheme): bool
|
||||||
|
{
|
||||||
|
if ('all' === $redirectScheme) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $redirectScheme === $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function matchDomain(string $domain, string $type, string $host): bool
|
||||||
|
{
|
||||||
|
if ('domain' === $type) {
|
||||||
|
return $domain === $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
return preg_match('`'.$domain.'`', $host) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function matchRule(string $rule, string $type, string $path): bool
|
||||||
|
{
|
||||||
|
if ('path' === $type) {
|
||||||
|
return $rule === $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return preg_match('`'.$rule.'`', $path) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function parse($url): array
|
||||||
|
{
|
||||||
|
return parse_url($url);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue