Compare commits

...

106 commits

Author SHA1 Message Date
Simon Vieille 26cbaa8469 Merge branch 'develop' 2024-05-03 17:58:22 +02:00
Simon Vieille 7897bafcc2
fix undefined window.tinymce.murph 2024-05-03 17:57:43 +02:00
Simon Vieille 63a8a60e2d Merge branch 'develop' 2024-03-31 16:57:56 +02:00
Simon Vieille d01e9d618b
add border color on tinymce editor 2024-03-31 16:57:46 +02:00
Simon Vieille 96347a1730 Merge branch 'develop' 2024-03-31 16:50:47 +02:00
Simon Vieille aeb0e6c109 allow to use window.tinymceModes to add or override tinymce modes 2024-03-31 16:50:18 +02:00
Simon Vieille 64258a2d8c Merge branch 'develop' 2024-03-25 15:27:12 +01:00
Simon Vieille 70329ceeda
fix template of CrudController (maker) 2024-03-25 15:27:06 +01:00
Simon Vieille 1adb1ebe2b Merge branch 'develop' 2024-03-25 13:04:17 +01:00
Simon Vieille f57cc8e4d6
fix hidden save button in file manager 2024-03-25 13:04:16 +01:00
Simon Vieille b21967028e Merge branch 'develop' 2024-03-25 13:03:01 +01:00
Simon Vieille 5b22851674
fix hidden save button in file manager 2024-03-25 13:02:20 +01:00
Simon Vieille 4082bb171a
fix use of IsGranted is CrudController 2024-02-04 16:48:44 +01:00
Simon Vieille dc19617fb1 Merge branch 'develop' 2024-02-04 16:33:34 +01:00
Simon Vieille dbd2036fb0
rollback commit 614ae40 (add IsGranted in all methods of the CrudController maker template) 2024-02-04 16:33:30 +01:00
Simon Vieille 8e2566abc8
rollback commit 614ae40 (add IsGranted in all methods of the CrudController maker template) 2024-02-04 16:33:22 +01:00
Simon Vieille 66e1a9c87f
rollback commit 614ae40 (add IsGranted in all methods of the CrudController maker template) 2024-02-04 16:31:21 +01:00
Simon Vieille 6709c0a303
update changelog 2024-02-04 16:17:49 +01:00
Simon Vieille 614ae40901 add IsGranted in all methods of the CrudController maker template 2024-02-04 16:16:22 +01:00
Simon Vieille 430bff9433
fix default crud sort 2024-02-02 20:04:19 +01:00
Simon Vieille 6441da8a27
fix default crud sort 2024-02-02 20:03:57 +01:00
Simon Vieille 801e3317e7
update changelog 2024-02-02 20:02:00 +01:00
Simon Vieille f7604d2a45
fix default crud sort 2024-02-02 20:01:23 +01:00
Simon Vieille 8a632a1b14
update Murph version constant 2024-02-01 18:46:33 +01:00
Simon Vieille 4701090134
update changelog 2024-01-27 15:49:58 +01:00
Simon Vieille c40c7e3362
fix type casting in slugifier 2024-01-27 15:49:39 +01:00
Simon Vieille 8edbf0cc08
add default default on node's code when slugify 2023-11-27 10:32:38 +01:00
Simon Vieille 175321bc2d
fix issue on file manager when a file is selected in the file in the file picker 2023-11-16 23:06:00 +01:00
Simon Vieille 5d6531d197
fix sidebar scroll algo 2023-11-14 23:21:24 +01:00
Simon Vieille 90603f62e0
add side bar scroll animation 2023-11-14 23:08:09 +01:00
Simon Vieille 053f4aa5b8
add auto-scroll on current sidebar item 2023-11-14 23:03:43 +01:00
Simon Vieille 79754d45c1
add auto-scroll on current sidebar item
remove jquery from sidebar module
2023-11-14 22:51:40 +01:00
Simon Vieille c98ea50f30
fix undefined pager on index 2023-11-13 16:06:06 +01:00
Simon Vieille 0f1bc761b2
add no-wrap around the thead sort link 2023-11-10 19:42:45 +01:00
Simon Vieille 50dbb07314
update changelog 2023-11-10 19:35:48 +01:00
Simon Vieille 72e783f865
fix render of the URL when the window is small 2023-11-10 19:35:39 +01:00
Simon Vieille ee28c9abb7
copy the pager in the bottom of the index table
remove the with class of the action column
2023-11-10 19:28:20 +01:00
Simon Vieille 2bd6836a7f
fix issue on file manager when a file is selected in the file in the file picker 2023-11-09 17:19:09 +01:00
Simon Vieille 6f961ba79b
release v1.23.0 2023-11-01 16:31:33 +01:00
Simon Vieille e095fc4197
update changelog 2023-10-27 17:03:08 +02:00
Simon Vieille 6736f94eea
set searchFields option on jschoice manager 2023-10-27 17:02:15 +02:00
Simon Vieille 93a1e7811d
change colors on js-choices element 2023-10-25 20:15:21 +02:00
Simon Vieille a0027c0b69 Merge branch 'feature/theming' into develop 2023-10-25 19:59:32 +02:00
Simon Vieille 498c71081d
add red * on required label 2023-10-25 19:59:28 +02:00
Simon Vieille 8713b401f9
rollback modal changes 2023-10-25 19:36:10 +02:00
Simon Vieille 1463f43298
update changelog 2023-10-25 19:22:56 +02:00
Simon Vieille b89e036c49 change border colors of inputs when focused 2023-10-25 19:22:44 +02:00
Simon Vieille 1d0b657c83
add sass classes to mange with of elements
fix the aspect of the actions's column in the crud

add background in the modal header

change vavigation pills colors
2023-10-25 19:12:02 +02:00
Simon Vieille 0cadf28738
update changelog 2023-10-20 09:46:57 +02:00
Simon Vieille ede8d4fdcb
remove unsed twig in mail notifier 2023-10-20 09:45:58 +02:00
Simon Vieille 5c3f2ab1e7
refactor services using constructor property promotions 2023-10-20 09:44:18 +02:00
Simon Vieille c1eb277a6a
add 'Length' constraint in forms 2023-10-19 20:51:23 +02:00
Simon Vieille d3f27d97ad
apply php linter 2023-10-12 16:15:07 +02:00
Simon Vieille 5e392d469a
update changelog 2023-10-12 16:04:49 +02:00
Simon Vieille 67f79083ef
change params given to the callback of a global batch action (page removed, add selected items) 2023-10-12 16:04:43 +02:00
Simon Vieille b9b07c1409
fix issue with CrudConfiguration::setGlobalBatchAction 2023-10-12 16:04:03 +02:00
Simon Vieille 521ed5ce64 Merge branch 'feature/batch' into develop 2023-10-12 15:28:17 +02:00
Simon Vieille dda43ef3cc
change CrudController::doBatch to manage a global batch action 2023-10-12 15:28:14 +02:00
Simon Vieille c65cc26be8
batch form is not submitted with XHR when it's a global action 2023-10-12 15:27:36 +02:00
Simon Vieille 2f884df602
add CrudConfiguration::setGlobalBatchAction method 2023-10-12 15:25:52 +02:00
Simon Vieille 8979fc5beb
fix test in RepositoryQuery::addForcedFilterHandler 2023-10-10 10:18:25 +02:00
Simon Vieille bd663838f6
allow to define templates show before and after a murph collection item 2023-10-06 12:46:51 +02:00
Simon Vieille 177b23365b
update changelog 2023-10-04 13:55:08 +02:00
Simon Vieille 645ae700d4
remove parameter $option on CrudConfiguration::setForm
fix CrudController make template
2023-10-04 13:54:37 +02:00
Simon Vieille 7614c24012
fix regression on crud sorting 2023-10-04 13:47:01 +02:00
Simon Vieille 1f2edf183b
release v1.22.0 2023-09-28 18:14:49 +02:00
Simon Vieille 28a4f63640
fix compatibility of TranslatableEntityManager with the entity manager 2023-09-28 10:32:49 +02:00
Simon Vieille 7fceefa6d3
add flush option in the entity manager on create, update, remove, and persist methods 2023-09-28 10:31:00 +02:00
Simon Vieille a08c62229d
update changelog 2023-09-27 15:17:54 +02:00
Simon Vieille f97f1dfedf
add crud sorting in the session 2023-09-27 15:17:35 +02:00
Simon Vieille 45b3f6bb80
update changelog 2023-09-27 10:22:29 +02:00
Simon Vieille 21ee41ff29
add count method on repository query
add addForcedFilterHandler method
2023-09-27 10:21:49 +02:00
Simon Vieille 8d5de79192
add 'inline_form_validation' option to validate inline forms with custom algo 2023-09-26 16:12:23 +02:00
Simon Vieille 212afe2775
fix href_attr allowed types 2023-09-15 15:27:56 +02:00
Simon Vieille 16dd0d5744
fix session name in inline form controller 2023-09-14 16:57:39 +02:00
Simon Vieille 9957523c59
add context variable into admin controller's views 2023-09-09 13:48:03 +02:00
Simon Vieille 688f66e94e
update default class on toggle display 2023-08-31 16:20:07 +02:00
Simon Vieille c9b997e75d add new options in BooleanField: toggle|checkbox_class_when_true and toggle|checkbox_class_when_false 2023-08-31 16:12:57 +02:00
Simon Vieille 374db9117f
release v1.21.1 2023-08-17 20:24:12 +02:00
Simon Vieille 4048152a8e
update changelog 2023-08-17 20:16:04 +02:00
Simon Vieille 4385e7a525
add form error handle in settings actions 2023-08-17 20:15:02 +02:00
Simon Vieille 5d3999f766
update translations 2023-08-17 08:59:06 +02:00
Simon Vieille 03c0d6cfd2
fix modal hiding when a file is successfuly uploaded in the file manager on tinymce 2023-08-16 10:08:02 +02:00
Simon Vieille 290a4750bc Revert "remove sensio/framework-extra-bundle"
This reverts commit 7d647d3bb4.
2023-08-14 14:32:34 +02:00
Simon Vieille 7d647d3bb4
remove sensio/framework-extra-bundle 2023-08-14 14:29:11 +02:00
Simon Vieille d637c44e5c
replace the navigation badge with a fontawesome square 2023-08-14 12:47:33 +02:00
Simon Vieille 19d26e6bd0
fix modal hiding when a file is successfuly uploaded in the file manager 2023-08-14 11:39:53 +02:00
Simon Vieille 109584d933
update changelog 2023-08-13 22:38:42 +02:00
Simon Vieille c3209c68dd
fix modal hiding when a file is successfuly uploaded in the file manager 2023-08-13 22:38:10 +02:00
Simon Vieille 4a5b67dd93
update changelog 2023-08-13 22:12:44 +02:00
Simon Vieille 4573a8d31e
fix tinymce reload when modal is closed and reopened 2023-08-13 22:11:44 +02:00
Simon Vieille 405909a4e7
remove last request data after filling the form 2023-08-11 19:28:09 +02:00
Simon Vieille 2b3e2027c9
update changelog 2023-08-11 19:20:43 +02:00
Simon Vieille 76b25f3bca
add form error handle in inline edit action
refill the form using last request
2023-08-11 19:18:11 +02:00
Simon Vieille ca25210d1c
release v1.21.0 2023-08-11 09:47:46 +02:00
Simon Vieille 0ab960ed0a
allow to use array syntax in string builder filter 2023-08-08 14:18:10 +02:00
Simon Vieille 8e0a7f178b
fix block in BooleanField template 2023-08-08 12:15:01 +02:00
Simon Vieille 200dd0b8d6
add the option 'display' on BooleanField
add displays toggle and checkbox for BooleanField
2023-08-08 12:13:28 +02:00
Simon Vieille bd4338bb2d
fix getColor return type 2023-08-07 19:28:51 +02:00
Simon Vieille 64b524b04e
update changelog 2023-08-07 19:20:44 +02:00
Simon Vieille 6a5c5d899f
add color property in Navigation
add badge with navigation color in admin views
2023-08-07 19:20:14 +02:00
Simon Vieille b38fe0fe00
update changelog 2023-08-07 18:39:33 +02:00
Simon Vieille d21ab30ebe
add default_value option in crud fields 2023-08-07 18:31:22 +02:00
Simon Vieille a96a6377d5
add associated nodes in page form 2023-08-07 18:24:31 +02:00
Simon Vieille 946a421900
add default value for $route param in doDelete method 2023-08-04 11:08:56 +02:00
Simon Vieille f7eb5b0b49
fix routes in the global settings controller 2023-07-28 10:43:21 +02:00
129 changed files with 1133 additions and 754 deletions

View file

@ -1,5 +1,73 @@
## [Unreleased]
### Added
* allow to use `window.tinymceModes` to add or override tinymce modes
* add border color on tinymce editor
### Fixed
* fix default crud sort
* fix hidden save button in file manager
* fix template of CrudController (maker)
* fix undefined `window.tinymce.murph`
## [v1.24.1] - 2024-02-01
### Fixed
* update Murph version constant
## [v1.24.0] - 2024-01-27
### Added
* add CSS class `no-wrap`
* copy the pager of the CRUD at the bottom of the list
### Fixed
* fix an issue with the file manager when editing an item opened in a modal
* fix type casting in slugifier
## [v1.23.0] - 2023-11-01
### Added
* allow to define templates show before and after a murph collection item
* add global batch actions
* add constraint `Length` in forms
* add sass classes to mange with of elements
* set searchFields option on jschoice manager (search on labels)
### Changed
* refactor services using constructor property promotions
* remove twig in the mail notifier service
* change pills colors
* change border colors of inputs when focused
* change colors on js-choices element
### Fixed
* fix regression on crud sorting
* fix test in RepositoryQuery::addForcedFilterHandler
* remove parameter $option on CrudConfiguration::setForm and fix CrudController make template
* fix the aspect of the actions's column in the crud
## [v1.22.0] - 2023-09-28
### Added
* add new options in BooleanField: `toggle|checkbox_class_when_true` and `toggle|checkbox_class_when_false`
* add `count` method in repository query
* add `addForcedFilterHandler` method in repository query
* add `inline_form_validation` option to validate inline forms with custom algo
* add crud sorting parameters in the session
* add flush option in the entity manager on create, update, remove, and persist methods
## [1.21.1] - 2023-08-17
### Added
* add form error handle in inline edit action and refill the form using the previous request content
* add form error handle in ssettings actions and refill the form using the previous request content
### Fixed
* fix tinymce reload when modal is closed and reopened
* fix modal hiding when a file is successfuly uploaded in the file manager
## [1.21.0] - 2023-08-11
### Added
* allow to use array syntax in string builder filter
* add color property in Navigation
* add badge with navigation color in admin views
* add `default_value` option in crud fields
* add `display` option in BooleanField
* add associated nodes in page form
### Fixed
* fix routes in the global settings controller
## [1.20.0] - 2023-07-27
### Added
* enable double click on cruds

View file

@ -10,14 +10,12 @@ namespace App\Core\Ab;
class AbTest implements AbTestInterface
{
protected $results;
protected string $name;
protected array $variations = [];
protected array $probabilities = [];
protected int $duration = 3600 * 24;
public function __construct(string $name)
public function __construct(protected string $name)
{
$this->name = $name;
}
public function getName(): string

View file

@ -13,18 +13,16 @@ use App\Core\Repository\Analytic\ViewRepositoryQuery;
*/
class DateRangeAnalytic
{
protected ViewRepositoryQuery $viewQuery;
protected RefererRepositoryQuery $refererQuery;
protected ?Node $node;
protected ?\DateTime $from;
protected ?\DateTime $to;
protected bool $reload = true;
protected array $cache = [];
public function __construct(ViewRepositoryQuery $viewQuery, RefererRepositoryQuery $refererQuery)
{
$this->viewQuery = $viewQuery;
$this->refererQuery = $refererQuery;
public function __construct(
protected ViewRepositoryQuery $viewQuery,
protected RefererRepositoryQuery $refererQuery
) {
}
public function getViews(): array
@ -83,7 +81,7 @@ class DateRangeAnalytic
$datas[$index]['mobileViews'] += $entity->getMobileViews();
}
uasort($datas, function($a, $b) {
uasort($datas, function ($a, $b) {
if ($a['views'] > $b['views']) {
return -1;
}
@ -130,7 +128,7 @@ class DateRangeAnalytic
$datas[$index]['uris'][$path] += $entity->getViews();
}
uasort($datas, function($a, $b) {
uasort($datas, function ($a, $b) {
if ($a['views'] > $b['views']) {
return -1;
}

View file

@ -10,16 +10,10 @@ namespace App\Core\Annotation;
#[\Attribute]
class UrlGenerator
{
public string $service;
public string $method;
public array $options = [];
public function __construct(string $service, string $method, array $options = [])
{
$this->service = $service;
$this->method = $method;
$this->options = $options;
public function __construct(
public string $service,
public string $method,
public array $options = []
) {
}
}

View file

@ -23,20 +23,12 @@ class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
use TargetPathTrait;
private EntityManagerInterface $entityManager;
private UrlGeneratorInterface $urlGenerator;
private CsrfTokenManagerInterface $csrfTokenManager;
private UserPasswordEncoderInterface $passwordEncoder;
public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
{
$this->entityManager = $entityManager;
$this->urlGenerator = $urlGenerator;
$this->csrfTokenManager = $csrfTokenManager;
$this->passwordEncoder = $passwordEncoder;
public function __construct(
private EntityManagerInterface $entityManager,
private UrlGeneratorInterface $urlGenerator,
private CsrfTokenManagerInterface $csrfTokenManager,
private UserPasswordEncoderInterface $passwordEncoder
) {
}
public function supports(Request $request)

View file

@ -12,8 +12,8 @@
namespace App\Core\Bundle;
use App\Core\DependencyInjection\CoreExtension;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class CoreBundle extends Bundle
{

View file

@ -5,13 +5,13 @@ namespace App\Core\Cache;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\HttpClient\Exception\ClientException;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpClient\Exception\ClientException;
use Symfony\Component\HttpClient\Exception\TransportException;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
/**
* class SymfonyCacheManager.
@ -20,15 +20,11 @@ use Symfony\Component\HttpClient\Exception\TransportException;
*/
class SymfonyCacheManager
{
protected KernelInterface $kernel;
protected HttpClientInterface $httpClient;
protected UrlGeneratorInterface $urlGenerator;
public function __construct(KernelInterface $kernel, HttpClientInterface $httpClient, UrlGeneratorInterface $urlGenerator)
{
$this->kernel = $kernel;
$this->httpClient = $httpClient;
$this->urlGenerator = $urlGenerator;
public function __construct(
protected KernelInterface $kernel,
protected HttpClientInterface $httpClient,
protected UrlGeneratorInterface $urlGenerator
) {
}
public function cleanRouting()

View file

@ -18,19 +18,12 @@ class UserCreateCommand extends Command
{
protected static $defaultName = 'murph:user:create';
protected static $defaultDescription = 'Creates a user';
protected UserFactory $userFactory;
protected EntityManager $entityManager;
protected TokenGeneratorInterface $tokenGenerator;
public function __construct(
UserFactory $userFactory,
EntityManager $entityManager,
TokenGeneratorInterface $tokenGenerator
protected UserFactory $userFactory,
protected EntityManager $entityManager,
protected TokenGeneratorInterface $tokenGenerator
) {
$this->userFactory = $userFactory;
$this->entityManager = $entityManager;
$this->tokenGenerator = $tokenGenerator;
parent::__construct();
}

View file

@ -42,6 +42,7 @@ abstract class CrudController extends AdminController
'configuration' => $configuration,
'pager' => $pager,
'sort' => $this->sort,
'context' => $context,
'filters' => [
'show' => null !== $configuration->getForm('filter'),
'isEmpty' => empty($this->filters),
@ -79,6 +80,7 @@ abstract class CrudController extends AdminController
return $this->render($configuration->getView($context), [
'form' => $form->createView(),
'configuration' => $configuration,
'context' => $context,
'entity' => $entity,
]);
}
@ -89,6 +91,7 @@ abstract class CrudController extends AdminController
return $this->render($configuration->getView($context), [
'entity' => $entity,
'context' => $context,
'configuration' => $configuration,
]);
}
@ -122,6 +125,7 @@ abstract class CrudController extends AdminController
return $this->render($configuration->getView($context), [
'form' => $form->createView(),
'context' => $context,
'configuration' => $configuration,
'entity' => $entity,
]);
@ -135,6 +139,7 @@ abstract class CrudController extends AdminController
$builder = $this->createFormBuilder($entity);
$callback = $configuration->getFields($context)[$label]['options']['inline_form'] ?? null;
$validationCallback = $configuration->getFields($context)[$label]['options']['inline_form_validation'] ?? null;
if (null === $callback) {
throw $this->createNotFoundException();
@ -142,24 +147,64 @@ abstract class CrudController extends AdminController
call_user_func_array($callback, [$builder, $entity]);
$form = $builder->getForm();
$redirectTo = $request->query->get('redirectTo');
$form = $builder->getForm();
$session = $request->getSession();
$lastRequestId = sprintf(
'inline_request_%s_%s_%s_%s',
get_class($entity),
$entity->getId(),
$context,
$label
);
$lastRequest = $session->get($lastRequestId);
if (null !== $lastRequest && !$request->isMethod('POST')) {
$fakeRequest = Request::create(
uri: $request->getUri(),
method: 'POST',
parameters: [$form->getName() => $lastRequest]
);
$form->handleRequest($fakeRequest);
if (null !== $validationCallback) {
call_user_func_array($validationCallback, [$entity, $form, $request]);
}
$session->remove($lastRequestId);
}
if ($request->isMethod('POST')) {
$form->handleRequest($request);
if (null !== $validationCallback) {
call_user_func_array($validationCallback, [$entity, $form, $request]);
}
if ($form->isValid()) {
if (null !== $beforeUpdate) {
call_user_func_array($beforeUpdate, [$entity, $form, $request]);
}
$session->remove($lastRequestId);
$entityManager->update($entity);
$this->addFlash('success', 'The data has been saved.');
} else {
$this->addFlash('warning', 'The form is not valid.');
return $this->redirect($redirectTo);
}
return $this->redirect($redirectTo);
$session->set($lastRequestId, $request->request->get('form'));
$this->addFlash('warning', 'The form is not valid.');
return $this->redirect(sprintf(
'%s?data-modal=%s',
$redirectTo,
urlencode($request->getUri())
));
}
return $this->render($configuration->getView('inline_edit'), [
@ -239,16 +284,39 @@ abstract class CrudController extends AdminController
$query->useFilters($this->filters);
if ('selection' === $target) {
$isSelection = true;
$pager = $query->paginate($page, $configuration->getMaxPerPage($context));
} else {
$isSelection = false;
$pager = $query->find();
$useSelection = 'selection' === $target;
if ($batchAction['isGlobal']) {
$selection = null;
if ($useSelection) {
$queryClone = clone $query;
$pager = $queryClone->paginate($page, $configuration->getMaxPerPage($context));
$selection = [];
foreach ($pager as $key => $entity) {
if (isset($items[$key + 1])) {
$selection[] = $entity;
}
}
}
$result = $callback($query, $entityManager, $selection);
if ($result instanceof Response) {
return $result;
}
return $this->redirect($request->query->get('redirectTo'));
}
$pager = $useSelection
? $query->paginate($page, $configuration->getMaxPerPage($context))
: $query->find()
;
foreach ($pager as $key => $entity) {
if (($isSelection && isset($items[$key + 1])) || !$isSelection) {
if (($useSelection && isset($items[$key + 1])) || !$useSelection) {
$callback($entity, $entityManager);
}
}
@ -258,7 +326,7 @@ abstract class CrudController extends AdminController
return $this->json([]);
}
protected function doDelete(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeDelete = null, string $route): Response
protected function doDelete(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeDelete = null, string $route = 'index'): Response
{
$configuration = $this->getConfiguration();
@ -289,6 +357,7 @@ abstract class CrudController extends AdminController
return $this->render($configuration->getView($context), [
'form' => $form->createView(),
'context' => $context,
'configuration' => $configuration,
]);
}
@ -349,9 +418,27 @@ abstract class CrudController extends AdminController
}
$defaultSort = $configuration->getDefaultSort($context);
$session = $request->getSession();
$name = $request->query->get('_sort', $defaultSort['label'] ?? null);
$direction = strtolower($request->query->get('_sort_direction', $defaultSort['direction'] ?? 'asc'));
$sessionId = sprintf('%s_%s_sort', $context, get_called_class());
$sessionSortName = sprintf('%s_label', $sessionId);
$sessionSortDirection = sprintf('%s_direction', $sessionId);
$name = $request->query->get('_sort', $session->get($sessionSortName)) ?? $defaultSort['label'] ?? null;
$direction = strtolower(
$request->query->get(
'_sort_direction',
$session->get($sessionSortDirection)
) ?? $defaultSort['direction'] ?? 'asc'
);
$session->set($sessionSortName, $name);
$session->set($sessionSortDirection, $direction);
if (empty($name)) {
return;
}
if (!in_array($direction, ['asc', 'desc'])) {
$direction = 'asc';

View file

@ -3,8 +3,6 @@
namespace App\Core\Controller\Dashboard;
use App\Core\Controller\Admin\AdminController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class DashboardAdminController extends AdminController
{

View file

@ -102,11 +102,11 @@ class RedirectAdminController extends CrudController
'attr' => ['class' => 'col-6'],
])
->setField('index', 'Enabled', Field\ButtonField::class, [
'property_builder' => function(EntityInterface $entity) {
'property_builder' => function (EntityInterface $entity) {
return $entity->getIsEnabled() ? 'Yes' : 'No';
},
'attr' => ['class' => 'col-1'],
'button_attr_builder' => function(EntityInterface $entity) {
'button_attr_builder' => function (EntityInterface $entity) {
return ['class' => 'btn btn-sm btn-'.($entity->getIsEnabled() ? 'success' : 'primary')];
},
])

View file

@ -31,11 +31,28 @@ class NavigationSettingAdminController extends AdminController
$eventDispatcher->dispatch($event, NavigationSettingEvent::FORM_INIT_EVENT);
$form = $builder->getForm();
$redirectTo = $request->query->get('redirectTo');
$session = $request->getSession();
$lastRequestId = sprintf('setting_request_%s_%s', get_class($entity), $entity->getId());
$lastRequest = $session->get($lastRequestId);
if (null !== $lastRequest && !$request->isMethod('POST')) {
$fakeRequest = Request::create(
uri: $request->getUri(),
method: 'POST',
parameters: [$form->getName() => $lastRequest]
);
$form->handleRequest($fakeRequest);
$session->remove($lastRequestId);
}
if ($request->isMethod('POST')) {
$form->handleRequest($request);
if ($form->isValid()) {
$entityManager->update($entity);
$session->remove($lastRequestId);
$entityManager->update($entity);
$this->addFlash('success', 'The data has been saved.');
@ -44,13 +61,21 @@ class NavigationSettingAdminController extends AdminController
]);
}
$session->set($lastRequestId, $request->request->get('form'));
$this->addFlash('warning', 'The form is not valid.');
return $this->redirect(sprintf(
'%s?data-modal=%s',
$redirectTo,
urlencode($request->getUri())
));
}
return $this->render('@Core/setting/navigation_setting_admin/edit.html.twig', [
'form' => $form->createView(),
'entity' => $entity,
'options' => $event->getData()['options'],
'redirectTo' => $redirectTo,
]);
}

View file

@ -12,14 +12,10 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route("/admin/setting")
*/
#[Route(path: '/admin/setting')]
class SettingAdminController extends AdminController
{
/**
* @Route("/{page}", name="admin_setting_index", requirements={"page": "\d+"})
*/
#[Route(path: '/{page}', name: 'admin_setting_index', requirements: ['page' => '\d+'])]
public function index(
RepositoryQuery $query,
EventDispatcherInterface $eventDispatcher,
@ -38,9 +34,7 @@ class SettingAdminController extends AdminController
]);
}
/**
* @Route("/edit/{entity}", name="admin_setting_edit")
*/
#[Route(path: '/edit/{entity}', name: 'admin_setting_edit')]
public function edit(
Entity $entity,
EntityManager $entityManager,
@ -57,30 +51,53 @@ class SettingAdminController extends AdminController
$eventDispatcher->dispatch($event, SettingEvent::FORM_INIT_EVENT);
$form = $builder->getForm();
$redirectTo = $request->query->get('redirectTo');
$session = $request->getSession();
$lastRequestId = sprintf('setting_request_%s_%s', get_class($entity), $entity->getId());
$lastRequest = $session->get($lastRequestId);
if (null !== $lastRequest && !$request->isMethod('POST')) {
$fakeRequest = Request::create(
uri: $request->getUri(),
method: 'POST',
parameters: [$form->getName() => $lastRequest]
);
$form->handleRequest($fakeRequest);
$session->remove($lastRequestId);
}
if ($request->isMethod('POST')) {
$form->handleRequest($request);
if ($form->isValid()) {
$entityManager->update($entity);
$session->remove($lastRequestId);
$entityManager->update($entity);
$this->addFlash('success', 'The data has been saved.');
return $this->redirectToRoute('admin_setting_index');
}
$session->set($lastRequestId, $request->request->get('form'));
$this->addFlash('warning', 'The form is not valid.');
return $this->redirect(sprintf(
'%s?data-modal=%s',
$redirectTo,
urlencode($request->getUri())
));
}
return $this->render('@Core/setting/setting_admin/edit.html.twig', [
'form' => $form->createView(),
'entity' => $entity,
'options' => $event->getData()['options'],
'redirectTo' => $redirectTo,
]);
}
/**
* @Route("/delete/{entity}", name="admin_setting_delete", methods={"DELETE"})
*/
#[Route(path: '/delete/{entity}', name: 'admin_setting_delete', methods: ['DELETE', 'POST'])]
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
{
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {

View file

@ -67,7 +67,7 @@ class NavigationAdminController extends CrudController
}
#[Route(path: '/admin/site/navigation/sort/{page}', name: 'admin_site_navigation_sort', methods: ['POST'], requirements: ['page' => '\d+'])]
public function sort(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1, ): Response
public function sort(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
{
return $this->doSort($page, $query, $entityManager, $request, $session);
}
@ -108,6 +108,7 @@ class NavigationAdminController extends CrudController
->setField('index', 'Label', Field\TextField::class, [
'property' => 'label',
'view' => '@Core/site/navigation_admin/field/label.html.twig',
'attr' => ['class' => 'miw-200'],
])
->setField('index', 'Domain', Field\ButtonField::class, [

View file

@ -2,7 +2,6 @@
namespace App\Core\Controller\Site;
use App\Core\Controller\Admin\AdminController;
use App\Core\Entity\Site\Node;
use App\Core\Entity\Site\Node as Entity;
use App\Core\Entity\Site\Page\Page;
@ -14,15 +13,15 @@ use App\Core\Form\Site\NodeType as EntityType;
use App\Core\Manager\EntityManager;
use App\Core\Repository\Site\NodeRepository;
use App\Core\Site\ControllerLocator;
use App\Core\Site\RoleLocator;
use App\Core\Site\PageLocator;
use App\Core\Site\RoleLocator;
use App\Core\Sitemap\SitemapBuilder;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
#[Route(path: '/admin/site/node')]
class NodeAdminController extends AbstractController
@ -145,7 +144,7 @@ class NodeAdminController extends AbstractController
$page = $entity->getPage();
if ($page !== null) {
if (null !== $page) {
$pageConfiguration = $pageLocator->getPages()[get_class($page)] ?? null;
} else {
$pageConfiguration = null;

View file

@ -5,19 +5,19 @@ namespace App\Core\Controller\Site;
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\Site\Page\Page as Entity;
use App\Core\Event\Page\PageEditEvent;
use App\Core\Form\Site\Page\Filter\PageFilterType as FilterType;
use App\Core\Form\Site\Page\PageType as Type;
use App\Core\Manager\EntityManager;
use App\Core\Repository\Site\Page\PageRepositoryQuery as RepositoryQuery;
use App\Core\Site\PageLocator;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Routing\Annotation\Route;
use App\Core\Event\Page\PageEditEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use App\Core\Entity\EntityInterface;
class PageAdminController extends CrudController
{
@ -114,7 +114,7 @@ class PageAdminController extends CrudController
}],
'attr' => ['class' => 'col-6'],
])
->setBatchAction('index', 'delete', 'Delete', function(EntityInterface $entity, EntityManager $manager) {
->setBatchAction('index', 'delete', 'Delete', function (EntityInterface $entity, EntityManager $manager) {
$manager->delete($entity);
})
;

View file

@ -17,7 +17,6 @@ use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Routing\Annotation\Route;
class UserAdminController extends CrudController
{

View file

@ -40,7 +40,7 @@ class CrudConfiguration
return self::$self;
}
/* -- */
// --
public function setPageTitle(string $page, string $title): self
{
@ -54,7 +54,7 @@ class CrudConfiguration
return $this->pageTitles[$page] ?? $default;
}
/* -- */
// --
public function setPageRoute(string $page, string $route): self
{
@ -80,9 +80,9 @@ class CrudConfiguration
return $this->pageRouteParams[$page] ?? [];
}
/* -- */
// --
public function setForm(string $context, string $form, array $options = []): self
public function setForm(string $context, string $form): self
{
$this->forms[$context] = $form;
@ -106,7 +106,7 @@ class CrudConfiguration
return $this->formOptions[$context] ?? [];
}
/* -- */
// --
public function setAction(string $page, string $action, bool|callable $enabled): self
{
@ -135,8 +135,24 @@ class CrudConfiguration
);
}
public function setBatchAction(string $page, string $action, string $label, callable $callback): self
{
public function setGlobalBatchAction(
string $page,
string $action,
string $label,
callable $callback
): self {
$this->setBatchAction($page, $action, $label, $callback);
$this->batchActions[$page][$action]['isGlobal'] = true;
return $this;
}
public function setBatchAction(
string $page,
string $action,
string $label,
callable $callback
): self {
if (!isset($this->batchActions[$page])) {
$this->batchActions[$page] = [];
}
@ -144,6 +160,7 @@ class CrudConfiguration
$this->batchActions[$page][$action] = [
'label' => $label,
'callback' => $callback,
'isGlobal' => false,
];
return $this;
@ -164,7 +181,7 @@ class CrudConfiguration
return !empty($this->batchActions[$page]);
}
/* -- */
// --
public function setActionTitle(string $page, string $action, string $title): self
{
@ -182,7 +199,7 @@ class CrudConfiguration
return $this->actionTitles[$page][$action] ?? $default;
}
/* -- */
// --
public function setView(string $context, string $view): self
{
@ -230,7 +247,7 @@ class CrudConfiguration
return $this->viewDatas[$context][$name] ?? $defaultValue;
}
/* -- */
// --
public function setField(string $context, string $label, string $field, array $options): self
{
@ -258,7 +275,7 @@ class CrudConfiguration
return $this;
}
/* -- */
// --
public function setMaxPerPage(string $page, int $max): self
{
@ -272,7 +289,7 @@ class CrudConfiguration
return $this->maxPerPage[$page] ?? $default;
}
/* -- */
// --
public function setDoubleClick(string $page, bool $enabled): self
{
@ -286,7 +303,7 @@ class CrudConfiguration
return $this->doubleClick[$page] ?? false;
}
/* -- */
// --
public function setI18n(array $locales, string $defaultLocale): self
{
@ -311,7 +328,7 @@ class CrudConfiguration
return !empty($this->locales);
}
/* -- */
// --
public function setDefaultSort(string $context, string $label, string $direction = 'asc'): self
{

View file

@ -17,8 +17,16 @@ class BooleanField extends Field
$resolver->setDefaults([
'view' => '@Core/admin/crud/field/boolean.html.twig',
'display' => 'toggle',
'checkbox_class_when_true' => 'fa-check-square',
'checkbox_class_when_false' => 'fa-square',
'toggle_class_when_true' => 'bg-success',
'toggle_class_when_false' => 'bg-secondary',
'default_value' => false,
]);
$resolver->setAllowedTypes('display', 'string');
return $resolver;
}
}

View file

@ -28,6 +28,7 @@ abstract class Field
$resolver->setDefaults([
'property' => null,
'property_builder' => null,
'default_value' => null,
'view' => null,
'action' => null,
'raw' => false,
@ -36,6 +37,7 @@ abstract class Field
'href_attr' => [],
'attr' => [],
'inline_form' => null,
'inline_form_validation' => null,
]);
$resolver->setRequired('view');
@ -45,11 +47,12 @@ abstract class Field
$resolver->setAllowedTypes('attr', 'array');
$resolver->setAllowedTypes('href', ['null', 'string', 'callable']);
$resolver->setAllowedTypes('inline_form', ['null', 'callable']);
$resolver->setAllowedTypes('href_attr', 'array', 'callable');
$resolver->setAllowedTypes('inline_form_validation', ['null', 'callable']);
$resolver->setAllowedTypes('href_attr', ['array', 'callable']);
$resolver->setAllowedTypes('raw', 'boolean');
$resolver->setAllowedTypes('property_builder', ['null', 'callable']);
$resolver->setAllowedValues('sort', function($value) {
if ($value === null) {
$resolver->setAllowedValues('sort', function ($value) {
if (null === $value) {
return true;
}

View file

@ -46,99 +46,100 @@ class Configuration implements ConfigurationInterface
$treeBuilder->getRootNode()
->children()
->arrayNode('site')
->children()
->scalarNode('name')
->defaultValue('Murph')
->isRequired()
->end()
->scalarNode('logo')
->defaultValue('build/images/core/logo.svg')
->isRequired()
->end()
->arrayNode('controllers')
->prototype('array')
->children()
->scalarNode('name')
->cannotBeEmpty()
->end()
->scalarNode('action')
->cannotBeEmpty()
->end()
->end()
->end()
->end()
->arrayNode('security')
->children()
->arrayNode('roles')
->prototype('array')
->children()
->scalarNode('name')
->cannotBeEmpty()
->end()
->scalarNode('role')
->cannotBeEmpty()
->end()
->end()
->end()
->end()
->end()
->end()
->arrayNode('pages')
->prototype('array')
->children()
->scalarNode('name')
->isRequired()
->cannotBeEmpty()
->end()
->arrayNode('templates')
->prototype('array')
->children()
->scalarNode('name')
->cannotBeEmpty()
->end()
->scalarNode('file')
->cannotBeEmpty()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->arrayNode('editor_js')
->children()
->arrayNode('blocks')
->scalarPrototype()
->end()
->end()
->end()
->end()
->arrayNode('file_manager')
->children()
->arrayNode('mimes')
->scalarPrototype()
->end()
->defaultValue($defaultMimetypes)
->end()
->scalarNode('path')
->defaultValue('%kernel.project_dir%/public/uploads')
->cannotBeEmpty()
->end()
->scalarNode('path_uri')
->defaultValue('/uploads')
->cannotBeEmpty()
->end()
->arrayNode('path_locked')
->scalarPrototype()
->end()
->defaultValue($defaultLocked)
->end()
->end()
->end()
->end();
->arrayNode('site')
->children()
->scalarNode('name')
->defaultValue('Murph')
->isRequired()
->end()
->scalarNode('logo')
->defaultValue('build/images/core/logo.svg')
->isRequired()
->end()
->arrayNode('controllers')
->prototype('array')
->children()
->scalarNode('name')
->cannotBeEmpty()
->end()
->scalarNode('action')
->cannotBeEmpty()
->end()
->end()
->end()
->end()
->arrayNode('security')
->children()
->arrayNode('roles')
->prototype('array')
->children()
->scalarNode('name')
->cannotBeEmpty()
->end()
->scalarNode('role')
->cannotBeEmpty()
->end()
->end()
->end()
->end()
->end()
->end()
->arrayNode('pages')
->prototype('array')
->children()
->scalarNode('name')
->isRequired()
->cannotBeEmpty()
->end()
->arrayNode('templates')
->prototype('array')
->children()
->scalarNode('name')
->cannotBeEmpty()
->end()
->scalarNode('file')
->cannotBeEmpty()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->arrayNode('editor_js')
->children()
->arrayNode('blocks')
->scalarPrototype()
->end()
->end()
->end()
->end()
->arrayNode('file_manager')
->children()
->arrayNode('mimes')
->scalarPrototype()
->end()
->defaultValue($defaultMimetypes)
->end()
->scalarNode('path')
->defaultValue('%kernel.project_dir%/public/uploads')
->cannotBeEmpty()
->end()
->scalarNode('path_uri')
->defaultValue('/uploads')
->cannotBeEmpty()
->end()
->arrayNode('path_locked')
->scalarPrototype()
->end()
->defaultValue($defaultLocked)
->end()
->end()
->end()
->end()
;
return $treeBuilder;
}

View file

@ -2,10 +2,9 @@
namespace App\Core\Entity\Analytic;
use App\Core\Entity\Site\Node;
use App\Repository\Entity\Analytic\NodeViewRepository;
use Doctrine\ORM\Mapping as ORM;
use App\Core\Entity\EntityInterface;
use App\Core\Entity\Site\Node;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Table(name: 'analytic_referer')]
#[ORM\Entity(repositoryClass: ViewRepository::class)]

View file

@ -2,10 +2,9 @@
namespace App\Core\Entity\Analytic;
use App\Core\Entity\Site\Node;
use App\Repository\Entity\Analytic\NodeViewRepository;
use Doctrine\ORM\Mapping as ORM;
use App\Core\Entity\EntityInterface;
use App\Core\Entity\Site\Node;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Table(name: 'analytic_view')]
#[ORM\Entity(repositoryClass: ViewRepository::class)]

View file

@ -42,6 +42,9 @@ class Navigation implements EntityInterface
#[ORM\Column(type: 'string', length: 10)]
protected $locale = 'en';
#[ORM\Column(type: 'string', length: 7, nullable: true)]
protected $color;
#[ORM\Column(type: 'integer', nullable: true)]
protected $sortOrder;
@ -64,7 +67,7 @@ class Navigation implements EntityInterface
return $this->label;
}
public function setLabel(string $label): self
public function setLabel(?string $label): self
{
$this->label = $label;
@ -237,4 +240,16 @@ class Navigation implements EntityInterface
return false;
}
public function setColor(string $color): self
{
$this->color = $color;
return $this;
}
public function getColor(): ?string
{
return $this->color;
}
}

View file

@ -5,13 +5,12 @@ namespace App\Core\Entity\Site\Page;
use App\Core\Doctrine\Timestampable;
use App\Core\Entity\EntityInterface;
use App\Core\Entity\Site\Node;
use App\Core\File\FileAttribute;
use App\Core\Repository\Site\Page\PageRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\HttpFoundation\File\File;
use App\Core\File\FileAttribute;
#[ORM\Entity(repositoryClass: PageRepository::class)]
#[ORM\DiscriminatorColumn(name: 'class_key', type: 'string')]
@ -89,7 +88,7 @@ class Page implements EntityInterface
}
/**
* @return Collection|Block[]
* @return Block[]|Collection
*/
public function getBlocks(): Collection
{

View file

@ -15,11 +15,8 @@ class AbTestEvent extends Event
public const INIT_EVENT = 'ab_test.init';
public const RUN_EVENT = 'ab_test.run';
protected AbTest $test;
public function __construct(AbTest $test)
public function __construct(protected AbTest $test)
{
$this->test = $test;
}
public function getTest(): AbTest

View file

@ -12,13 +12,10 @@ use Symfony\Contracts\EventDispatcher\Event;
*/
class PasswordRequestEvent extends Event
{
const EVENT = 'account_event.password_request';
public const EVENT = 'account_event.password_request';
protected User $user;
public function __construct(User $user)
public function __construct(protected User $user)
{
$this->user = $user;
}
public function getUser(): User

View file

@ -12,18 +12,15 @@ use Symfony\Contracts\EventDispatcher\Event;
*/
class EntityManagerEvent extends Event
{
const CREATE_EVENT = 'entity_manager_event.create';
const UPDATE_EVENT = 'entity_manager_event.update';
const DELETE_EVENT = 'entity_manager_event.delete';
const PRE_CREATE_EVENT = 'entity_manager_event.pre_create';
const PRE_UPDATE_EVENT = 'entity_manager_event.pre_update';
const PRE_DELETE_EVENT = 'entity_manager_event.pre_delete';
public const CREATE_EVENT = 'entity_manager_event.create';
public const UPDATE_EVENT = 'entity_manager_event.update';
public const DELETE_EVENT = 'entity_manager_event.delete';
public const PRE_CREATE_EVENT = 'entity_manager_event.pre_create';
public const PRE_UPDATE_EVENT = 'entity_manager_event.pre_update';
public const PRE_DELETE_EVENT = 'entity_manager_event.pre_delete';
protected EntityInterface $entity;
public function __construct(EntityInterface $entity)
public function __construct(protected EntityInterface $entity)
{
$this->entity = $entity;
}
public function getEntity(): EntityInterface

View file

@ -2,8 +2,8 @@
namespace App\Core\Event\Page;
use Symfony\Contracts\EventDispatcher\Event;
use App\Core\Entity\Site\Page\Page;
use Symfony\Contracts\EventDispatcher\Event;
/**
* class PageEditEvent.
@ -12,14 +12,12 @@ use App\Core\Entity\Site\Page\Page;
*/
class PageEditEvent extends Event
{
const FORM_INIT_EVENT = 'page_edit_event.form_init';
public const FORM_INIT_EVENT = 'page_edit_event.form_init';
protected Page $page;
protected array $pageBuilderOptions = [];
public function __construct(Page $page)
public function __construct(protected Page $page)
{
$this->page = $page;
}
public function getPage()

View file

@ -11,14 +11,11 @@ use Symfony\Contracts\EventDispatcher\Event;
*/
class NavigationSettingEvent extends Event
{
const INIT_EVENT = 'navigation_setting_event.init';
const FORM_INIT_EVENT = 'navigation_setting_event.form_init';
public const INIT_EVENT = 'navigation_setting_event.init';
public const FORM_INIT_EVENT = 'navigation_setting_event.form_init';
protected $data;
public function __construct($data = null)
public function __construct(protected $data = null)
{
$this->data = $data;
}
public function getData()

View file

@ -11,14 +11,11 @@ use Symfony\Contracts\EventDispatcher\Event;
*/
class SettingEvent extends Event
{
const INIT_EVENT = 'setting_event.init';
const FORM_INIT_EVENT = 'setting_event.form_init';
public const INIT_EVENT = 'setting_event.init';
public const FORM_INIT_EVENT = 'setting_event.form_init';
protected $data;
public function __construct($data = null)
public function __construct(protected $data = null)
{
$this->data = $data;
}
public function getData()

View file

@ -11,7 +11,7 @@ use Symfony\Contracts\EventDispatcher\Event;
*/
class TaskInitEvent extends Event
{
const INIT_EVENT = 'task_event.init';
public const INIT_EVENT = 'task_event.init';
protected array $tasks = [];

View file

@ -14,17 +14,13 @@ use Symfony\Contracts\EventDispatcher\Event;
*/
class TaskRunRequestedEvent extends Event
{
const RUN_REQUEST_EVENT = 'task_event.run_request';
public const RUN_REQUEST_EVENT = 'task_event.run_request';
protected string $task;
protected InputBag $parameters;
protected BufferedOutput $output;
public function __construct(string $task, InputBag $parameters, BufferedOutput $output)
{
$this->task = $task;
$this->parameters = $parameters;
$this->output = $output;
public function __construct(
protected string $task,
protected InputBag $parameters,
protected BufferedOutput $output
) {
}
public function getTask(): string

View file

@ -6,7 +6,6 @@ use App\Core\Ab\AbContainer;
use App\Core\Ab\AbTest;
use App\Core\Entity\Site\Node;
use App\Core\Event\Ab\AbTestEvent;
use App\Core\Repository\Site\NodeRepository;
use App\Core\Site\SiteRequest;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Cookie;
@ -21,19 +20,13 @@ use Symfony\Component\HttpKernel\Event\ResponseEvent;
*/
class AbListener
{
protected EventDispatcherInterface $eventDispatcher;
protected AbContainer $container;
protected SiteRequest $siteRequest;
protected ?Node $node;
public function __construct(
AbContainer $container,
EventDispatcherInterface $eventDispatcher,
SiteRequest $siteRequest
protected AbContainer $container,
protected EventDispatcherInterface $eventDispatcher,
protected SiteRequest $siteRequest
) {
$this->eventDispatcher = $eventDispatcher;
$this->container = $container;
$this->siteRequest = $siteRequest;
}
public function onKernelRequest(RequestEvent $event)
@ -70,6 +63,16 @@ class AbListener
}
}
public function onKernelResponse(ResponseEvent $event)
{
$cookies = $event->getRequest()->attributes->get('ab_test_cookies', []);
foreach ($cookies as $name => $value) {
$cookie = Cookie::create($name, $value['value'], time() + $value['duration']);
$event->getResponse()->headers->setCookie($cookie);
}
}
protected function getCookieName(): string
{
return 'ab_test_'.$this->getAbTestCode();
@ -96,14 +99,4 @@ class AbListener
return true;
}
public function onKernelResponse(ResponseEvent $event)
{
$cookies = $event->getRequest()->attributes->get('ab_test_cookies', []);
foreach ($cookies as $name => $value) {
$cookie = Cookie::create($name, $value['value'], time() + $value['duration']);
$event->getResponse()->headers->setCookie($cookie);
}
}
}

View file

@ -23,30 +23,18 @@ use Symfony\Component\HttpKernel\Event\RequestEvent;
*/
class AnalyticListener
{
protected NodeRepository $nodeRepository;
protected ViewRepositoryQuery $viewRepositoryQuery;
protected ViewFactory $viewFactory;
protected RefererRepositoryQuery $refererRepositoryQuery;
protected RefererFactory $refererFactory;
protected EntityManager $manager;
protected DeviceDetector $deviceDetector;
protected Request $request;
protected Node $node;
public function __construct(
NodeRepository $nodeRepository,
ViewRepositoryQuery $viewRepositoryQuery,
ViewFactory $viewFactory,
RefererRepositoryQuery $refererRepositoryQuery,
RefererFactory $refererFactory,
EntityManager $manager
protected NodeRepository $nodeRepository,
protected ViewRepositoryQuery $viewRepositoryQuery,
protected ViewFactory $viewFactory,
protected RefererRepositoryQuery $refererRepositoryQuery,
protected RefererFactory $refererFactory,
protected EntityManager $manager
) {
$this->nodeRepository = $nodeRepository;
$this->viewRepositoryQuery = $viewRepositoryQuery;
$this->viewFactory = $viewFactory;
$this->refererRepositoryQuery = $refererRepositoryQuery;
$this->refererFactory = $refererFactory;
$this->manager = $manager;
$this->createDeviceDetector();
}

View file

@ -3,11 +3,10 @@
namespace App\Core\EventListener;
use App\Core\Repository\RedirectRepositoryQuery;
use App\Core\Router\RedirectBuilder;
use App\Core\Router\RedirectMatcher;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use App\Core\Router\RedirectBuilder;
/**
* class RedirectListener.
@ -16,15 +15,11 @@ use App\Core\Router\RedirectBuilder;
*/
class RedirectListener
{
protected RedirectMatcher $matcher;
protected RedirectBuilder $builder;
protected RedirectRepositoryQuery $repository;
public function __construct(RedirectMatcher $matcher, RedirectBuilder $builder, RedirectRepositoryQuery $repository)
{
$this->matcher = $matcher;
$this->builder = $builder;
$this->repository = $repository;
public function __construct(
protected RedirectMatcher $matcher,
protected RedirectBuilder $builder,
protected RedirectRepositoryQuery $repository
) {
}
public function onKernelException(ExceptionEvent $event)

View file

@ -17,24 +17,13 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
class PasswordRequestEventSubscriber implements EventSubscriberInterface
{
protected MailNotifier $notifier;
protected UrlGeneratorInterface $urlGenerator;
protected EntityManager $entityManager;
protected TokenGeneratorInterface $tokenGenerator;
protected TranslatorInterface $translator;
public function __construct(
MailNotifier $notifier,
UrlGeneratorInterface $urlGenerator,
EntityManager $entityManager,
TokenGeneratorInterface $tokenGenerator,
TranslatorInterface $translator
protected MailNotifier $notifier,
protected UrlGeneratorInterface $urlGenerator,
protected EntityManager $entityManager,
protected TokenGeneratorInterface $tokenGenerator,
protected TranslatorInterface $translator
) {
$this->notifier = $notifier;
$this->urlGenerator = $urlGenerator;
$this->entityManager = $entityManager;
$this->tokenGenerator = $tokenGenerator;
$this->translator = $translator;
}
public static function getSubscribedEvents()

View file

@ -16,12 +16,12 @@ use Symfony\Component\Security\Core\Exception\AccessDeniedException;
*/
class RequestSecurityEventSubscriber implements EventSubscriberInterface
{
protected NodeRepository $nodeRepository;
protected AuthorizationChecker $authorizationChecker;
public function __construct(NodeRepository $nodeRepository, ContainerInterface $container)
{
$this->nodeRepository = $nodeRepository;
public function __construct(
protected NodeRepository $nodeRepository,
ContainerInterface $container
) {
$this->authorizationChecker = $container->get('security.authorization_checker');
}

View file

@ -4,18 +4,15 @@ namespace App\Core\EventSubscriber\Site;
use App\Core\Site\SiteRequest;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use function Symfony\Component\String\u;
class ForcedDomainEventSubscriber implements EventSubscriberInterface
{
protected SiteRequest $siteRequest;
public function __construct(SiteRequest $siteRequest)
public function __construct(protected SiteRequest $siteRequest)
{
$this->siteRequest = $siteRequest;
}
public function onKernelResponse(ResponseEvent $event)
@ -38,7 +35,8 @@ class ForcedDomainEventSubscriber implements EventSubscriberInterface
->replace(
'://'.$this->siteRequest->getDomain(),
'://'.$navigation->getDomain()
);
)
;
$event->getResponse()->headers->set('Location', $uri);
$event->getResponse()->setStatusCode(Response::HTTP_MOVED_PERMANENTLY);

View file

@ -20,27 +20,14 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
class MenuEventSubscriber extends EntityManagerEventSubscriber
{
protected NodeFactory $nodeFactory;
protected NodeRepository $nodeRepository;
protected EntityManager $entityManager;
protected CodeSlugify $slugify;
protected SymfonyCacheManager $cacheManager;
protected TranslatorInterface $translation;
public function __construct(
NodeFactory $nodeFactory,
NodeRepository $nodeRepository,
EntityManager $entityManager,
CodeSlugify $slugify,
SymfonyCacheManager $cacheManager,
TranslatorInterface $translator
protected NodeFactory $nodeFactory,
protected NodeRepository $nodeRepository,
protected EntityManager $entityManager,
protected CodeSlugify $slugify,
protected SymfonyCacheManager $cacheManager,
protected TranslatorInterface $translator
) {
$this->nodeFactory = $nodeFactory;
$this->nodeRepository = $nodeRepository;
$this->entityManager = $entityManager;
$this->slugify = $slugify;
$this->cacheManager = $cacheManager;
$this->translator = $translator;
}
public function supports(EntityInterface $entity): bool

View file

@ -17,11 +17,9 @@ use App\Core\Slugify\CodeSlugify;
class NavigationEventSubscriber extends EntityManagerEventSubscriber
{
public function __construct(
EntityManager $entityManager,
CodeSlugify $slugify
protected EntityManager $entityManager,
protected CodeSlugify $slugify
) {
$this->entityManager = $entityManager;
$this->slugify = $slugify;
}
public function supports(EntityInterface $entity): bool

View file

@ -12,7 +12,6 @@ use App\Core\Repository\Site\NodeRepository;
use App\Core\Slugify\CodeSlugify;
use App\Core\Slugify\RouteParameterSlugify;
use App\Core\Slugify\Slugify;
use Symfony\Component\HttpKernel\KernelInterface;
use function Symfony\Component\String\u;
/**
@ -22,27 +21,14 @@ use function Symfony\Component\String\u;
*/
class NodeEventSubscriber extends EntityManagerEventSubscriber
{
protected NodeFactory $nodeFactory;
protected EntityManager $entityManager;
protected KernelInterface $kernel;
protected Slugify $slugify;
protected CodeSlugify $codeSlugify;
protected RouteParameterSlugify $routeParameterSlugify;
public function __construct(
NodeFactory $nodeFactory,
NodeRepository $nodeRepository,
EntityManager $entityManager,
Slugify $slugify,
CodeSlugify $codeSlugify,
RouteParameterSlugify $routeParameterSlugify
protected NodeFactory $nodeFactory,
protected NodeRepository $nodeRepository,
protected EntityManager $entityManager,
protected Slugify $slugify,
protected CodeSlugify $codeSlugify,
protected RouteParameterSlugify $routeParameterSlugify
) {
$this->nodeFactory = $nodeFactory;
$this->nodeRepository = $nodeRepository;
$this->entityManager = $entityManager;
$this->slugify = $slugify;
$this->codeSlugify = $codeSlugify;
$this->routeParameterSlugify = $routeParameterSlugify;
}
public function supports(EntityInterface $entity): bool
@ -63,7 +49,7 @@ class NodeEventSubscriber extends EntityManagerEventSubscriber
$node = $event->getEntity();
$node->setCode($this->codeSlugify->slugify($node->getCode()));
$node->setCode($this->codeSlugify->slugify($node->getCode() ?? ''));
if ($node->getDisableUrl()) {
$node->setUrl(null);

View file

@ -17,11 +17,8 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
*/
class BlockEventSubscriber extends EntityManagerEventSubscriber
{
protected FileUploadHandler $fileUpload;
public function __construct(FileUploadHandler $fileUpload)
public function __construct(protected FileUploadHandler $fileUpload)
{
$this->fileUpload = $fileUpload;
}
public function supports(EntityInterface $entity): bool

View file

@ -16,11 +16,8 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
*/
class PageEventSubscriber extends EntityManagerEventSubscriber
{
protected FileUploadHandler $fileUpload;
public function __construct(FileUploadHandler $fileUpload)
public function __construct(protected FileUploadHandler $fileUpload)
{
$this->fileUpload = $fileUpload;
}
public function supports(EntityInterface $entity): bool

View file

@ -18,13 +18,10 @@ use Symfony\Component\HttpKernel\KernelInterface;
*/
class SiteEventSubscriber extends EntityManagerEventSubscriber
{
protected KernelInterface $kernel;
protected SymfonyCacheManager $cacheManager;
public function __construct(KernelInterface $kernel, SymfonyCacheManager $cacheManager)
{
$this->kernel = $kernel;
$this->cacheManager = $cacheManager;
public function __construct(
protected KernelInterface $kernel,
protected SymfonyCacheManager $cacheManager
) {
}
public function supports(EntityInterface $entity): bool

View file

@ -13,11 +13,8 @@ use App\Core\Event\Task\TaskRunRequestedEvent;
*/
class CacheCleanTaskEventSubscriber extends TaskEventSubscriber
{
protected SymfonyCacheManager $cacheManager;
public function __construct(SymfonyCacheManager $cacheManager)
public function __construct(protected SymfonyCacheManager $cacheManager)
{
$this->cacheManager = $cacheManager;
}
public function onInit(TaskInitEvent $event)

View file

@ -2,7 +2,6 @@
namespace App\Core\Factory;
use App\Core\Factory\FactoryInterface;
use App\Core\Entity\Redirect as Entity;
class RedirectFactory implements FactoryInterface

View file

@ -23,22 +23,15 @@ class FsFileManager
protected string $path;
protected string $pathUri;
protected array $pathLocked;
protected FileUploadHandler $uploadHandler;
protected FileInformationFactory $fileInformationFactory;
protected FileInformationRepositoryQuery $fileInformationRepositoryQuery;
public function __construct(
ParameterBagInterface $params,
FileUploadHandler $uploadHandler,
FileInformationFactory $fileInformationFactory,
FileInformationRepositoryQuery $fileInformationRepositoryQuery
protected FileUploadHandler $uploadHandler,
protected FileInformationFactory $fileInformationFactory,
protected FileInformationRepositoryQuery $fileInformationRepositoryQuery
) {
$config = $params->get('core')['file_manager'];
$this->uploadHandler = $uploadHandler;
$this->fileInformationFactory = $fileInformationFactory;
$this->fileInformationRepositoryQuery = $fileInformationRepositoryQuery;
$this->mimes = $config['mimes'];
$this->path = $config['path'];
$this->pathUri = $this->normalizePath($config['path_uri']);

View file

@ -6,7 +6,6 @@ use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
class FilePickerType extends AbstractType
{

View file

@ -2,13 +2,11 @@
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
{

View file

@ -4,12 +4,12 @@ namespace App\Core\Form;
use App\Core\Entity\Redirect;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
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
{

View file

@ -7,6 +7,7 @@ use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
class MenuType extends AbstractType
@ -23,6 +24,7 @@ class MenuType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);
@ -37,6 +39,7 @@ class MenuType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);

View file

@ -7,6 +7,7 @@ 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\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
class NavigationAdditionalDomainType extends AbstractType
@ -24,6 +25,7 @@ class NavigationAdditionalDomainType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);

View file

@ -6,6 +6,7 @@ use App\Core\Entity\Site\Navigation;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\ColorType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
@ -26,6 +27,7 @@ class NavigationType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);
@ -38,6 +40,21 @@ class NavigationType extends AbstractType
'required' => true,
'attr' => [
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);
$builder->add(
'color',
ColorType::class,
[
'label' => 'Color',
'required' => true,
'attr' => [
],
'constraints' => [
new NotBlank(),
],
@ -54,6 +71,7 @@ class NavigationType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);
@ -94,7 +112,7 @@ class NavigationType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(['min' => 2, 'max' => 10]),
new Length(min: 2, max: 10),
],
]
);

View file

@ -13,6 +13,7 @@ use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
class NodeType extends AbstractType
@ -29,6 +30,7 @@ class NodeType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);
@ -43,6 +45,7 @@ class NodeType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);
@ -82,6 +85,7 @@ class NodeType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);
@ -116,6 +120,9 @@ class NodeType extends AbstractType
return $choices;
}),
'constraints' => [
new Length(max: 255),
],
]
);

View file

@ -2,11 +2,11 @@
namespace App\Core\Form\Site\Page;
use App\Core\Entity\Site\Page\Block;
use App\Core\Form\FileManager\FilePickerType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\AbstractType;
use App\Core\Entity\Site\Page\Block;
class FilePickerBlockType extends AbstractType
{

View file

@ -6,10 +6,10 @@ use App\Core\Entity\Site\Navigation;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
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\Form\Extension\Core\Type\ChoiceType;
class PageFilterType extends AbstractType
{

View file

@ -10,6 +10,7 @@ use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Image;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
class PageType extends AbstractType
@ -26,6 +27,7 @@ class PageType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);
@ -39,6 +41,7 @@ class PageType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);
@ -52,6 +55,7 @@ class PageType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);
@ -65,6 +69,7 @@ class PageType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);
@ -78,6 +83,7 @@ class PageType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);

View file

@ -22,6 +22,8 @@ class CollectionType extends BaseCollectionType
'label_delete' => $options['label_delete'],
'allow_add' => $options['allow_add'],
'allow_delete' => $options['allow_delete'],
'template_before_item' => $options['template_before_item'],
'template_after_item' => $options['template_after_item'],
]);
}
@ -33,6 +35,8 @@ class CollectionType extends BaseCollectionType
'collection_name' => '',
'label_add' => 'Add',
'label_delete' => 'Delete',
'template_before_item' => null,
'template_after_item' => null,
]);
}

View file

@ -11,8 +11,8 @@ use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use function Symfony\Component\String\u;
use Symfony\Component\Filesystem\Filesystem;
use function Symfony\Component\String\u;
class MakeCrudController extends AbstractMaker
{

View file

@ -11,7 +11,6 @@ use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use function Symfony\Component\String\u;
class MakeFactory extends AbstractMaker
{

View file

@ -8,11 +8,11 @@ use Symfony\Bundle\MakerBundle\DependencyBuilder;
use Symfony\Bundle\MakerBundle\Generator;
use Symfony\Bundle\MakerBundle\InputConfiguration;
use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
use Symfony\Bundle\MakerBundle\Str;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Bundle\MakerBundle\Str;
use Symfony\Component\Filesystem\Filesystem;
class MakePage extends AbstractMaker
@ -92,7 +92,8 @@ class MakePage extends AbstractMaker
$this->writeSuccessMessage($io);
$io->text('Register the page in <comment>config/packages/app.yaml</comment>: ');
$io->text(<<< EOF
$io->text(
<<< EOF
core:
site:
@ -100,10 +101,18 @@ core:
{$pageClassNameDetails->getFullName()}:
name: {$pageClassNameDetails->getShortName()}
templates:
- {name: "Default", file: "${templatePath}"}
- {name: "Default", file: "{$templatePath}"}
EOF
);
);
}
public function configureDependencies(DependencyBuilder $dependencies)
{
$dependencies->addClassDependency(
Annotation::class,
'doctrine/annotations'
);
}
private function askForNextBlock(ConsoleStyle $io, array $blocks, bool $isFirstField)
@ -183,12 +192,4 @@ EOF
$io->writeln(sprintf(' * <comment>%s</comment>', $type));
}
}
public function configureDependencies(DependencyBuilder $dependencies)
{
$dependencies->addClassDependency(
Annotation::class,
'doctrine/annotations'
);
}
}

View file

@ -4,7 +4,6 @@ namespace App\Core\Manager;
use App\Core\Entity\EntityInterface;
use App\Core\Event\EntityManager\EntityManagerEvent;
use Doctrine\ORM\EntityManager as DoctrineEntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@ -15,23 +14,19 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
*/
class EntityManager
{
protected EventDispatcherInterface $eventDispatcher;
protected DoctrineEntityManager $entityManager;
public function __construct(EventDispatcherInterface $eventDispatcher, EntityManagerInterface $entityManager)
{
$this->eventDispatcher = $eventDispatcher;
$this->entityManager = $entityManager;
public function __construct(
protected EventDispatcherInterface $eventDispatcher,
protected EntityManagerInterface $entityManager
) {
}
public function create(EntityInterface $entity, bool $dispatchEvent = true): self
public function create(EntityInterface $entity, bool $dispatchEvent = true, bool $flush = true): self
{
if ($dispatchEvent) {
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::PRE_CREATE_EVENT);
}
$this->persist($entity);
$this->persist($entity, $flush);
if ($dispatchEvent) {
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::CREATE_EVENT);
@ -40,13 +35,13 @@ class EntityManager
return $this;
}
public function update(EntityInterface $entity, bool $dispatchEvent = true): self
public function update(EntityInterface $entity, bool $dispatchEvent = true, bool $flush = true): self
{
if ($dispatchEvent) {
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::PRE_UPDATE_EVENT);
}
$this->persist($entity);
$this->persist($entity, $flush);
if ($dispatchEvent) {
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::UPDATE_EVENT);
@ -55,14 +50,17 @@ class EntityManager
return $this;
}
public function delete(EntityInterface $entity, bool $dispatchEvent = true): self
public function delete(EntityInterface $entity, bool $dispatchEvent = true, bool $flush = true): self
{
if ($dispatchEvent) {
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::PRE_DELETE_EVENT);
}
$this->entityManager->remove($entity);
$this->flush();
if ($flush) {
$this->flush();
}
if ($dispatchEvent) {
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::DELETE_EVENT);
@ -90,9 +88,12 @@ class EntityManager
return $this->entityManager;
}
protected function persist(EntityInterface $entity)
protected function persist(EntityInterface $entity, bool $flush = true)
{
$this->entityManager->persist($entity);
$this->flush();
if ($flush) {
$this->flush();
}
}
}

View file

@ -11,10 +11,13 @@ use App\Core\Entity\EntityInterface;
*/
class TranslatableEntityManager extends EntityManager
{
protected function persist(EntityInterface $entity)
protected function persist(EntityInterface $entity, bool $flush = true)
{
$this->entityManager->persist($entity);
$this->entityManager->persist($entity, $flush);
$entity->mergeNewTranslations();
$this->flush();
if ($flush) {
$this->flush();
}
}
}

View file

@ -3,7 +3,7 @@
namespace App\Core;
if (!defined('MURPH_VERSION')) {
define('MURPH_VERSION', 'v1.20.0');
define('MURPH_VERSION', 'v1.24.1');
}
/**

View file

@ -2,10 +2,10 @@
namespace App\Core\Notification;
use App\Entity\User;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Mailer\MailerInterface;
use Twig\Environment as TwigEnvironment;
use App\Entity\User;
/**
* class MailNotifier.
@ -14,7 +14,6 @@ use App\Entity\User;
*/
class MailNotifier
{
protected MailerInterface $mailer;
protected array $attachments = [];
protected array $recipients = [];
protected array $bccRecipients = [];
@ -22,10 +21,8 @@ class MailNotifier
protected ?string $from = null;
protected ?string $replyTo = null;
public function __construct(TwigEnvironment $twig, MailerInterface $mailer)
public function __construct(protected MailerInterface $mailer)
{
$this->mailer = $mailer;
$this->twig = $twig;
}
public function setMailer(Swift_Mailer $mailer): self

View file

@ -7,8 +7,8 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method Referer|null find($id, $lockMode = null, $lockVersion = null)
* @method Referer|null findOneBy(array $criteria, array $orderBy = null)
* @method null|Referer find($id, $lockMode = null, $lockVersion = null)
* @method null|Referer findOneBy(array $criteria, array $orderBy = null)
* @method Referer[] findAll()
* @method Referer[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/

View file

@ -3,9 +3,9 @@
namespace App\Core\Repository\Analytic;
use App\Core\Repository\Analytic\RefererRepository as Repository;
use App\Core\Repository\RepositoryQuery;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\HttpFoundation\Request;
use App\Core\Repository\RepositoryQuery;
class RefererRepositoryQuery extends RepositoryQuery
{

View file

@ -7,8 +7,8 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method View|null find($id, $lockMode = null, $lockVersion = null)
* @method View|null findOneBy(array $criteria, array $orderBy = null)
* @method null|View find($id, $lockMode = null, $lockVersion = null)
* @method null|View findOneBy(array $criteria, array $orderBy = null)
* @method View[] findAll()
* @method View[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/

View file

@ -3,9 +3,9 @@
namespace App\Core\Repository\Analytic;
use App\Core\Repository\Analytic\ViewRepository as Repository;
use App\Core\Repository\RepositoryQuery;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\HttpFoundation\Request;
use App\Core\Repository\RepositoryQuery;
class ViewRepositoryQuery extends RepositoryQuery
{

View file

@ -7,8 +7,8 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method FileInformation|null find($id, $lockMode = null, $lockVersion = null)
* @method FileInformation|null findOneBy(array $criteria, array $orderBy = null)
* @method null|FileInformation find($id, $lockMode = null, $lockVersion = null)
* @method null|FileInformation findOneBy(array $criteria, array $orderBy = null)
* @method FileInformation[] findAll()
* @method FileInformation[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/

View file

@ -7,8 +7,8 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method NavigationSetting|null find($id, $lockMode = null, $lockVersion = null)
* @method NavigationSetting|null findOneBy(array $criteria, array $orderBy = null)
* @method null|NavigationSetting find($id, $lockMode = null, $lockVersion = null)
* @method null|NavigationSetting findOneBy(array $criteria, array $orderBy = null)
* @method NavigationSetting[] findAll()
* @method NavigationSetting[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/

View file

@ -7,8 +7,8 @@ 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 null|Redirect find($id, $lockMode = null, $lockVersion = null)
* @method null|Redirect findOneBy(array $criteria, array $orderBy = null)
* @method Redirect[] findAll()
* @method Redirect[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/

View file

@ -2,9 +2,8 @@
namespace App\Core\Repository;
use App\Core\Repository\RepositoryQuery;
use Knp\Component\Pager\PaginatorInterface;
use App\Core\Repository\RedirectRepository as Repository;
use Knp\Component\Pager\PaginatorInterface;
class RedirectRepositoryQuery extends RepositoryQuery
{

View file

@ -98,6 +98,26 @@ abstract class RepositoryQuery
return $this;
}
public function count()
{
return $this
->select(sprintf('COUNT(%s.id) as total', $this->id))
->query
->getQuery()
->setMaxResults(1)
->getOneOrNullResult()['total']
;
}
protected function addForcedFilterHandler(string $name): self
{
if (!in_array($name, $this->forcedFilterHandlers)) {
$this->forcedFilterHandlers[] = $name;
}
return $this;
}
protected function populateDqlId(&$data)
{
if (is_string($data)) {

View file

@ -7,8 +7,8 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method Setting|null find($id, $lockMode = null, $lockVersion = null)
* @method Setting|null findOneBy(array $criteria, array $orderBy = null)
* @method null|Setting find($id, $lockMode = null, $lockVersion = null)
* @method null|Setting findOneBy(array $criteria, array $orderBy = null)
* @method Setting[] findAll()
* @method Setting[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/

View file

@ -32,9 +32,9 @@ class NodeRepository extends NestedTreeRepository
;
}
return $query->getQuery()
return null !== $query->getQuery()
->setMaxResults(1)
->getOneOrNullResult() !== null
->getOneOrNullResult()
;
}
}

View file

@ -12,6 +12,17 @@ $pagination-active-bg: #343a40 !default;
$sidebar-width: 260px !default;
$input-border-color: map-get($theme-colors, 'dark-blue');
$input-btn-focus-color: $input-border-color;
$component-active-color: map-get($theme-colors, 'dark-blue');
$nav-tabs-link-active-bg: map-get($theme-colors, 'dark-blue');
$nav-pills-link-active-bg: map-get($theme-colors, 'dark-blue');
$nav-tabs-link-active-color: lighten(map-get($theme-colors, 'dark-blue'), 100%);
$nav-pills-link-active-color: lighten(map-get($theme-colors, 'dark-blue'), 100%);
$input-focus-border-color: lighten(map-get($theme-colors, 'dark-blue'), 80%);
@import "~choices.js/src/styles/choices.scss";
@import "~bootstrap/scss/bootstrap.scss";
@import "~@fortawesome/fontawesome-free/css/all.css";
@ -19,9 +30,29 @@ $sidebar-width: 260px !default;
@import '~grapesjs/dist/css/grapes.min.css';
@import '~grapesjs-component-code-editor/dist/grapesjs-component-code-editor.min.css';
@for $i from 1 through 100 {
.miw-#{$i*5} {
min-width: $i * 5px;
@for $i from 1 through 400 {
.miw-#{$i}, .min-width-#{$i} {
min-width: #{$i}px;
}
.min-width-#{$i}p {
min-width: #{$i}#{"%"};
}
.maw-#{$i}, .max-width-#{$i} {
max-width: #{$i}px;
}
.max-width-#{$i}p {
max-width: #{$i}#{"%"};
}
.width-#{$i} {
width: #{$i}px;
}
.width-#{$i}p {
width: #{$i}#{"%"};
}
}
@ -51,6 +82,11 @@ body {
display: block;
}
.choices__inner, .is-focused .choices__inner, .is-open .choices__inner {
border: 1px solid map-get($theme-colors, 'dark-blue');
background: #fff;
}
.dropdown-toggle-hide-after {
&::after {
display: none;
@ -110,6 +146,7 @@ body {
.table .thead-light {
a, th {
color: map-get($theme-colors, 'dark-blue');
background: lighten(map-get($theme-colors, 'dark-blue'), 80%);
}
}
@ -402,6 +439,21 @@ th {
margin-right: 4px;
}
.form-color {
display: inline-block;
line-height: 0;
border-radius: 25%;
input {
padding: 0;
border: 0;
height: auto;
width: 30px;
aspect-ratio: 1/1;
display: inline-block;
}
}
#form-main {
> .tab-content {
@media screen and (min-width: 500px) {
@ -567,7 +619,11 @@ fieldset.form-group {
&-filter {
padding-right: 20px;
padding-bottom: 20px;
padding-bottom: 15px;
.pagination {
margin-bottom: 0;
}
@media screen and (max-width: 769px) {
padding-right: 10px;
@ -598,8 +654,20 @@ fieldset.form-group {
}
}
.table .crud-batch-column {
width: 1%;
.table {
.crud-batch-column {
width: 1%;
}
.crud-action-column {
text-align: right;
white-space: nowrap;
width: 1px;
}
}
.no-wrap {
white-space: nowrap;
}
form {
@ -657,6 +725,16 @@ form {
}
}
label.required::after {
content: '*';
margin-left: 3px;
color: #b41215;
}
.invalid-feedback {
margin-top: -3px;
}
.gjs-editor-cont {
border-radius: 10px;
overflow: hidden !important;
@ -666,6 +744,18 @@ form {
background: map-get($theme-colors, 'dark-blue');
}
.tox.tox-silver-sink.tox-tinymce-aux {
z-index: 3000 !important;
.tox {
&.tox-silver-sink.tox-tinymce-aux {
z-index: 3000 !important;
}
&.tox-tinymce {
border-color: $input-border-color;
border-radius: 5px;
}
}
.field-boolean {
color: #49555b;
font-size: 20px;
}

View file

@ -1,6 +1,7 @@
import '../../../../../../../../assets/css/admin.scss';
require('../../../../../../../../node_modules/bootstrap/dist/js/bootstrap.min.js')
require('./modules/sidebar.js')()
require('./modules/table-fixed.js')()
require('./modules/form-confirm.js')()
require('./modules/form-file.js')()
@ -27,5 +28,4 @@ require('./modules/file-manager.js')()
require('./modules/file-picker.js')()
require('./modules/analytics.js')()
require('./modules/page.js')()
require('./modules/sidebar.js')()
require('./modules/node.js')()

View file

@ -385,7 +385,7 @@ export default {
$(events).each((k, event) => {
body.on(event + '.success', () => {
$('div[id^=modal-container]').modal('hide')
$('div[id^=modal-container-]').modal('hide')
that.refresh()
})
})

View file

@ -8,16 +8,28 @@ module.exports = () => {
const form = $('#form-batch')
form.submit((e) => {
e.preventDefault()
const select = document.querySelector('#form-batch-action')
const options = select.querySelectorAll('#form-batch-action option')
let doPrevent = true
const route = form.attr('action')
const datas = form.serialize()
options.forEach((option) => {
if (option.value === select.value && option.getAttribute('data-isglobal') === 'true') {
doPrevent = false
}
})
form.addClass('is-loading')
if (doPrevent) {
e.preventDefault()
$.post(route, datas)
.always(() => {
document.location.reload()
})
const route = form.attr('action')
const datas = form.serialize()
form.addClass('is-loading')
$.post(route, datas)
.always(() => {
document.location.reload()
})
}
})
}

View file

@ -1,8 +1,9 @@
const Choices = require('choices.js')
const $ = require('jquery')
module.exports = function () {
$('*[data-jschoice]').each(function (key, item) {
return new Choices(item)
module.exports = () => {
document.querySelectorAll('*[data-jschoice]').forEach((item) => {
return new Choices(item, {
searchFields: ['label'],
})
})
}

View file

@ -36,8 +36,9 @@ const fileManagerBrowser = function (callback) {
const clickCallback = (e) => {
callback($(e.target).attr('data-value'), {})
$('div[id^="modal-container-"]').modal('hide')
container.modal('hide')
$('#fm-modal').next('.modal-backdrop').remove()
$('#fm-modal').next('div[id^="modal-container-"]').modal('hide')
$('#fm-modal').modal('hide')
$('body').off('click', '#file-manager-insert', clickCallback)
}

View file

@ -1,9 +1,34 @@
const $ = require('jquery')
const SidebarOpener = () => {
$('.sidebar-toggler .btn').click(() => {
$('.sidebar').toggleClass('is-open')
const Sidebar = () => {
const menu = document.querySelector('.sidebar')
if (!menu) {
return
}
const stickyMenu = menu.querySelector('.sidebar-sticky')
const items = stickyMenu.querySelectorAll('a.nav-link')
const currentItem = menu.querySelector('.nav-link.active')
items.forEach((item) => {
item.addEventListener('click', () => {
localStorage.setItem('sidebar-item-top', stickyMenu.scrollTop)
})
})
const toggler = menu.querySelector('.sidebar-toggler .btn')
toggler.addEventListener('click', () => {
menu.classList.toggle('is-open')
})
if (currentItem) {
stickyMenu.scrollTo({
top: Math.min(currentItem.offsetTop, localStorage.getItem('sidebar-item-top') ?? 0),
behavior: 'smooth'
})
}
}
module.exports = SidebarOpener
module.exports = Sidebar

View file

@ -36,7 +36,7 @@ const fileManagerBrowser = function (callback) {
const clickCallback = (e) => {
callback($(e.target).attr('data-value'), {})
$('div[id^="modal-container"]').modal('hide')
$('div[id^="modal-container-"]').modal('hide')
container.modal('hide')
$('body').off('click', '#file-manager-insert', clickCallback)
@ -53,7 +53,7 @@ const fileManagerBrowser = function (callback) {
})
}
if (typeof window.tinymce !== 'undefined') {
const createTinymceConfig = function () {
window.tinymce.murph = window.tinymce.murph || {}
window.tinymce.murph.selector = window.tinymce.murph.selector || '*[data-tinymce]'
window.tinymce.murph.configurationBase = window.tinymce.murph.configurationBase || {
@ -80,6 +80,7 @@ if (typeof window.tinymce !== 'undefined') {
}
window.tinymce.murph.modes = window.tinymce.murph.modes || {}
window.tinymceModes = window.tinymceModes || {}
window.tinymce.murph.modes.default = window.tinymce.murph.modes.default || {
plugins: 'print preview importcss searchreplace visualblocks visualchars fullscreen template table charmap hr pagebreak nonbreaking toc insertdatetime advlist lists wordcount textpattern noneditable help charmap quickbars link image code autoresize',
@ -95,6 +96,11 @@ if (typeof window.tinymce !== 'undefined') {
toolbar: 'undo redo | bold italic underline'
}
window.tinymce.murph.modes = {
...window.tinymce.murph.modes,
...window.tinymceModes
}
tinymce.addI18n('fr_FR', {
Redo: 'R\u00e9tablir',
Undo: 'Annuler',
@ -612,15 +618,20 @@ const doInitEditor = () => {
}
module.exports = function () {
if (typeof tinymce === 'undefined') {
return
}
const observer = new MutationObserver(doInitEditor)
const config = { attributes: false, childList: true, subtree: true }
observer.observe(document.querySelector('body'), config)
$(() => {
createTinymceConfig()
doInitEditor()
const observer = new MutationObserver(doInitEditor)
const config = { attributes: false, childList: true, subtree: true }
observer.observe(document.querySelector('body'), config)
$('body').on('hidden.bs.modal', '.modal', (e) => {
if (!$('.tox-dialog').length) {
$(e.target).find('.tox-tinymce').each(() => {
window.tinymce.remove($(this).prev().attr('id'))
})
}
})
})
}

View file

@ -1,78 +1,78 @@
<?= "<?php\n" ?>
<?php echo "<?php\n"; ?>
namespace <?= $namespace; ?>;
namespace <?php echo $namespace; ?>;
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\Manager\EntityManager;
use <?= $entity ?> as Entity;
use <?= $factory ?> as Factory;
use <?= $form ?> as Type;
use <?= $repository_query ?> as RepositoryQuery;
use <?php echo $entity; ?> as Entity;
use <?php echo $factory; ?> as Factory;
use <?php echo $form; ?> as Type;
use <?php echo $repository_query; ?> as RepositoryQuery;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
class <?= $class_name; ?> extends CrudController
class <?php echo $class_name; ?> extends CrudController
{
protected ?CrudConfiguration $configuration = null;
#[Route(path: '/admin/<?= $route; ?>/{page}', name: 'admin_<?= $route; ?>_index', methods: ['GET'], requirements: ['page' => '\d+'])]
#[Route(path: '/admin/<?php echo $route; ?>/{page}', name: 'admin_<?php echo $route; ?>_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(path: '/admin/<?= $route; ?>/new', name: 'admin_<?= $route; ?>_new', methods: ['GET', 'POST'])]
#[Route(path: '/admin/<?php echo $route; ?>/new', name: 'admin_<?php echo $route; ?>_new', methods: ['GET', 'POST'])]
public function new(Factory $factory, EntityManager $entityManager, Request $request): Response
{
return $this->doNew($factory->create(), $entityManager, $request);
}
#[Route(path: '/admin/<?= $route; ?>/show/{entity}', name: 'admin_<?= $route; ?>_show', methods: ['GET'])]
#[Route(path: '/admin/<?php echo $route; ?>/show/{entity}', name: 'admin_<?php echo $route; ?>_show', methods: ['GET'])]
#[IsGranted('show', 'entity')]
public function show(Entity $entity): Response
{
return $this->doShow($entity);
}
#[Route(path: '/admin/<?= $route; ?>/filter', name: 'admin_<?= $route; ?>_filter', methods: ['GET'])]
#[Route(path: '/admin/<?php echo $route; ?>/filter', name: 'admin_<?php echo $route; ?>_filter', methods: ['GET'])]
public function filter(Session $session): Response
{
return $this->doFilter($session);
}
#[Route(path: '/admin/<?= $route; ?>/edit/{entity}', name: 'admin_<?= $route; ?>_edit', methods: ['GET', 'POST'])]
#[Route(path: '/admin/<?php echo $route; ?>/edit/{entity}', name: 'admin_<?php echo $route; ?>_edit', methods: ['GET', 'POST'])]
#[IsGranted('edit', 'entity')]
public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response
{
return $this->doEdit($entity, $entityManager, $request);
}
#[Route(path: '/admin/<?= $route; ?>/inline_edit/{entity}/{context}/{label}', name: 'admin_<?= $route; ?>_inline_edit', methods: ['GET', 'POST'])]
#[Route(path: '/admin/<?php echo $route; ?>/inline_edit/{entity}/{context}/{label}', name: 'admin_<?php echo $route; ?>_inline_edit', methods: ['GET', 'POST'])]
#[IsGranted('edit', 'entity')]
public function inlineEdit(string $context, string $label, Entity $entity, EntityManager $entityManager, Request $request): Response
{
return $this->doInlineEdit($context, $label, $entity, $entityManager, $request);
}
#[Route(path: '/admin/<?= $route; ?>/sort/{page}', name: 'admin_<?= $route; ?>_sort', methods: ['POST'], requirements: ['page' => '\d+'])]
#[Route(path: '/admin/<?php echo $route; ?>/sort/{page}', name: 'admin_<?php echo $route; ?>_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(path: '/admin/<?= $route; ?>/batch/{page}', name: 'admin_<?= $route; ?>_batch', methods: ['POST'], requirements: ['page' => '\d+'])]
#[Route(path: '/admin/<?php echo $route; ?>/batch/{page}', name: 'admin_<?php echo $route; ?>_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(path: '/admin/<?= $route; ?>/delete/{entity}', name: 'admin_<?= $route; ?>_delete', methods: ['DELETE', 'POST'])]
#[Route(path: '/admin/<?php echo $route; ?>/delete/{entity}', name: 'admin_<?php echo $route; ?>_delete', methods: ['DELETE', 'POST'])]
#[IsGranted('delete', 'entity')]
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
{
@ -86,26 +86,26 @@ class <?= $class_name; ?> extends CrudController
}
return $this->configuration = CrudConfiguration::create()
->setPageTitle('index', 'List of <?= $entity; ?>')
->setPageTitle('index', 'List of <?php echo $entity; ?>')
->setPageTitle('edit', 'Edition of {id}')
->setPageTitle('new', 'New <?= $entity; ?>')
->setPageTitle('new', 'New <?php echo $entity; ?>')
->setPageTitle('show', 'View of {id}')
->setPageRoute('index', 'admin_<?= $route; ?>_index')
->setPageRoute('new', 'admin_<?= $route; ?>_new')
->setPageRoute('edit', 'admin_<?= $route; ?>_edit')
->setPageRoute('inline_edit', 'admin_<?= $route; ?>_inline_edit')
->setPageRoute('show', 'admin_<?= $route; ?>_show')
->setPageRoute('sort', 'admin_<?= $route; ?>_sort')
->setPageRoute('batch', 'admin_<?= $route; ?>_batch')
->setPageRoute('delete', 'admin_<?= $route; ?>_delete')
->setPageRoute('filter', 'admin_<?= $route; ?>_filter')
->setPageRoute('index', 'admin_<?php echo $route; ?>_index')
->setPageRoute('new', 'admin_<?php echo $route; ?>_new')
->setPageRoute('edit', 'admin_<?php echo $route; ?>_edit')
->setPageRoute('inline_edit', 'admin_<?php echo $route; ?>_inline_edit')
->setPageRoute('show', 'admin_<?php echo $route; ?>_show')
->setPageRoute('sort', 'admin_<?php echo $route; ?>_sort')
->setPageRoute('batch', 'admin_<?php echo $route; ?>_batch')
->setPageRoute('delete', 'admin_<?php echo $route; ?>_delete')
->setPageRoute('filter', 'admin_<?php echo $route; ?>_filter')
->setForm('edit', Type::class, [])
->setForm('edit', Type::class)
->setForm('new', Type::class)
->setView('form', 'admin/<?= $route; ?>_admin/_form.html.twig')
->setView('show_entity', 'admin/<?= $route; ?>_admin/_show.html.twig')
->setView('form', 'admin/<?php echo $route; ?>_admin/_form.html.twig')
->setView('show_entity', 'admin/<?php echo $route; ?>_admin/_show.html.twig')
// ->setForm('filter', Type::class)
@ -149,6 +149,6 @@ class <?= $class_name; ?> extends CrudController
protected function getSection(): string
{
return '<?= $route; ?>';
return '<?php echo $route; ?>';
}
}

View file

@ -1,11 +1,11 @@
<?= "<?php\n" ?>
<?php echo "<?php\n"; ?>
namespace <?= $namespace; ?>;
namespace <?php echo $namespace; ?>;
use App\Core\Factory\FactoryInterface;
use <?= $entity ?> as Entity;
use <?php echo $entity; ?> as Entity;
class <?= $class_name; ?> implements FactoryInterface
class <?php echo $class_name; ?> implements FactoryInterface
{
public function create(): Entity
{

View file

@ -1,6 +1,6 @@
<?= "<?php\n" ?>
<?php echo "<?php\n"; ?>
namespace <?= $namespace; ?>;
namespace <?php echo $namespace; ?>;
use App\Core\Entity\Site\Page\Page;
use App\Core\Entity\Site\Page as BlockEntity;
@ -9,28 +9,28 @@ use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Form\FormBuilderInterface;
#[ORM\Entity]
class <?= $class_name; ?> extends Page
class <?php echo $class_name; ?> extends Page
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
<?php if (count($blocks)): ?> $builder
<?php foreach ($blocks as $block): ?>
->add('<?= $block['name'] ?>', <?= $block['type'] ?>)
<?php endforeach; ?>
<?php if (count($blocks)) { ?> $builder
<?php foreach ($blocks as $block) { ?>
->add('<?php echo $block['name']; ?>', <?php echo $block['type']; ?>)
<?php } ?>
;
<?php endif; ?>
<?php } ?>
}
<?php foreach ($blocks as $block): ?>
public function set<?= $block['camelCase'] ?>(BlockEntity\Block $block)
<?php foreach ($blocks as $block) { ?>
public function set<?php echo $block['camelCase']; ?>(BlockEntity\Block $block)
{
return $this->setBlock($block);
}
public function get<?= $block['camelCase'] ?>()
public function get<?php echo $block['camelCase']; ?>()
{
return $this->getBlock('<?= $block['name'] ?>'<?php if ($block['class']): ?>, <?= $block['class'] ?><?php endif; ?>);
return $this->getBlock('<?php echo $block['name']; ?>'<?php if ($block['class']) { ?>, <?php echo $block['class']; ?><?php } ?>);
}
<?php endforeach; ?>
<?php } ?>
}

View file

@ -1,15 +1,15 @@
<?= "<?php\n" ?>
<?php echo "<?php\n"; ?>
namespace <?= $namespace; ?>;
namespace <?php echo $namespace; ?>;
use App\Core\Repository\RepositoryQuery;
use Knp\Component\Pager\PaginatorInterface;
use <?= $repository; ?> as Repository;
use <?php echo $repository; ?> as Repository;
class <?= $class_name; ?> extends RepositoryQuery
class <?php echo $class_name; ?> extends RepositoryQuery
{
public function __construct(Repository $repository, PaginatorInterface $paginator)
{
parent::__construct($repository, '<?= $id; ?>', $paginator);
parent::__construct($repository, '<?php echo $id; ?>', $paginator);
}
}

View file

@ -64,7 +64,7 @@
"Display name": "Nom d'affichage"
"Administrator": "Administrateur⋅trice"
"Writer": "Rédacteur⋅trice"
"Reference": "Élement de référence"
"Reference": "Référence"
"My account": "Mon compte"
"Logout": "Déconnexion"
"Dashboard": "Tableau de bord"
@ -223,3 +223,4 @@
"At least one role": "Au moins un rôle"
"All roles": "Tous les rôles"
"Enable A/B Testing": "Activer le test A/B"
"Color": "Couleur"

View file

@ -1,11 +1,22 @@
{%- block value -%}
{% if value %}
<span class="rounded-pill pl-3 pr-1 bg-secondary text-white">
<span class="fa fa-circle"></span>
</span>
{% else %}
<span class="rounded-pill pr-3 pl-1 bg-secondary text-white">
<span class="fa fa-circle"></span>
</span>
{% endif %}
{% set value = value|default(options.default_value) %}
<span class="field-boolean">
{% if options.display == 'checkbox' %}
{% block checkbox %}
<span class="fa {{ value ? options.checkbox_class_when_true : options.checkbox_class_when_false }}"></span>
{% endblock %}
{% elseif options.display == 'toggle' %}
{% block toggle %}
{% if value %}
<span class="rounded-pill pl-3 pr-1 text-white {{ options.toggle_class_when_true }}">
<span class="fa fa-circle"></span>
</span>
{% else %}
<span class="rounded-pill pr-3 pl-1 text-white {{ options.toggle_class_when_false }}">
<span class="fa fa-circle"></span>
</span>
{% endif %}
{% endblock %}
{% endif %}
</span>
{%- endblock -%}

View file

@ -2,7 +2,7 @@
{%- if value -%}
<span class="btn btn-sm btn-light">
<span class="fa fa-calendar-alt text-black-50 mr-2"></span>
{{- value|date(options.format) -}}
{{- value|default(options.default_value)|date(options.format) -}}
</span>
{%- endif -%}
{%- endblock -%}

View file

@ -1,3 +1,7 @@
{%- block value -%}
<img {% for k, v in options.image_attr %}{{ k }}="{{ v }}" {% endfor %} src="{{ asset(value) }}">
{% set value = value|default(options.default_value) %}
{% if value %}
<img {% for k, v in options.image_attr %}{{ k }}="{{ v }}" {% endfor %} src="{{ asset(value) }}">
{% endif %}
{%- endblock -%}

View file

@ -1,7 +1,7 @@
{%- block value -%}
{%- if options.raw -%}
{{- value|raw -}}
{{- value|default(options.default_value)|raw -}}
{%- else -%}
{{- value|trans -}}
{{- value|default(options.default_value)|trans -}}
{%- endif -%}
{%- endblock -%}

View file

@ -1,6 +1,7 @@
{% extends '@Core/admin/layout.html.twig' %}
{% set context = context ?? 'index' %}
{% set pager_render = pager is defined ? knp_pagination_render(pager) : null %}
{% block title %}{{ configuration.pageTitle(context)|trans }} {{ parent() ? ('- ' ~ parent()) : '' }}{% endblock %}
@ -67,16 +68,17 @@
<div class="row crud-header-filter">
<div class="col-auto ml-auto {% if pager.getPaginationData.pageCount > 1 %}mr-3{% endif %}">
<button data-modal="{{ path(configuration.pageRoute('filter'), configuration.pageRouteParams('filter')) }}" class="btn btn-sm btn-secondary">
<span class="fa fa-search mr-1"></span>
{{ 'Filter'|trans }} {% if not filters.isEmpty %}({{ 'yes'|trans }}){% endif %}
</button>
</div>
<div class="col-auto">
{{ knp_pagination_render(pager) }}
{{ pager_render|raw }}
</div>
</div>
{% else %}
<div class="crud-header-filter">
{{ knp_pagination_render(pager) }}
{{ pager_render|raw }}
</div>
{% endif %}
{% endblock %}
@ -85,6 +87,7 @@
{% block list %}
{% set isSortable = configuration.isSortableCollection(context) %}
{% set colspan = configuration.fields(context)|length + 1 + (configuration.hasBatchAction(context) ? 1 : 0) %}
<div class="table-responsive">
<table class="table">
@ -129,10 +132,13 @@
<a href="{{ url }}">
{% if icon is defined %}
<span class="{{ icon }}"></span>
<span class="no-wrap">
<span class="{{ icon }}"></span>
{{ label|trans }}
</span>
{% else %}
{{ label|trans }}
{% endif %}
{{ label|trans }}
</a>
{% else %}
{{ label|trans }}
@ -144,7 +150,7 @@
{% endblock %}
{% endfor %}
{% if configuration.showActions %}
<th class="crud-action-column col-2 miw-100 text-right">
<th class="crud-action-column">
{{ 'Actions'|trans }}
</th>
{% endif %}
@ -204,7 +210,7 @@
entity: item.id,
context: context,
label: label,
redirectTo: app.request.uri,
redirectTo: app.request.pathInfo,
}|merge(configuration.pagerouteparams('inline_edit'))) }}" data-modal-create>
{{ render_field(item, config, configuration.defaultlocale) }}
</a>
@ -223,7 +229,7 @@
{% endfor %}
{% if configuration.showActions %}
<td class="crud-action-column col-2 miw-200 text-right">
<td class="crud-action-column">
{% block list_item_actions %}
{% block list_item_actions_before %}{% endblock %}
@ -281,17 +287,24 @@
</div>
{% endif %}
<div class="float-right mr-2">
{{ pager_render|raw }}
</div>
{% if configuration.hasBatchAction(context) %}
<div class="mb-2">
<form class="form-inline" action="{{ path(configuration.pageRoute('batch'), {page: pager.currentPageNumber}|merge(configuration.pageRouteParams('batch'))) }}" id="form-batch" method="POST">
<form class="form-inline" action="{{ path(configuration.pageRoute('batch'), {
page: pager.currentPageNumber,
redirectTo: app.request.uri
}|merge(configuration.pageRouteParams('batch'))) }}" id="form-batch" method="POST">
<select class="form-control my-1 mr-sm-2" name="batch[target]">
<option value="selection">{{ 'For selection'|trans }}</option>
<option value="all">{{ 'For all items'|trans }}</option>
</select>
<select class="form-control my-1 mr-sm-2" name="batch[action]">
<select class="form-control my-1 mr-sm-2" id="form-batch-action" name="batch[action]">
<option value=""></option>
{% for action, conf in configuration.batchActions(context) %}
<option value="{{ action }}">
<option value="{{ action }}" {% if conf.isGlobal %}data-isglobal="true"{% endif %}>
{{ conf.label|trans }}
</option>
{% endfor %}
@ -312,13 +325,8 @@
{% endblock %}
{% endif %}
{% else %}
{% set count = configuration.fields(context)|length + 1 %}
{% if configuration.hasBatchAction(context) %}
{% set count = count + 1 %}
{% endif %}
<tr>
<td class="col-12 text-center p-4 text-black-50" colspan="{{ count }}">
<td class="col-12 text-center p-4 text-black-50" colspan="{{ colspan }}">
<div class="display-1">
<span class="fa fa-search"></span>
</div>

View file

@ -7,7 +7,7 @@
</button>
</div>
<div class="modal-body">
<form action="{{ path(configuration.pageRoute('inline_edit'), {entity: entity.id, context: context, label: label, redirectTo, redirectTo}|merge(configuration.pageRouteParams('inline_edit'))) }}" id="form-entity-edit" method="POST">
<form action="{{ path(configuration.pageRoute('inline_edit'), {entity: entity.id, context: context, label: label, redirectTo: redirectTo}|merge(configuration.pageRouteParams('inline_edit'))) }}" id="form-entity-edit" method="POST">
{{ include('@Core/setting/setting_admin/_form.html.twig') }}
</form>
</div>

View file

@ -197,6 +197,10 @@
</div>
<div>
{% if splInfo.isFile and not isLocked %}
<button type="submit" class="btn btn-primary" form="form-fm-attributes">{{ 'Save'|trans }}</button>
{% endif %}
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ 'Close'|trans }}</button>
</div>
{% endif %}

View file

@ -7,6 +7,15 @@
</div>
{% endblock %}
{% block color_widget %}
<div class="form-color-container">
<div class="form-color">
{%- set type = type|default('color') -%}
{{ block('form_widget_simple') }}
</div>
</div>
{% endblock %}
{% block file_widget -%}
<div class="row">
<div class="col-12">
@ -51,6 +60,9 @@
{% endblock %}
{% block collection_block_widget %}
{% set allow_delete = allow_delete|default(false) %}
{% set allow_add = allow_add|default(false) %}
<div data-collection="collection-{{ collection_name }}">
{% for item in form.value %}
<div data-collection-item="{{ loop.index }}">
@ -98,14 +110,24 @@
{% block murph_collection_widget %}
{% set attrs = attr|merge({class: 'mb-1 ' ~ (attr.class ?? '')}) %}
{% set allow_delete = allow_delete|default(false) %}
{% set allow_add = allow_add|default(false) %}
<div data-collection="collection-{{ collection_name }}" {% for attr, value in row_attr %}{{ attr }}="{{ value }}"{% endfor %}>
{% for item in form %}
<div data-collection-item="{{ loop.index }}" {% for attr, value in attrs %}{{ attr }}="{{ value }}"{% endfor %}>
{% if template_before_item %}
{{ include(template_before_item) }}
{% endif %}
{% for child in item %}
{{ form_row(child) }}
{% endfor %}
{% if template_after_item %}
{{ include(template_after_item) }}
{% endif %}
{% if allow_delete %}
<div class="text-right">
<span data-collection-delete-container class="btn btn-sm btn-danger">
@ -232,3 +254,51 @@
{{- form_help(form) -}}
</{{ element|default('div') }}>
{%- endblock form_row %}
{% block form_label -%}
{% if label is not same as(false) -%}
{%- if compound is defined and compound -%}
{%- set element = 'legend' -%}
{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' col-form-label')|trim}) -%}
{%- else -%}
{%- set label_attr = label_attr|merge({for: id}) -%}
{%- endif -%}
{% if required -%}
{% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) %}
{%- endif -%}
{% if label is empty -%}
{%- if label_format is not empty -%}
{% set label = label_format|replace({
'%name%': name,
'%id%': id,
}) %}
{%- else -%}
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
<{{ element|default('label') }}{% if label_attr %}{% with { attr: label_attr } %}{{ block('attributes') }}{% endwith %}{% endif %}>
{%- if translation_domain is same as(false) -%}
{%- if label_html is same as(false) -%}
{{- label -}}
{%- else -%}
{{- label|raw -}}
{%- endif -%}
{%- else -%}
{%- if label_html is same as(false) -%}
{{- label|trans(label_translation_parameters, translation_domain) -}}
{%- else -%}
{{- label|trans(label_translation_parameters, translation_domain)|raw -}}
{%- endif -%}
{%- endif -%}
</{{ element|default('label') }}>
{% block form_label_errors %}{{- form_errors(form) -}}{% endblock form_label_errors %}
{%- else -%}
{%- if errors|length > 0 -%}
<div id="{{ id }}_errors" class="mb-2">
{{- form_errors(form) -}}
</div>
{%- endif -%}
{%- endif -%}
{%- endblock form_label %}

View file

@ -7,7 +7,7 @@
</button>
</div>
<div class="modal-body">
<form action="{{ path('admin_navigation_setting_edit', {entity: entity.id}) }}" id="form-entity-edit" method="POST">
<form action="{{ path('admin_navigation_setting_edit', {entity: entity.id, redirectTo: redirectTo}) }}" id="form-entity-edit" method="POST">
{{ include('@Core/setting/navigation_setting_admin/_form.html.twig') }}
</form>
</div>

View file

@ -7,7 +7,7 @@
</button>
</div>
<div class="modal-body">
<form action="{{ path('admin_setting_edit', {entity: entity.id}) }}" id="form-entity-edit" method="POST">
<form action="{{ path('admin_setting_edit', {entity: entity.id, redirectTo: redirectTo}) }}" id="form-entity-edit" method="POST">
{{ include('@Core/setting/setting_admin/_form.html.twig') }}
</form>
</div>

Some files were not shown because too many files have changed in this diff Show more