Compare commits

..

13 commits
master ... v2

Author SHA1 Message Date
Simon Vieille ce4deb24f8
feat(upgrade): add compliance with symfony 6.2 2023-01-25 22:18:55 +01:00
Simon Vieille 79c799748b
feat(upgrade): add compliance with symfony 6.2 2023-01-25 22:17:28 +01:00
Simon Vieille b20d542dc3
feat(upgrade): add compliance with symfony 6.2 2023-01-25 22:14:18 +01:00
Simon Vieille c19dc624c7
feat(upgrade): add compliance with symfony 6.2 2023-01-25 22:12:16 +01:00
Simon Vieille 8e7a7e25a1
feat(upgrade): upgrade php dep 2023-01-25 21:51:47 +01:00
Simon Vieille 01622180be
feat(upgrade): add compliance with symfony 6.2 2023-01-25 21:39:37 +01:00
Simon Vieille f3f4417d7c
feat(upgrade): add compliance with symfony 6.2 2023-01-25 21:38:37 +01:00
Simon Vieille 027b7df80e
feat(upgrade): upgrade php dep 2023-01-25 21:36:17 +01:00
Simon Vieille a8ab2d3945
feat(upgrade): upgrade php dep 2023-01-25 21:34:33 +01:00
Simon Vieille 76a8cf1751
feat(upgrade): upgrade php dep 2023-01-25 21:28:38 +01:00
Simon Vieille 8ff8ec9dac
feat(upgrade): upgrade php dep 2023-01-25 20:58:38 +01:00
Simon Vieille 8518b05b8d Merge branch 'develop' into v2 2023-01-25 20:53:13 +01:00
Simon Vieille 7e8281aa8f update symfony to v6.0 2022-03-25 15:18:54 +01:00
177 changed files with 971 additions and 3176 deletions

View file

@ -1,115 +1,5 @@
## [Unreleased]
## [v1.25.1] - 2024-05-13
### Added
* add drag&drop in the block builder
## [v1.25.0] - 2024-05-12
### Added
* add block builder widget
* 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
* add block class name for the choice type in the page maker
* update file details view on the file manager
* add form options in the crud filter action
* add trans filter in inline form modal title
* add setter to define all fields in a defined context
* add filename generator setter in FileUploadHandler
* add variable for the sidebar size
* add twig block to override defaults actions in crud index template
* add option to remove iterable values and/or specifics keys in the twig toArray function
* add boolean field for CRUD
* add context variable in each controllers to simplify overrides
* core.site.name and core.site.logo are not longer required
* add default templates when a crud is generated
* add boolean 'is_disabled' in the menu item template options
### Fixed
* fix filemanager date ordering
* fix maker CrudController template: remove bad pasted code
* fix redirect listener: use boolean instead of integer
* fix responsive of account edit template
* fix collection widget: allow_add/allow_delete and prototype
### Changed
* user admin routes are defined in core, custom controller is not required
## [1.19.0] - 2023-04-15
### Added
* feat(page): forms for metas, opengraph and extra informations can be removed
* feat(navigation): user interface is improved
* feat(file): webp is allowed and shown in form widgets and in file manager details
* feat(file): the file manager now show the size and the modification date of a file
* feat(crud): add option `action` in field to add a link to the view page or to the edition page
* feat(crud): add option `inline_form` in field to configure to edit the data
* feat(crud): add `setDoubleClick` in the crud configuration
## [1.18.0] - 2023-01-13
### Added
* feat(dep): add symfony/runtime

View file

@ -17,55 +17,61 @@
"doctrine/doctrine-migrations-bundle": "^3.2",
"doctrine/orm": "^2.11",
"fusonic/opengraph": "^2.2",
"friendsofsymfony/jsrouting-bundle": "^2.8",
"friendsofsymfony/jsrouting-bundle": "^3.2",
"jaybizzle/crawler-detect": "^1.2",
"knplabs/doctrine-behaviors": "^2.6",
"knplabs/knp-paginator-bundle": "^5.8",
"liip/imagine-bundle": "^2.7",
"matomo/device-detector": "^5.0",
"phpdocumentor/reflection-docblock": "^5.3",
"scheb/2fa-google-authenticator": "^5.13",
"scheb/2fa-qr-code": "^5.13",
"scheb/2fa-bundle": "^6.5",
"sensio/framework-extra-bundle": "^6.2",
"sensiolabs/ansi-to-html": "^1.2",
"spe/filesize-extension-bundle": "~2.0.0",
"stof/doctrine-extensions-bundle": "^1.7",
"symfony/apache-pack": "^1.0",
"symfony/asset": "5.4.*",
"symfony/console": "5.4.*",
"symfony/dotenv": "5.4.*",
"symfony/event-dispatcher": "5.4.*",
"symfony/expression-language": "5.4.*",
"symfony/finder": "5.4.*",
"symfony/asset": "6.2.*",
"symfony/console": "6.2.*",
"symfony/dotenv": "6.2.*",
"symfony/event-dispatcher": "6.2.*",
"symfony/expression-language": "6.2.*",
"symfony/finder": "6.2.*",
"symfony/form": "6.2.*",
"symfony/framework-bundle": "6.2.*",
"symfony/http-client": "6.2.*",
"symfony/intl": "6.2.*",
"symfony/mailer": "6.2.*",
"symfony/mime": "6.2.*",
"symfony/flex": "^2.2",
"symfony/form": "5.4.*",
"symfony/framework-bundle": "5.4.*",
"symfony/http-client": "5.4.*",
"symfony/intl": "5.4.*",
"symfony/mailer": "5.4.*",
"symfony/mime": "5.4.*",
"symfony/monolog-bundle": "^3.1",
"symfony/notifier": "5.4.*",
"symfony/process": "5.4.*",
"symfony/property-access": "5.4.*",
"symfony/property-info": "5.4.*",
"symfony/proxy-manager-bridge": "5.4.*",
"symfony/security-bundle": "5.4.*",
"symfony/serializer": "5.4.*",
"symfony/string": "5.4.*",
"symfony/translation": "5.4.*",
"symfony/twig-bundle": "^5.2",
"symfony/validator": "5.4.*",
"symfony/web-link": "5.4.*",
"symfony/notifier": "6.2.*",
"symfony/process": "6.2.*",
"symfony/property-access": "6.2.*",
"symfony/property-info": "6.2.*",
"symfony/proxy-manager-bridge": "6.2.*",
"symfony/security-bundle": "6.2.*",
"symfony/serializer": "6.2.*",
"symfony/string": "6.2.*",
"symfony/translation": "6.2.*",
"symfony/twig-bundle": "^6.0",
"symfony/validator": "6.2.*",
"symfony/web-link": "6.2.*",
"symfony/webpack-encore-bundle": "^1.11",
"symfony/yaml": "5.4.*",
"symfony/yaml": "6.2.*",
"twig/extra-bundle": "^2.12|^3.3",
"twig/twig": "^2.12|^3.3",
"symfony/runtime": "^5.4"
"symfony/runtime": "6.2.*",
"scheb/2fa-totp": "^6.5"
},
"autoload": {
"psr-4": {
"App\\Core\\": "src/core/"
}
},
"config": {
"allow-plugins": {
"symfony/flex": false,
"symfony/runtime": false
}
}
}

View file

@ -10,12 +10,14 @@ 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(protected string $name)
public function __construct(string $name)
{
$this->name = $name;
}
public function getName(): string

View file

@ -13,16 +13,18 @@ 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(
protected ViewRepositoryQuery $viewQuery,
protected RefererRepositoryQuery $refererQuery
) {
public function __construct(ViewRepositoryQuery $viewQuery, RefererRepositoryQuery $refererQuery)
{
$this->viewQuery = $viewQuery;
$this->refererQuery = $refererQuery;
}
public function getViews(): array
@ -81,7 +83,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;
}
@ -128,7 +130,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,10 +10,16 @@ namespace App\Core\Annotation;
#[\Attribute]
class UrlGenerator
{
public function __construct(
public string $service,
public string $method,
public array $options = []
) {
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;
}
}

View file

@ -16,26 +16,53 @@ use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
{
use TargetPathTrait;
public function __construct(
private EntityManagerInterface $entityManager,
private UrlGeneratorInterface $urlGenerator,
private CsrfTokenManagerInterface $csrfTokenManager,
private UserPasswordEncoderInterface $passwordEncoder
) {
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 supports(Request $request)
public function supports(Request $request): bool
{
return 'auth_login' === $request->attributes->get('_route') && $request->isMethod('POST');
}
public function authenticate(Request $request): Passport
{
$id = $request->request->get('id', '');
$request->getSession()->set(Security::LAST_USERNAME, $id);
return new Passport(
new UserBadge($id),
new PasswordCredentials($request->request->get('password', '')),
[
new CsrfTokenBadge('authenticate', $request->request->get('_csrf_token')),
]
);
}
public function getCredentials(Request $request)
{
$credentials = [
@ -72,7 +99,7 @@ class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey): RedirectResponse
{
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
@ -81,7 +108,7 @@ class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
return new RedirectResponse($this->urlGenerator->generate('admin_dashboard_index'));
}
protected function getLoginUrl()
protected function getLoginUrl(Request $request): string
{
return $this->urlGenerator->generate('auth_login');
}

View file

@ -1,47 +0,0 @@
<?php
namespace App\Core\BuilderBlock\Block\Bootstrap;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
use Symfony\Contracts\Translation\TranslatorInterface;
#[AutoconfigureTag('builder_block.widget')]
class AlertBlock extends BootstrapBlock
{
public function __construct(protected TranslatorInterface $translator)
{
}
public function configure()
{
parent::configure();
$options = [];
foreach ([
'Primary' => 'primary',
'Secondary' => 'secondary',
'Info' => 'info',
'Success' => 'success',
'Danger' => 'danger',
'Warning' => 'warning',
'Light' => 'light',
'Dark' => 'dark',
] as $k => $v) {
$options[] = [
'text' => $this->translator->trans($k),
'value' => $v,
];
}
$this
->setName('bsAlert')
->setLabel('Alert')
->setOrder(4)
->setIsContainer(true)
->setIcon('<i class="fas fa-exclamation-circle"></i>')
->setTemplate('@Core/builder_block/bootstrap/alert.html.twig')
->addSetting(name: 'level', label: 'Level', type: 'select', extraOptions: ['options' => $options])
;
}
}

View file

@ -1,14 +0,0 @@
<?php
namespace App\Core\BuilderBlock\Block\Bootstrap;
use App\Core\BuilderBlock\BuilderBlock;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
class BootstrapBlock extends BuilderBlock
{
public function configure()
{
$this->setCategory('Bootstrap');
}
}

View file

@ -1,30 +0,0 @@
<?php
namespace App\Core\BuilderBlock\Block\Bootstrap;
use App\Core\BuilderBlock\BuilderBlock;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
#[AutoconfigureTag('builder_block.widget')]
class ColumnBlock extends BootstrapBlock
{
public function configure()
{
parent::configure();
$this
->setName('bsColumn')
->setLabel('Column')
->setIsContainer(true)
->setOrder(3)
->setClass('col-12 col-lg-2 pr-md-1')
->setTemplate('@Core/builder_block/bootstrap/column.html.twig')
->setIcon('<i class="fas fa-columns"></i>')
->addSetting(name: 'size', label: 'Extra small', type: 'number', attributes: ['min' => 0, 'max' => 12])
->addSetting(name: 'sizeSm', label: 'Small', type: 'number', attributes: ['min' => 0, 'max' => 12])
->addSetting(name: 'sizeMd', label: 'Medium', type: 'number', attributes: ['min' => 0, 'max' => 12])
->addSetting(name: 'sizeLg', label: 'Large', type: 'number', attributes: ['min' => 0, 'max' => 12])
->addSetting(name: 'sizeXl', label: 'Extra large', type: 'number', attributes: ['min' => 0, 'max' => 12])
;
}
}

View file

@ -1,25 +0,0 @@
<?php
namespace App\Core\BuilderBlock\Block\Bootstrap;
use App\Core\BuilderBlock\BuilderBlock;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
#[AutoconfigureTag('builder_block.widget')]
class ContainerBlock extends BootstrapBlock
{
public function configure()
{
parent::configure();
$this
->setName('bsContainer')
->setLabel('Container')
->setIsContainer(true)
->setOrder(1)
->setTemplate('@Core/builder_block/bootstrap/container.html.twig')
->setIcon('<i class="fas fa-th"></i>')
->addSetting(name: 'isFluid', label: 'Fluid', type: 'checkbox')
;
}
}

View file

@ -1,25 +0,0 @@
<?php
namespace App\Core\BuilderBlock\Block\Bootstrap;
use App\Core\BuilderBlock\BuilderBlock;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
#[AutoconfigureTag('builder_block.widget')]
class RowBlock extends BootstrapBlock
{
public function configure()
{
parent::configure();
$this
->setName('bsRow')
->setLabel('Row')
->setOrder(2)
->setIsContainer(true)
->setIcon('<i class="fas fa-align-justify"></i>')
->setTemplate('@Core/builder_block/bootstrap/row.html.twig')
->addWidget('bsColumn')
;
}
}

View file

@ -1,14 +0,0 @@
<?php
namespace App\Core\BuilderBlock\Block\Editor;
use App\Core\BuilderBlock\BuilderBlock;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
class EditorBlock extends BuilderBlock
{
public function configure()
{
$this->setCategory('Editor');
}
}

View file

@ -1,26 +0,0 @@
<?php
namespace App\Core\BuilderBlock\Block\Editor;
use App\Core\BuilderBlock\BuilderBlock;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
#[AutoconfigureTag('builder_block.widget')]
class TextareaBlock extends EditorBlock
{
public function configure()
{
parent::configure();
$this
->setName('textarea')
->setLabel('Text')
->setIsContainer(false)
->setIcon('<i class="fas fa-pencil-alt"></i>')
->setTemplate('@Core/builder_block/editor/textarea.html.twig')
->addSetting(name: 'nl2br', label: 'Insert line breaks', type: 'checkbox', default: true)
->addSetting(name: 'allowHtml', label: 'Allow HTML', type: 'checkbox', default: false)
->addSetting(name: 'value', type: 'textarea')
;
}
}

View file

@ -1,24 +0,0 @@
<?php
namespace App\Core\BuilderBlock\Block\Editor;
use App\Core\BuilderBlock\BuilderBlock;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
#[AutoconfigureTag('builder_block.widget')]
class TinymceBlock extends EditorBlock
{
public function configure()
{
parent::configure();
$this
->setName('tinymce')
->setLabel('TinyMCE')
->setIsContainer(false)
->setIcon('<i class="fas fa-pencil-alt"></i>')
->setTemplate('@Core/builder_block/editor/tinymce.html.twig')
->addSetting(name: 'value', type: 'textarea', attributes: ['data-tinymce' => ''])
;
}
}

View file

@ -1,195 +0,0 @@
<?php
namespace App\Core\BuilderBlock;
abstract class BuilderBlock
{
protected string $name;
protected string $label;
protected ?string $class = 'col-12';
protected ?string $category = null;
protected array $settings = [];
protected array $widgets = [];
protected array $vars = [];
protected string $template = '';
protected bool $isContainer = false;
protected ?string $icon = null;
protected int $order = 1;
abstract public function configure();
public function setLabel(string $label): self
{
$this->label = $label;
return $this;
}
public function getLabel(): string
{
return $this->label;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getName(): string
{
return $this->name;
}
public function setCategory(?string $category): self
{
$this->category = $category;
return $this;
}
public function getCategory(): ?string
{
return $this->category;
}
public function setIsContainer(bool $isContainer): self
{
$this->isContainer = $isContainer;
return $this;
}
public function getIsContainer(): bool
{
return $this->isContainer;
}
public function setWidgets(array $widgets): self
{
$this->widgets = $widgets;
return $this;
}
public function addWidget(string $widget): self
{
if (!in_array($widget, $this->widgets)) {
$this->widgets[] = $widget;
}
return $this;
}
public function getWidgets(): array
{
return $this->widgets;
}
public function setSettings(array $settings): self
{
$this->settings = $settings;
return $this;
}
public function addSetting(
string $name,
string $type = 'input',
?string $label = null,
array $attributes = [],
array $extraOptions = [],
$default = null
) {
$this->settings[$name] = [
'label' => $label,
'type' => $type,
'attr' => $attributes,
'default' => $default,
];
foreach ($extraOptions as $key => $value) {
if (!in_array($key, array_keys($this->settings[$name]))) {
$this->settings[$name][$key] = $value;
}
}
return $this;
}
public function getSettings(): array
{
return $this->settings;
}
public function setTemplate(string $template): self
{
$this->template = $template;
return $this;
}
public function getTemplate(): string
{
return $this->template;
}
public function setClass(?string $class): self
{
$this->class = $class;
return $this;
}
public function getClass(): ?string
{
return $this->class;
}
public function setIcon(?string $icon): self
{
$this->icon = $icon;
return $this;
}
public function getIcon(): ?string
{
return $this->icon;
}
public function setOrder(int $order): self
{
$this->order = $order;
return $this;
}
public function getOrder(): int
{
return $this->order;
}
public function buildVars(array $data, array $context)
{
}
public function getVars(): array
{
return $this->vars;
}
public function toArray(): array
{
return [
'label' => $this->getLabel(),
'category' => $this->getCategory(),
'isContainer' => $this->getIsContainer(),
'widgets' => $this->getWidgets(),
'settings' => $this->getSettings(),
'class' => $this->getClass(),
'icon' => $this->getIcon(),
];
}
}

View file

@ -1,41 +0,0 @@
<?php
namespace App\Core\BuilderBlock;
class BuilderBlockContainer
{
protected array $widgets = [];
public function addWidget(BuilderBlock $widget): self
{
$widget->configure();
$this->widgets[$widget->getName()] = $widget;
return $this;
}
public function removeWidget(string $name)
{
unset($this->widgets[$name]);
return $this;
}
public function getWidgets(): array
{
usort($this->widgets, fn(BuilderBlock $a, BuilderBlock $b) => $a->getOrder() <=> $b->getOrder());
return $this->widgets;
}
public function hasWidget(string $name)
{
return isset($this->widgets[$name]);
}
public function getWidget(string $name): BuilderBlock
{
return $this->widgets[$name];
}
}

View file

@ -12,8 +12,8 @@
namespace App\Core\Bundle;
use App\Core\DependencyInjection\CoreExtension;
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
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\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;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\HttpClient\Exception\ClientException;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpClient\Exception\TransportException;
/**
* class SymfonyCacheManager.
@ -20,11 +20,15 @@ use Symfony\Contracts\HttpClient\HttpClientInterface;
*/
class SymfonyCacheManager
{
public function __construct(
protected KernelInterface $kernel,
protected HttpClientInterface $httpClient,
protected UrlGeneratorInterface $urlGenerator
) {
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 cleanRouting()

View file

@ -18,12 +18,19 @@ 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(
protected UserFactory $userFactory,
protected EntityManager $entityManager,
protected TokenGeneratorInterface $tokenGenerator
UserFactory $userFactory,
EntityManager $entityManager,
TokenGeneratorInterface $tokenGenerator
) {
$this->userFactory = $userFactory;
$this->entityManager = $entityManager;
$this->tokenGenerator = $tokenGenerator;
parent::__construct();
}

View file

@ -26,7 +26,7 @@ abstract class CrudController extends AdminController
abstract protected function getConfiguration(): CrudConfiguration;
protected function doIndex(int $page, RepositoryQuery $query, Request $request, Session $session, string $context = 'index'): Response
protected function doIndex(int $page, RepositoryQuery $query, Request $request, Session $session): Response
{
$configuration = $this->getConfiguration();
@ -35,14 +35,13 @@ abstract class CrudController extends AdminController
$pager = $query
->usefilters($this->filters)
->paginate($page, $configuration->getMaxPerPage($context))
->paginate($page, $configuration->getmaxperpage('index'))
;
return $this->render($this->getConfiguration()->getView($context), [
return $this->render($this->getConfiguration()->getView('index'), [
'configuration' => $configuration,
'pager' => $pager,
'sort' => $this->sort,
'context' => $context,
'filters' => [
'show' => null !== $configuration->getForm('filter'),
'isEmpty' => empty($this->filters),
@ -50,13 +49,13 @@ abstract class CrudController extends AdminController
]);
}
protected function doNew(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeCreate = null, string $context = 'new'): Response
protected function doNew(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeCreate = null): Response
{
$configuration = $this->getConfiguration();
$this->prepareEntity($entity);
$form = $this->createForm($configuration->getForm('new'), $entity, $configuration->getFormOptions($context));
$form = $this->createForm($configuration->getForm('new'), $entity, $configuration->getFormOptions('new'));
if ($request->isMethod('POST')) {
$form->handleRequest($request);
@ -77,32 +76,30 @@ abstract class CrudController extends AdminController
$this->addFlash('warning', 'The form is not valid.');
}
return $this->render($configuration->getView($context), [
return $this->render($configuration->getView('new'), [
'form' => $form->createView(),
'configuration' => $configuration,
'context' => $context,
'entity' => $entity,
]);
}
protected function doShow(EntityInterface $entity, string $context = 'show'): Response
protected function doShow(EntityInterface $entity): Response
{
$configuration = $this->getConfiguration();
return $this->render($configuration->getView($context), [
return $this->render($configuration->getView('show'), [
'entity' => $entity,
'context' => $context,
'configuration' => $configuration,
]);
}
protected function doEdit(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeUpdate = null, string $context = 'edit'): Response
protected function doEdit(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeUpdate = null): Response
{
$configuration = $this->getConfiguration();
$this->prepareEntity($entity);
$form = $this->createForm($configuration->getForm('edit'), $entity, $configuration->getFormOptions($context));
$form = $this->createForm($configuration->getForm('edit'), $entity, $configuration->getFormOptions('edit'));
if ($request->isMethod('POST')) {
$form->handleRequest($request);
@ -115,105 +112,18 @@ abstract class CrudController extends AdminController
$entityManager->update($entity);
$this->addFlash('success', 'The data has been saved.');
return $this->redirectToRoute($configuration->getPageRoute($context), array_merge(
return $this->redirectToRoute($configuration->getPageRoute('edit'), array_merge(
['entity' => $entity->getId()],
$configuration->getPageRouteParams($context)
$configuration->getPageRouteParams('edit')
));
}
$this->addFlash('warning', 'The form is not valid.');
}
return $this->render($configuration->getView($context), [
'form' => $form->createView(),
'context' => $context,
'configuration' => $configuration,
'entity' => $entity,
]);
}
protected function doInlineEdit(string $context, string $label, EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeUpdate = null): Response
{
$configuration = $this->getConfiguration();
$this->prepareEntity($entity);
$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();
}
call_user_func_array($callback, [$builder, $entity]);
$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.');
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'), [
return $this->render($configuration->getView('edit'), [
'form' => $form->createView(),
'configuration' => $configuration,
'entity' => $entity,
'context' => $context,
'label' => $label,
'redirectTo' => $redirectTo,
]);
}
@ -284,39 +194,16 @@ abstract class CrudController extends AdminController
$query->useFilters($this->filters);
$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'));
if ('selection' === $target) {
$isSelection = true;
$pager = $query->paginate($page, $configuration->getMaxPerPage($context));
} else {
$isSelection = false;
$pager = $query->find();
}
$pager = $useSelection
? $query->paginate($page, $configuration->getMaxPerPage($context))
: $query->find()
;
foreach ($pager as $key => $entity) {
if (($useSelection && isset($items[$key + 1])) || !$useSelection) {
if (($isSelection && isset($items[$key + 1])) || !$isSelection) {
$callback($entity, $entityManager);
}
}
@ -326,7 +213,7 @@ abstract class CrudController extends AdminController
return $this->json([]);
}
protected function doDelete(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeDelete = null, string $route = 'index'): Response
protected function doDelete(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeDelete = null): Response
{
$configuration = $this->getConfiguration();
@ -340,10 +227,10 @@ abstract class CrudController extends AdminController
$this->addFlash('success', 'The data has been removed.');
}
return $this->redirectToRoute($configuration->getPageRoute($route));
return $this->redirectToRoute($configuration->getPageRoute('index'));
}
protected function doFilter(Session $session, string $context = 'filter'): Response
protected function doFilter(Session $session): Response
{
$configuration = $this->getConfiguration();
$type = $configuration->getForm('filter');
@ -352,12 +239,11 @@ abstract class CrudController extends AdminController
throw $this->createNotFoundException();
}
$form = $this->createForm($type, null, $configuration->getFormOptions('filter'));
$form = $this->createForm($type);
$form->submit($session->get($form->getName(), []));
return $this->render($configuration->getView($context), [
return $this->render($configuration->getView('filter'), [
'form' => $form->createView(),
'context' => $context,
'configuration' => $configuration,
]);
}
@ -371,7 +257,7 @@ abstract class CrudController extends AdminController
return;
}
$form = $this->createForm($type, null, $configuration->getFormOptions('filter'));
$form = $this->createForm($type);
if ($request->query->has($form->getName())) {
$filters = $request->query->get($form->getName());
@ -418,27 +304,9 @@ abstract class CrudController extends AdminController
}
$defaultSort = $configuration->getDefaultSort($context);
$session = $request->getSession();
$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;
}
$name = $request->query->get('_sort', $defaultSort['label'] ?? null);
$direction = strtolower($request->query->get('_sort_direction', $defaultSort['direction'] ?? 'asc'));
if (!in_array($direction, ['asc', 'desc'])) {
$direction = 'asc';

View file

@ -3,6 +3,8 @@
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

@ -1,40 +0,0 @@
<?php
namespace App\Core\Controller\Editor;
use App\Core\BuilderBlock\BuilderBlockContainer;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
#[Route(path: '/admin/editor/builder_block')]
class BuilderBlockController extends AbstractController
{
public function __construct(protected TranslatorInterface $translator)
{
}
#[Route(path: '/widgets', name: 'admin_editor_builder_block_widgets', options: ['expose' => true])]
public function widgets(BuilderBlockContainer $container): JsonResponse
{
$data = [];
foreach ($container->getWidgets() as $widget) {
$data[$widget->getName()] = $this->translate($widget->toArray());
}
return $this->json($data);
}
protected function translate(array $data)
{
$data['label'] = $this->translator->trans($data['label']);
foreach ($data['settings'] as $key => $value) {
$data['settings'][$key]['label'] = $this->translator->trans($data['settings'][$key]['label']);
}
return $data;
}
}

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,28 +31,11 @@ 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.');
@ -61,21 +44,13 @@ 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,10 +12,14 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
#[Route(path: '/admin/setting')]
/**
* @Route("/admin/setting")
*/
class SettingAdminController extends AdminController
{
#[Route(path: '/{page}', name: 'admin_setting_index', requirements: ['page' => '\d+'])]
/**
* @Route("/{page}", name="admin_setting_index", requirements={"page": "\d+"})
*/
public function index(
RepositoryQuery $query,
EventDispatcherInterface $eventDispatcher,
@ -34,7 +38,9 @@ class SettingAdminController extends AdminController
]);
}
#[Route(path: '/edit/{entity}', name: 'admin_setting_edit')]
/**
* @Route("/edit/{entity}", name="admin_setting_edit")
*/
public function edit(
Entity $entity,
EntityManager $entityManager,
@ -51,53 +57,30 @@ 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(path: '/delete/{entity}', name: 'admin_setting_delete', methods: ['DELETE', 'POST'])]
/**
* @Route("/delete/{entity}", name="admin_setting_delete", methods={"DELETE"})
*/
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);
}
@ -104,11 +104,9 @@ class NavigationAdminController extends CrudController
->setView('form', '@Core/site/navigation_admin/_form.html.twig')
->setIsSortableCollection('index', true)
->setDoubleClick('index', true)
->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,6 +2,7 @@
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;
@ -13,15 +14,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\PageLocator;
use App\Core\Site\RoleLocator;
use App\Core\Site\PageLocator;
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
@ -144,7 +145,7 @@ class NodeAdminController extends AbstractController
$page = $entity->getPage();
if (null !== $page) {
if ($page !== null) {
$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
{
@ -95,8 +95,6 @@ class PageAdminController extends CrudController
->setAction('index', 'show', false)
->setAction('edit', 'show', false)
->setDoubleClick('index', true)
->setField('index', 'Name', Field\TextField::class, [
'property' => 'name',
'sort' => ['name', '.name'],
@ -114,7 +112,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

@ -8,55 +8,55 @@ use App\Core\Crud\Field;
use App\Core\Event\Account\PasswordRequestEvent;
use App\Core\Factory\UserFactory as Factory;
use App\Core\Manager\EntityManager;
use App\Core\Security\TokenGenerator;
use App\Entity\User as Entity;
use App\Form\UserType as Type;
use App\Repository\UserRepositoryQuery as RepositoryQuery;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
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;
use App\Core\Security\TokenGenerator;
class UserAdminController extends CrudController
{
protected ?CrudConfiguration $configuration = null;
#[Route(path: '/admin/user/{page}', name: 'admin_user_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/user/new', name: 'admin_user_new', methods: ['GET', 'POST'])]
public function new(Factory $factory, EntityManager $entityManager, Request $request, TokenGenerator $tokenGenerator): Response
{
return $this->doNew($factory->create(null, $tokenGenerator->generateToken()), $entityManager, $request);
}
#[Route(path: '/admin/user/show/{entity}', name: 'admin_user_show', methods: ['GET'])]
public function show(Entity $entity): Response
{
return $this->doShow($entity);
}
#[Route(path: '/admin/user/filter', name: 'admin_user_filter', methods: ['GET'])]
public function filter(Session $session): Response
{
return $this->doFilter($session);
}
#[Route(path: '/admin/user/edit/{entity}', name: 'admin_user_edit', methods: ['GET', 'POST'])]
public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response
{
return $this->doEdit($entity, $entityManager, $request);
}
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/user/delete/{entity}', name: 'admin_user_delete', methods: ['DELETE', 'POST'])]
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
{
return $this->doDelete($entity, $entityManager, $request);
}
#[Route(path: '/admin/user/resetting_request/{entity}', name: 'admin_user_resetting_request', methods: ['POST'])]
public function requestResetting(Entity $entity, EventDispatcherInterface $eventDispatcher, Request $request): Response
{
if ($this->isCsrfTokenValid('resetting_request'.$entity->getId(), $request->request->get('_token'))) {
@ -81,7 +81,6 @@ class UserAdminController extends CrudController
->setPageRoute('index', 'admin_user_index')
->setPageRoute('new', 'admin_user_new')
->setPageRoute('edit', 'admin_user_edit')
->setPageRoute('inline_edit', 'admin_user_inline_edit')
->setPageRoute('show', 'admin_user_show')
->setPageRoute('delete', 'admin_user_delete')
->setPageRoute('filter', 'admin_user_filter')
@ -97,7 +96,6 @@ class UserAdminController extends CrudController
->setView('edit', '@Core/user/user_admin/edit.html.twig')
->setDefaultSort('index', 'username')
->setDoubleClick('index', true)
->setField('index', 'E-mail', Field\TextField::class, [
'property' => 'email',
@ -108,9 +106,6 @@ class UserAdminController extends CrudController
'property' => 'displayName',
'sort' => ['displayName', '.displayName'],
'attr' => ['class' => 'miw-200'],
'inline_form' => function (FormBuilderInterface $builder) {
$builder->add('displayName', null);
},
])
;
}

View file

@ -17,7 +17,6 @@ class CrudConfiguration
protected array $actionTitles = [];
protected array $forms = [];
protected array $formOptions = [];
protected array $inlineForms = [];
protected array $views = [];
protected array $viewDatas = [];
protected array $fields = [];
@ -40,7 +39,7 @@ class CrudConfiguration
return self::$self;
}
// --
/* -- */
public function setPageTitle(string $page, string $title): self
{
@ -54,7 +53,7 @@ class CrudConfiguration
return $this->pageTitles[$page] ?? $default;
}
// --
/* -- */
public function setPageRoute(string $page, string $route): self
{
@ -80,9 +79,9 @@ class CrudConfiguration
return $this->pageRouteParams[$page] ?? [];
}
// --
/* -- */
public function setForm(string $context, string $form): self
public function setForm(string $context, string $form, array $options = []): self
{
$this->forms[$context] = $form;
@ -106,7 +105,7 @@ class CrudConfiguration
return $this->formOptions[$context] ?? [];
}
// --
/* -- */
public function setAction(string $page, string $action, bool|callable $enabled): self
{
@ -135,24 +134,8 @@ class CrudConfiguration
);
}
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 {
public function setBatchAction(string $page, string $action, string $label, callable $callback): self
{
if (!isset($this->batchActions[$page])) {
$this->batchActions[$page] = [];
}
@ -160,7 +143,6 @@ class CrudConfiguration
$this->batchActions[$page][$action] = [
'label' => $label,
'callback' => $callback,
'isGlobal' => false,
];
return $this;
@ -181,7 +163,7 @@ class CrudConfiguration
return !empty($this->batchActions[$page]);
}
// --
/* -- */
public function setActionTitle(string $page, string $action, string $title): self
{
@ -199,7 +181,7 @@ class CrudConfiguration
return $this->actionTitles[$page][$action] ?? $default;
}
// --
/* -- */
public function setView(string $context, string $view): self
{
@ -247,7 +229,7 @@ class CrudConfiguration
return $this->viewDatas[$context][$name] ?? $defaultValue;
}
// --
/* -- */
public function setField(string $context, string $label, string $field, array $options): self
{
@ -268,16 +250,9 @@ class CrudConfiguration
return $this->fields[$context] ?? [];
}
public function setFields(string $context, array $fields): self
{
$this->fields[$context] = $fields;
/* -- */
return $this;
}
// --
public function setMaxPerPage(string $page, int $max): self
public function setMaxPerPage(string $page, int $max)
{
$this->maxPerPage[$page] = $max;
@ -289,21 +264,7 @@ class CrudConfiguration
return $this->maxPerPage[$page] ?? $default;
}
// --
public function setDoubleClick(string $page, bool $enabled): self
{
$this->doubleClick[$page] = $enabled;
return $this;
}
public function getDoubleClick(string $page): bool
{
return $this->doubleClick[$page] ?? false;
}
// --
/* -- */
public function setI18n(array $locales, string $defaultLocale): self
{
@ -328,7 +289,7 @@ class CrudConfiguration
return !empty($this->locales);
}
// --
/* -- */
public function setDefaultSort(string $context, string $label, string $direction = 'asc'): self
{

View file

@ -1,32 +0,0 @@
<?php
namespace App\Core\Crud\Field;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* class BooleanField.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class BooleanField extends Field
{
public function configureOptions(OptionsResolver $resolver): OptionsResolver
{
parent::configureOptions($resolver);
$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,31 +28,24 @@ abstract class Field
$resolver->setDefaults([
'property' => null,
'property_builder' => null,
'default_value' => null,
'view' => null,
'action' => null,
'raw' => false,
'sort' => null,
'href' => null,
'href_attr' => [],
'attr' => [],
'inline_form' => null,
'inline_form_validation' => null,
]);
$resolver->setRequired('view');
$resolver->setAllowedTypes('property', ['null', 'string']);
$resolver->setAllowedTypes('view', 'string');
$resolver->setAllowedTypes('action', ['null', 'string']);
$resolver->setAllowedTypes('attr', 'array');
$resolver->setAllowedTypes('href', ['null', 'string', 'callable']);
$resolver->setAllowedTypes('inline_form', ['null', 'callable']);
$resolver->setAllowedTypes('inline_form_validation', ['null', 'callable']);
$resolver->setAllowedTypes('href_attr', ['array', 'callable']);
$resolver->setAllowedTypes('href_attr', 'array', 'callable');
$resolver->setAllowedTypes('raw', 'boolean');
$resolver->setAllowedTypes('property_builder', ['null', 'callable']);
$resolver->setAllowedValues('sort', function ($value) {
if (null === $value) {
$resolver->setAllowedValues('sort', function($value) {
if ($value === null) {
return true;
}

View file

@ -1,25 +0,0 @@
<?php
namespace App\Core\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use App\Core\BuilderBlock\BuilderBlockContainer;
class BuilderBlockPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
if (!$container->has(BuilderBlockContainer::class)) {
return;
}
$definition = $container->findDefinition(BuilderBlockContainer::class);
$taggedServices = $container->findTaggedServiceIds('builder_block.widget');
foreach ($taggedServices as $id => $tags) {
$definition->addMethodCall('addWidget', [new Reference($id)]);
}
}
}

View file

@ -15,7 +15,6 @@ class Configuration implements ConfigurationInterface
'image/jpeg',
'image/gif',
'image/svg+xml',
'image/webp',
'video/mp4',
'audio/mpeg3',
'audio/x-mpeg-3',
@ -51,10 +50,12 @@ class Configuration implements ConfigurationInterface
->scalarNode('name')
->defaultValue('Murph')
->isRequired()
->cannotBeEmpty()
->end()
->scalarNode('logo')
->defaultValue('build/images/core/logo.svg')
->isRequired()
->cannotBeEmpty()
->end()
->arrayNode('controllers')
->prototype('array')
@ -139,7 +140,6 @@ class Configuration implements ConfigurationInterface
->end()
->end()
->end();
;
return $treeBuilder;
}

View file

@ -2,7 +2,6 @@
namespace App\Core\DependencyInjection;
use App\Core\DependencyInjection\Compiler\BuilderBlockPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;

View file

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

View file

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

View file

@ -42,9 +42,6 @@ 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;
@ -67,7 +64,7 @@ class Navigation implements EntityInterface
return $this->label;
}
public function setLabel(?string $label): self
public function setLabel(string $label): self
{
$this->label = $label;
@ -240,16 +237,4 @@ 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

@ -1,20 +0,0 @@
<?php
namespace App\Core\Entity\Site\Page;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class BuilderBlock extends JsonBlock
{
public function getValue()
{
$value = parent::getValue();
if (is_string($value)) {
return json_decode($value, true);
}
return [];
}
}

View file

@ -5,6 +5,15 @@ namespace App\Core\Entity\Site\Page;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class ChoiceBlock extends JsonBlock
class ChoiceBlock extends Block
{
public function getValue()
{
return json_decode(parent::getValue(), true);
}
public function setValue($value): self
{
return parent::setValue(json_encode($value));
}
}

View file

@ -1,19 +0,0 @@
<?php
namespace App\Core\Entity\Site\Page;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class JsonBlock extends Block
{
public function getValue()
{
return json_decode(parent::getValue(), true);
}
public function setValue($value): self
{
return parent::setValue(json_encode($value));
}
}

View file

@ -5,12 +5,13 @@ 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')]
@ -88,7 +89,7 @@ class Page implements EntityInterface
}
/**
* @return Block[]|Collection
* @return Collection|Block[]
*/
public function getBlocks(): Collection
{

View file

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

View file

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

View file

@ -12,15 +12,18 @@ use Symfony\Contracts\EventDispatcher\Event;
*/
class EntityManagerEvent extends Event
{
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';
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 function __construct(protected EntityInterface $entity)
protected EntityInterface $entity;
public function __construct(EntityInterface $entity)
{
$this->entity = $entity;
}
public function getEntity(): EntityInterface

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -6,6 +6,7 @@ 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;
@ -20,13 +21,19 @@ use Symfony\Component\HttpKernel\Event\ResponseEvent;
*/
class AbListener
{
protected EventDispatcherInterface $eventDispatcher;
protected AbContainer $container;
protected SiteRequest $siteRequest;
protected ?Node $node;
public function __construct(
protected AbContainer $container,
protected EventDispatcherInterface $eventDispatcher,
protected SiteRequest $siteRequest
AbContainer $container,
EventDispatcherInterface $eventDispatcher,
SiteRequest $siteRequest
) {
$this->eventDispatcher = $eventDispatcher;
$this->container = $container;
$this->siteRequest = $siteRequest;
}
public function onKernelRequest(RequestEvent $event)
@ -63,16 +70,6 @@ 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();
@ -99,4 +96,14 @@ 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,18 +23,30 @@ 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(
protected NodeRepository $nodeRepository,
protected ViewRepositoryQuery $viewRepositoryQuery,
protected ViewFactory $viewFactory,
protected RefererRepositoryQuery $refererRepositoryQuery,
protected RefererFactory $refererFactory,
protected EntityManager $manager
NodeRepository $nodeRepository,
ViewRepositoryQuery $viewRepositoryQuery,
ViewFactory $viewFactory,
RefererRepositoryQuery $refererRepositoryQuery,
RefererFactory $refererFactory,
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,10 +3,11 @@
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.
@ -15,11 +16,15 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
*/
class RedirectListener
{
public function __construct(
protected RedirectMatcher $matcher,
protected RedirectBuilder $builder,
protected RedirectRepositoryQuery $repository
) {
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 onKernelException(ExceptionEvent $event)
@ -32,7 +37,7 @@ class RedirectListener
$redirects = $this->repository
->orderBy('.sortOrder')
->where('.isEnabled=true')
->where('.isEnabled=1')
->find()
;

View file

@ -17,13 +17,24 @@ 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(
protected MailNotifier $notifier,
protected UrlGeneratorInterface $urlGenerator,
protected EntityManager $entityManager,
protected TokenGeneratorInterface $tokenGenerator,
protected TranslatorInterface $translator
MailNotifier $notifier,
UrlGeneratorInterface $urlGenerator,
EntityManager $entityManager,
TokenGeneratorInterface $tokenGenerator,
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(
protected NodeRepository $nodeRepository,
ContainerInterface $container
) {
public function __construct(NodeRepository $nodeRepository, ContainerInterface $container)
{
$this->nodeRepository = $nodeRepository;
$this->authorizationChecker = $container->get('security.authorization_checker');
}

View file

@ -4,15 +4,18 @@ namespace App\Core\EventSubscriber\Site;
use App\Core\Site\SiteRequest;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpFoundation\Response;
use function Symfony\Component\String\u;
class ForcedDomainEventSubscriber implements EventSubscriberInterface
{
public function __construct(protected SiteRequest $siteRequest)
protected SiteRequest $siteRequest;
public function __construct(SiteRequest $siteRequest)
{
$this->siteRequest = $siteRequest;
}
public function onKernelResponse(ResponseEvent $event)
@ -35,8 +38,7 @@ 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,14 +20,27 @@ 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(
protected NodeFactory $nodeFactory,
protected NodeRepository $nodeRepository,
protected EntityManager $entityManager,
protected CodeSlugify $slugify,
protected SymfonyCacheManager $cacheManager,
protected TranslatorInterface $translator
NodeFactory $nodeFactory,
NodeRepository $nodeRepository,
EntityManager $entityManager,
CodeSlugify $slugify,
SymfonyCacheManager $cacheManager,
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,9 +17,11 @@ use App\Core\Slugify\CodeSlugify;
class NavigationEventSubscriber extends EntityManagerEventSubscriber
{
public function __construct(
protected EntityManager $entityManager,
protected CodeSlugify $slugify
EntityManager $entityManager,
CodeSlugify $slugify
) {
$this->entityManager = $entityManager;
$this->slugify = $slugify;
}
public function supports(EntityInterface $entity): bool

View file

@ -12,6 +12,7 @@ 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;
/**
@ -21,14 +22,27 @@ 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(
protected NodeFactory $nodeFactory,
protected NodeRepository $nodeRepository,
protected EntityManager $entityManager,
protected Slugify $slugify,
protected CodeSlugify $codeSlugify,
protected RouteParameterSlugify $routeParameterSlugify
NodeFactory $nodeFactory,
NodeRepository $nodeRepository,
EntityManager $entityManager,
Slugify $slugify,
CodeSlugify $codeSlugify,
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
@ -49,7 +63,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,8 +17,11 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
*/
class BlockEventSubscriber extends EntityManagerEventSubscriber
{
public function __construct(protected FileUploadHandler $fileUpload)
protected FileUploadHandler $fileUpload;
public function __construct(FileUploadHandler $fileUpload)
{
$this->fileUpload = $fileUpload;
}
public function supports(EntityInterface $entity): bool

View file

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

View file

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

View file

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

View file

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

View file

@ -23,15 +23,22 @@ 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,
protected FileUploadHandler $uploadHandler,
protected FileInformationFactory $fileInformationFactory,
protected FileInformationRepositoryQuery $fileInformationRepositoryQuery
FileUploadHandler $uploadHandler,
FileInformationFactory $fileInformationFactory,
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']);
@ -84,16 +91,12 @@ class FsFileManager
$this->applySort($finder, $options['sort'] ?? 'name', $options['sort_direction'] ?? 'asc');
foreach ($finder as $file) {
$splInfo = $this->getSplInfo($directory.'/'.$file->getBasename());
$data['files'][] = [
'basename' => $file->getBasename(),
'path' => $directory,
'webPath' => $this->pathUri.'/'.$directory.'/'.$file->getBasename(),
'locked' => $this->isLocked($directory.'/'.$file->getBasename()),
'mime' => mime_content_type($file->getRealPath()),
'size' => $splInfo ? $splInfo->getSize() : null,
'updated_at' => $splInfo ? date('Y-m-d H:i', $splInfo->getMTime()) : null,
];
}
@ -294,16 +297,14 @@ class FsFileManager
protected function applySort(Finder $finder, string $sort, string $direction)
{
$sorts = [
'name' => 'sortByName',
'type' => 'sortByType',
'updated_at' => 'sortByModifiedTime',
];
if ('name' === $sort) {
$finder->sortByName();
} elseif ('modification_date' === $sort) {
$finder->sortByModifiedTime();
}
if ('desc' === $direction) {
$finder->{$sorts[$sort]}()->reverseSorting();
} else {
$finder->{$sorts[$sort]}();
$finder->reverseSorting();
}
}

View file

@ -6,6 +6,7 @@ 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
{
@ -29,7 +30,7 @@ class FilePickerType extends AbstractType
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
public function getBlockPrefix(): string
{
return 'file_picker';
}

View file

@ -11,15 +11,6 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
*/
class FileUploadHandler
{
protected $filenameGenerator;
public function setFilenameGenerator(callable $filenameGenerator): self
{
$this->filenameGenerator = $filenameGenerator;
return $this;
}
public function handleForm(?UploadedFile $uploadedFile, string $path, ?callable $afterUploadCallback = null, bool $keepOriginalFilename = false): void
{
if (null === $uploadedFile) {
@ -30,11 +21,9 @@ class FileUploadHandler
if ($keepOriginalFilename) {
$filename = $originalFilename.'.'.$uploadedFile->guessExtension();
} elseif (!is_callable($this->filenameGenerator)) {
} else {
$safeFilename = transliterator_transliterate('Any-Latin; Latin-ASCII; [^A-Za-z0-9_] remove; Lower()', $originalFilename);
$filename = date('Ymd-his').$safeFilename.'.'.$uploadedFile->guessExtension();
} else {
$filename = call_user_func($this->filenameGenerator, $uploadedFile);
}
$uploadedFile->move($path, $filename);

View file

@ -2,11 +2,13 @@
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,7 +7,6 @@ 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
@ -24,7 +23,6 @@ class MenuType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);
@ -39,7 +37,6 @@ class MenuType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);

View file

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

View file

@ -6,7 +6,6 @@ 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;
@ -27,7 +26,6 @@ class NavigationType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);
@ -40,21 +38,6 @@ 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(),
],
@ -71,7 +54,6 @@ class NavigationType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);
@ -112,7 +94,7 @@ class NavigationType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(min: 2, max: 10),
new Length(['min' => 2, 'max' => 10]),
],
]
);

View file

@ -13,7 +13,6 @@ 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
@ -30,7 +29,6 @@ class NodeType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);
@ -45,7 +43,6 @@ class NodeType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);
@ -85,7 +82,6 @@ class NodeType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);
@ -120,9 +116,6 @@ class NodeType extends AbstractType
return $choices;
}),
'constraints' => [
new Length(max: 255),
],
]
);

View file

@ -1,32 +0,0 @@
<?php
namespace App\Core\Form\Site\Page;
use App\Core\Entity\Site\Page\BuilderBlock;
use App\Core\Form\Type\BuilderType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class BuilderBlockType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(
'value',
BuilderType::class,
array_merge([
'required' => false,
'label' => false,
], $options['options']),
);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => BuilderBlock::class,
'options' => [],
]);
}
}

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,7 +10,6 @@ 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
@ -27,7 +26,6 @@ class PageType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);
@ -41,7 +39,6 @@ class PageType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);
@ -55,7 +52,6 @@ class PageType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);
@ -69,7 +65,6 @@ class PageType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);
@ -83,7 +78,6 @@ class PageType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);

View file

@ -1,38 +0,0 @@
<?php
namespace App\Core\Form\Type;
use Symfony\Component\Form\Extension\Core\Type\CollectionType as BaseCollectionType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
class BuilderType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
parent::buildView($view, $form, $options);
$view->vars = array_replace($view->vars, [
]);
}
public function configureOptions(OptionsResolver $resolver)
{
parent::configureOptions($resolver);
$resolver->setDefaults([
'compound' => false,
]);
}
public function getBlockPrefix()
{
return 'builder';
}
}

View file

@ -20,10 +20,6 @@ class CollectionType extends BaseCollectionType
'collection_name' => $options['collection_name'],
'label_add' => $options['label_add'],
'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'],
]);
}
@ -35,12 +31,10 @@ class CollectionType extends BaseCollectionType
'collection_name' => '',
'label_add' => 'Add',
'label_delete' => 'Delete',
'template_before_item' => null,
'template_after_item' => null,
]);
}
public function getBlockPrefix()
public function getBlockPrefix(): string
{
return 'murph_collection';
}

View file

@ -23,7 +23,7 @@ class GrapesJsType extends TextareaType
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
public function getBlockPrefix(): string
{
return 'grapesjs';
}

View file

@ -1,97 +0,0 @@
<?php
namespace App\Core\Maker;
use Doctrine\Common\Annotations\Annotation;
use Symfony\Bundle\MakerBundle\ConsoleStyle;
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\Component\Filesystem\Filesystem;
class MakeBuilderBlock extends AbstractMaker
{
public static function getCommandName(): string
{
return 'make:builder-block';
}
public static function getCommandDescription(): string
{
return 'Creates a new builder block class';
}
public function configureCommand(Command $command, InputConfiguration $inputConf)
{
$command
->addArgument(
'builder-block-class',
InputArgument::OPTIONAL,
'Choose a name for your block class (e.g. <fg=yellow>ExampleBlock</>)'
)
->setHelp('')
;
}
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator)
{
$blockClassNameDetails = $generator->createClassNameDetails(
$input->getArgument('builder-block-class'),
'BuilderBlock\\',
'Block'
);
$templatePath = sprintf(
'builder_block/%s.html.twig',
Str::asSnakeCase(preg_replace('/Block$/', '', $blockClassNameDetails->getShortName()))
);
$options = [
'entity' => $blockClassNameDetails->getFullName(),
'template' => $templatePath,
'label' => Str::asHumanWords($blockClassNameDetails->getShortName())
];
$blockPath = $generator->generateController(
$blockClassNameDetails->getFullName(),
__DIR__.'/../Resources/maker/builder/Block.tpl.php',
$options
);
$generator->writeChanges();
$realTemplatePath = 'templates/'.$templatePath;
$filesystem = new Filesystem();
if (!$filesystem->exists($templatePath)) {
$filesystem->mkdir(dirname($realTemplatePath));
$filesystem->dumpFile($realTemplatePath, $this->getTemplate());
$io->comment(sprintf('<fg=blue>created</>: %s', $realTemplatePath));
}
$this->writeSuccessMessage($io);
}
protected function getTemplate(): string
{
return <<< EOF
<div id="{{ id }}">
{% for item in children %}
{{ item|block_to_html(context) }}
{% endfor %}
</div>
EOF;
}
public function configureDependencies(DependencyBuilder $dependencies)
{
}
}

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 Symfony\Component\Filesystem\Filesystem;
use function Symfony\Component\String\u;
class MakeCrudController extends AbstractMaker
@ -104,17 +103,6 @@ class MakeCrudController extends AbstractMaker
$options
);
$views = ['_form.html.twig', '_show.html.twig'];
$directory = sprintf('templates/admin/%s_admin/', $options['route']);
$filesystem = new Filesystem();
$filesystem->mkdir($directory);
foreach ($views as $view) {
$filesystem->dumpFile(
$directory.$view,
sprintf("{{ include('@Core/admin/crud/%s') }}\n", $view)
);
}
$generator->writeChanges();
$this->writeSuccessMessage($io);

View file

@ -11,6 +11,7 @@ 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,8 +92,7 @@ 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:
@ -101,18 +100,10 @@ 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)
@ -147,9 +138,8 @@ EOF
$types = [
'text' => null,
'textarea' => null,
'choice' => 'BlockEntity\\ChoiceBlock::class',
'choice' => null,
'collection' => 'BlockEntity\\CollectionBlock::class',
'builder' => 'BlockEntity\\BuilderBlock::class',
'editor_js_textarea' => null,
'file' => 'BlockEntity\\FileBlock::class',
'file_picker' => null,
@ -193,4 +183,12 @@ EOF
$io->writeln(sprintf(' * <comment>%s</comment>', $type));
}
}
public function configureDependencies(DependencyBuilder $dependencies)
{
$dependencies->addClassDependency(
Annotation::class,
'doctrine/annotations'
);
}
}

View file

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

View file

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

View file

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

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,6 +14,7 @@ use Twig\Environment as TwigEnvironment;
*/
class MailNotifier
{
protected MailerInterface $mailer;
protected array $attachments = [];
protected array $recipients = [];
protected array $bccRecipients = [];
@ -21,8 +22,10 @@ class MailNotifier
protected ?string $from = null;
protected ?string $replyTo = null;
public function __construct(protected MailerInterface $mailer)
public function __construct(TwigEnvironment $twig, 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 null|Referer find($id, $lockMode = null, $lockVersion = null)
* @method null|Referer findOneBy(array $criteria, array $orderBy = null)
* @method Referer|null find($id, $lockMode = null, $lockVersion = null)
* @method Referer|null 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 null|View find($id, $lockMode = null, $lockVersion = null)
* @method null|View findOneBy(array $criteria, array $orderBy = null)
* @method View|null find($id, $lockMode = null, $lockVersion = null)
* @method View|null 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 null|FileInformation find($id, $lockMode = null, $lockVersion = null)
* @method null|FileInformation findOneBy(array $criteria, array $orderBy = null)
* @method FileInformation|null find($id, $lockMode = null, $lockVersion = null)
* @method FileInformation|null 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 null|NavigationSetting find($id, $lockMode = null, $lockVersion = null)
* @method null|NavigationSetting findOneBy(array $criteria, array $orderBy = null)
* @method NavigationSetting|null find($id, $lockMode = null, $lockVersion = null)
* @method NavigationSetting|null 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 null|Redirect find($id, $lockMode = null, $lockVersion = null)
* @method null|Redirect findOneBy(array $criteria, array $orderBy = null)
* @method Redirect|null find($id, $lockMode = null, $lockVersion = null)
* @method Redirect|null findOneBy(array $criteria, array $orderBy = null)
* @method Redirect[] findAll()
* @method Redirect[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/

View file

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

View file

@ -98,26 +98,6 @@ 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 null|Setting find($id, $lockMode = null, $lockVersion = null)
* @method null|Setting findOneBy(array $criteria, array $orderBy = null)
* @method Setting|null find($id, $lockMode = null, $lockVersion = null)
* @method Setting|null 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 null !== $query->getQuery()
return $query->getQuery()
->setMaxResults(1)
->getOneOrNullResult()
->getOneOrNullResult() !== null
;
}
}

View file

@ -10,19 +10,6 @@ $pagination-bg: #ffffff !default;
$pagination-active-color: #ffffff !default;
$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";
@ -30,29 +17,9 @@ $input-focus-border-color: lighten(map-get($theme-colors, 'dark-blue'), 80%);
@import '~grapesjs/dist/css/grapes.min.css';
@import '~grapesjs-component-code-editor/dist/grapesjs-component-code-editor.min.css';
@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}#{"%"};
@for $i from 1 through 100 {
.miw-#{$i*5} {
min-width: $i * 5px;
}
}
@ -82,11 +49,6 @@ 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;
@ -146,7 +108,6 @@ body {
.table .thead-light {
a, th {
color: map-get($theme-colors, 'dark-blue');
background: lighten(map-get($theme-colors, 'dark-blue'), 80%);
}
}
@ -212,7 +173,7 @@ tr.table-primary-light {
.sidebar {
width: $sidebar-width;
width: 260px;
display: inline-block;
.sidebar-toggler {
@ -292,8 +253,8 @@ tr.table-primary-light {
.body {
padding-top: 60px;
width: calc(100% - $sidebar-width);
margin-left: $sidebar-width;
width: calc(100% - 260px);
margin-left: 260px;
display: inline-block;
.nav {
@ -439,21 +400,6 @@ 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) {
@ -619,11 +565,7 @@ fieldset.form-group {
&-filter {
padding-right: 20px;
padding-bottom: 15px;
.pagination {
margin-bottom: 0;
}
padding-bottom: 20px;
@media screen and (max-width: 769px) {
padding-right: 10px;
@ -654,20 +596,8 @@ fieldset.form-group {
}
}
.table {
.crud-batch-column {
width: 1%;
}
.crud-action-column {
text-align: right;
white-space: nowrap;
width: 1px;
}
}
.no-wrap {
white-space: nowrap;
.table .crud-batch-column {
width: 1%;
}
form {
@ -725,16 +655,6 @@ form {
}
}
label.required::after {
content: '*';
margin-left: 3px;
color: #b41215;
}
.invalid-feedback {
margin-top: -3px;
}
.gjs-editor-cont {
border-radius: 10px;
overflow: hidden !important;
@ -744,93 +664,6 @@ label.required::after {
background: map-get($theme-colors, 'dark-blue');
}
.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;
}
.builder-widget {
.block {
border: 1px solid rgba(map-get($theme-colors, 'dark-blue'), 0.3);
padding: 10px;
border-radius: 4px;
margin-bottom: 10px;
background: rgba(map-get($theme-colors, 'dark-blue'), 0.02);
}
> .block {
border: 1px solid map-get($theme-colors, 'dark-blue');
}
.block-header {
.block-header-item {
font-size: 12px;
display: inline-block;
margin-bottom: 10px;
padding: 2px 6px;
border-radius: 4px;
margin-right: 2px;
cursor: pointer;
}
}
$block-colors: #E183F5 #E3F7C6 #82DDF5 #F5BA82 #A088A6;
$block-colors-length: length($block-colors);
@for $i from 1 through 100 {
$block-color-index: ($block-colors-length + $i) % $block-colors-length + 1;
.block-depth-#{$i} {
.block-label {
background: nth($block-colors, $block-color-index);
border: 1px solid darken(nth($block-colors, $block-color-index), 50%);
color: darken(nth($block-colors, $block-color-index), 50%);
}
.builder-add .btn {
background: nth($block-colors, $block-color-index);
border: 1px solid darken(nth($block-colors, $block-color-index), 50%);
color: darken(nth($block-colors, $block-color-index), 50%);
}
}
}
.builder-add .btn {
font-size: 12px;
line-height: 14px;
padding: 3px 5px;
}
.block-settings-inverse {
background: none;
border: 1px solid map-get($theme-colors, 'dark-blue');
color: map-get($theme-colors, 'dark-blue');
}
.block-settings {
padding: 4px;
margin-bottom: 5px;
}
.block-id {
font-size: 12px;
margin-right: 5px;
}
.block-show-dropzone {
.block-dropzone {
min-height: 40px;
}
}
.tox.tox-silver-sink.tox-tinymce-aux {
z-index: 3000 !important;
}

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