Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions doc/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,13 @@ twig:
`exit_impersonation_path` manages the url to which you will be redirected after exiting impersonation (default: `homepage`).

![](img/exit_impersonation_button.png)

`ignore_referer_routes` allow you to configure routes that will not be saved in the user's navigation session.

The value is an array of regexes matching the routes

```yaml
ignore_referer_routes:
- /\/admin\/[0-9]+/
- /\/login
```
10 changes: 6 additions & 4 deletions src/Brick/FormBrick/FormFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use Lle\CruditBundle\Brick\BrickResponse\RedirectBrickResponse;
use Lle\CruditBundle\Brick\BrickResponseCollector;
use Lle\CruditBundle\Contracts\BrickConfigInterface;
use Lle\CruditBundle\Contracts\CrudConfigInterface;
use Lle\CruditBundle\Dto\BrickView;
use Lle\CruditBundle\Exception\CruditException;
use Lle\CruditBundle\Resolver\ResourceResolver;
Expand Down Expand Up @@ -74,6 +73,7 @@ public function buildView(BrickConfigInterface $brickConfigurator): BrickView
'resource' => $resource,
'options' => $brickConfigurator->getOptions(),
'cancel_path' => $brickConfigurator->getCancelPath(),
'default_path' => $brickConfigurator->getCrudConfig()->getPath(),
'referer' => $referer,
]);

Expand Down Expand Up @@ -172,6 +172,8 @@ private function addFlash(string $type, string $message): void

private function getRedirectPath(FormConfig $brickConfig, mixed $resource): string
{
$cruditReferers = json_decode($this->getRequest()->getSession()->get('lle_crudit_referers'), true);

if ($successRedirectPath = $brickConfig->getSuccessRedirectPath()) {
return $this->urlGenerator->generate(
$successRedirectPath->getRoute(),
Expand All @@ -188,11 +190,11 @@ private function getRedirectPath(FormConfig $brickConfig, mixed $resource): stri
$afterEditPath->getParams()
)
);
} elseif ($referer = (string)$this->getRequest()->request->get('referer')) {
return $referer;
} elseif (count($cruditReferers)) {
return end($cruditReferers);
} else {
return $this->urlGenerator->generate(
$brickConfig->getCrudConfig()->getPath(CrudConfigInterface::INDEX)->getRoute()
$brickConfig->getCrudConfig()->getPath()->getRoute()
);
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/Contracts/CrudConfigInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ interface CrudConfigInterface
public const ACTION_DELETE = 'delete';
public const ACTION_EXPORT = 'export';

public const LABEL_ACTION_LIST = 'action.list';

public const AVERAGE = 'AVG';
public const COUNT = 'COUNT';
public const SUM = 'SUM';
Expand Down
25 changes: 13 additions & 12 deletions src/Crud/AbstractCrudConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,14 @@ public function getListActions(): array
{
$actions = [];

/**
* Create new resource action
*/
$actions[CrudConfigInterface::LABEL_ACTION_LIST] = ListAction::new(
'action.list',
$this->getPath('homepage'),
Icon::new('arrow-circle-left')
)
->setCssClass('btn btn-sm btn-secondary mt-2 ms-1 mt-md-0 crudit-action')
->setTemplate('@LleCrudit/brick/links/actions/_list.html.twig');

$actions[CrudConfigInterface::ACTION_ADD] = ListAction::new(
'action.add',
$this->getPath(CrudConfigInterface::NEW),
Expand All @@ -92,9 +97,6 @@ public function getListActions(): array
->setCssClass('btn btn-sm btn-primary mt-2 ms-1 mt-md-0 crudit-action')
->setRole(sprintf('ROLE_%s_%s', $this->getName(), CrudConfigInterface::NEW));

/**
* Export filtered list action
*/
$actions[CrudConfigInterface::ACTION_EXPORT] = ListAction::new(
'action.export',
$this->getPath(CrudConfigInterface::EXPORT),
Expand Down Expand Up @@ -148,12 +150,13 @@ public function getShowActions(): array
{
$actions = [];

$actions[CrudConfigInterface::ACTION_LIST] = ItemAction::new(
$actions[CrudConfigInterface::LABEL_ACTION_LIST] = ItemAction::new(
'action.list',
$this->getPath(),
Icon::new('list')
Icon::new('arrow-circle-left')
)
->setCssClass('btn btn-secondary btn-sm ms-1 crudit-action')
->setTemplate('@LleCrudit/brick/links/actions/_list.html.twig')
->setRole(sprintf('ROLE_%s_%s', $this->getName(), CrudConfigInterface::INDEX));

$actions[CrudConfigInterface::ACTION_EDIT] = EditAction::new(
Expand Down Expand Up @@ -273,14 +276,12 @@ public function getBrickConfigs(): array
CrudConfigInterface::EDIT => [
LinksConfig::new(['title' => $this->getTitle('edit')]),
FormConfig::new()
->setForm($this->getFormType(CrudConfigInterface::EDIT))
->setCancelPath($this->getPath()),
->setForm($this->getFormType(CrudConfigInterface::EDIT)),
],
CrudConfigInterface::NEW => [
LinksConfig::new(['title' => $this->getTitle('new')]),
FormConfig::new()
->setForm($this->getFormType(CrudConfigInterface::NEW))
->setCancelPath($this->getPath()),
->setForm($this->getFormType(CrudConfigInterface::NEW)),
],
];
}
Expand Down
5 changes: 5 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ public function getConfigTreeBuilder(): TreeBuilder
])
->thenInvalid('Invalid value %s')
->end()
->end()
->arrayNode('ignore_referer_routes')
->acceptAndWrap(['string'])
->scalarPrototype()
->end()
->end();

return $treeBuilder;
Expand Down
1 change: 1 addition & 0 deletions src/DependencyInjection/LleCruditExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public function load(array $configs, ContainerBuilder $container): void
$container->setParameter('lle_crudit.add_exit_impersonation_button', $processedConfig['add_exit_impersonation_button']);
$container->setParameter('lle_crudit.exit_impersonation_path', $processedConfig['exit_impersonation_path']);
$container->setParameter('lle_crudit.generate_default_role', $processedConfig['generate_default_role']);
$container->setParameter('lle_crudit.ignore_referer_routes', $processedConfig['ignore_referer_routes']);

// Load the templates for the Crudit form types
if ($container->hasParameter('twig.form.resources')) {
Expand Down
61 changes: 61 additions & 0 deletions src/EventListener/KernelRequestListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

namespace Lle\CruditBundle\EventListener;

use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;

// This listener allows you to manage your browsing history.
class KernelRequestListener
{
public function __construct(
protected ParameterBagInterface $parameterBag,
) {
}

public function onKernelRequest(RequestEvent $event): void
{
// Ignore AJAX calls and referrers not related to documents
if (
$event->getRequest()->isXmlHttpRequest()
|| $event->getRequest()->attributes->get('_stateless') === true
|| $event->getRequest()->headers->get('sec-fetch-dest') !== 'document'
) {
return;
}

$referer = $event->getRequest()->headers->get('referer');
$requestUri = $event->getRequest()->getUri();
$requestMethod = $event->getRequest()->getMethod();

// Ignore routes defined in the configuration
/** @var array $ignoredRoutes */
$ignoredRoutes = $this->parameterBag->get('lle_crudit.ignore_referer_routes');
if (array_any($ignoredRoutes, fn($ignoredRoute) => preg_match($ignoredRoute, $referer))) {
return;
}

$session = $event->getRequest()->getSession();
$cruditReferer = json_decode($session->get('lle_crudit_referers'), true);

// Remove the last referer if it's the current one
if ($cruditReferer && end($cruditReferer) === $requestUri) {
array_pop($cruditReferer);

$session->set('lle_crudit_referers', json_encode($cruditReferer));
} elseif (
$requestMethod !== 'POST'
&& (!$cruditReferer || end($cruditReferer) !== $referer)
&& $referer !== $requestUri
) {
$cruditReferer[] = $referer;

// Keep only the last 20 referers
while (count($cruditReferer) > 20) {
array_shift($cruditReferer);
}

$session->set('lle_crudit_referers', json_encode($cruditReferer));
}
}
}
5 changes: 5 additions & 0 deletions src/Resources/config/listeners.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@ services:
arguments: ['@Lle\CruditBundle\Filter\FilterState']
tags:
- { name: kernel.event_listener, event: kernel.controller }

Lle\CruditBundle\EventListener\KernelRequestListener:
arguments: ['@parameter_bag']
tags:
- { name: kernel.event_listener, event: kernel.request }
13 changes: 10 additions & 3 deletions src/Resources/views/brick/form/index.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,18 @@

{% block form_buttons %}
<div class="d-flex">
{% set cruditReferers = app.session.get('lle_crudit_referers')|json_decode %}

{% if view.data.cancel_path %}
<a class="btn btn-danger" href="{{ path(view.data.cancel_path.route, view.data.cancel_path.params) }}">
{{ 'crudit.action.cancel'|trans({}, 'LleCruditBundle') }}
</a>
{% set cancelHref = path(view.data.cancel_path.route, view.data.cancel_path.params) %}
{% elseif cruditReferers|length %}
{% set cancelHref = cruditReferers|last %}
{% else %}
{% set cancelHref = path(view.data.default_path.route) %}
{% endif %}
<a class="btn btn-danger" href="{{ cancelHref }}">
{{ 'crudit.action.cancel'|trans({}, 'LleCruditBundle') }}
</a>
<button class="ms-auto btn btn-primary" type="submit">
{{ 'crudit.action.save'|trans({}, 'LleCruditBundle') }}
</button>
Expand Down
12 changes: 8 additions & 4 deletions src/Resources/views/brick/links/actions/_actions.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@
{% include action.modal with {action: action, id: 'modal_' ~ action.id, params: params} %}
{% endif %}

{% set disabled = action.disabled or not
((action.path.role is null or is_granted(action.path.role, view.data.resource))
and (action.role is null or is_granted(action.role, view.data.resource)))
%}
{% if action.label === constant('Lle\\CruditBundle\\Contracts\\CrudConfigInterface::LABEL_ACTION_LIST') %}
{% set disabled = action.disabled %}
{% else %}
{% set disabled = action.disabled or not
((action.path.role is null or is_granted(action.path.role, view.data.resource))
and (action.role is null or is_granted(action.role, view.data.resource)))
%}
{% endif %}

{% set isDisplayed = false %}
{% if action.resource is defined %}
Expand Down
45 changes: 45 additions & 0 deletions src/Resources/views/brick/links/actions/_list.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{% block list_action %}
{% set cruditReferers = app.session.get('lle_crudit_referers')|json_decode %}
{% if not cruditReferers|length %}
{% set disabled = disabled or not
((action.path.role is null or is_granted(action.path.role, view.data.resource))
and (action.role is null or is_granted(action.role, view.data.resource)))
%}
{% endif %}

<a
{% block attributes %}
rel="noreferrer"
class="
{{ action.cssClass ?? 'btn btn-sm btn-primary mt-2 ms-1 mt-md-0 crudit-action' }}
{% if disabled %} disabled{% endif %}
"
{% if action.modal %}
data-bs-toggle="modal"
data-bs-target="#modal_{{ action.id }}"
{% elseif action.confirmModal %}
data-bs-toggle="modal"
data-bs-target="#modal-confirm"
data-confirm-link="{{ path(action.path.route, params) }}"
data-confirm-text="{{ ('modal.confirm.' ~ action.label)|trans(domain=view.config.translation_domain) }}"
{% elseif not disabled %}
{% if cruditReferers|length %}
href="{{ cruditReferers|last }}"
{% else %}
href="{{ action.url ?? path(action.path.route, params) }}"
{% endif %}
{% endif %}

{% if action.target is defined and action.target %}
target="{{ action.target }}"
{% endif %}
{% endblock %}
>
{% if action.icon %}
{% block icon %}<i class="{{ action.icon.cssClass }}"></i>{% endblock %}
{% endif %}
{% if not action.hideLabel %}
{% block label %}{{ action.label|trans(domain=view.config.translation_domain) }}{% endblock %}
{% endif %}
</a>
{% endblock %}
Loading