Merge branch 'develop' into v2
This commit is contained in:
commit
8518b05b8d
77
CHANGELOG.md
77
CHANGELOG.md
|
@ -1,10 +1,85 @@
|
|||
## [Unreleased]
|
||||
|
||||
## [1.18.0] - 2023-01-13
|
||||
### Added
|
||||
* add page maker
|
||||
* feat(dep): add symfony/runtime
|
||||
* feat(dep): add symfony/flex
|
||||
### Fixed
|
||||
* fix(crud): allow POST in delete actions
|
||||
* fix(crud): remove default page value in abstract crud controller
|
||||
* fix(admin): test site_logo before using it
|
||||
* fix(ui): update z-index of choices__list--dropdown
|
||||
|
||||
## [1.17.1] - 2022-12-03
|
||||
### Fixed
|
||||
* add mising attribute on timestampable (doctrine)
|
||||
|
||||
## [1.17.0] - 2022-11-19
|
||||
### Fixed
|
||||
* fix tinymce modal z-index in tox
|
||||
### Changed
|
||||
* replace annotation with attributes
|
||||
|
||||
## [1.16.0] - 2022-09-06
|
||||
### Added
|
||||
* add A/B testing feature
|
||||
* add cleanup of html string extracted from grapesjs content
|
||||
### Fixed
|
||||
* fix file block type
|
||||
### Changed
|
||||
* remove dashboard action from the core
|
||||
|
||||
## [1.15.0] - 2022-05-09
|
||||
### Added
|
||||
* CrudConfiguration::setAction can receive a callable instead of a boolean in 'enabled' param
|
||||
* add grapesjs-component-code-editor and grapesjs-parser-postcss
|
||||
* hide the backoffice site name when small resolution
|
||||
* add entity_to_array twig function
|
||||
* add default field to show in crud configuration
|
||||
### Fixed
|
||||
* fix the mail notifier
|
||||
* fix sitemap: navigation with several domains
|
||||
* fix regression with editorjs: content not loaded
|
||||
### Changed
|
||||
* change default template to show an entity using `entity_to_array`
|
||||
|
||||
## [1.14.1] - 2022-04-30
|
||||
### Added
|
||||
* add allowed chars in RouteParameterSlugify and CodeSlugify
|
||||
* improve sidebar in mobile view
|
||||
### Fixed
|
||||
* fix creation of new element when a menu is edited
|
||||
* fix editorjs error when the textarea is empty
|
||||
|
||||
## [1.14.0] - 2022-04-20
|
||||
### Added
|
||||
* add grapesjs modes
|
||||
* add tinymce block type
|
||||
* add editor types in page maker
|
||||
* add the page template when the page is generated with the maker
|
||||
### Changed
|
||||
* replace flag-icon-css with flag-icons
|
||||
|
||||
## [1.13.0] - 2022-04-17
|
||||
### Added
|
||||
* add editorjs hyperlink block
|
||||
* add button to show and hide metas (admin)
|
||||
* add grapesjs editor
|
||||
* add editorjs type
|
||||
### Fixed
|
||||
* fix editorjs inline tools (bold and italic)
|
||||
### Changed
|
||||
* update editorjs quote block template
|
||||
|
||||
## [1.12.0] - 2022-03-26
|
||||
### Added
|
||||
* add page maker command (`make:page`)
|
||||
* add CrudConfiguration::getViewData in complement of CrudConfiguration::getViewDatas
|
||||
* add editorjs link block endpoint
|
||||
### Fixed
|
||||
* fix issue with empty user-agent in AnalyticListener
|
||||
### Changed
|
||||
* update editorjs image block view
|
||||
|
||||
## [1.11.0] - 2022-03-22
|
||||
### Added
|
||||
|
|
12
SECURITY.md
Normal file
12
SECURITY.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| ---------- | --------- |
|
||||
| >= 2.0 | :x: |
|
||||
| >= 1.0 | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you discover a security vulnerability within Murph, send an email to security [at] murph-project.org.
|
11
UPGRADE.md
11
UPGRADE.md
|
@ -1,3 +1,14 @@
|
|||
## General process
|
||||
|
||||
Upgrade dependencies:
|
||||
|
||||
* `composer update`
|
||||
* `yarn upgrade`
|
||||
|
||||
Build:
|
||||
|
||||
* `make build`
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## Upgrade to v1.10.0
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
"doctrine/doctrine-bundle": "^2.5",
|
||||
"doctrine/doctrine-migrations-bundle": "^3.2",
|
||||
"doctrine/orm": "^2.11",
|
||||
"fusonic/opengraph": "^2.2",
|
||||
"friendsofsymfony/jsrouting-bundle": "^2.8",
|
||||
"jaybizzle/crawler-detect": "^1.2",
|
||||
"knplabs/doctrine-behaviors": "^2.6",
|
||||
|
@ -30,19 +31,19 @@
|
|||
"spe/filesize-extension-bundle": "~2.0.0",
|
||||
"stof/doctrine-extensions-bundle": "^1.7",
|
||||
"symfony/apache-pack": "^1.0",
|
||||
"symfony/asset": "6.0.*",
|
||||
"symfony/console": "6.0.*",
|
||||
"symfony/dotenv": "6.0.*",
|
||||
"symfony/event-dispatcher": "6.0.*",
|
||||
"symfony/expression-language": "6.0.*",
|
||||
"symfony/finder": "6.0.*",
|
||||
"symfony/flex": "^1.3.1",
|
||||
"symfony/form": "6.0.*",
|
||||
"symfony/framework-bundle": "6.0.*",
|
||||
"symfony/http-client": "6.0.*",
|
||||
"symfony/intl": "6.0.*",
|
||||
"symfony/mailer": "6.0.*",
|
||||
"symfony/mime": "6.0.*",
|
||||
"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/monolog-bundle": "^3.1",
|
||||
"symfony/notifier": "6.0.*",
|
||||
"symfony/process": "6.0.*",
|
||||
|
@ -59,7 +60,8 @@
|
|||
"symfony/webpack-encore-bundle": "^1.11",
|
||||
"symfony/yaml": "6.0.*",
|
||||
"twig/extra-bundle": "^2.12|^3.3",
|
||||
"twig/twig": "^2.12|^3.3"
|
||||
"twig/twig": "^2.12|^3.3",
|
||||
"symfony/runtime": "^5.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
30
src/core/Ab/AbContainer.php
Normal file
30
src/core/Ab/AbContainer.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Ab;
|
||||
|
||||
/**
|
||||
* class AbContainer.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class AbContainer implements AbContainerInterface
|
||||
{
|
||||
protected array $tests = [];
|
||||
|
||||
public function add(AbTestInterface $test): self
|
||||
{
|
||||
$this->tests[$test->getName()] = $test;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return isset($this->tests[$name]);
|
||||
}
|
||||
|
||||
public function get(string $name): AbTestInterface
|
||||
{
|
||||
return $this->tests[$name];
|
||||
}
|
||||
}
|
17
src/core/Ab/AbContainerInterface.php
Normal file
17
src/core/Ab/AbContainerInterface.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Ab;
|
||||
|
||||
/**
|
||||
* interface AbContainerInterface.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
interface AbContainerInterface
|
||||
{
|
||||
public function add(AbTestInterface $test): self;
|
||||
|
||||
public function has(string $name): bool;
|
||||
|
||||
public function get(string $name): AbTestInterface;
|
||||
}
|
125
src/core/Ab/AbTest.php
Normal file
125
src/core/Ab/AbTest.php
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Ab;
|
||||
|
||||
/**
|
||||
* class AbTest.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class AbTest implements AbTestInterface
|
||||
{
|
||||
protected $results;
|
||||
protected string $name;
|
||||
protected array $variations = [];
|
||||
protected array $probabilities = [];
|
||||
protected int $duration = 3600 * 24;
|
||||
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
public function setResult(string $result): self
|
||||
{
|
||||
$this->result = $result;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isValidVariation($variation): bool
|
||||
{
|
||||
return array_key_exists($variation, $this->variations);
|
||||
}
|
||||
|
||||
public function addVariation(string $name, $value, int $probability = null): self
|
||||
{
|
||||
$this->variations[$name] = $value;
|
||||
$this->probabilities[$name] = $probability;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getVariation($variation)
|
||||
{
|
||||
return $this->variations[$variation];
|
||||
}
|
||||
|
||||
public function getResultValue()
|
||||
{
|
||||
return $this->getVariation($this->getResult());
|
||||
}
|
||||
|
||||
public function setDuration(int $duration): self
|
||||
{
|
||||
$this->duration = $duration;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDuration(): int
|
||||
{
|
||||
return $this->duration;
|
||||
}
|
||||
|
||||
public function run(): self
|
||||
{
|
||||
$this->result = $this->chance();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function chance(): string
|
||||
{
|
||||
$sum = 0;
|
||||
$empty = 0;
|
||||
|
||||
foreach ($this->probabilities as $name => $value) {
|
||||
$sum += $value;
|
||||
|
||||
if (empty($value)) {
|
||||
++$empty;
|
||||
}
|
||||
}
|
||||
|
||||
if ($sum > 100) {
|
||||
throw new \LogicException('Test Error: Total variation probabilities is bigger than 100%');
|
||||
}
|
||||
|
||||
if ($sum < 100) {
|
||||
foreach ($this->probabilities as $name => $value) {
|
||||
if (empty($value)) {
|
||||
$this->probabilities[$name] = (100 - $sum) / $empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
krsort($this->probabilities);
|
||||
|
||||
$number = mt_rand(0, (int) array_sum($this->probabilities) * 10);
|
||||
$starter = 0;
|
||||
$return = '';
|
||||
|
||||
foreach ($this->probabilities as $key => $val) {
|
||||
$starter += $val * 10;
|
||||
|
||||
if ($number <= $starter) {
|
||||
$return = $key;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
31
src/core/Ab/AbTestInterface.php
Normal file
31
src/core/Ab/AbTestInterface.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Ab;
|
||||
|
||||
/**
|
||||
* interface AbTestInterface.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
interface AbTestInterface
|
||||
{
|
||||
public function getName(): string;
|
||||
|
||||
public function getResult();
|
||||
|
||||
public function setResult(string $result): self;
|
||||
|
||||
public function isValidVariation($variation): bool;
|
||||
|
||||
public function addVariation(string $name, $value, int $probability = null): self;
|
||||
|
||||
public function getVariation($variation);
|
||||
|
||||
public function getResultValue();
|
||||
|
||||
public function setDuration(int $duration): self;
|
||||
|
||||
public function getDuration(): int;
|
||||
|
||||
public function run(): self;
|
||||
}
|
|
@ -2,14 +2,12 @@
|
|||
|
||||
namespace App\Core\Annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* class UrlGenerator.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
* @Annotation
|
||||
*/
|
||||
#[\Attribute]
|
||||
class UrlGenerator
|
||||
{
|
||||
public string $service;
|
||||
|
@ -17,4 +15,11 @@ class UrlGenerator
|
|||
public string $method;
|
||||
|
||||
public array $options = [];
|
||||
|
||||
public function __construct(string $service, string $method, array $options = [])
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->method = $method;
|
||||
$this->options = $options;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,14 +14,10 @@ use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
|
|||
use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface;
|
||||
use ZxcvbnPhp\Zxcvbn;
|
||||
|
||||
/**
|
||||
* @Route("/admin/account")
|
||||
*/
|
||||
#[Route(path: '/admin/account')]
|
||||
class AccountAdminController extends AdminController
|
||||
{
|
||||
/**
|
||||
* @Route("/", name="admin_account")
|
||||
*/
|
||||
#[Route(path: '/', name: 'admin_account')]
|
||||
public function account(Request $request, TotpAuthenticatorInterface $totpAuthenticatorService): Response
|
||||
{
|
||||
$account = $this->getUser();
|
||||
|
@ -31,9 +27,7 @@ class AccountAdminController extends AdminController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/2fa", name="admin_account_2fa")
|
||||
*/
|
||||
#[Route(path: '/2fa', name: 'admin_account_2fa')]
|
||||
public function twoFactorAuthentication(
|
||||
Request $request,
|
||||
GoogleAuthenticatorInterface $totpAuthenticatorService,
|
||||
|
@ -93,9 +87,7 @@ class AccountAdminController extends AdminController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/password", name="admin_account_password", methods={"POST"})
|
||||
*/
|
||||
#[Route(path: '/password', name: 'admin_account_password', methods: ['POST'])]
|
||||
public function password(
|
||||
Request $request,
|
||||
UserRepository $repository,
|
||||
|
|
|
@ -9,9 +9,7 @@ use Symfony\Component\Routing\Annotation\Route;
|
|||
|
||||
abstract class AdminController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @Route("/_ping", name="_ping")
|
||||
*/
|
||||
#[Route(path: '/_ping', name: '_ping')]
|
||||
public function ping()
|
||||
{
|
||||
return $this->json(true);
|
||||
|
|
|
@ -26,7 +26,7 @@ abstract class CrudController extends AdminController
|
|||
|
||||
abstract protected function getConfiguration(): CrudConfiguration;
|
||||
|
||||
protected function doIndex(int $page = 1, RepositoryQuery $query, Request $request, Session $session): Response
|
||||
protected function doIndex(int $page, RepositoryQuery $query, Request $request, Session $session): Response
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
|
||||
|
@ -127,7 +127,7 @@ abstract class CrudController extends AdminController
|
|||
]);
|
||||
}
|
||||
|
||||
protected function doSort(int $page = 1, RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session): Response
|
||||
protected function doSort(int $page, RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session): Response
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
$context = $request->query->get('context', 'index');
|
||||
|
@ -165,7 +165,7 @@ abstract class CrudController extends AdminController
|
|||
return $this->json([]);
|
||||
}
|
||||
|
||||
protected function doBatch(int $page = 1, RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session): Response
|
||||
protected function doBatch(int $page, RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session): Response
|
||||
{
|
||||
$configuration = $this->getConfiguration();
|
||||
$datas = $request->request->get('batch', []);
|
||||
|
|
|
@ -8,14 +8,10 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
* @Route("/admin/analytic")
|
||||
*/
|
||||
#[Route(path: '/admin/analytic')]
|
||||
class AnalyticController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @Route("/stats/{node}/{range}", name="admin_analytic_stats")
|
||||
*/
|
||||
#[Route(path: '/stats/{node}/{range}', name: 'admin_analytic_stats')]
|
||||
public function stats(Node $node, DateRangeAnalytic $analytic, string $range = '7days'): Response
|
||||
{
|
||||
if (!in_array($range, ['7days', '30days', '90days', '1year'])) {
|
||||
|
|
|
@ -25,9 +25,7 @@ class AuthController extends AbstractController
|
|||
$this->coreParameters = $parameters->get('core');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/login", name="auth_login")
|
||||
*/
|
||||
#[Route(path: '/login', name: 'auth_login')]
|
||||
public function login(AuthenticationUtils $authenticationUtils): Response
|
||||
{
|
||||
if ($this->getUser()) {
|
||||
|
@ -45,9 +43,7 @@ class AuthController extends AbstractController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/resetting/request", name="auth_resetting_request")
|
||||
*/
|
||||
#[Route(path: '/resetting/request', name: 'auth_resetting_request')]
|
||||
public function requestResetting(Request $request, UserRepository $repository, EventDispatcherInterface $eventDispatcher): Response
|
||||
{
|
||||
if ($this->getUser()) {
|
||||
|
@ -85,9 +81,7 @@ class AuthController extends AbstractController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/resetting/update/{token}", name="auth_resetting_update")
|
||||
*/
|
||||
#[Route(path: '/resetting/update/{token}', name: 'auth_resetting_update')]
|
||||
public function requestUpdate(
|
||||
string $token,
|
||||
Request $request,
|
||||
|
@ -145,9 +139,7 @@ class AuthController extends AbstractController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/logout", name="auth_logout")
|
||||
*/
|
||||
#[Route(path: '/logout', name: 'auth_logout')]
|
||||
public function logout()
|
||||
{
|
||||
throw new \Exception('This method can be blank - it will be intercepted by the logout key on your firewall');
|
||||
|
|
|
@ -6,20 +6,8 @@ use App\Core\Controller\Admin\AdminController;
|
|||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
* @Route("/admin")
|
||||
*/
|
||||
class DashboardAdminController extends AdminController
|
||||
{
|
||||
/**
|
||||
* @Route("/", name="admin_dashboard_index")
|
||||
*/
|
||||
public function index(): Response
|
||||
{
|
||||
return $this->render('@Core/dashboard/index.html.twig', [
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getSection(): string
|
||||
{
|
||||
return 'dashboard';
|
||||
|
|
48
src/core/Controller/Editor/EditorJsController.php
Normal file
48
src/core/Controller/Editor/EditorJsController.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Controller\Editor;
|
||||
|
||||
use Fusonic\OpenGraph\Consumer;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
#[Route(path: '/admin/editor/editorjs')]
|
||||
class EditorJsController extends AbstractController
|
||||
{
|
||||
#[Route(path: '/fetch_url', name: 'admin_editor_editorjs_fetch_url', options: ['expose' => true])]
|
||||
public function fetchUrl(Request $request, HttpClientInterface $client): JsonResponse
|
||||
{
|
||||
$url = filter_var($request->query->get('url'), FILTER_VALIDATE_URL);
|
||||
$datas = [];
|
||||
|
||||
if (!$url) {
|
||||
$data['success'] = 0;
|
||||
} else {
|
||||
try {
|
||||
$consumer = new Consumer();
|
||||
$response = $client->request('GET', $url);
|
||||
$openGraph = $consumer->loadHtml($response->getContent());
|
||||
|
||||
$data = [
|
||||
'success' => 1,
|
||||
'link' => $openGraph->url,
|
||||
'meta' => [
|
||||
'title' => $openGraph->title,
|
||||
'description' => $openGraph->description,
|
||||
],
|
||||
];
|
||||
|
||||
if (isset($openGraph->images[0])) {
|
||||
$data['meta']['image']['url'] = $openGraph->images[0]->url;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$data['success'] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->json($data);
|
||||
}
|
||||
}
|
|
@ -15,22 +15,16 @@ use Symfony\Component\HttpFoundation\Response;
|
|||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @Route("/admin/file_manager")
|
||||
*/
|
||||
#[Route(path: '/admin/file_manager')]
|
||||
class FileManagerAdminController extends AdminController
|
||||
{
|
||||
/**
|
||||
* @Route("/", name="admin_file_manager_index")
|
||||
*/
|
||||
#[Route(path: '/', name: 'admin_file_manager_index')]
|
||||
public function index(): Response
|
||||
{
|
||||
return $this->render('@Core/file_manager/index.html.twig');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/api/directory", name="admin_file_manager_api_directory", options={"expose"=true})
|
||||
*/
|
||||
#[Route(path: '/api/directory', name: 'admin_file_manager_api_directory', options: ['expose' => true])]
|
||||
public function directory(FsFileManager $manager, Request $request): Response
|
||||
{
|
||||
$options = [
|
||||
|
@ -43,9 +37,7 @@ class FileManagerAdminController extends AdminController
|
|||
return $this->json($files);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/info/{tab}/{context}/{ajax}", name="admin_file_manager_info", options={"expose"=true})
|
||||
*/
|
||||
#[Route(path: '/info/{tab}/{context}/{ajax}', name: 'admin_file_manager_info', options: ['expose' => true])]
|
||||
public function info(
|
||||
FsFileManager $manager,
|
||||
Request $request,
|
||||
|
@ -115,9 +107,7 @@ class FileManagerAdminController extends AdminController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/directory/new/{ajax}", name="admin_file_manager_directory_new", options={"expose"=true}, methods={"GET", "POST"})
|
||||
*/
|
||||
#[Route(path: '/directory/new/{ajax}', name: 'admin_file_manager_directory_new', options: ['expose' => true], methods: ['GET', 'POST'])]
|
||||
public function directoryNew(FsFileManager $manager, Request $request, TranslatorInterface $translator, bool $ajax = false): Response
|
||||
{
|
||||
$splInfo = $manager->getSplInfo($request->query->get('file'));
|
||||
|
@ -184,9 +174,7 @@ class FileManagerAdminController extends AdminController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/directory/rename/{ajax}", name="admin_file_manager_directory_rename", methods={"GET", "POST"})
|
||||
*/
|
||||
#[Route(path: '/directory/rename/{ajax}', name: 'admin_file_manager_directory_rename', methods: ['GET', 'POST'])]
|
||||
public function directoryRename(FsFileManager $manager, Request $request, TranslatorInterface $translator, bool $ajax = false): Response
|
||||
{
|
||||
$splInfo = $manager->getSplInfo($request->query->get('file'));
|
||||
|
@ -255,9 +243,7 @@ class FileManagerAdminController extends AdminController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/file/rename/{ajax}", name="admin_file_manager_file_rename", methods={"GET", "POST"})
|
||||
*/
|
||||
#[Route(path: '/file/rename/{ajax}', name: 'admin_file_manager_file_rename', methods: ['GET', 'POST'])]
|
||||
public function fileRename(FsFileManager $manager, Request $request, TranslatorInterface $translator, bool $ajax = false): Response
|
||||
{
|
||||
$splInfo = $manager->getSplInfo($request->query->get('file'));
|
||||
|
@ -327,9 +313,7 @@ class FileManagerAdminController extends AdminController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/upload/{ajax}", name="admin_file_manager_upload", options={"expose"=true}, methods={"GET", "POST"})
|
||||
*/
|
||||
#[Route(path: '/upload/{ajax}', name: 'admin_file_manager_upload', options: ['expose' => true], methods: ['GET', 'POST'])]
|
||||
public function upload(FsFileManager $manager, Request $request, TranslatorInterface $translator, bool $ajax = false): Response
|
||||
{
|
||||
$splInfo = $manager->getSplInfo($request->query->get('file'));
|
||||
|
@ -406,9 +390,7 @@ class FileManagerAdminController extends AdminController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/delete", name="admin_file_manager_delete", methods={"DELETE"})
|
||||
*/
|
||||
#[Route(path: '/delete', name: 'admin_file_manager_delete', methods: ['DELETE', 'POST'])]
|
||||
public function delete(FsFileManager $manager, Request $request): Response
|
||||
{
|
||||
$path = $request->request->get('file');
|
||||
|
|
|
@ -19,65 +19,49 @@ use Symfony\Component\Routing\Annotation\Route;
|
|||
|
||||
class RedirectAdminController extends CrudController
|
||||
{
|
||||
/**
|
||||
* @Route("/admin/redirect/{page}", name="admin_redirect_index", methods={"GET"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
#[Route(path: '/admin/redirect/{page}', name: 'admin_redirect_index', methods: ['GET'], requirements: ['page' => '\d+'])]
|
||||
public function index(RepositoryQuery $query, Request $request, Session $session, int $page = 1): Response
|
||||
{
|
||||
return $this->doIndex($page, $query, $request, $session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/redirect/new", name="admin_redirect_new", methods={"GET", "POST"})
|
||||
*/
|
||||
#[Route(path: '/admin/redirect/new', name: 'admin_redirect_new', methods: ['GET', 'POST'])]
|
||||
public function new(Factory $factory, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doNew($factory->create(), $entityManager, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/redirect/show/{entity}", name="admin_redirect_show", methods={"GET"})
|
||||
*/
|
||||
#[Route(path: '/admin/redirect/show/{entity}', name: 'admin_redirect_show', methods: ['GET'])]
|
||||
public function show(Entity $entity): Response
|
||||
{
|
||||
return $this->doShow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/redirect/filter", name="admin_redirect_filter", methods={"GET"})
|
||||
*/
|
||||
#[Route(path: '/admin/redirect/filter', name: 'admin_redirect_filter', methods: ['GET'])]
|
||||
public function filter(Session $session): Response
|
||||
{
|
||||
return $this->doFilter($session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/redirect/edit/{entity}", name="admin_redirect_edit", methods={"GET", "POST"})
|
||||
*/
|
||||
#[Route(path: '/admin/redirect/edit/{entity}', name: 'admin_redirect_edit', methods: ['GET', 'POST'])]
|
||||
public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doEdit($entity, $entityManager, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/redirect/sort/{page}", name="admin_redirect_sort", methods={"POST"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
#[Route(path: '/admin/redirect/sort/{page}', name: 'admin_redirect_sort', methods: ['POST'], requirements: ['page' => '\d+'])]
|
||||
public function sort(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
|
||||
{
|
||||
return $this->doSort($page, $query, $entityManager, $request, $session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/redirect/batch/{page}", name="admin_redirect_batch", methods={"POST"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
#[Route(path: '/admin/redirect/batch/{page}', name: 'admin_redirect_batch', methods: ['POST'], requirements: ['page' => '\d+'])]
|
||||
public function batch(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
|
||||
{
|
||||
return $this->doBatch($page, $query, $entityManager, $request, $session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/redirect/delete/{entity}", name="admin_redirect_delete", methods={"DELETE"})
|
||||
*/
|
||||
#[Route(path: '/admin/redirect/delete/{entity}', name: 'admin_redirect_delete', methods: ['DELETE', 'POST'])]
|
||||
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doDelete($entity, $entityManager, $request);
|
||||
|
|
|
@ -11,14 +11,10 @@ use Symfony\Component\HttpFoundation\Request;
|
|||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
* @Route("/admin/navigation_setting")
|
||||
*/
|
||||
#[Route(path: '/admin/navigation_setting')]
|
||||
class NavigationSettingAdminController extends AdminController
|
||||
{
|
||||
/**
|
||||
* @Route("/edit/{entity}", name="admin_navigation_setting_edit")
|
||||
*/
|
||||
#[Route(path: '/edit/{entity}', name: 'admin_navigation_setting_edit')]
|
||||
public function edit(
|
||||
Entity $entity,
|
||||
EntityManager $entityManager,
|
||||
|
@ -58,9 +54,7 @@ class NavigationSettingAdminController extends AdminController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/delete/{entity}", name="admin_navigation_setting_delete", methods={"DELETE"})
|
||||
*/
|
||||
#[Route(path: '/delete/{entity}', name: 'admin_navigation_setting_delete', methods: ['DELETE', 'POST'])]
|
||||
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
|
||||
|
|
|
@ -12,14 +12,10 @@ use Symfony\Component\HttpFoundation\Request;
|
|||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/menu")
|
||||
*/
|
||||
#[Route(path: '/admin/site/menu')]
|
||||
class MenuAdminController extends AdminController
|
||||
{
|
||||
/**
|
||||
* @Route("/new/{navigation}", name="admin_site_menu_new", methods={"POST"})
|
||||
*/
|
||||
#[Route(path: '/new/{navigation}', name: 'admin_site_menu_new', methods: ['POST'])]
|
||||
public function new(Navigation $navigation, EntityFactory $factory, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
$entity = $factory->create($navigation);
|
||||
|
@ -39,9 +35,7 @@ class MenuAdminController extends AdminController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/edit/{entity}", name="admin_site_menu_edit", methods={"POST"})
|
||||
*/
|
||||
#[Route(path: '/edit/{entity}', name: 'admin_site_menu_edit', methods: ['POST'])]
|
||||
public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
$form = $this->createForm(EntityType::class, $entity);
|
||||
|
@ -59,9 +53,7 @@ class MenuAdminController extends AdminController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/delete/{entity}", name="admin_site_menu_delete", methods={"DELETE"})
|
||||
*/
|
||||
#[Route(path: '/delete/{entity}', name: 'admin_site_menu_delete', methods: ['DELETE', 'POST'])]
|
||||
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
|
||||
|
|
|
@ -20,25 +20,19 @@ use Symfony\Component\Routing\Annotation\Route;
|
|||
|
||||
class NavigationAdminController extends CrudController
|
||||
{
|
||||
/**
|
||||
* @Route("/admin/site/navigation/{page}", name="admin_site_navigation_index", methods={"GET"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
#[Route(path: '/admin/site/navigation/{page}', name: 'admin_site_navigation_index', methods: ['GET'], requirements: ['page' => '\d+'])]
|
||||
public function index(RepositoryQuery $query, Request $request, Session $session, int $page = 1): Response
|
||||
{
|
||||
return $this->doIndex($page, $query, $request, $session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/navigation/new", name="admin_site_navigation_new", methods={"GET", "POST"})
|
||||
*/
|
||||
#[Route(path: '/admin/site/navigation/new', name: 'admin_site_navigation_new', methods: ['GET', 'POST'])]
|
||||
public function new(Factory $factory, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doNew($factory->create(), $entityManager, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/navigation/show/{entity}", name="admin_site_navigation_show", methods={"GET"})
|
||||
*/
|
||||
#[Route(path: '/admin/site/navigation/show/{entity}', name: 'admin_site_navigation_show', methods: ['GET'])]
|
||||
public function show(
|
||||
Entity $entity,
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
|
@ -60,33 +54,25 @@ class NavigationAdminController extends CrudController
|
|||
return $this->doShow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/navigation/filter", name="admin_site_navigation_filter", methods={"GET"})
|
||||
*/
|
||||
#[Route(path: '/admin/site/navigation/filter', name: 'admin_site_navigation_filter', methods: ['GET'])]
|
||||
public function filter(Session $session): Response
|
||||
{
|
||||
return $this->doFilter($session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/navigation/edit/{entity}", name="admin_site_navigation_edit", methods={"GET", "POST"})
|
||||
*/
|
||||
#[Route(path: '/admin/site/navigation/edit/{entity}', name: 'admin_site_navigation_edit', methods: ['GET', 'POST'])]
|
||||
public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doEdit($entity, $entityManager, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/navigation/sort/{page}", name="admin_site_navigation_sort", methods={"POST"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
#[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
|
||||
{
|
||||
return $this->doSort($page, $query, $entityManager, $request, $session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/navigation/delete/{entity}", name="admin_site_navigation_delete", methods={"DELETE"})
|
||||
*/
|
||||
#[Route(path: '/admin/site/navigation/delete/{entity}', name: 'admin_site_navigation_delete', methods: ['DELETE', 'POST'])]
|
||||
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doDelete($entity, $entityManager, $request);
|
||||
|
|
|
@ -24,14 +24,10 @@ use Symfony\Component\HttpFoundation\Response;
|
|||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/node")
|
||||
*/
|
||||
#[Route(path: '/admin/site/node')]
|
||||
class NodeAdminController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @Route("/new/{node}", name="admin_site_node_new")
|
||||
*/
|
||||
#[Route(path: '/new/{node}', name: 'admin_site_node_new')]
|
||||
public function new(
|
||||
Node $node,
|
||||
EntityFactory $factory,
|
||||
|
@ -103,9 +99,7 @@ class NodeAdminController extends AbstractController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/edit/{entity}/{tab}", name="admin_site_node_edit")
|
||||
*/
|
||||
#[Route(path: '/edit/{entity}/{tab}', name: 'admin_site_node_edit')]
|
||||
public function edit(
|
||||
Entity $entity,
|
||||
EntityManager $entityManager,
|
||||
|
@ -166,9 +160,7 @@ class NodeAdminController extends AbstractController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/urls/{entity}", name="admin_site_node_urls")
|
||||
*/
|
||||
#[Route(path: '/urls/{entity}', name: 'admin_site_node_urls')]
|
||||
public function urls(Entity $entity, SitemapBuilder $builder): Response
|
||||
{
|
||||
return $this->render('@Core/site/node_admin/urls.html.twig', [
|
||||
|
@ -177,9 +169,7 @@ class NodeAdminController extends AbstractController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/move/{entity}", name="admin_site_node_move")
|
||||
*/
|
||||
#[Route(path: '/move/{entity}', name: 'admin_site_node_move')]
|
||||
public function move(
|
||||
Entity $entity,
|
||||
EntityManager $entityManager,
|
||||
|
@ -233,9 +223,7 @@ class NodeAdminController extends AbstractController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/toggle/visibility/{entity}", name="admin_site_node_toggle_visibility", methods={"POST"})
|
||||
*/
|
||||
#[Route(path: '/toggle/visibility/{entity}', name: 'admin_site_node_toggle_visibility', methods: ['POST'])]
|
||||
public function toggleVisibility(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
if ($this->isCsrfTokenValid('toggle_visibility'.$entity->getId(), $request->request->get('_token'))) {
|
||||
|
@ -251,9 +239,7 @@ class NodeAdminController extends AbstractController
|
|||
]).sprintf('#node-%d', $entity->getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/delete/{entity}", name="admin_site_node_delete", methods={"DELETE"})
|
||||
*/
|
||||
#[Route(path: '/delete/{entity}', name: 'admin_site_node_delete', methods: ['DELETE', 'POST'])]
|
||||
public function delete(
|
||||
Entity $entity,
|
||||
NodeRepository $nodeRepository,
|
||||
|
|
|
@ -21,33 +21,25 @@ use App\Core\Entity\EntityInterface;
|
|||
|
||||
class PageAdminController extends CrudController
|
||||
{
|
||||
/**
|
||||
* @Route("/admin/site/page/{page}", name="admin_site_page_index", methods={"GET"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
#[Route(path: '/admin/site/page/{page}', name: 'admin_site_page_index', methods: ['GET'], requirements: ['page' => '\d+'])]
|
||||
public function index(RepositoryQuery $query, Request $request, Session $session, int $page = 1): Response
|
||||
{
|
||||
return $this->doIndex($page, $query, $request, $session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/page/show/{entity}", name="admin_site_page_show", methods={"GET"})
|
||||
*/
|
||||
#[Route(path: '/admin/site/page/show/{entity}', name: 'admin_site_page_show', methods: ['GET'])]
|
||||
public function show(Entity $entity): Response
|
||||
{
|
||||
return $this->doShow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/page/filter", name="admin_site_page_filter", methods={"GET"})
|
||||
*/
|
||||
#[Route(path: '/admin/site/page/filter', name: 'admin_site_page_filter', methods: ['GET'])]
|
||||
public function filter(Session $session): Response
|
||||
{
|
||||
return $this->doFilter($session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/page/edit/{entity}", name="admin_site_page_edit", methods={"GET", "POST"})
|
||||
*/
|
||||
#[Route(path: '/admin/site/page/edit/{entity}', name: 'admin_site_page_edit', methods: ['GET', 'POST'])]
|
||||
public function edit(
|
||||
int $entity,
|
||||
EntityManager $entityManager,
|
||||
|
@ -69,17 +61,13 @@ class PageAdminController extends CrudController
|
|||
return $this->doEdit($entity, $entityManager, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/page/delete/{entity}", name="admin_site_page_delete", methods={"DELETE"})
|
||||
*/
|
||||
#[Route(path: '/admin/site/page/delete/{entity}', name: 'admin_site_page_delete', methods: ['DELETE', 'POST'])]
|
||||
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doDelete($entity, $entityManager, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/page/batch/{page}", name="admin_site_page_batch", methods={"POST"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
#[Route(path: '/admin/site/page/batch/{page}', name: 'admin_site_page_batch', methods: ['POST'], requirements: ['page' => '\d+'])]
|
||||
public function batch(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
|
||||
{
|
||||
return $this->doBatch($page, $query, $entityManager, $request, $session);
|
||||
|
@ -101,6 +89,7 @@ class PageAdminController extends CrudController
|
|||
->setForm('edit', Type::class, [])
|
||||
->setForm('filter', FilterType::class)
|
||||
->setView('form', '@Core/site/page_admin/_form.html.twig')
|
||||
->setView('edit', '@Core/site/page_admin/edit.html.twig')
|
||||
|
||||
->setAction('index', 'new', false)
|
||||
->setAction('index', 'show', false)
|
||||
|
|
|
@ -11,22 +11,21 @@ use Symfony\Component\Routing\Annotation\Route;
|
|||
|
||||
class SitemapController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @Route("/sitemap.xml", name="sitemap")
|
||||
*/
|
||||
public function sitemap(Request $request, NavigationRepositoryQuery $navigationRepositoryQuery, SitemapBuilder $builder): Response
|
||||
#[Route(path: '/sitemap.xml', name: 'sitemap')]
|
||||
public function sitemap(Request $request, NavigationRepositoryQuery $query, SitemapBuilder $builder): Response
|
||||
{
|
||||
$navigations = $navigationRepositoryQuery
|
||||
->whereDomain($request->getHost())
|
||||
->find()
|
||||
;
|
||||
$navigations = $query->create()->find();
|
||||
|
||||
$items = [];
|
||||
|
||||
foreach ($navigations as $navigation) {
|
||||
if (!$navigation->matchDomain($request->getHost())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$items = array_merge(
|
||||
$items,
|
||||
$builder->build($navigation)
|
||||
$builder->build($navigation, $request->getHost())
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,14 +11,10 @@ use Symfony\Component\HttpFoundation\Response;
|
|||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
* @Route("/admin/site/tree")
|
||||
*/
|
||||
#[Route(path: '/admin/site/tree')]
|
||||
class TreeAdminController extends AdminController
|
||||
{
|
||||
/**
|
||||
* @Route("/", name="admin_site_tree_index")
|
||||
*/
|
||||
#[Route(path: '/', name: 'admin_site_tree_index')]
|
||||
public function index(NavigationRepositoryQuery $navigationQuery, Session $session): Response
|
||||
{
|
||||
$navigation = null;
|
||||
|
@ -48,9 +44,7 @@ class TreeAdminController extends AdminController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/navigation/{navigation}", name="admin_site_tree_navigation")
|
||||
*/
|
||||
#[Route(path: '/navigation/{navigation}', name: 'admin_site_tree_navigation')]
|
||||
public function navigation(
|
||||
Navigation $navigation,
|
||||
NavigationRepositoryQuery $navigationQuery,
|
||||
|
|
|
@ -13,14 +13,10 @@ use Symfony\Component\HttpFoundation\Request;
|
|||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
* @Route("/admin/task")
|
||||
*/
|
||||
#[Route(path: '/admin/task')]
|
||||
class TaskAdminController extends AdminController
|
||||
{
|
||||
/**
|
||||
* @Route("/", name="admin_task_index")
|
||||
*/
|
||||
#[Route(path: '/', name: 'admin_task_index')]
|
||||
public function index(EventDispatcherInterface $eventDispatcher): Response
|
||||
{
|
||||
$event = new TaskInitEvent();
|
||||
|
@ -31,9 +27,7 @@ class TaskAdminController extends AdminController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/run/{task}", name="admin_task_run", methods={"GET"})
|
||||
*/
|
||||
#[Route(path: '/run/{task}', name: 'admin_task_run', methods: ['GET'])]
|
||||
public function run(
|
||||
string $task,
|
||||
Request $request,
|
||||
|
|
|
@ -20,57 +20,43 @@ use App\Core\Security\TokenGenerator;
|
|||
|
||||
class UserAdminController extends CrudController
|
||||
{
|
||||
/**
|
||||
* @Route("/admin/user/{page}", name="admin_user_index", methods={"GET"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
#[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("/admin/user/new", name="admin_user_new", methods={"GET", "POST"})
|
||||
*/
|
||||
#[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("/admin/user/show/{entity}", name="admin_user_show", methods={"GET"})
|
||||
*/
|
||||
#[Route(path: '/admin/user/show/{entity}', name: 'admin_user_show', methods: ['GET'])]
|
||||
public function show(Entity $entity): Response
|
||||
{
|
||||
return $this->doShow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/user/filter", name="admin_user_filter", methods={"GET"})
|
||||
*/
|
||||
#[Route(path: '/admin/user/filter', name: 'admin_user_filter', methods: ['GET'])]
|
||||
public function filter(Session $session): Response
|
||||
{
|
||||
return $this->doFilter($session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/user/edit/{entity}", name="admin_user_edit", methods={"GET", "POST"})
|
||||
*/
|
||||
#[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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/user/delete/{entity}", name="admin_user_delete", methods={"DELETE"})
|
||||
*/
|
||||
#[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("/admin/user/resetting_request/{entity}", name="admin_user_resetting_request", methods={"POST"})
|
||||
*/
|
||||
#[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'))) {
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace App\Core\Crud;
|
||||
|
||||
use App\Core\Crud\Exception\CrudConfigurationException;
|
||||
|
||||
/**
|
||||
* class CrudConfiguration.
|
||||
*
|
||||
|
@ -109,7 +107,7 @@ class CrudConfiguration
|
|||
|
||||
/* -- */
|
||||
|
||||
public function setAction(string $page, string $action, bool $enabled): self
|
||||
public function setAction(string $page, string $action, bool|callable $enabled): self
|
||||
{
|
||||
if (!isset($this->actions[$page])) {
|
||||
$this->actions[$page] = [];
|
||||
|
@ -120,9 +118,20 @@ class CrudConfiguration
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getAction(string $page, string $action, bool $default = true)
|
||||
public function getAction(string $page, string $action, bool $default = true, array $callableParamaters = [])
|
||||
{
|
||||
return $this->actions[$page][$action] ?? $default;
|
||||
if (!isset($this->actions[$page][$action])) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
if (is_bool($this->actions[$page][$action])) {
|
||||
return $this->actions[$page][$action];
|
||||
}
|
||||
|
||||
return call_user_func_array(
|
||||
$this->actions[$page][$action],
|
||||
$callableParamaters
|
||||
);
|
||||
}
|
||||
|
||||
public function setBatchAction(string $page, string $action, string $label, callable $callback): self
|
||||
|
|
|
@ -6,28 +6,20 @@ use Doctrine\ORM\Mapping as ORM;
|
|||
|
||||
trait Timestampable
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(name="created_at", type="datetime")
|
||||
*/
|
||||
#[ORM\Column(name: 'created_at', type: 'datetime')]
|
||||
protected $createdAt;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="updated_at", type="datetime")
|
||||
*/
|
||||
#[ORM\Column(name: 'updated_at', type: 'datetime')]
|
||||
protected $updatedAt;
|
||||
|
||||
/**
|
||||
* @ORM\PrePersist
|
||||
*/
|
||||
#[ORM\PrePersist]
|
||||
public function onPrePersist(): void
|
||||
{
|
||||
$this->createdAt = new \DateTime();
|
||||
$this->updatedAt = new \DateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\PreUpdate
|
||||
*/
|
||||
#[ORM\PreUpdate]
|
||||
public function onPreUpdate(): void
|
||||
{
|
||||
$this->updatedAt = new \DateTime();
|
||||
|
|
|
@ -7,38 +7,26 @@ use App\Repository\Entity\Analytic\NodeViewRepository;
|
|||
use Doctrine\ORM\Mapping as ORM;
|
||||
use App\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=ViewRepository::class)
|
||||
* @ORM\Table(name="analytic_referer")
|
||||
*/
|
||||
#[ORM\Table(name: 'analytic_referer')]
|
||||
#[ORM\Entity(repositoryClass: ViewRepository::class)]
|
||||
class Referer implements EntityInterface
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Node::class, inversedBy="analyticReferers")
|
||||
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
|
||||
*/
|
||||
#[ORM\ManyToOne(targetEntity: Node::class, inversedBy: 'analyticReferers')]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
protected $node;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $uri;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer", options={"default"=0})
|
||||
*/
|
||||
#[ORM\Column(type: 'integer', options: ['default' => 0])]
|
||||
protected $views = 0;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="date")
|
||||
*/
|
||||
#[ORM\Column(type: 'date')]
|
||||
protected $date;
|
||||
|
||||
public function getId(): ?int
|
||||
|
|
|
@ -7,48 +7,32 @@ use App\Repository\Entity\Analytic\NodeViewRepository;
|
|||
use Doctrine\ORM\Mapping as ORM;
|
||||
use App\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=ViewRepository::class)
|
||||
* @ORM\Table(name="analytic_view")
|
||||
*/
|
||||
#[ORM\Table(name: 'analytic_view')]
|
||||
#[ORM\Entity(repositoryClass: ViewRepository::class)]
|
||||
class View implements EntityInterface
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Node::class, inversedBy="analyticViews")
|
||||
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
|
||||
*/
|
||||
#[ORM\ManyToOne(targetEntity: Node::class, inversedBy: 'analyticViews')]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
protected $node;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer", options={"default"=0})
|
||||
*/
|
||||
#[ORM\Column(type: 'integer', options: ['default' => 0])]
|
||||
protected $views = 0;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer", options={"default"=0})
|
||||
*/
|
||||
#[ORM\Column(type: 'integer', options: ['default' => 0])]
|
||||
protected $desktopViews = 0;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer", options={"default"=0})
|
||||
*/
|
||||
#[ORM\Column(type: 'integer', options: ['default' => 0])]
|
||||
protected $mobileViews = 0;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="date")
|
||||
*/
|
||||
#[ORM\Column(type: 'date')]
|
||||
protected $date;
|
||||
|
||||
public function getId(): ?int
|
||||
|
|
|
@ -5,21 +5,15 @@ namespace App\Core\Entity;
|
|||
use App\Repository\Entity\FileInformationRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=FileInformationRepository::class)
|
||||
*/
|
||||
#[ORM\Entity(repositoryClass: FileInformationRepository::class)]
|
||||
class FileInformation implements EntityInterface
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue(strategy="NONE")
|
||||
* @ORM\Column(type="string", length=96, unique=true)
|
||||
*/
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue(strategy: 'NONE')]
|
||||
#[ORM\Column(type: 'string', length: 96, unique: true)]
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text", nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
protected $attributes;
|
||||
|
||||
public function getId(): ?string
|
||||
|
|
|
@ -6,42 +6,28 @@ use App\Core\Entity\Site\Navigation;
|
|||
use App\Core\Repository\NavigationSettingRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=NavigationSettingRepository::class)
|
||||
*/
|
||||
#[ORM\Entity(repositoryClass: NavigationSettingRepository::class)]
|
||||
class NavigationSetting implements EntityInterface
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $section;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $code;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text", nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Navigation::class, inversedBy="navigationSettings")
|
||||
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
|
||||
*/
|
||||
#[ORM\ManyToOne(targetEntity: Navigation::class, inversedBy: 'navigationSettings')]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
protected $navigation;
|
||||
|
||||
public function getId(): ?int
|
||||
|
|
|
@ -5,71 +5,45 @@ namespace App\Core\Entity;
|
|||
use App\Core\Repository\RedirectRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=RedirectRepository::class)
|
||||
*/
|
||||
#[ORM\Entity(repositoryClass: RedirectRepository::class)]
|
||||
class Redirect implements EntityInterface
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=5)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 5)]
|
||||
protected $scheme;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $domain;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=6)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 6)]
|
||||
protected $domainType;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $rule;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=6)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 6)]
|
||||
protected $ruleType;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $location;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
#[ORM\Column(type: 'integer')]
|
||||
protected $redirectCode;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer", nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'integer', nullable: true)]
|
||||
protected $sortOrder;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean")
|
||||
*/
|
||||
#[ORM\Column(type: 'boolean')]
|
||||
protected $isEnabled;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean")
|
||||
*/
|
||||
#[ORM\Column(type: 'boolean')]
|
||||
protected $reuseQueryString;
|
||||
|
||||
public function getId(): ?int
|
||||
|
|
|
@ -5,36 +5,24 @@ namespace App\Core\Entity;
|
|||
use App\Core\Repository\SettingRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=SettingRepository::class)
|
||||
*/
|
||||
#[ORM\Entity(repositoryClass: SettingRepository::class)]
|
||||
class Setting implements EntityInterface
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $section;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $code;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text", nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
protected $value;
|
||||
|
||||
public function getId(): ?int
|
||||
|
|
|
@ -9,46 +9,32 @@ use Doctrine\Common\Collections\ArrayCollection;
|
|||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=MenuRepository::class)
|
||||
* @ORM\HasLifecycleCallbacks
|
||||
*/
|
||||
#[ORM\Entity(repositoryClass: MenuRepository::class)]
|
||||
#[ORM\HasLifecycleCallbacks]
|
||||
class Menu implements EntityInterface
|
||||
{
|
||||
use Timestampable;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $code;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Navigation::class, inversedBy="menus")
|
||||
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
|
||||
*/
|
||||
#[ORM\ManyToOne(targetEntity: Navigation::class, inversedBy: 'menus')]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
protected $navigation;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity=Node::class, mappedBy="menu", orphanRemoval=true, cascade={"remove", "persist"})
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: Node::class, mappedBy: 'menu', orphanRemoval: true, cascade: ['remove', 'persist'])]
|
||||
protected $nodes;
|
||||
|
||||
/**
|
||||
* @ORM\OneToOne(targetEntity=Node::class, cascade={"persist"})
|
||||
* @ORM\JoinColumn(onDelete="CASCADE")
|
||||
*/
|
||||
#[ORM\OneToOne(targetEntity: Node::class, cascade: ['persist'])]
|
||||
#[ORM\JoinColumn(onDelete: 'CASCADE')]
|
||||
protected $rootNode;
|
||||
|
||||
public function __construct()
|
||||
|
|
|
@ -10,64 +10,42 @@ use Doctrine\Common\Collections\ArrayCollection;
|
|||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=NavigationRepository::class)
|
||||
* @ORM\HasLifecycleCallbacks
|
||||
*/
|
||||
#[ORM\Entity(repositoryClass: NavigationRepository::class)]
|
||||
#[ORM\HasLifecycleCallbacks]
|
||||
class Navigation implements EntityInterface
|
||||
{
|
||||
use Timestampable;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $code;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $domain;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean", options={"default"=0})
|
||||
*/
|
||||
#[ORM\Column(type: 'boolean', options: ['default' => 0])]
|
||||
protected $forceDomain = false;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text", nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
protected $additionalDomains = '[]';
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity=Menu::class, mappedBy="navigation")
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: Menu::class, mappedBy: 'navigation')]
|
||||
protected $menus;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=10)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 10)]
|
||||
protected $locale = 'en';
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer", nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'integer', nullable: true)]
|
||||
protected $sortOrder;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity=NavigationSetting::class, mappedBy="navigation", orphanRemoval=true)
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: NavigationSetting::class, mappedBy: 'navigation', orphanRemoval: true)]
|
||||
protected $navigationSettings;
|
||||
|
||||
public function __construct()
|
||||
|
@ -240,4 +218,23 @@ class Navigation implements EntityInterface
|
|||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function matchDomain(string $domain): bool
|
||||
{
|
||||
if ($domain === $this->getDomain()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($this->getAdditionalDomains() as $additionalDomain) {
|
||||
if ('domain' === $additionalDomain['type'] && $additionalDomain['domain'] === $domain) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ('regexp' === $additionalDomain['type'] && preg_match('#'.$additionalDomain['domain'].'#', $domain) > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,156 +16,120 @@ use function Symfony\Component\String\u;
|
|||
|
||||
/**
|
||||
* @Gedmo\Tree(type="nested")
|
||||
* @ORM\HasLifecycleCallbacks
|
||||
* @ORM\Entity(repositoryClass=NodeRepository::class)
|
||||
*/
|
||||
#[ORM\HasLifecycleCallbacks]
|
||||
#[ORM\Entity(repositoryClass: NodeRepository::class)]
|
||||
class Node implements EntityInterface
|
||||
{
|
||||
use Timestampable;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Menu::class, inversedBy="nodes", cascade={"persist", "remove"})
|
||||
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
|
||||
*/
|
||||
#[ORM\ManyToOne(targetEntity: Menu::class, inversedBy: 'nodes', cascade: ['persist', 'remove'])]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
protected $menu;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255, nullable: true)]
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255, nullable: true)]
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean", options={"default"=0})
|
||||
*/
|
||||
#[ORM\Column(type: 'boolean', options: ['default' => 0])]
|
||||
protected $disableUrl = false;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean", options={"default"=0})
|
||||
*/
|
||||
#[ORM\Column(type: 'boolean', options: ['default' => 0])]
|
||||
protected $isVisible = false;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeLeft
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
#[ORM\Column(type: 'integer')]
|
||||
protected $treeLeft;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeLevel
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
#[ORM\Column(type: 'integer')]
|
||||
protected $treeLevel;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeRight
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
#[ORM\Column(type: 'integer')]
|
||||
protected $treeRight;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeRoot
|
||||
* @ORM\ManyToOne(targetEntity="Node")
|
||||
* @ORM\JoinColumn(referencedColumnName="id", onDelete="CASCADE")
|
||||
*/
|
||||
#[ORM\ManyToOne(targetEntity: 'Node')]
|
||||
#[ORM\JoinColumn(referencedColumnName: 'id', onDelete: 'CASCADE')]
|
||||
protected $treeRoot;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeParent
|
||||
* @ORM\ManyToOne(targetEntity="Node", inversedBy="children")
|
||||
* @ORM\JoinColumn(referencedColumnName="id", onDelete="CASCADE")
|
||||
*/
|
||||
#[ORM\ManyToOne(targetEntity: 'Node', inversedBy: 'children')]
|
||||
#[ORM\JoinColumn(referencedColumnName: 'id', onDelete: 'CASCADE')]
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="Node", mappedBy="parent")
|
||||
* @ORM\OrderBy({"treeLeft"="ASC"})
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: 'Node', mappedBy: 'parent')]
|
||||
#[ORM\OrderBy(['treeLeft' => 'ASC'])]
|
||||
protected $children;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Page::class, inversedBy="nodes", cascade={"persist"})
|
||||
* @ORM\JoinColumn(nullable=true, onDelete="SET NULL")
|
||||
*/
|
||||
#[ORM\ManyToOne(targetEntity: Page::class, inversedBy: 'nodes', cascade: ['persist'])]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
protected $page;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255, nullable: true)]
|
||||
protected $code;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="array", nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'array', nullable: true)]
|
||||
protected $parameters = [];
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="array", nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'array', nullable: true)]
|
||||
protected $attributes = [];
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255, nullable: true)]
|
||||
protected $controller;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="array", nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'array', nullable: true)]
|
||||
protected $sitemapParameters = [];
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Node::class, inversedBy="aliasNodes")
|
||||
* @ORM\JoinColumn(nullable=true, onDelete="SET NULL")
|
||||
*/
|
||||
#[ORM\ManyToOne(targetEntity: Node::class, inversedBy: 'aliasNodes')]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
protected $aliasNode;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity=Node::class, mappedBy="aliasNode")
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: Node::class, mappedBy: 'aliasNode')]
|
||||
protected $aliasNodes;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255, nullable: true)]
|
||||
protected $contentType;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean", options={"default"=0})
|
||||
*/
|
||||
#[ORM\Column(type: 'boolean', options: ['default' => 0])]
|
||||
protected $enableAnalytics = false;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity=View::class, mappedBy="node")
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: View::class, mappedBy: 'node')]
|
||||
protected $analyticViews;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity=Referer::class, mappedBy="node")
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: Referer::class, mappedBy: 'node')]
|
||||
protected $analyticReferers;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="array", nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'array', nullable: true)]
|
||||
private $securityRoles = [];
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=3, nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 3, nullable: true)]
|
||||
private $securityOperator = 'or';
|
||||
|
||||
#[ORM\Column(type: 'boolean', options: ['default' => 0])]
|
||||
private $hasAbTest = false;
|
||||
|
||||
#[ORM\Column(type: 'string', length: 255, nullable: true)]
|
||||
private $abTestCode;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->children = new ArrayCollection();
|
||||
|
@ -673,4 +637,28 @@ class Node implements EntityInterface
|
|||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHasAbTest(): ?bool
|
||||
{
|
||||
return $this->hasAbTest;
|
||||
}
|
||||
|
||||
public function setHasAbTest(bool $hasAbTest): self
|
||||
{
|
||||
$this->hasAbTest = $hasAbTest;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAbTestCode(): ?string
|
||||
{
|
||||
return $this->abTestCode;
|
||||
}
|
||||
|
||||
public function setAbTestCode(?string $abTestCode): self
|
||||
{
|
||||
$this->abTestCode = $abTestCode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,37 +6,27 @@ use App\Core\Doctrine\Timestampable;
|
|||
use App\Core\Repository\Site\Page\BlockRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=BlockRepository::class)
|
||||
* @ORM\DiscriminatorColumn(name="class_key", type="string")
|
||||
* @ORM\InheritanceType("SINGLE_TABLE")
|
||||
* @ORM\HasLifecycleCallbacks
|
||||
*/
|
||||
#[ORM\Entity(repositoryClass: BlockRepository::class)]
|
||||
#[ORM\DiscriminatorColumn(name: 'class_key', type: 'string')]
|
||||
#[ORM\InheritanceType('SINGLE_TABLE')]
|
||||
#[ORM\HasLifecycleCallbacks]
|
||||
class Block
|
||||
{
|
||||
use Timestampable;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text", nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Page::class, inversedBy="blocks")
|
||||
* @ORM\JoinColumn(onDelete="CASCADE")
|
||||
*/
|
||||
#[ORM\ManyToOne(targetEntity: Page::class, inversedBy: 'blocks')]
|
||||
#[ORM\JoinColumn(onDelete: 'CASCADE')]
|
||||
protected $page;
|
||||
|
||||
public function getId(): ?int
|
||||
|
|
|
@ -4,9 +4,7 @@ namespace App\Core\Entity\Site\Page;
|
|||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
#[ORM\Entity]
|
||||
class ChoiceBlock extends Block
|
||||
{
|
||||
public function getValue()
|
||||
|
|
|
@ -4,9 +4,7 @@ namespace App\Core\Entity\Site\Page;
|
|||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
#[ORM\Entity]
|
||||
class CollectionBlock extends Block
|
||||
{
|
||||
public function getValue()
|
||||
|
|
|
@ -6,9 +6,7 @@ use App\Core\File\FileAttribute;
|
|||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\HttpFoundation\File\File;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
#[ORM\Entity]
|
||||
class FileBlock extends Block
|
||||
{
|
||||
public function getValue()
|
||||
|
|
|
@ -13,66 +13,44 @@ 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")
|
||||
* @ORM\InheritanceType("SINGLE_TABLE")
|
||||
* @ORM\HasLifecycleCallbacks
|
||||
*/
|
||||
#[ORM\Entity(repositoryClass: PageRepository::class)]
|
||||
#[ORM\DiscriminatorColumn(name: 'class_key', type: 'string')]
|
||||
#[ORM\InheritanceType('SINGLE_TABLE')]
|
||||
#[ORM\HasLifecycleCallbacks]
|
||||
class Page implements EntityInterface
|
||||
{
|
||||
use Timestampable;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255, nullable: true)]
|
||||
protected $template;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity=Block::class, mappedBy="page", cascade={"persist"})
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: Block::class, mappedBy: 'page', cascade: ['persist'])]
|
||||
protected $blocks;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255, nullable: true)]
|
||||
protected $metaTitle;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255, nullable: true)]
|
||||
protected $metaDescription;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255, nullable: true)]
|
||||
protected $ogTitle;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255, nullable: true)]
|
||||
protected $ogDescription;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
#[ORM\Column(type: 'string', length: 255, nullable: true)]
|
||||
protected $ogImage;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity=Node::class, mappedBy="page")
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: Node::class, mappedBy: 'page')]
|
||||
protected $nodes;
|
||||
|
||||
public function __construct()
|
||||
|
|
|
@ -4,9 +4,7 @@ namespace App\Core\Entity\Site\Page;
|
|||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
#[ORM\Entity]
|
||||
class TextBlock extends Block
|
||||
{
|
||||
}
|
||||
|
|
29
src/core/Event/Ab/AbTestEvent.php
Normal file
29
src/core/Event/Ab/AbTestEvent.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Event\Ab;
|
||||
|
||||
use App\Core\Ab\AbTest;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* class AbTestEvent.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class AbTestEvent extends Event
|
||||
{
|
||||
public const INIT_EVENT = 'ab_test.init';
|
||||
public const RUN_EVENT = 'ab_test.run';
|
||||
|
||||
protected AbTest $test;
|
||||
|
||||
public function __construct(AbTest $test)
|
||||
{
|
||||
$this->test = $test;
|
||||
}
|
||||
|
||||
public function getTest(): AbTest
|
||||
{
|
||||
return $this->test;
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ class PasswordRequestEvent extends Event
|
|||
$this->user = $user;
|
||||
}
|
||||
|
||||
public function getUser(): USer
|
||||
public function getUser(): User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
|
109
src/core/EventListener/AbListener.php
Normal file
109
src/core/EventListener/AbListener.php
Normal file
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\EventListener;
|
||||
|
||||
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;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||
use Symfony\Component\HttpKernel\Event\ResponseEvent;
|
||||
|
||||
/**
|
||||
* class AbListener.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class AbListener
|
||||
{
|
||||
protected EventDispatcherInterface $eventDispatcher;
|
||||
protected AbContainer $container;
|
||||
protected SiteRequest $siteRequest;
|
||||
protected ?Node $node;
|
||||
|
||||
public function __construct(
|
||||
AbContainer $container,
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
SiteRequest $siteRequest
|
||||
) {
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->container = $container;
|
||||
$this->siteRequest = $siteRequest;
|
||||
}
|
||||
|
||||
public function onKernelRequest(RequestEvent $event)
|
||||
{
|
||||
$this->node = $this->siteRequest->getNode();
|
||||
|
||||
if (!$this->supports($event->getRequest())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$request = $event->getRequest();
|
||||
$cookieName = md5($this->getCookieName());
|
||||
$cookieValue = $event->getRequest()->cookies->get($cookieName);
|
||||
|
||||
$abTest = new AbTest($this->getAbTestCode());
|
||||
$event = new AbTestEvent($abTest);
|
||||
$this->container->add($abTest);
|
||||
|
||||
$this->eventDispatcher->dispatch($event, AbTestEvent::INIT_EVENT);
|
||||
|
||||
if (!$abTest->isValidVariation($cookieValue)) {
|
||||
$abTest->run();
|
||||
$result = $abTest->getResult();
|
||||
|
||||
$attributes = array_merge($request->attributes->get('ab_test_cookies', []), [
|
||||
$cookieName => ['value' => $result, 'duration' => $abTest->getDuration()],
|
||||
]);
|
||||
|
||||
$request->attributes->set('ab_test_cookies', $attributes);
|
||||
|
||||
$this->eventDispatcher->dispatch($event, AbTestEvent::RUN_EVENT);
|
||||
} else {
|
||||
$abTest->setResult($cookieValue);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getCookieName(): string
|
||||
{
|
||||
return 'ab_test_'.$this->getAbTestCode();
|
||||
}
|
||||
|
||||
protected function getAbTestCode(): string
|
||||
{
|
||||
return $this->node->getAbTestCode();
|
||||
}
|
||||
|
||||
protected function supports(Request $request): bool
|
||||
{
|
||||
if (!$this->node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->node->getHasAbTest()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->node->getAbTestCode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
32
src/core/EventSubscriber/AbEventSubscriber.php
Normal file
32
src/core/EventSubscriber/AbEventSubscriber.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\EventSubscriber;
|
||||
|
||||
use App\Core\Event\Ab\AbTestEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* class AbEventSubscriber.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
abstract class AbEventSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
protected static int $priority = 0;
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
AbTestEvent::INIT_EVENT => ['onInit', self::$priority],
|
||||
AbTestEvent::RUN_EVENT => ['onRun', self::$priority],
|
||||
];
|
||||
}
|
||||
|
||||
public function onInit(AbTestEvent $event)
|
||||
{
|
||||
}
|
||||
|
||||
public function onRun(AbTestEvent $event)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -43,14 +43,14 @@ class MenuEventSubscriber extends EntityManagerEventSubscriber
|
|||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
public function support(EntityInterface $entity)
|
||||
public function supports(EntityInterface $entity): bool
|
||||
{
|
||||
return $entity instanceof Menu;
|
||||
}
|
||||
|
||||
public function onPreUpdate(EntityManagerEvent $event)
|
||||
{
|
||||
if (!$this->support($event->getEntity())) {
|
||||
if (!$this->supports($event->getEntity())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -60,13 +60,13 @@ class MenuEventSubscriber extends EntityManagerEventSubscriber
|
|||
|
||||
public function onCreate(EntityManagerEvent $event)
|
||||
{
|
||||
if (!$this->support($event->getEntity())) {
|
||||
if (!$this->supports($event->getEntity())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$menu = $event->getEntity();
|
||||
|
||||
if (count($menu->getNodes()) > 2) {
|
||||
if (count($menu->getNodes()) >= 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -84,10 +84,10 @@ class MenuEventSubscriber extends EntityManagerEventSubscriber
|
|||
|
||||
$menu->setRootNode($rootNode);
|
||||
|
||||
$this->entityManager->getEntityManager()->persist($rootNode);
|
||||
$this->entityManager->getEntityManager()->persist($childNode);
|
||||
foreach ([$rootNode, $childNode, $menu] as $entity) {
|
||||
$this->entityManager->getEntityManager()->persist($entity);
|
||||
}
|
||||
|
||||
$this->entityManager->getEntityManager()->persist($menu);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$this->nodeRepository->persistAsFirstChild($childNode, $rootNode);
|
||||
|
|
|
@ -24,14 +24,14 @@ class NavigationEventSubscriber extends EntityManagerEventSubscriber
|
|||
$this->slugify = $slugify;
|
||||
}
|
||||
|
||||
public function support(EntityInterface $entity)
|
||||
public function supports(EntityInterface $entity): bool
|
||||
{
|
||||
return $entity instanceof Navigation;
|
||||
}
|
||||
|
||||
public function onPreUpdate(EntityManagerEvent $event)
|
||||
{
|
||||
if (!$this->support($event->getEntity())) {
|
||||
if (!$this->supports($event->getEntity())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class NodeEventSubscriber extends EntityManagerEventSubscriber
|
|||
$this->routeParameterSlugify = $routeParameterSlugify;
|
||||
}
|
||||
|
||||
public function support(EntityInterface $entity)
|
||||
public function supports(EntityInterface $entity): bool
|
||||
{
|
||||
return $entity instanceof Node;
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ class NodeEventSubscriber extends EntityManagerEventSubscriber
|
|||
|
||||
public function onPreUpdate(EntityManagerEvent $event)
|
||||
{
|
||||
if (!$this->support($event->getEntity())) {
|
||||
if (!$this->supports($event->getEntity())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,7 @@ class NodeEventSubscriber extends EntityManagerEventSubscriber
|
|||
|
||||
public function onDelete(EntityManagerEvent $event)
|
||||
{
|
||||
if (!$this->support($event->getEntity())) {
|
||||
if (!$this->supports($event->getEntity())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,14 +24,14 @@ class BlockEventSubscriber extends EntityManagerEventSubscriber
|
|||
$this->fileUpload = $fileUpload;
|
||||
}
|
||||
|
||||
public function support(EntityInterface $entity)
|
||||
public function supports(EntityInterface $entity): bool
|
||||
{
|
||||
return $entity instanceof Page;
|
||||
}
|
||||
|
||||
public function onPreUpdate(EntityManagerEvent $event)
|
||||
{
|
||||
if (!$this->support($event->getEntity())) {
|
||||
if (!$this->supports($event->getEntity())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,14 +23,14 @@ class PageEventSubscriber extends EntityManagerEventSubscriber
|
|||
$this->fileUpload = $fileUpload;
|
||||
}
|
||||
|
||||
public function support(EntityInterface $entity)
|
||||
public function supports(EntityInterface $entity): bool
|
||||
{
|
||||
return $entity instanceof Page;
|
||||
}
|
||||
|
||||
public function onPreUpdate(EntityManagerEvent $event)
|
||||
{
|
||||
if (!$this->support($event->getEntity())) {
|
||||
if (!$this->supports($event->getEntity())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,14 +27,14 @@ class SiteEventSubscriber extends EntityManagerEventSubscriber
|
|||
$this->cacheManager = $cacheManager;
|
||||
}
|
||||
|
||||
public function support(EntityInterface $entity)
|
||||
public function supports(EntityInterface $entity): bool
|
||||
{
|
||||
return $entity instanceof Node || $entity instanceof Menu || $entity instanceof Navigation;
|
||||
}
|
||||
|
||||
public function onUpdate(EntityManagerEvent $event)
|
||||
{
|
||||
if (!$this->support($event->getEntity())) {
|
||||
if (!$this->supports($event->getEntity())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -154,6 +154,37 @@ class NodeType extends AbstractType
|
|||
);
|
||||
}
|
||||
|
||||
$builder->add(
|
||||
'hasAbTest',
|
||||
CheckboxType::class,
|
||||
[
|
||||
'label' => 'Enable A/B Testing',
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'abTestCode',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'Code',
|
||||
'required' => $builder->getData()->getHasAbTest(),
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'securityOperator',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'label' => 'Condition',
|
||||
'required' => true,
|
||||
'choices' => [
|
||||
'At least one role' => 'or',
|
||||
'All roles' => 'and',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$actions = [
|
||||
'New page' => 'new',
|
||||
'Use an existing page' => 'existing',
|
||||
|
|
21
src/core/Form/Site/Page/EditorJsTextareaBlockType.php
Normal file
21
src/core/Form/Site/Page/EditorJsTextareaBlockType.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Form\Site\Page;
|
||||
|
||||
use App\Core\Form\Type\EditorJsTextareaType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class EditorJsTextareaBlockType extends TextareaBlockType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->add(
|
||||
'value',
|
||||
EditorJsTextareaType::class,
|
||||
array_merge([
|
||||
'required' => false,
|
||||
'label' => false,
|
||||
], $options['options']),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -5,6 +5,8 @@ namespace App\Core\Form\Site\Page;
|
|||
use App\Core\Entity\Site\Page\FileBlock;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FileType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class FileBlockType extends TextBlockType
|
||||
|
@ -21,11 +23,21 @@ class FileBlockType extends TextBlockType
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$view->vars = array_replace($view->vars, [
|
||||
'file_type' => $options['file_type'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => FileBlock::class,
|
||||
'block_prefix' => 'file_block',
|
||||
'file_type' => 'auto',
|
||||
'options' => [],
|
||||
]);
|
||||
}
|
||||
|
|
21
src/core/Form/Site/Page/GrapesJsBlockType.php
Normal file
21
src/core/Form/Site/Page/GrapesJsBlockType.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Form\Site\Page;
|
||||
|
||||
use App\Core\Form\Type\GrapesJsType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class GrapesJsBlockType extends TextareaBlockType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->add(
|
||||
'value',
|
||||
GrapesJsType::class,
|
||||
array_merge([
|
||||
'required' => false,
|
||||
'label' => false,
|
||||
], $options['options']),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ namespace App\Core\Form\Site\Page;
|
|||
|
||||
use Symfony\Component\Form\Extension\Core\Type\FileType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\Image;
|
||||
|
||||
class ImageBlockType extends FileBlockType
|
||||
|
@ -22,4 +23,11 @@ class ImageBlockType extends FileBlockType
|
|||
], $options['options']),
|
||||
);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
parent::configureOptions($resolver);
|
||||
|
||||
$resolver->setDefault('is_image', true);
|
||||
}
|
||||
}
|
||||
|
|
21
src/core/Form/Site/Page/TinymceTextareaBlockType.php
Normal file
21
src/core/Form/Site/Page/TinymceTextareaBlockType.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Form\Site\Page;
|
||||
|
||||
use App\Core\Form\Type\TinymceTextareaType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class TinymceTextareaBlockType extends TextareaBlockType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->add(
|
||||
'value',
|
||||
TinymceTextareaType::class,
|
||||
array_merge([
|
||||
'required' => false,
|
||||
'label' => false,
|
||||
], $options['options']),
|
||||
);
|
||||
}
|
||||
}
|
30
src/core/Form/Type/GrapesJsType.php
Normal file
30
src/core/Form/Type/GrapesJsType.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Core\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
|
||||
class GrapesJsType extends TextareaType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
if (!isset($view->vars['attr']['data-grapesjs'])) {
|
||||
$view->vars['attr']['data-grapesjs'] = '';
|
||||
}
|
||||
|
||||
return parent::buildView($view, $form, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'grapesjs';
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ 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
|
||||
{
|
||||
|
@ -75,17 +76,31 @@ class MakePage extends AbstractMaker
|
|||
|
||||
$generator->writeChanges();
|
||||
|
||||
$templatePath = sprintf(
|
||||
'page/%s/default.html.twig',
|
||||
Str::asSnakeCase(preg_replace('/Page$/', '', $pageClassNameDetails->getShortName()))
|
||||
);
|
||||
|
||||
$realTemplatePath = 'templates/'.$templatePath;
|
||||
|
||||
$filesystem = new Filesystem();
|
||||
|
||||
if (!$filesystem->exists($templatePath)) {
|
||||
$filesystem->mkdir(dirname($realTemplatePath));
|
||||
$filesystem->dumpFile($realTemplatePath, "{% extends 'base.html.twig' %}\n\n{% block page %}\n\n{% endblock %}\n");
|
||||
}
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
$io->text('Register the page in <comment>config/packages/app.yaml</comment>: ');
|
||||
$io->text(<<< EOF
|
||||
|
||||
core:
|
||||
site:
|
||||
pages:
|
||||
{$pageClassNameDetails->getFullName()}:
|
||||
name: {$pageClassNameDetails->getShortName()}
|
||||
templates:
|
||||
- {name: "Default", file: "path/to/template.html.twig"}
|
||||
core:
|
||||
site:
|
||||
pages:
|
||||
{$pageClassNameDetails->getFullName()}:
|
||||
name: {$pageClassNameDetails->getShortName()}
|
||||
templates:
|
||||
- {name: "Default", file: "${templatePath}"}
|
||||
|
||||
EOF
|
||||
);
|
||||
|
@ -125,9 +140,12 @@ EOF
|
|||
'textarea' => null,
|
||||
'choice' => null,
|
||||
'collection' => 'BlockEntity\\CollectionBlock::class',
|
||||
'editor_js_textarea' => null,
|
||||
'file' => 'BlockEntity\\FileBlock::class',
|
||||
'file_picker' => null,
|
||||
'grapes_js' => null,
|
||||
'image' => 'BlockEntity\\FileBlock::class',
|
||||
'tinymce_textarea' => null,
|
||||
];
|
||||
|
||||
while (null === $type) {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace App\Core;
|
||||
|
||||
if (!defined('MURPH_VERSION')) {
|
||||
define('MURPH_VERSION', 'v1.11.0');
|
||||
define('MURPH_VERSION', 'v1.18.0');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace App\Core\Notification;
|
|||
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
|
||||
use Symfony\Component\Mailer\MailerInterface;
|
||||
use Twig\Environment as TwigEnvironment;
|
||||
use App\Entity\User;
|
||||
|
||||
/**
|
||||
* class MailNotifier.
|
||||
|
@ -139,19 +140,19 @@ class MailNotifier
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function addRecipientByAccount(Account $account, bool $isBcc = false): self
|
||||
public function addRecipientByUser(User $user, bool $isBcc = false): self
|
||||
{
|
||||
return $this->addRecipient($account->getEmail(), $isBcc);
|
||||
return $this->addRecipient($user->getEmail(), $isBcc);
|
||||
}
|
||||
|
||||
public function addRecipientsByAccounts($accounts, bool $isBcc = false)
|
||||
public function addRecipientsByUsers($users, bool $isBcc = false)
|
||||
{
|
||||
if (!is_array($accounts)) {
|
||||
if (!is_array($users)) {
|
||||
throw new InvalidArgumentException('The "accounts" parameter must be an array or an instance of ObjectCollection');
|
||||
}
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$this->addRecipientByAccount($account, $isBcc);
|
||||
foreach ($users as $user) {
|
||||
$this->addRecipientByUser($user, $isBcc);
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
|
|
@ -17,7 +17,7 @@ abstract class RepositoryQuery
|
|||
protected QueryBuilder $query;
|
||||
protected PaginatorInterface $paginator;
|
||||
protected string $id;
|
||||
protected array $forcedFilterHandlers;
|
||||
protected array $forcedFilterHandlers = [];
|
||||
|
||||
public function __construct(ServiceEntityRepository $repository, string $id, PaginatorInterface $paginator = null)
|
||||
{
|
||||
|
@ -25,7 +25,6 @@ abstract class RepositoryQuery
|
|||
$this->query = $repository->createQueryBuilder($id);
|
||||
$this->paginator = $paginator;
|
||||
$this->id = $id;
|
||||
$this->forcedFilterHandlers = [];
|
||||
}
|
||||
|
||||
public function __call(string $name, $params): self
|
||||
|
|
|
@ -13,565 +13,657 @@ $pagination-active-bg: #343a40 !default;
|
|||
@import "~choices.js/src/styles/choices.scss";
|
||||
@import "~bootstrap/scss/bootstrap.scss";
|
||||
@import "~@fortawesome/fontawesome-free/css/all.css";
|
||||
@import "~flag-icon-css/sass/flag-icon.scss";
|
||||
@import "~flag-icon-css/sass/flag-icon.scss";
|
||||
@import "~flag-icons/sass/flag-icons.scss";
|
||||
@import '~grapesjs/dist/css/grapes.min.css';
|
||||
@import '~grapesjs-component-code-editor/dist/grapesjs-component-code-editor.min.css';
|
||||
|
||||
@for $i from 1 through 100 {
|
||||
.miw-#{$i*5} {
|
||||
min-width: $i * 5px;
|
||||
}
|
||||
.miw-#{$i*5} {
|
||||
min-width: $i * 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.flag-icon-en {
|
||||
background-image: url(~flag-icon-css/flags/4x3/gb.svg);
|
||||
.fi-en {
|
||||
background-image: url(~flag-icons/flags/4x3/gb.svg);
|
||||
}
|
||||
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.body.has-form {
|
||||
min-height: 100vh;
|
||||
background: lighten(map-get($theme-colors, 'dark-blue'), 75%);
|
||||
}
|
||||
|
||||
#logo {
|
||||
width: 30px;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.choices__list--dropdown {
|
||||
display: none;
|
||||
display: none;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.choices__list--dropdown.is-active {
|
||||
display: block;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropdown-toggle-hide-after {
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.login {
|
||||
&-container {
|
||||
margin-top: 5%;
|
||||
margin-bottom: 5%;
|
||||
}
|
||||
&-container {
|
||||
margin-top: 5%;
|
||||
margin-bottom: 5%;
|
||||
}
|
||||
|
||||
&-form {
|
||||
padding: 5%;
|
||||
}
|
||||
&-form {
|
||||
padding: 5%;
|
||||
}
|
||||
|
||||
&-image {
|
||||
width: 100%;
|
||||
max-width: 80%;
|
||||
}
|
||||
&-image {
|
||||
width: 100%;
|
||||
max-width: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
padding: 71px 0 0;
|
||||
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
padding: 71px 0 0;
|
||||
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
|
||||
}
|
||||
|
||||
.sidebar-sticky {
|
||||
position: relative;
|
||||
top: 0;
|
||||
height: calc(100vh - 71px);
|
||||
padding-top: .5rem;
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
position: relative;
|
||||
top: 0;
|
||||
height: calc(100vh - 71px);
|
||||
padding-top: .5rem;
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
|
||||
&:hover {
|
||||
overflow-y: auto;
|
||||
}
|
||||
&:hover {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@supports ((position: -webkit-sticky) or (position: sticky)) {
|
||||
.sidebar-sticky {
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
}
|
||||
.sidebar-sticky {
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
}
|
||||
}
|
||||
|
||||
.actions-container {
|
||||
padding-right: 25px;
|
||||
padding-right: 25px;
|
||||
}
|
||||
|
||||
.table .thead-light {
|
||||
a, th {
|
||||
color: map-get($theme-colors, 'dark-blue');
|
||||
}
|
||||
a, th {
|
||||
color: map-get($theme-colors, 'dark-blue');
|
||||
}
|
||||
}
|
||||
|
||||
tr.table-primary-light {
|
||||
background-color: #ecf5fa;
|
||||
background-color: #ecf5fa;
|
||||
}
|
||||
|
||||
.td-nowrap {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.wrap {
|
||||
word-wrap: anywhere;
|
||||
word-wrap: anywhere;
|
||||
}
|
||||
|
||||
.analytic .analytic-referer::before {
|
||||
content: '▸';
|
||||
padding-right: 10px;
|
||||
content: '▸';
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.analytic[open] .analytic-referer::before {
|
||||
content: '▾';
|
||||
content: '▾';
|
||||
}
|
||||
|
||||
.analytic-referer {
|
||||
max-width: calc(100% - 60px);
|
||||
max-width: calc(100% - 60px);
|
||||
}
|
||||
|
||||
.table tr {
|
||||
td {
|
||||
transition: border 500ms ease-out;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
td {
|
||||
transition: border 500ms ease-out;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
td {
|
||||
border-bottom: 1px solid #a8aaac;
|
||||
}
|
||||
&:hover {
|
||||
td {
|
||||
border-bottom: 1px solid #a8aaac;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bg-dark-blue {
|
||||
background: map-get($theme-colors, 'dark-blue');
|
||||
color: #fff;
|
||||
background: map-get($theme-colors, 'dark-blue');
|
||||
color: #fff;
|
||||
|
||||
.nav-item-label {
|
||||
color: #fff;
|
||||
}
|
||||
.nav-item-label {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-pills {
|
||||
.nav-item {
|
||||
margin-right: 3px;
|
||||
}
|
||||
.nav-item {
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.nav-link:not(.active) {
|
||||
color: #333;
|
||||
background: #eee;
|
||||
}
|
||||
.nav-link:not(.active) {
|
||||
color: #333;
|
||||
background: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.sidebar {
|
||||
width: 260px;
|
||||
display: inline-block;
|
||||
width: 260px;
|
||||
display: inline-block;
|
||||
|
||||
.nav-link {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
border-left: 4px solid map-get($theme-colors, 'dark-blue');
|
||||
padding-top: 14px;
|
||||
padding-bottom: 14px;
|
||||
.sidebar-toggler {
|
||||
display: none;
|
||||
text-align: right;
|
||||
padding-right: 5px;
|
||||
|
||||
.fa {
|
||||
font-size: 1.2rem;
|
||||
margin-right: 5px;
|
||||
color: #fff;
|
||||
width: 35px;
|
||||
display: inline-block;
|
||||
}
|
||||
.fa {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-item-label {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav-link {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
border-left: 4px solid map-get($theme-colors, 'dark-blue');
|
||||
padding-top: 14px;
|
||||
padding-bottom: 14px;
|
||||
|
||||
&.active {
|
||||
font-weight: bold;
|
||||
border-left: 4px solid map-get($theme-colors, 'primary');
|
||||
background: map-get($theme-colors, 'dark-blue');
|
||||
}
|
||||
.fa {
|
||||
font-size: 1.2rem;
|
||||
margin-right: 5px;
|
||||
color: #fff;
|
||||
width: 35px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&-heading {
|
||||
font-size: .75rem;
|
||||
text-transform: uppercase;
|
||||
display: flex;
|
||||
.nav-item-label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&.active {
|
||||
font-weight: bold;
|
||||
border-left: 4px solid map-get($theme-colors, 'primary');
|
||||
background: map-get($theme-colors, 'dark-blue');
|
||||
}
|
||||
}
|
||||
|
||||
&-heading {
|
||||
font-size: .75rem;
|
||||
text-transform: uppercase;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
*[data-selectable-selector] {
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
*[data-selectable-selector] {
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
*[data-sortable-item] {
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.sortable-chosen {
|
||||
background: map-get($theme-colors, 'primary-light');
|
||||
}
|
||||
&.sortable-chosen {
|
||||
background: map-get($theme-colors, 'primary-light');
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
height: 35px;
|
||||
background: #f8f9fa;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
height: 35px;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.body {
|
||||
padding-top: 60px;
|
||||
width: calc(100% - 260px);
|
||||
margin-left: 260px;
|
||||
display: inline-block;
|
||||
padding-top: 60px;
|
||||
width: calc(100% - 260px);
|
||||
margin-left: 260px;
|
||||
display: inline-block;
|
||||
|
||||
.nav {
|
||||
padding-left: 10px;
|
||||
}
|
||||
.nav {
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1080px) {
|
||||
.sidebar-sticky {
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
.sidebar-sticky {
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 770px) {
|
||||
.body {
|
||||
margin-left: 50px;
|
||||
width: calc(100vw - 50px);
|
||||
.body {
|
||||
margin-left: 50px;
|
||||
width: calc(100vw - 50px);
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
.sidebar-toggler {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar:not(.is-open) {
|
||||
width: 50px;
|
||||
max-width: 100% !important;
|
||||
|
||||
.sidebar-sticky {
|
||||
width: 50px;
|
||||
max-width: 100% !important;
|
||||
width: 50px;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 50px;
|
||||
max-width: 100% !important;
|
||||
|
||||
.nav {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
padding-left: 10px;
|
||||
|
||||
.nav-item-label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-heading {
|
||||
display: none;
|
||||
}
|
||||
.nav {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
padding-left: 10px;
|
||||
|
||||
.nav-item-label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-heading {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
&.sorted {
|
||||
&::before {
|
||||
content: '\f0dc';
|
||||
font-family: 'FontAwesome';
|
||||
color: #aaa;
|
||||
margin-right: 3px;
|
||||
}
|
||||
&.sorted {
|
||||
&::before {
|
||||
content: '\f0dc';
|
||||
font-family: 'FontAwesome';
|
||||
color: #aaa;
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
max-width: 100%;
|
||||
overflow-y: hidden;
|
||||
max-width: 100%;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.toast-container {
|
||||
display: flex;
|
||||
position: relative;
|
||||
z-index: 4000;
|
||||
display: flex;
|
||||
position: relative;
|
||||
z-index: 4000;
|
||||
|
||||
.toast-wrapper {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 1060;
|
||||
width: 300px;
|
||||
}
|
||||
.toast-wrapper {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 1060;
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
.bg-tiles {
|
||||
background-color: #c1c1c1;
|
||||
background-image: linear-gradient(45deg, #646464 25%, transparent 25%, transparent 75%, #646464 75%), linear-gradient(45deg, #646464 25%, transparent 25%, transparent 75%, #646464 75%);
|
||||
background-size: 20px 20px;
|
||||
background-position: 0 0, 10px 10px;
|
||||
background-color: #c1c1c1;
|
||||
background-image: linear-gradient(45deg, #646464 25%, transparent 25%, transparent 75%, #646464 75%), linear-gradient(45deg, #646464 25%, transparent 25%, transparent 75%, #646464 75%);
|
||||
background-size: 20px 20px;
|
||||
background-position: 0 0, 10px 10px;
|
||||
}
|
||||
|
||||
.tab-form {
|
||||
padding: 15px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.icon-margin {
|
||||
margin-right: 4px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
font-size: 2em;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.d-ib {
|
||||
display: inline-block;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.list-checkbox {
|
||||
vertical-align: middle;
|
||||
margin-right: 10px;
|
||||
margin-top: -2px;
|
||||
vertical-align: middle;
|
||||
margin-right: 10px;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.password-strenth {
|
||||
padding: 0 0 5px 0;
|
||||
margin-top: -4px;
|
||||
padding: 0 0 5px 0;
|
||||
margin-top: -4px;
|
||||
|
||||
.col-sm {
|
||||
height: 8px;
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
.col-sm {
|
||||
height: 8px;
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
|
||||
&-info {
|
||||
font-size: 13px;
|
||||
height: 22px;
|
||||
}
|
||||
&-info {
|
||||
font-size: 13px;
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.notification-bell:not([disabled]) {
|
||||
[data-counter]:after {
|
||||
display: block;
|
||||
color: #fff;
|
||||
background: red;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
position: absolute;
|
||||
content: ' ';
|
||||
top: 4px;
|
||||
right: 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
[data-counter]:after {
|
||||
display: block;
|
||||
color: #fff;
|
||||
background: red;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
position: absolute;
|
||||
content: ' ';
|
||||
top: 4px;
|
||||
right: 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-error-icon {
|
||||
margin-right: 4px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
#form-main {
|
||||
> .tab-content {
|
||||
@media screen and (min-width: 500px) {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
background: lighten(map-get($theme-colors, 'dark-blue'), 75%);
|
||||
|
||||
.tab-form {
|
||||
@media screen and (min-width: 500px) {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
background: #fff;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
legend.col-form-label, label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
*[data-collection] label, .form-check-label {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.custom-file-label::after {
|
||||
content: "Parcourir";
|
||||
content: "Parcourir";
|
||||
}
|
||||
|
||||
#lease_template_html {
|
||||
height: calc(100vh - 270px);
|
||||
height: calc(100vh - 270px);
|
||||
}
|
||||
|
||||
.panel {
|
||||
&-toggler {
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
&-toggler {
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
display: block;
|
||||
&-content {
|
||||
display: block;
|
||||
|
||||
&:not(.active) {
|
||||
display: none;
|
||||
}
|
||||
&:not(.active) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*[data-collection-delete-container] {
|
||||
cursor: pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
*[data-collection-add] {
|
||||
cursor: pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.login-image {
|
||||
width: 50%;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.tree {
|
||||
position: relative;
|
||||
background: white;
|
||||
color: #212529;
|
||||
position: relative;
|
||||
background: white;
|
||||
color: #212529;
|
||||
|
||||
span {
|
||||
font-style: italic;
|
||||
letter-spacing: .4px;
|
||||
color: #a8a8a8;
|
||||
span {
|
||||
font-style: italic;
|
||||
letter-spacing: .4px;
|
||||
color: #a8a8a8;
|
||||
}
|
||||
|
||||
.fa-folder-open, .fa-folder {
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.fa-html5 {
|
||||
color: #f21f10;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 5px;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding-bottom: 0;
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-left: 15px;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 0;
|
||||
width: 10px;
|
||||
height: 1px;
|
||||
margin: auto;
|
||||
content: '';
|
||||
background-color: #666;
|
||||
}
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
content: '';
|
||||
background-color: #666;
|
||||
}
|
||||
|
||||
&:last-child:after {
|
||||
height: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.fa-folder-open, .fa-folder {
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.fa-html5 {
|
||||
color: #f21f10;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 5px;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding-bottom: 0;
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-left: 15px;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 0;
|
||||
width: 10px;
|
||||
height: 1px;
|
||||
margin: auto;
|
||||
content: '';
|
||||
background-color: #666;
|
||||
}
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
content: '';
|
||||
background-color: #666;
|
||||
}
|
||||
|
||||
&:last-child:after {
|
||||
height: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
a {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fieldset.form-group {
|
||||
margin-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.crud-header {
|
||||
padding: 20px;
|
||||
|
||||
.btn-group:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 769px) {
|
||||
padding: 10px;
|
||||
|
||||
.btn {
|
||||
@include button-size($btn-padding-y-sm, $btn-padding-x-sm, $btn-font-size-sm, $btn-line-height-sm, $btn-border-radius-sm);
|
||||
}
|
||||
}
|
||||
|
||||
&-title {
|
||||
font-size: 2em;
|
||||
|
||||
@media screen and (max-width: 769px) {
|
||||
font-size: 1.5em;
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
&-filter {
|
||||
padding-right: 20px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
@media screen and (max-width: 769px) {
|
||||
padding-right: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&-actions {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 770px) {
|
||||
&-title {
|
||||
font-size: 2em;
|
||||
float: left;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
&-actions {
|
||||
text-align: right;
|
||||
margin-bottom: 10px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 770px) {
|
||||
&-title {
|
||||
float: left;
|
||||
font-size: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
&-actions {
|
||||
float: right;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
content: "";
|
||||
clear: both;
|
||||
}
|
||||
&::after {
|
||||
display: block;
|
||||
content: "";
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
.table .crud-batch-column {
|
||||
width: 1%;
|
||||
width: 1%;
|
||||
}
|
||||
|
||||
form {
|
||||
.loader {
|
||||
display: none;
|
||||
}
|
||||
.loader {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.is-loading .loader {
|
||||
display: inline-block;
|
||||
}
|
||||
&.is-loading .loader {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.modal {
|
||||
z-index: 3000;
|
||||
z-index: 3000;
|
||||
}
|
||||
|
||||
.modal-dialog-large {
|
||||
max-width: 80%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 80%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.modal.show.blur {
|
||||
filter: blur(4px);
|
||||
filter: blur(4px);
|
||||
}
|
||||
|
||||
.output {
|
||||
&-console {
|
||||
background: #073642;
|
||||
line-height: normal;
|
||||
}
|
||||
&-console {
|
||||
background: #073642;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
||||
#murph-version {
|
||||
display: inline;
|
||||
font-size: 14px;
|
||||
display: inline;
|
||||
font-size: 14px;
|
||||
|
||||
a {
|
||||
color: #333;
|
||||
}
|
||||
a {
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.editorjs {
|
||||
@extend .form-control;
|
||||
@extend .form-control;
|
||||
|
||||
height: auto;
|
||||
padding-left: 50px;
|
||||
padding-right: 50px;
|
||||
height: auto;
|
||||
padding-left: 50px;
|
||||
padding-right: 50px;
|
||||
|
||||
&-block-image {
|
||||
img {
|
||||
max-width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
&-block-image {
|
||||
img {
|
||||
max-width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gjs-editor-cont {
|
||||
border-radius: 10px;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.gjs-one-bg {
|
||||
background: map-get($theme-colors, 'dark-blue');
|
||||
}
|
||||
|
||||
.tox.tox-silver-sink.tox-tinymce-aux {
|
||||
z-index: 3000 !important;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ require('./modules/password.js')()
|
|||
require('./modules/tooltip.js')()
|
||||
require('./modules/tinymce.js')()
|
||||
require('./modules/editorjs.js')()
|
||||
require('./modules/grapesjs.js')()
|
||||
require('./modules/panel.js')()
|
||||
require('./modules/choices.js')()
|
||||
require('./modules/checkbox-checker.js')()
|
||||
|
@ -25,3 +26,6 @@ require('./modules/batch.js')()
|
|||
require('./modules/file-manager.js')()
|
||||
require('./modules/file-picker.js')()
|
||||
require('./modules/analytics.js')()
|
||||
require('./modules/page.js')()
|
||||
require('./modules/sidebar.js')()
|
||||
require('./modules/node.js')()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div>
|
||||
<nav aria-label="breadcrumb" class="d-flex justify-content-between">
|
||||
<nav aria-label="breadcrumb" class="d-block d-md-flex justify-content-between">
|
||||
<ol class="breadcrumb mb-0">
|
||||
<li class="breadcrumb-item" v-for="item in breadcrumb">
|
||||
<a class="btn btn-sm" href="#" v-on:click="setDirectory(item.path)" v-html="item.label"></a>
|
||||
|
@ -12,7 +12,7 @@
|
|||
</li>
|
||||
</ol>
|
||||
|
||||
<div class="d-flex">
|
||||
<div class="d-block d-md-flex">
|
||||
<div class="breadcrumb mb-0 file-manager-actions">
|
||||
<span class="btn btn-sm btn-primary ml-1" v-bind:data-modal="generateUploadLink(directory)" data-modal-create>
|
||||
<span class="fa fa-upload" v-bind:data-modal="generateUploadLink(directory)" data-modal-create></span>
|
||||
|
@ -173,6 +173,12 @@ tr {
|
|||
background: #e9ecef;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 770px) {
|
||||
.breadcrumb {
|
||||
padding: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.file-manager-actions .fa {
|
||||
padding: 3px;
|
||||
cursor: pointer;
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
const $ = require('jquery')
|
||||
const EditorJS = require('@editorjs/editorjs')
|
||||
const InlineTools = require('editorjs-inline-tool')
|
||||
const Routing = require('../../../../../../../../friendsofsymfony/jsrouting-bundle/Resources/public/js/router.min.js')
|
||||
const routes = require('../../../../../../../../../public/js/fos_js_routes.json')
|
||||
|
||||
const UnderlineInlineTool = InlineTools.UnderlineInlineTool
|
||||
const StrongInlineTool = InlineTools.StrongInlineTool
|
||||
const ItalicInlineTool = InlineTools.ItalicInlineTool
|
||||
const createGenericInlineTool = require('editorjs-inline-tool/es/tool').default
|
||||
|
||||
Routing.setRoutingData(routes)
|
||||
|
||||
const tools = {
|
||||
header: {
|
||||
|
@ -39,9 +44,15 @@ const tools = {
|
|||
class: require('@editorjs/checklist'),
|
||||
inlineToolbar: true
|
||||
},
|
||||
hyperLink: {
|
||||
class: require('editorjs-hyperlink'),
|
||||
inlineToolbar: true
|
||||
},
|
||||
link: {
|
||||
class: require('@editorjs/link'),
|
||||
inlineToolbar: true
|
||||
config: {
|
||||
endpoint: Routing.generate('admin_editor_editorjs_fetch_url')
|
||||
}
|
||||
},
|
||||
table: {
|
||||
class: require('@editorjs/table'),
|
||||
|
@ -67,13 +78,32 @@ const tools = {
|
|||
class: require('@editorjs/underline'),
|
||||
inlineToolbar: true
|
||||
},
|
||||
linkAutocomplete: {
|
||||
class: require('@editorjs/link-autocomplete'),
|
||||
inlineToolbar: true
|
||||
},
|
||||
image: {
|
||||
class: require('../components/editorjs/image-tool.js')
|
||||
}
|
||||
},
|
||||
bold: {
|
||||
class: createGenericInlineTool({
|
||||
sanitize: {
|
||||
strong: {}
|
||||
},
|
||||
shortcut: 'CMD+B',
|
||||
tagName: 'STRONG',
|
||||
toolboxIcon:
|
||||
'<svg class="icon icon--bold" width="12px" height="14px"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#bold"></use></svg>'
|
||||
})
|
||||
},
|
||||
italic: {
|
||||
class: createGenericInlineTool({
|
||||
sanitize: {
|
||||
em: {}
|
||||
},
|
||||
shortcut: 'CMD+I',
|
||||
tagName: 'EM',
|
||||
toolboxIcon:
|
||||
'<svg class="icon icon--italic" width="12px" height="14px"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#italic"></use></svg>'
|
||||
})
|
||||
},
|
||||
underline: UnderlineInlineTool
|
||||
}
|
||||
|
||||
const makeId = () => {
|
||||
|
@ -89,10 +119,7 @@ const makeId = () => {
|
|||
}
|
||||
|
||||
const configurationBase = {
|
||||
tools,
|
||||
bold: StrongInlineTool,
|
||||
italic: ItalicInlineTool,
|
||||
underline: UnderlineInlineTool
|
||||
tools
|
||||
}
|
||||
|
||||
const buildConfiguration = (conf) => {
|
||||
|
@ -120,9 +147,20 @@ const doInitEditor = () => {
|
|||
editorContainer.attr('id', id)
|
||||
element.hide()
|
||||
|
||||
let data = { time: null, blocks: [] }
|
||||
|
||||
try {
|
||||
const value = JSON.parse(element.val())
|
||||
|
||||
if (value.time && Array.isArray(value.blocks)) {
|
||||
data = value
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
const editor = new EditorJS(buildConfiguration({
|
||||
holder: id,
|
||||
data: JSON.parse(element.val()),
|
||||
data: data,
|
||||
onReady: () => {
|
||||
ready = true
|
||||
}
|
||||
|
@ -143,6 +181,7 @@ const doInitEditor = () => {
|
|||
const value = JSON.stringify(data)
|
||||
element.val(value)
|
||||
} catch (e) {
|
||||
element.val('[]')
|
||||
}
|
||||
})
|
||||
}, 500)
|
||||
|
|
|
@ -71,7 +71,7 @@ const FormCollection = () => {
|
|||
const item = collectionContainer.children('*[data-collection-item]:last-child')
|
||||
const deleteBtn = $('<span data-collection-delete="__name__" class="fa fa-trash"></span>')
|
||||
|
||||
item.find('*[data-collection-delete-container]').first().append(deleteBtn)
|
||||
item.find('*[data-collection-delete-container]').first().prepend(deleteBtn)
|
||||
item.html(item.html().replace(/__name__/g, name))
|
||||
item.attr('data-collection-item', name)
|
||||
|
||||
|
|
157
src/core/Resources/assets/js/modules/grapesjs.js
Normal file
157
src/core/Resources/assets/js/modules/grapesjs.js
Normal file
|
@ -0,0 +1,157 @@
|
|||
const $ = require('jquery')
|
||||
const GrapesJs = require('grapesjs')
|
||||
|
||||
require('grapesjs-blocks-bootstrap4').default
|
||||
require('grapesjs-preset-webpage').default
|
||||
require('grapesjs-preset-newsletter').default
|
||||
require('grapesjs-plugin-header').default
|
||||
|
||||
const makeId = () => {
|
||||
let result = ''
|
||||
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789'
|
||||
const charactersLength = characters.length
|
||||
|
||||
for (let i = 0; i < 20; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength))
|
||||
}
|
||||
|
||||
return 'grapesjs-' + result
|
||||
}
|
||||
|
||||
const modes = {
|
||||
bootstrap4: {
|
||||
id: 'grapesjs-blocks-bootstrap4',
|
||||
options: {
|
||||
blocks: {},
|
||||
blockCategories: {},
|
||||
labels: {},
|
||||
gridDevicesPanel: true,
|
||||
formPredefinedActions: [
|
||||
]
|
||||
},
|
||||
canvas: {
|
||||
styles: [
|
||||
'https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css'
|
||||
],
|
||||
scripts: [
|
||||
'https://code.jquery.com/jquery-3.5.1.slim.min.js',
|
||||
'https://unpkg.com/@popperjs/core@2',
|
||||
'https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js'
|
||||
]
|
||||
}
|
||||
},
|
||||
presetWebpage: {
|
||||
id: 'gjs-preset-webpage',
|
||||
options: {
|
||||
},
|
||||
canvas: {
|
||||
}
|
||||
},
|
||||
presetNewsletter: {
|
||||
id: 'gjs-preset-newsletter',
|
||||
options: {
|
||||
},
|
||||
canvas: {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const doInitEditor = () => {
|
||||
$('textarea[data-grapesjs]').each((i, v) => {
|
||||
const textarea = $(v)
|
||||
const element = textarea.parent().prev()
|
||||
const id = element.attr('id') ? element.attr('id') : makeId()
|
||||
|
||||
let mode = textarea.attr('data-grapesjs')
|
||||
const pluginsOpts = {}
|
||||
|
||||
if (!mode || typeof modes[mode] === 'undefined') {
|
||||
mode = 'bootstrap4'
|
||||
}
|
||||
|
||||
pluginsOpts[modes[mode].id] = modes[mode].options
|
||||
element.attr('id', id)
|
||||
|
||||
const editor = GrapesJs.init({
|
||||
container: '#' + id,
|
||||
fromElement: false,
|
||||
height: '900px',
|
||||
width: 'auto',
|
||||
storageManager: {
|
||||
autoload: false
|
||||
},
|
||||
noticeOnUnload: 0,
|
||||
showOffsets: 1,
|
||||
showDevices: false,
|
||||
plugins: [
|
||||
modes[mode].id,
|
||||
'grapesjs-plugin-header',
|
||||
'grapesjs-component-code-editor',
|
||||
'grapesjs-parser-postcss'
|
||||
],
|
||||
colorPicker: {
|
||||
appendTo: 'parent',
|
||||
offset: {
|
||||
top: 26,
|
||||
left: -166
|
||||
}
|
||||
},
|
||||
pluginsOpts: pluginsOpts,
|
||||
canvas: modes[mode].canvas
|
||||
})
|
||||
|
||||
const deviceManager = editor.DeviceManager
|
||||
|
||||
const devices = [
|
||||
'Extra Small',
|
||||
'Small',
|
||||
'Medium',
|
||||
'Large',
|
||||
'Extra Large',
|
||||
'Desktop',
|
||||
'Tablet',
|
||||
'mobileLandscape',
|
||||
'mobilePortrait'
|
||||
]
|
||||
|
||||
for (const device of devices) {
|
||||
deviceManager.remove(device)
|
||||
}
|
||||
|
||||
deviceManager.add('All', '100%')
|
||||
|
||||
editor.Panels.getPanels().remove('devices-buttons')
|
||||
editor.Panels.addButton('views', {
|
||||
id: 'open-code',
|
||||
attributes: {
|
||||
title: 'Open Code'
|
||||
},
|
||||
className: 'fa fa-edit',
|
||||
command: 'open-code'
|
||||
})
|
||||
|
||||
editor.on('update', () => {
|
||||
textarea.val(JSON.stringify(editor.storeData()))
|
||||
})
|
||||
|
||||
try {
|
||||
editor.loadData(JSON.parse(textarea.val()))
|
||||
} catch (e) {
|
||||
editor.loadData({ html: '' })
|
||||
}
|
||||
|
||||
$(element).parents('form').keypress((e) => {
|
||||
if ($(e.target).parents('#' + id).length === 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
return (e.keyCode || e.which || e.charCode || 0) !== 13
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = () => {
|
||||
$(() => {
|
||||
doInitEditor()
|
||||
})
|
||||
}
|
|
@ -18,7 +18,7 @@ const openModal = function (url, createModal) {
|
|||
body.append(container)
|
||||
}
|
||||
|
||||
const loader = $('<div style="position: absolute; top: 25vh; left: 50vw; z-index: 2000">')
|
||||
const loader = $('<div style="position: absolute; top: 25vh; left: 50vw; z-index: 5000">')
|
||||
loader.html('<div class="spinner-border text-primary" role="status"><span class="sr-only">Loading...</span></div>')
|
||||
body.append(loader)
|
||||
|
||||
|
@ -56,7 +56,11 @@ module.exports = function () {
|
|||
body.on('shown.bs.modal', '.modal', onShownAndHide)
|
||||
|
||||
body.on('hidden.bs.modal', '.modal', (e) => {
|
||||
$(e.target).remove()
|
||||
const modal = $(e.target)
|
||||
|
||||
if (!modal.hasClass('modal-static')) {
|
||||
modal.remove()
|
||||
}
|
||||
|
||||
if ($('.modal.show').length) {
|
||||
$('body').addClass('modal-open')
|
||||
|
|
20
src/core/Resources/assets/js/modules/node.js
Normal file
20
src/core/Resources/assets/js/modules/node.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
const $ = require('jquery')
|
||||
|
||||
const abTestChecker = () => {
|
||||
$('body').on('change', '#node_hasAbTest', () => {
|
||||
const checkbox = document.querySelector('#node_hasAbTest')
|
||||
const code = document.querySelector('#node_abTestCode')
|
||||
|
||||
code.parentNode.parentNode.classList.toggle('d-none', !checkbox.checked)
|
||||
|
||||
if (checkbox.checked) {
|
||||
code.setAttribute('required', 'required')
|
||||
} else {
|
||||
code.removeAttribute('required')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = () => {
|
||||
$(abTestChecker)
|
||||
}
|
41
src/core/Resources/assets/js/modules/page.js
Normal file
41
src/core/Resources/assets/js/modules/page.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
const $ = require('jquery')
|
||||
|
||||
const doExpandCollapse = (stmt) => {
|
||||
stmt = (stmt == 1)
|
||||
|
||||
const button = $('#page-form-expand')
|
||||
const mainForm = $('#page-main-form')
|
||||
const metasForm = $('#page-metas-form')
|
||||
|
||||
mainForm
|
||||
.toggleClass('col-md-8', !stmt)
|
||||
.toggleClass('col-md-12', stmt)
|
||||
|
||||
metasForm
|
||||
.toggleClass('d-none', stmt)
|
||||
|
||||
button
|
||||
.children()
|
||||
.toggleClass('fa-expand-arrows-alt', !stmt)
|
||||
.toggleClass('fa-compress-arrows-alt', stmt)
|
||||
|
||||
localStorage.setItem('pageFormExpandStmt', stmt ? 1 : null)
|
||||
}
|
||||
|
||||
const initExpander = () => {
|
||||
const button = $('#page-form-expand')
|
||||
|
||||
if (button.length) {
|
||||
doExpandCollapse(localStorage.getItem('pageFormExpandStmt'))
|
||||
|
||||
button.click(() => {
|
||||
doExpandCollapse(button.children().hasClass('fa-expand-arrows-alt'))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = () => {
|
||||
$(() => {
|
||||
initExpander()
|
||||
})
|
||||
}
|
9
src/core/Resources/assets/js/modules/sidebar.js
Normal file
9
src/core/Resources/assets/js/modules/sidebar.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
const $ = require('jquery')
|
||||
|
||||
const SidebarOpener = () => {
|
||||
$('.sidebar-toggler .btn').click(() => {
|
||||
$('.sidebar').toggleClass('is-open')
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = SidebarOpener
|
|
@ -18,65 +18,51 @@ use Symfony\Component\Routing\Annotation\Route;
|
|||
|
||||
class <?= $class_name; ?> extends CrudController
|
||||
{
|
||||
/**
|
||||
* @Route("/admin/<?= $route; ?>/{page}", name="admin_<?= $route; ?>_index", methods={"GET"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
#[Route(path: '/admin/user/edit/{entity}', name: 'admin_user_edit', methods: ['GET', 'POST'])]
|
||||
|
||||
#[Route(path: "/admin/<?= $route; ?>/{page}", name: "admin_<?= $route; ?>_index", methods: ['GET'], requirements: ['page' => '\d+'])]
|
||||
public function index(RepositoryQuery $query, Request $request, Session $session, int $page = 1): Response
|
||||
{
|
||||
return $this->doIndex($page, $query, $request, $session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/<?= $route; ?>/new", name="admin_<?= $route; ?>_new", methods={"GET", "POST"})
|
||||
*/
|
||||
#[Route(path: "/admin/<?= $route; ?>/new", name: "admin_<?= $route; ?>_new", methods: ['GET', 'POST'])]
|
||||
public function new(Factory $factory, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doNew($factory->create(), $entityManager, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/<?= $route; ?>/show/{entity}", name="admin_<?= $route; ?>_show", methods={"GET"})
|
||||
*/
|
||||
#[Route(path: "/admin/<?= $route; ?>/show/{entity}", name: "admin_<?= $route; ?>_show", methods: ['GET'])]
|
||||
public function show(Entity $entity): Response
|
||||
{
|
||||
return $this->doShow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/<?= $route; ?>/filter", name="admin_<?= $route; ?>_filter", methods={"GET"})
|
||||
*/
|
||||
#[Route(path: "/admin/<?= $route; ?>/filter", name: "admin_<?= $route; ?>_filter", methods: ['GET'])]
|
||||
public function filter(Session $session): Response
|
||||
{
|
||||
return $this->doFilter($session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/<?= $route; ?>/edit/{entity}", name="admin_<?= $route; ?>_edit", methods={"GET", "POST"})
|
||||
*/
|
||||
#[Route(path: "/admin/<?= $route; ?>/edit/{entity}", name: "admin_<?= $route; ?>_edit", methods: ['GET', 'POST'])]
|
||||
public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doEdit($entity, $entityManager, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/<?= $route; ?>/sort/{page}", name="admin_<?= $route; ?>_sort", methods={"POST"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
#[Route(path: "/admin/<?= $route; ?>/sort/{page}", name: "admin_<?= $route; ?>_sort", methods: ['POST'], requirements: ['page' => '\d+'])]
|
||||
public function sort(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
|
||||
{
|
||||
return $this->doSort($page, $query, $entityManager, $request, $session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/<?= $route; ?>/batch/{page}", name="admin_<?= $route; ?>_batch", methods={"POST"}, requirements={"page":"\d+"})
|
||||
*/
|
||||
#[Route(path: "/admin/<?= $route; ?>/batch/{page}", name: "admin_<?= $route; ?>_batch", methods: ['POST'], requirements: ['page' => '\d+'])]
|
||||
public function batch(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
|
||||
{
|
||||
return $this->doBatch($page, $query, $entityManager, $request, $session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/admin/<?= $route; ?>/delete/{entity}", name="admin_<?= $route; ?>_delete", methods={"DELETE"})
|
||||
*/
|
||||
#[Route(path: "/admin/<?= $route; ?>/delete/{entity}", name: "admin_<?= $route; ?>_delete", methods: ['DELETE', 'POST'])]
|
||||
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||
{
|
||||
return $this->doDelete($entity, $entityManager, $request);
|
||||
|
@ -120,8 +106,18 @@ class <?= $class_name; ?> extends CrudController
|
|||
// ->setAction('show', 'back', true)
|
||||
// ->setAction('show', 'edit', true)
|
||||
|
||||
// ->setField('index', 'Label', Field\TextField::class, [
|
||||
// 'property' => 'label',
|
||||
->setField('index', 'Entity', Field\TextField::class, [
|
||||
'property_builder' => function (EntityInterface $entity) {
|
||||
try {
|
||||
return (string) $entity;
|
||||
} catch (\Error $e) {
|
||||
return $entity->getId();
|
||||
}
|
||||
},
|
||||
])
|
||||
|
||||
// ->setField('index', 'Foo', Field\TextField::class, [
|
||||
// 'property' => 'foo',
|
||||
// ])
|
||||
|
||||
// ->setBatchAction('index', 'delete', 'Delete', function(EntityInterface $entity, EntityManager $manager) {
|
||||
|
|
|
@ -8,9 +8,7 @@ use App\Core\Form\Site\Page as Block;
|
|||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
#[ORM\Entity]
|
||||
class <?= $class_name; ?> extends Page
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
|
|
|
@ -222,3 +222,4 @@
|
|||
"Security": "Sécurité"
|
||||
"At least one role": "Au moins un rôle"
|
||||
"All roles": "Tous les rôles"
|
||||
"Enable A/B Testing": "Activer le test A/B"
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
{% block title %}{{ 'My account'|trans }} - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="bg-light pl-5 pr-4 pt-5 pb-5">
|
||||
<div class="bg-light">
|
||||
<div class="crud-header">
|
||||
<h1 class="crud-header-title">{{ 'My account'|trans }}</h1>
|
||||
</div>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
{% for locale in configuration.locales %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if loop.first %}active{% endif %}" data-toggle="tab" href="#form-locale-{{ loop.index }}">
|
||||
<span class="flag-icon flag-icon-{{ locale }}"></span>
|
||||
<span class="fi fi-{{ locale }}"></span>
|
||||
|
||||
{{ locale|upper|trans }}
|
||||
</a>
|
||||
|
|
|
@ -1 +1,8 @@
|
|||
<p>{{ '{__toString}'|build_string(entity) }}</p>
|
||||
<div class="row">
|
||||
<div class="col-md-12 p-3">
|
||||
{% for item in entity_to_array(entity) %}
|
||||
<div class="font-weight-bold">{{ item.name|trans }}</div>
|
||||
<div class="mb-3">{{ item.value }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
{% set context = context ?? 'edit' %}
|
||||
|
||||
{% block body_class %}has-form{% endblock %}
|
||||
|
||||
{% block title %}{{ configuration.pageTitle(context)|trans|build_string(entity) }} - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% block header %}
|
||||
<div class="bg-light pl-5 pr-4 pt-5 pb-5">
|
||||
<div class="bg-light">
|
||||
<div class="crud-header">
|
||||
{% block header_title %}
|
||||
<h1 class="crud-header-title">{{ configuration.pageTitle(context)|trans|build_string(entity) }}</h1>
|
||||
|
@ -20,20 +22,26 @@
|
|||
{% if configuration.action(context, 'back', true) %}
|
||||
<a href="{{ path(configuration.pageRoute('index'), configuration.pageRouteParams('index')) }}" class="btn btn-light">
|
||||
<span class="fa fa-list pr-1"></span>
|
||||
{{ configuration.actionTitle(context, 'back', 'Back to the list')|trans }}
|
||||
<span class="d-none d-md-inline">
|
||||
{{ configuration.actionTitle(context, 'back', 'Back to the list')|trans }}
|
||||
</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if configuration.action(context, 'show', true) %}
|
||||
{% if configuration.action(context, 'show', true, [entity]) %}
|
||||
<a href="{{ path(configuration.pageRoute('show'), {entity: entity.id}|merge(configuration.pageRouteParams('show'))) }}" class="btn btn-secondary">
|
||||
<span class="fa fa-eye pr-1"></span>
|
||||
{{ configuration.actionTitle(context, 'show', 'Show')|trans|build_string(entity) }}
|
||||
<span class="d-none d-md-inline">
|
||||
{{ configuration.actionTitle(context, 'show', 'Show')|trans|build_string(entity) }}
|
||||
</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
<button type="submit" form="form-main" class="btn btn-primary">
|
||||
<span class="fa fa-save pr-1"></span>
|
||||
{{ configuration.actionTitle(context, 'save', 'Save')|trans|build_string(entity) }}
|
||||
<span class="d-none d-md-inline">
|
||||
{{ configuration.actionTitle(context, 'save', 'Save')|trans|build_string(entity) }}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{% block header_actions_after %}{% endblock %}
|
||||
|
@ -42,7 +50,7 @@
|
|||
{% set header_actions_dropdown_menu = '' %}
|
||||
{% endif %}
|
||||
|
||||
{% if configuration.action(context, 'delete', true) %}
|
||||
{% if configuration.action(context, 'delete', true, [entity]) %}
|
||||
{% set item %}
|
||||
<button type="submit" form="form-delete" class="dropdown-item">
|
||||
{{ configuration.actionTitle(context, 'delete', 'Delete')|trans|build_string(entity) }}
|
||||
|
@ -86,7 +94,7 @@
|
|||
</form>
|
||||
{% endblock %}
|
||||
|
||||
{% if configuration.action(context, 'delete', true) %}
|
||||
{% if configuration.action(context, 'delete', true, [entity]) %}
|
||||
<form method="post" action="{{ path(configuration.pageRoute('delete'), {entity: entity.id}|merge(configuration.pageRouteParams('delete'))) }}" id="form-delete" data-form-confirm>
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ entity.id) }}">
|
||||
|
|
|
@ -18,32 +18,34 @@
|
|||
{% endif %}
|
||||
{%- endset -%}
|
||||
|
||||
<div class="bg-light pl-5 pr-4 pt-5 {{ pb }}">
|
||||
<div class="bg-light">
|
||||
<div class="crud-header">
|
||||
{% block header_title %}
|
||||
<h1 class="crud-header-title">{{ configuration.pageTitle(context)|trans }}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block header_actions %}
|
||||
{%- block header_actions %}
|
||||
<div class="crud-header-actions">
|
||||
<div class="btn-group">
|
||||
{% block header_actions_before %}{% endblock %}
|
||||
{%- block header_actions_before %}{% endblock -%}
|
||||
|
||||
{% if configuration.action(context, 'new', true) %}
|
||||
{%- if configuration.action(context, 'new', true) %}
|
||||
<a href="{{ path(configuration.pageRoute('new'), configuration.pageRouteParams('new')) }}" class="btn btn-primary">
|
||||
<span class="fa fa-plus pr-1"></span>
|
||||
{{ configuration.actionTitle(context, 'new', 'New')|trans }}
|
||||
<span class="d-none d-md-inline">
|
||||
{{ configuration.actionTitle(context, 'new', 'New')|trans }}
|
||||
</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif -%}
|
||||
|
||||
{% if header_actions_dropdown_menu is not defined %}
|
||||
{%- if header_actions_dropdown_menu is not defined %}
|
||||
{% set header_actions_dropdown_menu = '' %}
|
||||
{% endif %}
|
||||
{% endif -%}
|
||||
|
||||
{% block header_actions_after %}{% endblock %}
|
||||
{%- block header_actions_after %}{% endblock -%}
|
||||
|
||||
{% block header_actions_dropdown %}
|
||||
{% if header_actions_dropdown_menu %}
|
||||
{%- block header_actions_dropdown %}
|
||||
{%- if header_actions_dropdown_menu %}
|
||||
<button type="button" class="btn btn-white dropdown-toggle dropdown-toggle-hide-after" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="font-weight-bold">
|
||||
⋅⋅⋅
|
||||
|
@ -53,8 +55,8 @@
|
|||
<div class="dropdown-menu dropdown-menu-right">
|
||||
{{ header_actions_dropdown_menu|raw }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endif -%}
|
||||
{% endblock -%}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -62,7 +64,7 @@
|
|||
|
||||
{% block header_filter_pager %}
|
||||
{% if filters.show %}
|
||||
<div class="row pb-3">
|
||||
<div class="row crud-header-filter">
|
||||
<div class="col-auto ml-auto {% if pager.getPaginationData.pageCount > 1 %}mr-3{% endif %}">
|
||||
<button data-modal="{{ path(configuration.pageRoute('filter'), configuration.pageRouteParams('filter')) }}" class="btn btn-sm btn-secondary">
|
||||
{{ 'Filter'|trans }} {% if not filters.isEmpty %}({{ 'yes'|trans }}){% endif %}
|
||||
|
@ -73,7 +75,9 @@
|
|||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ knp_pagination_render(pager) }}
|
||||
<div class="crud-header-filter">
|
||||
{{ knp_pagination_render(pager) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
@ -174,9 +178,9 @@
|
|||
|
||||
{% block list_item %}
|
||||
{%- set dbClick %}
|
||||
{% if configuration.action(context, 'show', true) %}
|
||||
{% if configuration.action(context, 'show', true, [item]) %}
|
||||
{{ path(configuration.pageRoute('show'), {entity: item.id}|merge(configuration.pageRouteParams('show'))) }}
|
||||
{% elseif configuration.action(context, 'edit', true) %}
|
||||
{% elseif configuration.action(context, 'edit', true, [item]) %}
|
||||
{{ path(configuration.pageRoute('edit'), {entity: item.id}|merge(configuration.pageRouteParams('show'))) }}
|
||||
{% endif %}
|
||||
{% endset -%}
|
||||
|
@ -211,19 +215,19 @@
|
|||
<td class="crud-action-column col-2 miw-200 text-right">
|
||||
{% block list_item_actions_before %}{% endblock %}
|
||||
|
||||
{% if configuration.action(context, 'show', true) %}
|
||||
{% if configuration.action(context, 'show', true, [item]) %}
|
||||
<a href="{{ path(configuration.pageRoute('show'), {entity: item.id}|merge(configuration.pageRouteParams('show'))) }}" class="btn btn-sm btn-secondary mr-1">
|
||||
<span class="fa fa-eye"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if configuration.action(context, 'edit', true) %}
|
||||
{% if configuration.action(context, 'edit', true, [item]) %}
|
||||
<a href="{{ path(configuration.pageRoute('edit'), {entity: item.id}|merge(configuration.pageRouteParams('edit'))) }}" class="btn btn-sm btn-primary mr-1">
|
||||
<span class="fa fa-edit"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if configuration.action(context, 'delete', true) %}
|
||||
{% if configuration.action(context, 'delete', true, [item]) %}
|
||||
<button type="submit" form="form-delete-{{ item.id }}" class="btn btn-sm btn-danger">
|
||||
<span class="fa fa-trash"></span>
|
||||
</button>
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
{% set context = context ?? 'new' %}
|
||||
|
||||
{% block body_class %}has-form{% endblock %}
|
||||
|
||||
{% block title %}{{ configuration.pageTitle(context)|trans|build_string(entity) }} - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% block header %}
|
||||
<div class="bg-light pl-5 pr-4 pt-5 pb-5">
|
||||
<div class="bg-light">
|
||||
<div class="crud-header">
|
||||
{% block header_title %}
|
||||
<h1 class="crud-header-title">{{ configuration.pageTitle(context)|trans|build_string(entity) }}</h1>
|
||||
|
@ -20,13 +22,17 @@
|
|||
{% if configuration.action(context, 'back', true) %}
|
||||
<a href="{{ path(configuration.pageRoute('index'), configuration.pageRouteParams('index')) }}" class="btn btn-light">
|
||||
<span class="fa fa-list pr-1"></span>
|
||||
{{ configuration.actionTitle(context, 'back', 'Back to the list')|trans }}
|
||||
<span class="d-none d-md-inline">
|
||||
{{ configuration.actionTitle(context, 'back', 'Back to the list')|trans }}
|
||||
</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
<button type="submit" form="form-main" class="btn btn-primary">
|
||||
<span class="fa fa-save pr-1"></span>
|
||||
{{ configuration.actionTitle(context, 'save', 'Save')|trans|build_string(entity) }}
|
||||
<span class="d-none d-md-inline">
|
||||
{{ configuration.actionTitle(context, 'save', 'Save')|trans|build_string(entity) }}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{% if header_actions_dropdown_menu is not defined %}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
{% block body %}
|
||||
{% block header %}
|
||||
<div class="bg-light pl-5 pr-4 pt-5 pb-5">
|
||||
<div class="bg-light">
|
||||
<div class="crud-header">
|
||||
{% block header_title %}
|
||||
<h1 class="crud-header-title">{{ configuration.pageTitle(context)|trans|build_string(entity) }}</h1>
|
||||
|
@ -20,15 +20,19 @@
|
|||
{% if configuration.action(context, 'back', true) %}
|
||||
<a href="{{ path(configuration.pageRoute('index')) }}" class="btn btn-light">
|
||||
<span class="fa fa-list pr-1"></span>
|
||||
{{ configuration.actionTitle(context, 'back', 'Back to the list')|trans }}
|
||||
<span class="d-none d-md-inline">
|
||||
{{ configuration.actionTitle(context, 'back', 'Back to the list')|trans }}
|
||||
</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if configuration.action(context, 'edit', true) %}
|
||||
{% if configuration.action(context, 'edit', true, [entity]) %}
|
||||
<a href="{{ path(configuration.pageRoute('edit'), {entity: entity.id}) }}" class="btn btn-primary">
|
||||
<span class="fa fa-edit pr-1"></span>
|
||||
|
||||
{{ configuration.actionTitle(context, 'edit', 'Edit')|trans|build_string(entity) }}
|
||||
<span class="d-none d-md-inline">
|
||||
{{ configuration.actionTitle(context, 'edit', 'Edit')|trans|build_string(entity) }}
|
||||
</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<a class="navbar-brand" href="{{ path('admin_dashboard_index') }}">
|
||||
<img id="logo" src="{{ asset(site_logo) }}" alt="{{ site_name }}" title="{{ site_name }}">
|
||||
|
||||
{{ site_name }}
|
||||
<span class="ml-2 d-none d-md-inline">{{ site_name }}</span>
|
||||
</a>
|
||||
|
||||
<div class="ml-auto">
|
||||
|
@ -34,11 +34,17 @@
|
|||
{% block body_container %}
|
||||
<div class="container-fluid">
|
||||
<nav class="bg-dark-blue sidebar">
|
||||
<div class="sidebar-toggler">
|
||||
<button class="btn">
|
||||
<span class="fa fa-bars"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-sticky">
|
||||
{{ include('@Core/admin/module/menu.html.twig') }}
|
||||
</div>
|
||||
</nav>
|
||||
<div class="body">
|
||||
<div class="body {% block body_class %}{% endblock %}">
|
||||
{% block body %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
@ -47,6 +53,8 @@
|
|||
|
||||
{% block js %}
|
||||
<script src="{{ asset('vendor/tinymce/tinymce.min.js') }}"></script>
|
||||
<script src="{{ asset('vendor/grapesjs-parser-postcss/dist/grapesjs-parser-postcss.min.js') }}"></script>
|
||||
<script src="{{ asset('vendor/grapesjs-component-code-editor/dist/grapesjs-component-code-editor.min.js') }}"></script>
|
||||
|
||||
{{ encore_entry_script_tags('admin') }}
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
{% import "@Core/admin/macros/menu.html.twig" as macros_menu %}
|
||||
|
||||
<ul class="nav flex-column">
|
||||
{{ include('@Core/admin/module/_menu_item.html.twig', {
|
||||
id: 'dashboard',
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" href="{{ asset(site_logo) }}">
|
||||
<link rel="icon" href="{{ asset(site_logo) }}">
|
||||
{% if site_logo is defined %}
|
||||
<link rel="shortcut icon" href="{{ asset(site_logo) }}">
|
||||
<link rel="icon" href="{{ asset(site_logo) }}">
|
||||
{% endif %}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{%- block render -%}
|
||||
<img src="{{ source }}" alt="{{ caption }}">
|
||||
<p><img src="{{ source }}" alt="{{ caption }}"></p>
|
||||
{%- endblock -%}
|
||||
|
|
7
src/core/Resources/views/editorjs/link.html.twig
Normal file
7
src/core/Resources/views/editorjs/link.html.twig
Normal file
|
@ -0,0 +1,7 @@
|
|||
{%- block render -%}
|
||||
<p><a href="{{ link }}">{{- meta.title -}}</a></p>
|
||||
|
||||
{%- if meta.description -%}
|
||||
<p>{{- meta.description -}}</p>
|
||||
{%- endif -%}
|
||||
{%- endblock -%}
|
|
@ -1,3 +1,3 @@
|
|||
{%- block render -%}
|
||||
<p {% block attributes %}{% endblock %}>{{- text -}}</p>
|
||||
<p {% block attributes %}{% endblock %}>{{- text|raw -}}</p>
|
||||
{%- endblock -%}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{%- block render -%}
|
||||
<figure {% block attributes %}{% endblock %}>
|
||||
{{- text -}}
|
||||
<blockquote><p>{{- text|nl2br -}}</p></blockquote>
|
||||
|
||||
{% if caption %}
|
||||
<figcaption>
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
{% extends '@Core/admin/layout.html.twig' %}
|
||||
|
||||
{% block body %}
|
||||
<div class="bg-light pl-5 pr-4 pt-5 pb-5">
|
||||
<div class="d-flex">
|
||||
<div class="mr-auto w-50">
|
||||
<h1 class="display-5">
|
||||
{{ 'File manager'|trans }}
|
||||
</h1>
|
||||
</div>
|
||||
<div class="bg-light">
|
||||
<div class="crud-header">
|
||||
<h1 class="crud-header-title">
|
||||
{{ 'File manager'|trans }}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,12 +1,25 @@
|
|||
{% extends 'bootstrap_4_layout.html.twig' %}
|
||||
|
||||
{% block grapesjs_widget %}
|
||||
<div class="gjs"></div>
|
||||
<div class="d-none">
|
||||
<textarea {{ block('widget_attributes') }}>{{ value }}</textarea>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block file_widget -%}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
{% set value = form.vars.data %}
|
||||
|
||||
{% if form.parent.vars.file_type is defined %}
|
||||
{% set fileType = form.parent.vars.file_type %}
|
||||
{% else %}
|
||||
{% set fileType = 'auto' %}
|
||||
{% endif %}
|
||||
|
||||
{% if value %}
|
||||
{% if value and value.extension in ['jpeg', 'jpg', 'gif', 'png', 'svg'] %}
|
||||
{% if fileType in ['auto', 'image'] and value.extension in ['jpeg', 'jpg', 'gif', 'png', 'svg'] %}
|
||||
<div class="card">
|
||||
<div class="card-img-top bg-tiles text-center">
|
||||
<a href="{{ asset(value.pathname) }}" target="_blank">
|
||||
|
@ -48,6 +61,7 @@
|
|||
<div class="text-right">
|
||||
<span data-collection-delete-container class="btn btn-sm btn-danger">
|
||||
<span data-collection-delete="{{ loop.index }}">
|
||||
<span data-collection-delete="{{ loop.index }}" class="fa fa-trash"></span>
|
||||
{{ label_delete|trans }}
|
||||
</span>
|
||||
</span>
|
||||
|
@ -58,6 +72,7 @@
|
|||
|
||||
<div data-collection-add="collection-{{ collection_name }}" class="collection-add">
|
||||
<span class="btn btn-sm btn-primary" data-collection-add="collection-{{ collection_name }}">
|
||||
<span data-collection-add="collection-{{ collection_name }}" class="fa fa-plus"></span>
|
||||
{{ label_add|trans }}
|
||||
</span>
|
||||
</div>
|
||||
|
@ -76,9 +91,11 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block murph_collection_widget %}
|
||||
<div data-collection="collection-{{ collection_name }}">
|
||||
{% set attrs = attr|merge({class: 'mb-1 ' ~ (attr.class ?? '')}) %}
|
||||
|
||||
<div data-collection="collection-{{ collection_name }}" {% for attr, value in row_attr %}{{ attr }}="{{ value }}"{% endfor %}>
|
||||
{% for item in form %}
|
||||
<div data-collection-item="{{ loop.index }}" class="mb-1">
|
||||
<div data-collection-item="{{ loop.index }}" {% for attr, value in attrs %}{{ attr }}="{{ value }}"{% endfor %}>
|
||||
{% for child in item %}
|
||||
{{ form_row(child) }}
|
||||
{% endfor %}
|
||||
|
@ -86,6 +103,7 @@
|
|||
<div class="text-right">
|
||||
<span data-collection-delete-container class="btn btn-sm btn-danger">
|
||||
<span data-collection-delete="{{ loop.index }}">
|
||||
<span data-collection-delete="{{ loop.index }}" class="fa fa-trash"></span>
|
||||
{{ label_delete|trans }}
|
||||
</span>
|
||||
</span>
|
||||
|
@ -96,12 +114,13 @@
|
|||
|
||||
<div data-collection-add="collection-{{ collection_name }}" class="collection-add">
|
||||
<span class="btn btn-sm btn-primary" data-collection-add="collection-{{ collection_name }}">
|
||||
<span data-collection-add="collection-{{ collection_name }}" class="fa fa-plus"></span>
|
||||
{{ label_add|trans }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<template type="text/template" id="collection-{{ collection_name }}">
|
||||
<div data-collection-item="__name__">
|
||||
<div data-collection-item="__name__" {% for attr, value in attrs %}{{ attr }}="{{ value }}"{% endfor %}>
|
||||
{{ form_rest(form.vars.prototype) }}
|
||||
|
||||
<div class="text-right">
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{% set message %}
|
||||
{% trans %}A password reset request has been made. If you are the source of this request, click on the link below or copy and paste the address if the link does not work.{% endtrans %}
|
||||
|
||||
<a href="{{ reseting_update_link }}">{{ reseting_update_link }}</a>
|
||||
<br><a href="{{ reseting_update_link }}">{{ reseting_update_link }}</a>
|
||||
{% endset %}
|
||||
|
||||
{% block body %}
|
||||
|
|
|
@ -7,42 +7,45 @@
|
|||
<meta name="format-detection" content="telephone=no" />
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
{% block css %}
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
#header {
|
||||
padding: 20px 10px;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
#content {
|
||||
padding: 10px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
}
|
||||
|
||||
#logo {
|
||||
width: 230px;
|
||||
}
|
||||
|
||||
#logo svg {
|
||||
width: 230px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #3183aa;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
<body style="box-sizing: border-box; margin: 0;">
|
||||
<table class="main-body" style="box-sizing: border-box; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; width: 100%; height: 100%; background-color: rgb(234, 236, 237);" width="100%" height="100%" bgcolor="rgb(234, 236, 237)">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr class="row" style="box-sizing: border-box; vertical-align: top;" valign="top">
|
||||
<td class="main-body-cell" style="box-sizing: border-box;">
|
||||
<table class="container" style="box-sizing: border-box; font-family: Helvetica, serif; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; margin-top: auto; margin-right: auto; margin-bottom: auto; margin-left: auto; height: 0px; width: 90%; max-width: 550px;" width="90%" height="0">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr style="box-sizing: border-box;">
|
||||
<td class="container-cell" style="box-sizing: border-box; vertical-align: top; font-size: medium; padding-bottom: 50px;" valign="top">
|
||||
<table class="card" style="box-sizing: border-box; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; margin-bottom: 20px; height: 0px;" height="0" width="100%">
|
||||
<tbody style="box-sizing: border-box;">
|
||||
<tr style="box-sizing: border-box;">
|
||||
<td class="card-cell" style="box-sizing: border-box; background-color: rgb(255, 255, 255); overflow-x: hidden; overflow-y: hidden; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px;" bgcolor="rgb(255, 255, 255)" align="center">
|
||||
<table class="table100 c1357" style="box-sizing: border-box; width: 100%; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; height: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-collapse: collapse;" width="100%" height="0" width="100%">
|
||||
<tbody id="if8tm" style="box-sizing: border-box;">
|
||||
<tr id="iu0an" style="box-sizing: border-box;">
|
||||
<td class="card-content" id="i9wt9" style="box-sizing: border-box; font-size: 13px; line-height: 20px; color: rgb(111, 119, 125); padding-top: 10px; padding-right: 20px; padding-bottom: 0px; padding-left: 20px; vertical-align: top;" valign="top">
|
||||
<div class="card-text" style="box-sizing: border-box;">
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="btn-group">
|
||||
<div class="btn-group mb-1">
|
||||
<span class="btn btn-sm btn-dark border-secondary disabled">
|
||||
{%- if entity.scheme == 'all' -%}
|
||||
http(s)
|
||||
|
@ -13,9 +13,7 @@
|
|||
{{ entity.rule }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
→
|
||||
|
||||
<br>
|
||||
<div class="btn-group">
|
||||
<span class="btn btn-sm btn-warning border-secondary disabled">
|
||||
{{ entity.location }}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{% block title %}{{ 'Settings'|trans }} - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="bg-light pl-5 pr-4 pt-5 {% if pager.paginationData.pageCount < 2 %}pb-5{% endif %}">
|
||||
<div class="bg-light">
|
||||
<div class="crud-header">
|
||||
<h1 class="crud-header-title">{{ 'Settings'|trans }}</h1>
|
||||
</div>
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
</div>
|
||||
<div data-collection-add="collection-{{ collection_name }}" class="collection-add col-12">
|
||||
<span class="btn btn-sm btn-primary" data-collection-add="collection-{{ collection_name }}">
|
||||
<span data-collection-add="collection-{{ collection_name }}" class="fa fa-plus"></span>
|
||||
{{ label_add|trans }}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
{% block header_actions_before %}
|
||||
<a href="{{ path(configuration.pageRoute('redirects'), configuration.pageRouteParams('redirects')) }}" class="btn btn-light">
|
||||
<span class="fa fa-map-signs pr-1"></span>
|
||||
{{ configuration.actionTitle(context, 'redirects', 'Redirects')|trans }}
|
||||
<span class="d-none d-md-inline">
|
||||
{{ configuration.actionTitle(context, 'redirects', 'Redirects')|trans }}
|
||||
</span>
|
||||
</a>
|
||||
{% endblock %}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue