add navigation CRUD; add menu CRUD; add base of node CRUD
This commit is contained in:
parent
2605d81adf
commit
8a7e323501
|
@ -352,3 +352,74 @@ table.table-fixed, .table-fixed > table {
|
||||||
.login-image {
|
.login-image {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tree {
|
||||||
|
position: relative;
|
||||||
|
background: white;
|
||||||
|
color: #212529;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,9 +5,20 @@ module.exports = function() {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
let id = $(e.target).attr('data-modal');
|
let container = $('#modal-container');
|
||||||
let modal = $(id);
|
|
||||||
|
|
||||||
modal.modal('toggle');
|
if (!container.length) {
|
||||||
|
container = $('<div id="modal-container" class="modal">');
|
||||||
|
|
||||||
|
$('body').append(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
container.html('');
|
||||||
|
|
||||||
|
const url = $(e.target).attr('data-modal');
|
||||||
|
|
||||||
|
container.load(url, function() {
|
||||||
|
$(container).modal('show');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"scheb/2fa-google-authenticator": "^5.7",
|
"scheb/2fa-google-authenticator": "^5.7",
|
||||||
"scheb/2fa-qr-code": "^5.7",
|
"scheb/2fa-qr-code": "^5.7",
|
||||||
"sensio/framework-extra-bundle": "^6.1",
|
"sensio/framework-extra-bundle": "^6.1",
|
||||||
|
"stof/doctrine-extensions-bundle": "^1.6",
|
||||||
"symfony/apache-pack": "^1.0",
|
"symfony/apache-pack": "^1.0",
|
||||||
"symfony/asset": "5.2.*",
|
"symfony/asset": "5.2.*",
|
||||||
"symfony/console": "5.2.*",
|
"symfony/console": "5.2.*",
|
||||||
|
|
|
@ -16,4 +16,5 @@ return [
|
||||||
Knp\Bundle\PaginatorBundle\KnpPaginatorBundle::class => ['all' => true],
|
Knp\Bundle\PaginatorBundle\KnpPaginatorBundle::class => ['all' => true],
|
||||||
Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true],
|
Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true],
|
||||||
Scheb\TwoFactorBundle\SchebTwoFactorBundle::class => ['all' => true],
|
Scheb\TwoFactorBundle\SchebTwoFactorBundle::class => ['all' => true],
|
||||||
|
Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true],
|
||||||
];
|
];
|
||||||
|
|
|
@ -16,3 +16,9 @@ doctrine:
|
||||||
dir: '%kernel.project_dir%/src/Entity'
|
dir: '%kernel.project_dir%/src/Entity'
|
||||||
prefix: 'App\Entity'
|
prefix: 'App\Entity'
|
||||||
alias: App
|
alias: App
|
||||||
|
gedmo_tree:
|
||||||
|
type: annotation
|
||||||
|
prefix: Gedmo\Tree\Entity
|
||||||
|
dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Tree/Entity"
|
||||||
|
alias: GedmoTree # (optional) it will default to the name set for the mapping
|
||||||
|
is_bundle: false
|
||||||
|
|
4
config/packages/stof_doctrine_extensions.yaml
Normal file
4
config/packages/stof_doctrine_extensions.yaml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Read the documentation: https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html
|
||||||
|
# See the official DoctrineExtensions documentation for more details: https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc/
|
||||||
|
stof_doctrine_extensions:
|
||||||
|
default_locale: en_US
|
|
@ -27,5 +27,12 @@ services:
|
||||||
resource: '../src/Controller/'
|
resource: '../src/Controller/'
|
||||||
tags: ['controller.service_arguments']
|
tags: ['controller.service_arguments']
|
||||||
|
|
||||||
|
gedmo.listener.tree:
|
||||||
|
class: Gedmo\Tree\TreeListener
|
||||||
|
tags:
|
||||||
|
- { name: doctrine.event_subscriber, connection: default }
|
||||||
|
calls:
|
||||||
|
- [ setAnnotationReader, [ "@annotation_reader" ] ]
|
||||||
|
|
||||||
# add more service definitions when explicit configuration is needed
|
# add more service definitions when explicit configuration is needed
|
||||||
# please note that last definitions always *replace* previous ones
|
# please note that last definitions always *replace* previous ones
|
||||||
|
|
|
@ -111,7 +111,7 @@ class CategoryAdminController extends AdminController
|
||||||
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
|
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
|
||||||
$entityManager->delete($entity);
|
$entityManager->delete($entity);
|
||||||
|
|
||||||
$this->addFlash('success', 'Données supprimées.');
|
$this->addFlash('success', 'Données supprimée..');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->redirectToRoute('admin_blog_category_index');
|
return $this->redirectToRoute('admin_blog_category_index');
|
||||||
|
|
|
@ -8,7 +8,6 @@ use App\Factory\Blog\PostFactory as EntityFactory;
|
||||||
use App\Form\Blog\PostType as EntityType;
|
use App\Form\Blog\PostType as EntityType;
|
||||||
use App\Form\FileUploadHandler;
|
use App\Form\FileUploadHandler;
|
||||||
use App\Manager\EntityManager;
|
use App\Manager\EntityManager;
|
||||||
use App\Repository\Blog\PostRepositoryQuery;
|
|
||||||
use App\Repository\Blog\PostRepositoryQuery as RepositoryQuery;
|
use App\Repository\Blog\PostRepositoryQuery as RepositoryQuery;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
@ -112,7 +111,7 @@ class PostAdminController extends AdminController
|
||||||
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
|
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
|
||||||
$entityManager->delete($entity);
|
$entityManager->delete($entity);
|
||||||
|
|
||||||
$this->addFlash('success', 'Données supprimées.');
|
$this->addFlash('success', 'Données supprimée..');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->redirectToRoute('admin_blog_post_index');
|
return $this->redirectToRoute('admin_blog_post_index');
|
||||||
|
|
82
src/Controller/Site/MenuAdminController.php
Normal file
82
src/Controller/Site/MenuAdminController.php
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller\Site;
|
||||||
|
|
||||||
|
use App\Controller\Admin\AdminController;
|
||||||
|
use App\Entity\Site\Menu as Entity;
|
||||||
|
use App\Entity\Site\Navigation;
|
||||||
|
use App\Factory\Site\MenuFactory as EntityFactory;
|
||||||
|
use App\Form\Site\MenuType as EntityType;
|
||||||
|
use App\Manager\EntityManager;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/admin/site/menu")
|
||||||
|
*/
|
||||||
|
class MenuAdminController extends AdminController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Route("/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);
|
||||||
|
$form = $this->createForm(EntityType::class, $entity);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
if ($form->isValid()) {
|
||||||
|
$entityManager->create($entity);
|
||||||
|
|
||||||
|
$this->addFlash('success', 'Donnée enregistrée.');
|
||||||
|
} else {
|
||||||
|
$this->addFlash('warning', 'Le formulaire est invalide.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redirectToRoute('admin_site_tree_navigation', [
|
||||||
|
'navigation' => $navigation->getId(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/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);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
if ($form->isValid()) {
|
||||||
|
$entityManager->update($entity);
|
||||||
|
$this->addFlash('success', 'Donnée enregistrée.');
|
||||||
|
} else {
|
||||||
|
$this->addFlash('warning', 'Le formulaire est invalide.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redirectToRoute('admin_site_tree_navigation', [
|
||||||
|
'navigation' => $entity->getNavigation()->getId(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/delete/{entity}", name="admin_site_menu_delete", methods={"DELETE"})
|
||||||
|
*/
|
||||||
|
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||||
|
{
|
||||||
|
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
|
||||||
|
$entityManager->delete($entity);
|
||||||
|
|
||||||
|
$this->addFlash('success', 'Données supprimée..');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redirectToRoute('admin_site_tree_navigation', [
|
||||||
|
'navigation' => $entity->getNavigation()->getId(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSection(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
116
src/Controller/Site/NavigationAdminController.php
Normal file
116
src/Controller/Site/NavigationAdminController.php
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller\Site;
|
||||||
|
|
||||||
|
use App\Controller\Admin\AdminController;
|
||||||
|
use App\Entity\Site\Navigation as Entity;
|
||||||
|
use App\Factory\Site\NavigationFactory as EntityFactory;
|
||||||
|
use App\Form\Site\NavigationType as EntityType;
|
||||||
|
use App\Manager\EntityManager;
|
||||||
|
use App\Repository\Site\NavigationRepositoryQuery as RepositoryQuery;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/admin/site/navigation")
|
||||||
|
*/
|
||||||
|
class NavigationAdminController extends AdminController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Route("/{page}", name="admin_site_navigation_index", requirements={"page": "\d+"})
|
||||||
|
*/
|
||||||
|
public function index(int $page = 1, RepositoryQuery $query, Request $request): Response
|
||||||
|
{
|
||||||
|
$pager = $query->paginate($page);
|
||||||
|
|
||||||
|
return $this->render('site/navigation_admin/index.html.twig', [
|
||||||
|
'pager' => $pager,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/new", name="admin_site_navigation_new")
|
||||||
|
*/
|
||||||
|
public function new(EntityFactory $factory, EntityManager $entityManager, Request $request): Response
|
||||||
|
{
|
||||||
|
$entity = $factory->create();
|
||||||
|
$form = $this->createForm(EntityType::class, $entity);
|
||||||
|
|
||||||
|
if ($request->isMethod('POST')) {
|
||||||
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
if ($form->isValid()) {
|
||||||
|
$entityManager->create($entity);
|
||||||
|
$this->addFlash('success', 'Donnée enregistrée.');
|
||||||
|
|
||||||
|
return $this->redirectToRoute('admin_site_navigation_edit', [
|
||||||
|
'entity' => $entity->getId(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$this->addFlash('warning', 'Le formulaire est invalide.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('site/navigation_admin/new.html.twig', [
|
||||||
|
'form' => $form->createView(),
|
||||||
|
'entity' => $entity,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/edit/{entity}", name="admin_site_navigation_edit")
|
||||||
|
*/
|
||||||
|
public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||||
|
{
|
||||||
|
$form = $this->createForm(EntityType::class, $entity);
|
||||||
|
|
||||||
|
if ($request->isMethod('POST')) {
|
||||||
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
if ($form->isValid()) {
|
||||||
|
$entityManager->update($entity);
|
||||||
|
$this->addFlash('success', 'Donnée enregistrée.');
|
||||||
|
|
||||||
|
return $this->redirectToRoute('admin_site_navigation_edit', [
|
||||||
|
'entity' => $entity->getId(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addFlash('warning', 'Le formulaire est invalide.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('site/navigation_admin/edit.html.twig', [
|
||||||
|
'form' => $form->createView(),
|
||||||
|
'entity' => $entity,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/show/{entity}", name="admin_site_navigation_show")
|
||||||
|
*/
|
||||||
|
public function show(Entity $entity): Response
|
||||||
|
{
|
||||||
|
return $this->render('site/navigation_admin/show.html.twig', [
|
||||||
|
'entity' => $entity,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/delete/{entity}", name="admin_site_navigation_delete", methods={"DELETE"})
|
||||||
|
*/
|
||||||
|
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
|
||||||
|
{
|
||||||
|
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
|
||||||
|
$entityManager->delete($entity);
|
||||||
|
|
||||||
|
$this->addFlash('success', 'Données supprimée..');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redirectToRoute('admin_site_navigation_index');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSection(): string
|
||||||
|
{
|
||||||
|
return 'site_navigation';
|
||||||
|
}
|
||||||
|
}
|
178
src/Controller/Site/NodeAdminController.php
Normal file
178
src/Controller/Site/NodeAdminController.php
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller\Site;
|
||||||
|
|
||||||
|
use App\Controller\Admin\AdminController;
|
||||||
|
use App\Entity\Site\Node;
|
||||||
|
use App\Entity\Site\Node as Entity;
|
||||||
|
use App\Event\EntityManager\EntityManagerEvent;
|
||||||
|
use App\Factory\Site\NodeFactory as EntityFactory;
|
||||||
|
use App\Form\Site\NodeMoveType;
|
||||||
|
use App\Form\Site\NodeType as EntityType;
|
||||||
|
use App\Manager\EntityManager;
|
||||||
|
use App\Repository\Site\NodeRepository;
|
||||||
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
|
use Symfony\Component\Form\FormError;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/admin/site/node")
|
||||||
|
*/
|
||||||
|
class NodeAdminController extends AdminController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Route("/new/{node}", name="admin_site_node_new")
|
||||||
|
*/
|
||||||
|
public function new(
|
||||||
|
Node $node,
|
||||||
|
EntityFactory $factory,
|
||||||
|
EntityManager $entityManager,
|
||||||
|
NodeRepository $nodeRepository,
|
||||||
|
Request $request
|
||||||
|
): Response {
|
||||||
|
$entity = $factory->create($node->getMenu());
|
||||||
|
$form = $this->createForm(EntityType::class, $entity);
|
||||||
|
|
||||||
|
if ($request->isMethod('POST')) {
|
||||||
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
if ($form->isValid()) {
|
||||||
|
$position = $form->get('position')->getData();
|
||||||
|
|
||||||
|
$parent = 'above' === $position ? $node : $node->getParent();
|
||||||
|
$entity->setParent($parent);
|
||||||
|
|
||||||
|
if ('above' === $position) {
|
||||||
|
$nodeRepository->persistAsLastChild($entity, $node);
|
||||||
|
$entityManager->flush();
|
||||||
|
} else {
|
||||||
|
if ('after' === $position) {
|
||||||
|
$nodeRepository->persistAsNextSiblingOf($entity, $node);
|
||||||
|
} elseif ('before' === $position) {
|
||||||
|
$nodeRepository->persistAsPrevSiblingOf($entity, $node);
|
||||||
|
}
|
||||||
|
|
||||||
|
$entityManager->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addFlash('success', 'Donnée enregistrée.');
|
||||||
|
} else {
|
||||||
|
$this->addFlash('warning', 'Le formulaire est invalide.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redirectToRoute('admin_site_tree_navigation', [
|
||||||
|
'navigation' => $node->getMenu()->getNavigation()->getId(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('site/node_admin/new.html.twig', [
|
||||||
|
'form' => $form->createView(),
|
||||||
|
'node' => $node,
|
||||||
|
'entity' => $entity,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/move/{entity}", name="admin_site_node_move")
|
||||||
|
*/
|
||||||
|
public function move(
|
||||||
|
Entity $entity,
|
||||||
|
EntityManager $entityManager,
|
||||||
|
NodeRepository $nodeRepository,
|
||||||
|
Request $request
|
||||||
|
): Response {
|
||||||
|
$form = $this->createForm(NodeMoveType::class, null, [
|
||||||
|
'menu' => $entity->getMenu(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($request->isMethod('POST')) {
|
||||||
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
if ($form->get('node')->getData()->getId() === $entity->getId()) {
|
||||||
|
$form->get('node')->addError(new FormError('Élement de référence invalide.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($form->isValid()) {
|
||||||
|
$position = $form->get('position')->getData();
|
||||||
|
$node = $form->get('node')->getData();
|
||||||
|
|
||||||
|
$parent = 'above' === $position ? $node : $node->getParent();
|
||||||
|
$entity->setParent($parent);
|
||||||
|
|
||||||
|
if ('above' === $position) {
|
||||||
|
$nodeRepository->persistAsLastChild($entity, $node);
|
||||||
|
$entityManager->flush();
|
||||||
|
} else {
|
||||||
|
if ('after' === $position) {
|
||||||
|
$nodeRepository->persistAsNextSiblingOf($entity, $node);
|
||||||
|
} elseif ('before' === $position) {
|
||||||
|
$nodeRepository->persistAsPrevSiblingOf($entity, $node);
|
||||||
|
}
|
||||||
|
|
||||||
|
$entityManager->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addFlash('success', 'Donnée enregistrée.');
|
||||||
|
} else {
|
||||||
|
$this->addFlash('warning', 'Le formulaire est invalide.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redirectToRoute('admin_site_tree_navigation', [
|
||||||
|
'navigation' => $entity->getMenu()->getNavigation()->getId(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('site/node_admin/move.html.twig', [
|
||||||
|
'form' => $form->createView(),
|
||||||
|
'entity' => $entity,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/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'))) {
|
||||||
|
$entity->setIsVisible(!$entity->getIsVisible());
|
||||||
|
|
||||||
|
$entityManager->update($entity);
|
||||||
|
|
||||||
|
$this->addFlash('success', 'Donnée enregistrée.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redirectToRoute('admin_site_tree_navigation', [
|
||||||
|
'navigation' => $entity->getMenu()->getNavigation()->getId(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/delete/{entity}", name="admin_site_node_delete", methods={"DELETE"})
|
||||||
|
*/
|
||||||
|
public function delete(
|
||||||
|
Entity $entity,
|
||||||
|
NodeRepository $nodeRepository,
|
||||||
|
EventDispatcherInterface $eventDispatcher,
|
||||||
|
Request $request
|
||||||
|
): Response {
|
||||||
|
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
|
||||||
|
$eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::PRE_DELETE_EVENT);
|
||||||
|
$nodeRepository->removeFromTree($entity);
|
||||||
|
$nodeRepository->reorder($entity->getMenu()->getRootNode());
|
||||||
|
$eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::DELETE_EVENT);
|
||||||
|
|
||||||
|
$this->addFlash('success', 'Donnée supprimée.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redirectToRoute('admin_site_tree_navigation', [
|
||||||
|
'navigation' => $entity->getMenu()->getNavigation()->getId(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSection(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
72
src/Controller/Site/TreeAdminController.php
Normal file
72
src/Controller/Site/TreeAdminController.php
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller\Site;
|
||||||
|
|
||||||
|
use App\Controller\Admin\AdminController;
|
||||||
|
use App\Entity\Site\Navigation;
|
||||||
|
use App\Factory\Site\MenuFactory;
|
||||||
|
use App\Form\Site\MenuType;
|
||||||
|
use App\Repository\Site\NavigationRepositoryQuery;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/admin/site/tree")
|
||||||
|
*/
|
||||||
|
class TreeAdminController extends AdminController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Route("/", name="admin_site_tree_index")
|
||||||
|
*/
|
||||||
|
public function index(NavigationRepositoryQuery $navigationQuery): Response
|
||||||
|
{
|
||||||
|
$navigation = $navigationQuery->create()
|
||||||
|
->orderBy('.label')
|
||||||
|
->findOne()
|
||||||
|
;
|
||||||
|
|
||||||
|
if (null === $navigation) {
|
||||||
|
$this->addFlash('warning', 'Vous devez ajouter une navigation.');
|
||||||
|
|
||||||
|
return $this->redirectToRoute('admin_site_navigation_new');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redirectToRoute('admin_site_tree_navigation', [
|
||||||
|
'navigation' => $navigation->getId(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/navigation/{navigation}", name="admin_site_tree_navigation")
|
||||||
|
*/
|
||||||
|
public function navigation(
|
||||||
|
Navigation $navigation,
|
||||||
|
NavigationRepositoryQuery $navigationQuery,
|
||||||
|
MenuFactory $menuFactory
|
||||||
|
): Response {
|
||||||
|
$navigations = $navigationQuery->create()
|
||||||
|
->orderBy('.label')
|
||||||
|
->find()
|
||||||
|
;
|
||||||
|
|
||||||
|
$forms = [
|
||||||
|
'menu' => $this->createForm(MenuType::class, $menuFactory->create())->createView(),
|
||||||
|
'menus' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($navigation->getMenus() as $menu) {
|
||||||
|
$forms['menus'][$menu->getId()] = $this->createForm(MenuType::class, $menu)->createView();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('site/tree_admin/navigation.html.twig', [
|
||||||
|
'navigation' => $navigation,
|
||||||
|
'navigations' => $navigations,
|
||||||
|
'forms' => $forms,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSection(): string
|
||||||
|
{
|
||||||
|
return 'site_tree';
|
||||||
|
}
|
||||||
|
}
|
|
@ -144,7 +144,7 @@ class UserAdminController extends AdminController
|
||||||
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
|
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {
|
||||||
$entityManager->delete($entity);
|
$entityManager->delete($entity);
|
||||||
|
|
||||||
$this->addFlash('success', 'Données supprimées.');
|
$this->addFlash('success', 'Données supprimée..');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->redirectToRoute('admin_user_index');
|
return $this->redirectToRoute('admin_user_index');
|
||||||
|
|
140
src/Entity/Site/Menu.php
Normal file
140
src/Entity/Site/Menu.php
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Entity\Site;
|
||||||
|
|
||||||
|
use App\Doctrine\Timestampable;
|
||||||
|
use App\Entity\EntityInterface;
|
||||||
|
use App\Repository\Site\MenuRepository;
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Doctrine\Common\Collections\Collection;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Entity(repositoryClass=MenuRepository::class)
|
||||||
|
* @ORM\HasLifecycleCallbacks
|
||||||
|
*/
|
||||||
|
class Menu implements EntityInterface
|
||||||
|
{
|
||||||
|
use Timestampable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Id
|
||||||
|
* @ORM\GeneratedValue
|
||||||
|
* @ORM\Column(type="integer")
|
||||||
|
*/
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", length=255)
|
||||||
|
*/
|
||||||
|
private $label;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", length=255)
|
||||||
|
*/
|
||||||
|
private $code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToOne(targetEntity=Navigation::class, inversedBy="menus")
|
||||||
|
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
|
||||||
|
*/
|
||||||
|
private $navigation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\OneToMany(targetEntity=Node::class, mappedBy="menu", orphanRemoval=true)
|
||||||
|
*/
|
||||||
|
private $nodes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\OneToOne(targetEntity=Node::class)
|
||||||
|
*/
|
||||||
|
private $rootNode;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->nodes = new ArrayCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): ?int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabel(): ?string
|
||||||
|
{
|
||||||
|
return $this->label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLabel(string $label): self
|
||||||
|
{
|
||||||
|
$this->label = $label;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCode(): ?string
|
||||||
|
{
|
||||||
|
return $this->code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCode(string $code): self
|
||||||
|
{
|
||||||
|
$this->code = $code;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNavigation(): ?Navigation
|
||||||
|
{
|
||||||
|
return $this->navigation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setNavigation(?Navigation $navigation): self
|
||||||
|
{
|
||||||
|
$this->navigation = $navigation;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection|Node[]
|
||||||
|
*/
|
||||||
|
public function getNodes(): Collection
|
||||||
|
{
|
||||||
|
return $this->nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addNode(Node $node): self
|
||||||
|
{
|
||||||
|
if (!$this->nodes->contains($node)) {
|
||||||
|
$this->nodes[] = $node;
|
||||||
|
$node->setMenu($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeNode(Node $node): self
|
||||||
|
{
|
||||||
|
if ($this->nodes->removeElement($node)) {
|
||||||
|
// set the owning side to null (unless already changed)
|
||||||
|
if ($node->getMenu() === $this) {
|
||||||
|
$node->setMenu(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRootNode(): ?Node
|
||||||
|
{
|
||||||
|
return $this->rootNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRootNode(?Node $rootNode): self
|
||||||
|
{
|
||||||
|
$this->rootNode = $rootNode;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
122
src/Entity/Site/Navigation.php
Normal file
122
src/Entity/Site/Navigation.php
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Entity\Site;
|
||||||
|
|
||||||
|
use App\Doctrine\Timestampable;
|
||||||
|
use App\Entity\EntityInterface;
|
||||||
|
use App\Repository\Site\NavigationRepository;
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Doctrine\Common\Collections\Collection;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Entity(repositoryClass=NavigationRepository::class)
|
||||||
|
* @ORM\HasLifecycleCallbacks
|
||||||
|
*/
|
||||||
|
class Navigation implements EntityInterface
|
||||||
|
{
|
||||||
|
use Timestampable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Id
|
||||||
|
* @ORM\GeneratedValue
|
||||||
|
* @ORM\Column(type="integer")
|
||||||
|
*/
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", length=255)
|
||||||
|
*/
|
||||||
|
private $label;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", length=255)
|
||||||
|
*/
|
||||||
|
private $code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", length=255)
|
||||||
|
*/
|
||||||
|
private $domain;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\OneToMany(targetEntity=Menu::class, mappedBy="navigation")
|
||||||
|
*/
|
||||||
|
private $menus;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->menus = new ArrayCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): ?int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabel(): ?string
|
||||||
|
{
|
||||||
|
return $this->label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLabel(string $label): self
|
||||||
|
{
|
||||||
|
$this->label = $label;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCode(): ?string
|
||||||
|
{
|
||||||
|
return $this->code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCode(string $code): self
|
||||||
|
{
|
||||||
|
$this->code = $code;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDomain(): ?string
|
||||||
|
{
|
||||||
|
return $this->domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDomain(string $domain): self
|
||||||
|
{
|
||||||
|
$this->domain = $domain;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection|Menu[]
|
||||||
|
*/
|
||||||
|
public function getMenus(): Collection
|
||||||
|
{
|
||||||
|
return $this->menus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addMenu(Menu $menu): self
|
||||||
|
{
|
||||||
|
if (!$this->menus->contains($menu)) {
|
||||||
|
$this->menus[] = $menu;
|
||||||
|
$menu->setNavigation($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeMenu(Menu $menu): self
|
||||||
|
{
|
||||||
|
if ($this->menus->removeElement($menu)) {
|
||||||
|
// set the owning side to null (unless already changed)
|
||||||
|
if ($menu->getNavigation() === $this) {
|
||||||
|
$menu->setNavigation(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
263
src/Entity/Site/Node.php
Normal file
263
src/Entity/Site/Node.php
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Entity\Site;
|
||||||
|
|
||||||
|
use App\Doctrine\Timestampable;
|
||||||
|
use App\Entity\EntityInterface;
|
||||||
|
use App\Repository\Site\NodeRepository;
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Doctrine\Common\Collections\Collection;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Gedmo\Mapping\Annotation as Gedmo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Gedmo\Tree(type="nested")
|
||||||
|
* @ORM\HasLifecycleCallbacks
|
||||||
|
* @ORM\Entity(repositoryClass=NodeRepository::class)
|
||||||
|
*/
|
||||||
|
class Node implements EntityInterface
|
||||||
|
{
|
||||||
|
use Timestampable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Id
|
||||||
|
* @ORM\GeneratedValue
|
||||||
|
* @ORM\Column(type="integer")
|
||||||
|
*/
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToOne(targetEntity=Menu::class, inversedBy="nodes", cascade={"persist", "remove"})
|
||||||
|
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
|
||||||
|
*/
|
||||||
|
private $menu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", length=255, nullable=true)
|
||||||
|
*/
|
||||||
|
private $label;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", length=255, nullable=true)
|
||||||
|
*/
|
||||||
|
private $url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="boolean", options={"default"=0})
|
||||||
|
*/
|
||||||
|
private $isVisible = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Gedmo\TreeLeft
|
||||||
|
* @ORM\Column(type="integer")
|
||||||
|
*/
|
||||||
|
private $treeLeft;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Gedmo\TreeLevel
|
||||||
|
* @ORM\Column(type="integer")
|
||||||
|
*/
|
||||||
|
private $treeLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Gedmo\TreeRight
|
||||||
|
* @ORM\Column(type="integer")
|
||||||
|
*/
|
||||||
|
private $treeRight;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Gedmo\TreeRoot
|
||||||
|
* @ORM\ManyToOne(targetEntity="Node")
|
||||||
|
* @ORM\JoinColumn(referencedColumnName="id", onDelete="CASCADE")
|
||||||
|
*/
|
||||||
|
private $treeRoot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Gedmo\TreeParent
|
||||||
|
* @ORM\ManyToOne(targetEntity="Node", inversedBy="children")
|
||||||
|
* @ORM\JoinColumn(referencedColumnName="id", onDelete="CASCADE")
|
||||||
|
*/
|
||||||
|
private $parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\OneToMany(targetEntity="Node", mappedBy="parent")
|
||||||
|
* @ORM\OrderBy({"treeLeft"="ASC"})
|
||||||
|
*/
|
||||||
|
private $children;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->children = new ArrayCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): ?int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMenu(): ?Menu
|
||||||
|
{
|
||||||
|
return $this->menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMenu(?Menu $menu): self
|
||||||
|
{
|
||||||
|
$this->menu = $menu;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTreeLeft(): ?int
|
||||||
|
{
|
||||||
|
return $this->treeLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTreeLeft(int $treeLeft): self
|
||||||
|
{
|
||||||
|
$this->treeLeft = $treeLeft;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTreeLevel(): ?int
|
||||||
|
{
|
||||||
|
return $this->treeLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTreeLevel(int $treeLevel): self
|
||||||
|
{
|
||||||
|
$this->treeLevel = $treeLevel;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTreeRight(): ?int
|
||||||
|
{
|
||||||
|
return $this->treeRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTreeRight(int $treeRight): self
|
||||||
|
{
|
||||||
|
$this->treeRight = $treeRight;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTreeRoot(): ?self
|
||||||
|
{
|
||||||
|
return $this->treeRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTreeRoot(?self $treeRoot): self
|
||||||
|
{
|
||||||
|
$this->treeRoot = $treeRoot;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParent(): ?self
|
||||||
|
{
|
||||||
|
return $this->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setParent(?self $parent): self
|
||||||
|
{
|
||||||
|
$this->parent = $parent;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection|Node[]
|
||||||
|
*/
|
||||||
|
public function getChildren(): Collection
|
||||||
|
{
|
||||||
|
return $this->children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addChild(Node $child): self
|
||||||
|
{
|
||||||
|
if (!$this->children->contains($child)) {
|
||||||
|
$this->children[] = $child;
|
||||||
|
$child->setParent($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeChild(Node $child): self
|
||||||
|
{
|
||||||
|
if ($this->children->removeElement($child)) {
|
||||||
|
// set the owning side to null (unless already changed)
|
||||||
|
if ($child->getParent() === $this) {
|
||||||
|
$child->setParent(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllChildren(): ArrayCollection
|
||||||
|
{
|
||||||
|
$children = [];
|
||||||
|
|
||||||
|
$getChildren = function (Node $node) use (&$children, &$getChildren) {
|
||||||
|
foreach ($node->getChildren() as $nodeChildren) {
|
||||||
|
$children[] = $nodeChildren;
|
||||||
|
|
||||||
|
$getChildren($nodeChildren);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$getChildren($this);
|
||||||
|
|
||||||
|
usort($children, function ($a, $b) {
|
||||||
|
return $a->getTreeLeft() < $b->getTreeLeft() ? -1 : 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
return new ArrayCollection($children);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabel(): ?string
|
||||||
|
{
|
||||||
|
return $this->label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLabel(?string $label): self
|
||||||
|
{
|
||||||
|
$this->label = $label;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUrl(): ?string
|
||||||
|
{
|
||||||
|
return $this->url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUrl(?string $url): self
|
||||||
|
{
|
||||||
|
$this->url = $url;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIsVisible(): ?bool
|
||||||
|
{
|
||||||
|
return $this->isVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIsVisible(bool $isVisible): self
|
||||||
|
{
|
||||||
|
$this->isVisible = $isVisible;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTreeLabel()
|
||||||
|
{
|
||||||
|
$prefix = str_repeat('-', ($this->getTreeLevel() - 1) * 5);
|
||||||
|
|
||||||
|
return trim($prefix.' '.$this->getLabel());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\EventSuscriber;
|
namespace App\EventSuscriber\Account;
|
||||||
|
|
||||||
use App\Event\Account\PasswordRequestEvent;
|
use App\Event\Account\PasswordRequestEvent;
|
||||||
use App\Notification\MailNotifier;
|
use App\Notification\MailNotifier;
|
||||||
|
@ -12,7 +12,7 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
*
|
*
|
||||||
* @author Simon Vieille <simon@deblan.fr>
|
* @author Simon Vieille <simon@deblan.fr>
|
||||||
*/
|
*/
|
||||||
class AccountPasswordRequestEventSubscriber implements EventSubscriberInterface
|
class PasswordRequestEventSubscriber implements EventSubscriberInterface
|
||||||
{
|
{
|
||||||
protected MailNotifier $notifier;
|
protected MailNotifier $notifier;
|
||||||
protected UrlGeneratorInterface $urlGenerator;
|
protected UrlGeneratorInterface $urlGenerator;
|
|
@ -1,21 +1,21 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\EventSuscriber;
|
namespace App\EventSuscriber\Blog;
|
||||||
|
|
||||||
use App\Entity\Blog\Post;
|
use App\Entity\Blog\Post;
|
||||||
use App\Entity\EntityInterface;
|
use App\Entity\EntityInterface;
|
||||||
use App\Event\EntityManager\EntityManagerEvent;
|
use App\Event\EntityManager\EntityManagerEvent;
|
||||||
|
use App\EventSuscriber\EntityManagerEventSubscriber;
|
||||||
use App\Repository\Blog\PostRepositoryQuery;
|
use App\Repository\Blog\PostRepositoryQuery;
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|
||||||
use Symfony\Component\Filesystem\Filesystem;
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
use Symfony\Component\Finder\Finder;
|
use Symfony\Component\Finder\Finder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* class BlogPostEventSubscriber.
|
* class PostEventSubscriber.
|
||||||
*
|
*
|
||||||
* @author Simon Vieille <simon@deblan.fr>
|
* @author Simon Vieille <simon@deblan.fr>
|
||||||
*/
|
*/
|
||||||
class BlogPostEventSubscriber extends EntityManagerEventSubscriber
|
class PostEventSubscriber extends EntityManagerEventSubscriber
|
||||||
{
|
{
|
||||||
protected Filesystem $filesystem;
|
protected Filesystem $filesystem;
|
||||||
protected PostRepositoryQuery $query;
|
protected PostRepositoryQuery $query;
|
72
src/EventSuscriber/Site/MenuEventSubscriber.php
Normal file
72
src/EventSuscriber/Site/MenuEventSubscriber.php
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\EventSuscriber\Site;
|
||||||
|
|
||||||
|
use App\Entity\EntityInterface;
|
||||||
|
use App\Entity\Site\Menu;
|
||||||
|
use App\Event\EntityManager\EntityManagerEvent;
|
||||||
|
use App\EventSuscriber\EntityManagerEventSubscriber;
|
||||||
|
use App\Factory\Site\NodeFactory;
|
||||||
|
use App\Manager\EntityManager;
|
||||||
|
use App\Repository\Site\NodeRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class MenuEventSubscriber.
|
||||||
|
*
|
||||||
|
* @author Simon Vieille <simon@deblan.fr>
|
||||||
|
*/
|
||||||
|
class MenuEventSubscriber extends EntityManagerEventSubscriber
|
||||||
|
{
|
||||||
|
protected NodeFactory $nodeFactory;
|
||||||
|
protected EntityManager $entityManager;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
NodeFactory $nodeFactory,
|
||||||
|
NodeRepository $nodeRepository,
|
||||||
|
EntityManager $entityManager
|
||||||
|
) {
|
||||||
|
$this->nodeFactory = $nodeFactory;
|
||||||
|
$this->nodeRepository = $nodeRepository;
|
||||||
|
$this->entityManager = $entityManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function support(EntityInterface $entity)
|
||||||
|
{
|
||||||
|
return $entity instanceof Menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onCreate(EntityManagerEvent $event)
|
||||||
|
{
|
||||||
|
if (!$this->support($event->getEntity())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$menu = $event->getEntity();
|
||||||
|
|
||||||
|
if (0 !== count($menu->getNodes())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rootNode = $this->nodeFactory->create($menu);
|
||||||
|
$childNode = $this->nodeFactory->create($menu);
|
||||||
|
$childNode
|
||||||
|
->setParent($rootNode)
|
||||||
|
->setLabel('Premier élément')
|
||||||
|
;
|
||||||
|
|
||||||
|
$menu->setRootNode($rootNode);
|
||||||
|
|
||||||
|
$this->entityManager->create($rootNode);
|
||||||
|
$this->entityManager->create($childNode);
|
||||||
|
|
||||||
|
$this->entityManager->getEntityManager()->persist($menu);
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
$this->nodeRepository->persistAsFirstChild($childNode, $rootNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onUpdate(EntityManagerEvent $event)
|
||||||
|
{
|
||||||
|
return $this->onCreate($event);
|
||||||
|
}
|
||||||
|
}
|
61
src/EventSuscriber/Site/NodeEventSubscriber.php
Normal file
61
src/EventSuscriber/Site/NodeEventSubscriber.php
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\EventSuscriber\Site;
|
||||||
|
|
||||||
|
use App\Entity\EntityInterface;
|
||||||
|
use App\Entity\Site\Node;
|
||||||
|
use App\Event\EntityManager\EntityManagerEvent;
|
||||||
|
use App\EventSuscriber\EntityManagerEventSubscriber;
|
||||||
|
use App\Factory\Site\NodeFactory;
|
||||||
|
use App\Manager\EntityManager;
|
||||||
|
use App\Repository\Site\NodeRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class NodeEventSubscriber.
|
||||||
|
*
|
||||||
|
* @author Simon Vieille <simon@deblan.fr>
|
||||||
|
*/
|
||||||
|
class NodeEventSubscriber extends EntityManagerEventSubscriber
|
||||||
|
{
|
||||||
|
protected NodeFactory $nodeFactory;
|
||||||
|
protected EntityManager $entityManager;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
NodeFactory $nodeFactory,
|
||||||
|
NodeRepository $nodeRepository,
|
||||||
|
EntityManager $entityManager
|
||||||
|
) {
|
||||||
|
$this->nodeFactory = $nodeFactory;
|
||||||
|
$this->nodeRepository = $nodeRepository;
|
||||||
|
$this->entityManager = $entityManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function support(EntityInterface $entity)
|
||||||
|
{
|
||||||
|
return $entity instanceof Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onDelete(EntityManagerEvent $event)
|
||||||
|
{
|
||||||
|
if (!$this->support($event->getEntity())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$menu = $event->getEntity()->getMenu();
|
||||||
|
$rootNode = $menu->getRootNode();
|
||||||
|
|
||||||
|
if (0 !== count($rootNode->getChildren())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$childNode = $this->nodeFactory->create($menu);
|
||||||
|
$childNode
|
||||||
|
->setParent($rootNode)
|
||||||
|
->setLabel('Premier élément')
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->entityManager->update($rootNode, false);
|
||||||
|
$this->entityManager->create($childNode, false);
|
||||||
|
$this->nodeRepository->persistAsFirstChild($childNode, $rootNode);
|
||||||
|
}
|
||||||
|
}
|
25
src/Factory/Site/MenuFactory.php
Normal file
25
src/Factory/Site/MenuFactory.php
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Factory\Site;
|
||||||
|
|
||||||
|
use App\Entity\Site\Menu;
|
||||||
|
use App\Entity\Site\Navigation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class MenuFactory.
|
||||||
|
*
|
||||||
|
* @author Simon Vieille <simon@deblan.fr>
|
||||||
|
*/
|
||||||
|
class MenuFactory
|
||||||
|
{
|
||||||
|
public function create(?Navigation $navigation = null): Menu
|
||||||
|
{
|
||||||
|
$entity = new Menu();
|
||||||
|
|
||||||
|
if (null !== $navigation) {
|
||||||
|
$entity->setNavigation($navigation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $entity;
|
||||||
|
}
|
||||||
|
}
|
18
src/Factory/Site/NavigationFactory.php
Normal file
18
src/Factory/Site/NavigationFactory.php
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Factory\Site;
|
||||||
|
|
||||||
|
use App\Entity\Site\Navigation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class NavigationFactory.
|
||||||
|
*
|
||||||
|
* @author Simon Vieille <simon@deblan.fr>
|
||||||
|
*/
|
||||||
|
class NavigationFactory
|
||||||
|
{
|
||||||
|
public function create(): Navigation
|
||||||
|
{
|
||||||
|
return new Navigation();
|
||||||
|
}
|
||||||
|
}
|
25
src/Factory/Site/NodeFactory.php
Normal file
25
src/Factory/Site/NodeFactory.php
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Factory\Site;
|
||||||
|
|
||||||
|
use App\Entity\Site\Menu;
|
||||||
|
use App\Entity\Site\Node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class NodeFactory.
|
||||||
|
*
|
||||||
|
* @author Simon Vieille <simon@deblan.fr>
|
||||||
|
*/
|
||||||
|
class NodeFactory
|
||||||
|
{
|
||||||
|
public function create(?Menu $menu = null): Node
|
||||||
|
{
|
||||||
|
$entity = new Node();
|
||||||
|
|
||||||
|
if (null !== $menu) {
|
||||||
|
$entity->setMenu($menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $entity;
|
||||||
|
}
|
||||||
|
}
|
51
src/Form/Site/MenuType.php
Normal file
51
src/Form/Site/MenuType.php
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form\Site;
|
||||||
|
|
||||||
|
use App\Entity\Site\Menu;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
|
||||||
|
class MenuType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$builder->add(
|
||||||
|
'label',
|
||||||
|
TextType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Libellé',
|
||||||
|
'required' => true,
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'code',
|
||||||
|
TextType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Code',
|
||||||
|
'required' => true,
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => Menu::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
65
src/Form/Site/NavigationType.php
Normal file
65
src/Form/Site/NavigationType.php
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form\Site;
|
||||||
|
|
||||||
|
use App\Entity\Site\Navigation;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
|
||||||
|
class NavigationType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$builder->add(
|
||||||
|
'label',
|
||||||
|
TextType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Libellé',
|
||||||
|
'required' => true,
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'code',
|
||||||
|
TextType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Code',
|
||||||
|
'required' => true,
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'domain',
|
||||||
|
TextType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Nom de domaine',
|
||||||
|
'required' => true,
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => Navigation::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
62
src/Form/Site/NodeMoveType.php
Normal file
62
src/Form/Site/NodeMoveType.php
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form\Site;
|
||||||
|
|
||||||
|
use App\Entity\Site\Node;
|
||||||
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
|
||||||
|
class NodeMoveType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$builder->add(
|
||||||
|
'position',
|
||||||
|
ChoiceType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Position',
|
||||||
|
'required' => true,
|
||||||
|
'choices' => [
|
||||||
|
'Après' => 'after',
|
||||||
|
'Avant' => 'before',
|
||||||
|
'En dessous' => 'above',
|
||||||
|
],
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'node',
|
||||||
|
EntityType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Élement de référence',
|
||||||
|
'class' => Node::class,
|
||||||
|
'choices' => call_user_func(function () use ($options) {
|
||||||
|
return $options['menu']->getRootNode()->getAllChildren();
|
||||||
|
}),
|
||||||
|
'choice_label' => 'treeLabel',
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => null,
|
||||||
|
'menu' => null,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
72
src/Form/Site/NodeType.php
Normal file
72
src/Form/Site/NodeType.php
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form\Site;
|
||||||
|
|
||||||
|
use App\Entity\Site\Node;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
|
||||||
|
class NodeType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$builder->add(
|
||||||
|
'label',
|
||||||
|
TextType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Libellé',
|
||||||
|
'required' => true,
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'url',
|
||||||
|
TextType::class,
|
||||||
|
[
|
||||||
|
'label' => 'URL',
|
||||||
|
'required' => false,
|
||||||
|
'help' => 'Laisser vide pour une génération automatique',
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder->add(
|
||||||
|
'position',
|
||||||
|
ChoiceType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Position',
|
||||||
|
'required' => true,
|
||||||
|
'mapped' => false,
|
||||||
|
'choices' => [
|
||||||
|
'Après' => 'after',
|
||||||
|
'Avant' => 'before',
|
||||||
|
'En dessous' => 'above',
|
||||||
|
],
|
||||||
|
'attr' => [
|
||||||
|
],
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => Node::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,36 +25,48 @@ class EntityManager
|
||||||
$this->entityManager = $entityManager;
|
$this->entityManager = $entityManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create(EntityInterface $entity): self
|
public function create(EntityInterface $entity, bool $dispatchEvent = true): self
|
||||||
{
|
{
|
||||||
|
if ($dispatchEvent) {
|
||||||
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::PRE_CREATE_EVENT);
|
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::PRE_CREATE_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
$this->persist($entity);
|
$this->persist($entity);
|
||||||
|
|
||||||
|
if ($dispatchEvent) {
|
||||||
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::CREATE_EVENT);
|
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::CREATE_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(EntityInterface $entity): self
|
public function update(EntityInterface $entity, bool $dispatchEvent = true): self
|
||||||
{
|
{
|
||||||
|
if ($dispatchEvent) {
|
||||||
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::PRE_UPDATE_EVENT);
|
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::PRE_UPDATE_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
$this->persist($entity);
|
$this->persist($entity);
|
||||||
|
|
||||||
|
if ($dispatchEvent) {
|
||||||
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::UPDATE_EVENT);
|
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::UPDATE_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete(EntityInterface $entity): self
|
public function delete(EntityInterface $entity, bool $dispatchEvent = true): self
|
||||||
{
|
{
|
||||||
|
if ($dispatchEvent) {
|
||||||
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::PRE_DELETE_EVENT);
|
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::PRE_DELETE_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
$this->entityManager->remove($entity);
|
$this->entityManager->remove($entity);
|
||||||
$this->flush();
|
$this->flush();
|
||||||
|
|
||||||
|
if ($dispatchEvent) {
|
||||||
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::DELETE_EVENT);
|
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::DELETE_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -73,6 +85,11 @@ class EntityManager
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getEntityManager(): EntityManagerInterface
|
||||||
|
{
|
||||||
|
return $this->entityManager;
|
||||||
|
}
|
||||||
|
|
||||||
protected function persist(EntityInterface $entity)
|
protected function persist(EntityInterface $entity)
|
||||||
{
|
{
|
||||||
$this->entityManager->persist($entity);
|
$this->entityManager->persist($entity);
|
||||||
|
|
|
@ -6,12 +6,6 @@ use App\Entity\Blog\Category;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
/**
|
|
||||||
* @method Category|null find($id, $lockMode = null, $lockVersion = null)
|
|
||||||
* @method Category|null findOneBy(array $criteria, array $orderBy = null)
|
|
||||||
* @method Category[] findAll()
|
|
||||||
* @method Category[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
|
||||||
*/
|
|
||||||
class CategoryRepository extends ServiceEntityRepository
|
class CategoryRepository extends ServiceEntityRepository
|
||||||
{
|
{
|
||||||
public function __construct(ManagerRegistry $registry)
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
|
|
@ -6,12 +6,6 @@ use App\Entity\Blog\Post;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
/**
|
|
||||||
* @method Post|null find($id, $lockMode = null, $lockVersion = null)
|
|
||||||
* @method Post|null findOneBy(array $criteria, array $orderBy = null)
|
|
||||||
* @method Post[] findAll()
|
|
||||||
* @method Post[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
|
||||||
*/
|
|
||||||
class PostRepository extends ServiceEntityRepository
|
class PostRepository extends ServiceEntityRepository
|
||||||
{
|
{
|
||||||
public function __construct(ManagerRegistry $registry)
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
|
|
@ -73,7 +73,10 @@ abstract class RepositoryQuery
|
||||||
|
|
||||||
public function findOne()
|
public function findOne()
|
||||||
{
|
{
|
||||||
return $this->query->getQuery()->getOneOrNullResult();
|
return $this->query->getQuery()
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getOneOrNullResult()
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function find()
|
public function find()
|
||||||
|
@ -85,4 +88,9 @@ abstract class RepositoryQuery
|
||||||
{
|
{
|
||||||
return $this->paginator->paginate($this->query->getQuery(), $page, $limit);
|
return $this->paginator->paginate($this->query->getQuery(), $page, $limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRepository(): ServiceEntityRepository
|
||||||
|
{
|
||||||
|
return $this->repository;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
15
src/Repository/Site/MenuRepository.php
Normal file
15
src/Repository/Site/MenuRepository.php
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repository\Site;
|
||||||
|
|
||||||
|
use App\Entity\Site\Menu;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
|
class MenuRepository extends ServiceEntityRepository
|
||||||
|
{
|
||||||
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
{
|
||||||
|
parent::__construct($registry, Menu::class);
|
||||||
|
}
|
||||||
|
}
|
19
src/Repository/Site/MenuRepositoryQuery.php
Normal file
19
src/Repository/Site/MenuRepositoryQuery.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repository\Site;
|
||||||
|
|
||||||
|
use App\Repository\RepositoryQuery;
|
||||||
|
use Knp\Component\Pager\PaginatorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class MenuRepositoryQuery.
|
||||||
|
*
|
||||||
|
* @author Simon Vieille <simon@deblan.fr>
|
||||||
|
*/
|
||||||
|
class MenuRepositoryQuery extends RepositoryQuery
|
||||||
|
{
|
||||||
|
public function __construct(MenuRepository $repository, PaginatorInterface $paginator)
|
||||||
|
{
|
||||||
|
parent::__construct($repository, 'm', $paginator);
|
||||||
|
}
|
||||||
|
}
|
15
src/Repository/Site/NavigationRepository.php
Normal file
15
src/Repository/Site/NavigationRepository.php
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repository\Site;
|
||||||
|
|
||||||
|
use App\Entity\Site\Navigation;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
|
class NavigationRepository extends ServiceEntityRepository
|
||||||
|
{
|
||||||
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
{
|
||||||
|
parent::__construct($registry, Navigation::class);
|
||||||
|
}
|
||||||
|
}
|
19
src/Repository/Site/NavigationRepositoryQuery.php
Normal file
19
src/Repository/Site/NavigationRepositoryQuery.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repository\Site;
|
||||||
|
|
||||||
|
use App\Repository\RepositoryQuery;
|
||||||
|
use Knp\Component\Pager\PaginatorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class NavigationRepositoryQuery.
|
||||||
|
*
|
||||||
|
* @author Simon Vieille <simon@deblan.fr>
|
||||||
|
*/
|
||||||
|
class NavigationRepositoryQuery extends RepositoryQuery
|
||||||
|
{
|
||||||
|
public function __construct(NavigationRepository $repository, PaginatorInterface $paginator)
|
||||||
|
{
|
||||||
|
parent::__construct($repository, 'n', $paginator);
|
||||||
|
}
|
||||||
|
}
|
15
src/Repository/Site/NodeRepository.php
Normal file
15
src/Repository/Site/NodeRepository.php
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repository\Site;
|
||||||
|
|
||||||
|
use App\Entity\Site\Node;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Gedmo\Tree\Entity\Repository\NestedTreeRepository;
|
||||||
|
|
||||||
|
class NodeRepository extends NestedTreeRepository
|
||||||
|
{
|
||||||
|
public function __construct(EntityManagerInterface $manager)
|
||||||
|
{
|
||||||
|
parent::__construct($manager, $manager->getClassMetadata(Node::class));
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,12 +9,6 @@ use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
|
||||||
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
|
||||||
/**
|
|
||||||
* @method User|null find($id, $lockMode = null, $lockVersion = null)
|
|
||||||
* @method User|null findOneBy(array $criteria, array $orderBy = null)
|
|
||||||
* @method User[] findAll()
|
|
||||||
* @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
|
||||||
*/
|
|
||||||
class UserRepository extends ServiceEntityRepository implements PasswordUpgraderInterface
|
class UserRepository extends ServiceEntityRepository implements PasswordUpgraderInterface
|
||||||
{
|
{
|
||||||
public function __construct(ManagerRegistry $registry)
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
@ -22,9 +16,6 @@ class UserRepository extends ServiceEntityRepository implements PasswordUpgrader
|
||||||
parent::__construct($registry, User::class);
|
parent::__construct($registry, User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to upgrade (rehash) the user's password automatically over time.
|
|
||||||
*/
|
|
||||||
public function upgradePassword(UserInterface $user, string $newEncodedPassword): void
|
public function upgradePassword(UserInterface $user, string $newEncodedPassword): void
|
||||||
{
|
{
|
||||||
if (!$user instanceof User) {
|
if (!$user instanceof User) {
|
||||||
|
|
18
symfony.lock
18
symfony.lock
|
@ -5,6 +5,9 @@
|
||||||
"beberlei/assert": {
|
"beberlei/assert": {
|
||||||
"version": "v3.3.0"
|
"version": "v3.3.0"
|
||||||
},
|
},
|
||||||
|
"behat/transliterator": {
|
||||||
|
"version": "v1.3.0"
|
||||||
|
},
|
||||||
"bjeavons/zxcvbn-php": {
|
"bjeavons/zxcvbn-php": {
|
||||||
"version": "1.2.0"
|
"version": "1.2.0"
|
||||||
},
|
},
|
||||||
|
@ -102,6 +105,9 @@
|
||||||
"friendsofphp/proxy-manager-lts": {
|
"friendsofphp/proxy-manager-lts": {
|
||||||
"version": "v1.0.3"
|
"version": "v1.0.3"
|
||||||
},
|
},
|
||||||
|
"gedmo/doctrine-extensions": {
|
||||||
|
"version": "v3.0.3"
|
||||||
|
},
|
||||||
"khanamiryan/qrcode-detector-decoder": {
|
"khanamiryan/qrcode-detector-decoder": {
|
||||||
"version": "1.0.4"
|
"version": "1.0.4"
|
||||||
},
|
},
|
||||||
|
@ -190,6 +196,18 @@
|
||||||
"spomky-labs/otphp": {
|
"spomky-labs/otphp": {
|
||||||
"version": "v10.0.1"
|
"version": "v10.0.1"
|
||||||
},
|
},
|
||||||
|
"stof/doctrine-extensions-bundle": {
|
||||||
|
"version": "1.2",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes-contrib",
|
||||||
|
"branch": "master",
|
||||||
|
"version": "1.2",
|
||||||
|
"ref": "6c1ceb662f8997085f739cd089bfbef67f245983"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"config/packages/stof_doctrine_extensions.yaml"
|
||||||
|
]
|
||||||
|
},
|
||||||
"swiftmailer/swiftmailer": {
|
"swiftmailer/swiftmailer": {
|
||||||
"version": "v6.2.7"
|
"version": "v6.2.7"
|
||||||
},
|
},
|
||||||
|
|
|
@ -65,7 +65,7 @@
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link {{ macros.active_class('site_menu', section) }}" href="{{ path('admin_blog_post_index') }}">
|
<a class="nav-link {{ macros.active_class('site_tree', section) }}" href="{{ path('admin_site_tree_index') }}">
|
||||||
<span class="fa fa-sitemap"></span>
|
<span class="fa fa-sitemap"></span>
|
||||||
|
|
||||||
<span class="nav-item-label">Arborescence</span>
|
<span class="nav-item-label">Arborescence</span>
|
||||||
|
@ -80,14 +80,6 @@
|
||||||
</h6>
|
</h6>
|
||||||
|
|
||||||
<ul class="nav flex-column">
|
<ul class="nav flex-column">
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link {{ macros.active_class('page', section) }}" href="{{ path('admin_dashboard_index') }}">
|
|
||||||
<span class="fa fa-file-alt"></span>
|
|
||||||
|
|
||||||
<span class="nav-item-label">Pages</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link {{ macros.active_class('blog_post', section) }}" href="{{ path('admin_blog_post_index') }}">
|
<a class="nav-link {{ macros.active_class('blog_post', section) }}" href="{{ path('admin_blog_post_index') }}">
|
||||||
<span class="fa fa-pen"></span>
|
<span class="fa fa-pen"></span>
|
||||||
|
|
12
templates/site/navigation_admin/_form.html.twig
Normal file
12
templates/site/navigation_admin/_form.html.twig
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 p-3">
|
||||||
|
<div class="row">
|
||||||
|
{% for item in ['label', 'code', 'domain'] %}
|
||||||
|
<div class="col-12">
|
||||||
|
{{ form_row(form[item]) }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
57
templates/site/navigation_admin/edit.html.twig
Normal file
57
templates/site/navigation_admin/edit.html.twig
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
{% extends '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">{{ entity.label }}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ml-auto">
|
||||||
|
<div class="btn-group">
|
||||||
|
<a href="{{ path('admin_site_navigation_index') }}" class="btn btn-light">
|
||||||
|
<span class="fa fa-list pr-1"></span>
|
||||||
|
Retour à la liste
|
||||||
|
</a>
|
||||||
|
<a href="{{ path('admin_site_navigation_show', {entity: entity.id}) }}" class="btn btn-secondary">
|
||||||
|
<span class="fa fa-eye pr-1"></span>
|
||||||
|
Voir
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<button type="submit" form="form-main" class="btn btn-primary">
|
||||||
|
<span class="fa fa-save pr-1"></span>
|
||||||
|
Enregistrer
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<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">
|
||||||
|
⋅⋅⋅
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-menu dropdown-menu-right">
|
||||||
|
<button type="submit" form="form-delete" class="dropdown-item">
|
||||||
|
Supprimer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form action="{{ app.request.uri }}" method="post" id="form-main" enctype="multipart/form-data">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-pane active">
|
||||||
|
<div class="tab-form">
|
||||||
|
{{ include('site/navigation_admin/_form.html.twig') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ form_rest(form) }}
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form method="post" action="{{ path('admin_site_navigation_delete', {entity: entity.id}) }}" id="form-delete" data-form-confirm>
|
||||||
|
<input type="hidden" name="_method" value="DELETE">
|
||||||
|
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ entity.id) }}">
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
77
templates/site/navigation_admin/index.html.twig
Normal file
77
templates/site/navigation_admin/index.html.twig
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
{% extends 'admin/layout.html.twig' %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div class="bg-light pl-5 pr-4 pt-5 {% if pager.getPaginationData.pageCount < 2 %}pb-5{% endif %}">
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="mr-auto w-50">
|
||||||
|
<h1 class="display-5">Navigations</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ml-auto">
|
||||||
|
<div class="btn-group">
|
||||||
|
<a href="{{ path('admin_site_navigation_new') }}" class="btn btn-primary">
|
||||||
|
<span class="fa fa-plus pr-1"></span>
|
||||||
|
Nouveau
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ knp_pagination_render(pager) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="table" data-table-fixed>
|
||||||
|
<thead class="thead-light">
|
||||||
|
<tr>
|
||||||
|
<th class="col-5">Libellé</th>
|
||||||
|
<th class="col-5">Domaine</th>
|
||||||
|
<th class="col-2 text-right">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for item in pager %}
|
||||||
|
{% set edit = path('admin_site_navigation_edit', {entity: item.id}) %}
|
||||||
|
{% set show = path('admin_site_navigation_show', {entity: item.id}) %}
|
||||||
|
|
||||||
|
<tr data-dblclick="{{ edit }}">
|
||||||
|
<td class="col-6">
|
||||||
|
<a href="{{ show }}" class="font-weight-bold text-body d-block">
|
||||||
|
{{ item.label }}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{{ item.code }}
|
||||||
|
</td>
|
||||||
|
<td class="col-4">
|
||||||
|
<a href="" class="btn btn-sm btn-light">
|
||||||
|
{{ item.domain }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td class="col-2 text-right">
|
||||||
|
<a href="{{ edit }}" class="btn btn-sm btn-primary mr-1">
|
||||||
|
<span class="fa fa-edit"></span>
|
||||||
|
</a>
|
||||||
|
<button type="submit" form="form-delete-{{ item.id }}" class="btn btn-sm btn-danger">
|
||||||
|
<span class="fa fa-trash"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<form method="post" action="{{ path('admin_site_navigation_delete', {entity: item.id}) }}" id="form-delete-{{ item.id }}" data-form-confirm>
|
||||||
|
<input type="hidden" name="_method" value="DELETE">
|
||||||
|
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ item.id) }}">
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
<td class="col-12 text-center p-4 text-black-50">
|
||||||
|
<div class="display-1">
|
||||||
|
<span class="fa fa-search"></span>
|
||||||
|
</div>
|
||||||
|
<div class="display-5 mt-3">
|
||||||
|
Aucun résultat
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endblock %}
|
39
templates/site/navigation_admin/new.html.twig
Normal file
39
templates/site/navigation_admin/new.html.twig
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
{% extends '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">Nouvelle navigation</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ml-auto">
|
||||||
|
<div class="btn-group">
|
||||||
|
<a href="{{ path('admin_site_navigation_index') }}" class="btn btn-light">
|
||||||
|
<span class="fa fa-list pr-1"></span>
|
||||||
|
|
||||||
|
Retour à la liste
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<button type="submit" form="form-main" class="btn btn-primary">
|
||||||
|
<span class="fa fa-save pr-1"></span>
|
||||||
|
|
||||||
|
Enregistrer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form action="{{ app.request.uri }}" method="post" id="form-main" enctype="multipart/form-data">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-pane active">
|
||||||
|
<div class="tab-form">
|
||||||
|
{{ include('site/navigation_admin/_form.html.twig') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ form_rest(form) }}
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
48
templates/site/navigation_admin/show.html.twig
Normal file
48
templates/site/navigation_admin/show.html.twig
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
{% extends '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">{{ entity.label }}</h1>
|
||||||
|
</div>:
|
||||||
|
|
||||||
|
<div class="ml-auto">
|
||||||
|
<div class="btn-group">
|
||||||
|
<a href="{{ path('admin_site_navigation_index') }}" class="btn btn-secondary">
|
||||||
|
<span class="fa fa-list pr-1"></span>
|
||||||
|
|
||||||
|
Retour à la liste
|
||||||
|
</a>
|
||||||
|
<a href="{{ path('admin_site_navigation_edit', {entity: entity.id}) }}" class="btn btn-primary">
|
||||||
|
<span class="fa fa-edit pr-1"></span>
|
||||||
|
|
||||||
|
Éditer
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 p-3">
|
||||||
|
<ul class="list-group">
|
||||||
|
<li class="list-group-item">
|
||||||
|
<span class="font-weight-bold pb-2 d-block">Libellé</span>
|
||||||
|
|
||||||
|
{{ entity.label }}
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<span class="font-weight-bold pb-2 d-block">Code</span>
|
||||||
|
|
||||||
|
{{ entity.code }}
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<span class="font-weight-bold pb-2 d-block">Nom de domaine</span>
|
||||||
|
|
||||||
|
{{ entity.domain }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
19
templates/site/node_admin/move.html.twig
Normal file
19
templates/site/node_admin/move.html.twig
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Déplacement de « {{ entity.label }} »</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form action="{{ path('admin_site_node_move', {entity: entity.id}) }}" id="form-node-move" method="POST">
|
||||||
|
{{ form_widget(form) }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annuler</button>
|
||||||
|
<button type="submit" form="form-node-move" class="btn btn-primary">Enregistrer</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
19
templates/site/node_admin/new.html.twig
Normal file
19
templates/site/node_admin/new.html.twig
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Nouvel élément</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form action="{{ path('admin_site_node_new', {node: node.id}) }}" id="form-node-new" method="POST">
|
||||||
|
{{ form_widget(form) }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annuler</button>
|
||||||
|
<button type="submit" form="form-node-new" class="btn btn-primary">Enregistrer</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
177
templates/site/tree_admin/navigation.html.twig
Normal file
177
templates/site/tree_admin/navigation.html.twig
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
{% extends '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">
|
||||||
|
<button type="button" class="btn btn-outline-dark dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<span class="font-weight-bold">
|
||||||
|
{{ navigation.label }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
{% for item in navigations %}
|
||||||
|
<a href="{{ path('admin_site_tree_navigation', {navigation: item.id}) }}" class="dropdown-item">
|
||||||
|
{{ item.label }}
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ml-auto">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button class="btn btn-primary" data-toggle="modal" data-target="#modal-menu-new">
|
||||||
|
<span class="fa fa-plus pr-1"></span>
|
||||||
|
Ajouter un menu
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
{% for menu in navigation.menus %}
|
||||||
|
<div class="col-12 p-3 m-0">
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="mr-auto">
|
||||||
|
<div class="h4">
|
||||||
|
{{ menu.label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ml-auto">
|
||||||
|
<div class="btn-group">
|
||||||
|
<span class="btn btn-light">
|
||||||
|
{{ menu.code }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-secondary dropdown-toggle " data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
Actions
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-menu dropdown-menu-right">
|
||||||
|
<button type="submit" class="dropdown-item" data-toggle="modal" data-target="#modal-menu-edit-{{ menu.id }}">
|
||||||
|
Modifier
|
||||||
|
</button>
|
||||||
|
<button type="submit" form="form-menu-delete-{{ menu.id }}" class="dropdown-item">
|
||||||
|
Supprimer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 pl-3 mr-3 mb-4">
|
||||||
|
<div class="list-group">
|
||||||
|
{% set rootNode = menu.rootNode %}
|
||||||
|
|
||||||
|
{% if rootNode %}
|
||||||
|
{% for node in rootNode.allChildren %}
|
||||||
|
{% set move = path('admin_site_node_move', {entity: node.id}) %}
|
||||||
|
{% set new = path('admin_site_node_new', {node: node.id}) %}
|
||||||
|
<div class="list-group-item">
|
||||||
|
<div class="float-right">
|
||||||
|
<button data-modal="{{ new }}" type="submit" class="btn btn-sm btn-primary mr-1">
|
||||||
|
<span data-modal="{{ new }}" class="fa fa-plus"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button data-modal="{{ move }}" type="submit" class="btn btn-sm btn-dark mr-1">
|
||||||
|
<span data-modal="{{ move }}" class="fa fa-arrows-alt"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="submit" form="form-node-visibility-{{ node.id }}" class="btn btn-sm btn-light border-dark mr-1">
|
||||||
|
{% if node.isVisible %}
|
||||||
|
<span class="fa fa-eye"></span>
|
||||||
|
{% else %}
|
||||||
|
<span class="fa fa-eye-slash"></span>
|
||||||
|
{% endif %}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="submit" form="form-node-delete-{{ node.id }}" class="btn btn-sm btn-danger">
|
||||||
|
<span class="fa fa-trash"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d block" style="padding-left: {{ (node.treeLevel - 1) * 20 }}px">
|
||||||
|
{{ node.label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="post" action="{{ path('admin_site_node_toggle_visibility', {entity: node.id}) }}" id="form-node-visibility-{{ node.id }}">
|
||||||
|
<input type="hidden" name="_token" value="{{ csrf_token('toggle_visibility' ~ node.id) }}">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form method="post" action="{{ path('admin_site_node_delete', {entity: node.id}) }}" id="form-node-delete-{{ node.id }}" data-form-confirm>
|
||||||
|
<input type="hidden" name="_method" value="DELETE">
|
||||||
|
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ node.id) }}">
|
||||||
|
</form>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="col-12 text-center p-4 text-black-50">
|
||||||
|
<div class="display-1">
|
||||||
|
<span class="fa fa-search"></span>
|
||||||
|
</div>
|
||||||
|
<div class="display-5 mt-4">
|
||||||
|
<button class="btn btn-primary" data-toggle="modal" data-target="#modal-menu-new">
|
||||||
|
<span class="fa fa-plus pr-1"></span>
|
||||||
|
Ajouter un menu
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal" id="modal-menu-new">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Nouveau menu</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form action="{{ path('admin_site_menu_new', {navigation: navigation.id}) }}" id="form-menu-new" method="POST">
|
||||||
|
{{ form_widget(forms.menu) }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" form="form-menu-new" class="btn btn-secondary" data-dismiss="modal">Annuler</button>
|
||||||
|
<button type="submit" form="form-menu-new" class="btn btn-primary">Enregistrer</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% for menuId, form in forms.menus %}
|
||||||
|
<div class="modal" id="modal-menu-edit-{{ menuId }}">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">{{ form.vars.data.label }}</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form action="{{ path('admin_site_menu_edit', {entity: menuId}) }}" id="form-menu-edit-{{ menuId }}" method="POST">
|
||||||
|
{{ form_widget(form) }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" form="form-menu-edit-{{ menuId }}" class="btn btn-secondary" data-dismiss="modal">Annuler</button>
|
||||||
|
<button type="submit" form="form-menu-edit-{{ menuId }}" class="btn btn-primary">Enregistrer</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="post" action="{{ path('admin_site_menu_delete', {entity: menuId}) }}" id="form-menu-delete-{{ menuId }}" data-form-confirm>
|
||||||
|
<input type="hidden" name="_method" value="DELETE">
|
||||||
|
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ menuId) }}">
|
||||||
|
</form>
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
Loading…
Reference in a new issue