diff --git a/assets/img/blank.png b/assets/img/blank.png new file mode 100644 index 0000000..5e21c8a Binary files /dev/null and b/assets/img/blank.png differ diff --git a/assets/img/no-image.png b/assets/img/no-image.png new file mode 100644 index 0000000..7957221 Binary files /dev/null and b/assets/img/no-image.png differ diff --git a/composer.json b/composer.json index 950f40f..d98caa8 100644 --- a/composer.json +++ b/composer.json @@ -25,6 +25,7 @@ "symfony/dotenv": "5.2.*", "symfony/event-dispatcher": "5.2.*", "symfony/expression-language": "5.2.*", + "symfony/finder": "5.2.*", "symfony/flex": "^1.3.1", "symfony/form": "5.2.*", "symfony/framework-bundle": "5.2.*", diff --git a/public/uploads/post/2021/20210317-035213ovh_feu1.jpg b/public/uploads/post/2021/20210317-035213ovh_feu1.jpg new file mode 100644 index 0000000..0e4e0e8 Binary files /dev/null and b/public/uploads/post/2021/20210317-035213ovh_feu1.jpg differ diff --git a/src/Controller/Account/AccountAdminController.php b/src/Controller/Account/AccountAdminController.php index bc02a75..03b69b3 100644 --- a/src/Controller/Account/AccountAdminController.php +++ b/src/Controller/Account/AccountAdminController.php @@ -68,7 +68,7 @@ class AccountAdminController extends AdminController } else { $this->addFlash('success', 'Double authentification activée.'); - $entityManager->update($account)->flush()->clear(); + $entityManager->update($account); return $this->redirectToRoute('admin_account'); } @@ -78,7 +78,7 @@ class AccountAdminController extends AdminController if (!$enable && $account->isTotpAuthenticationEnabled()) { $account->setTotpSecret(null); - $entityManager->update($account)->flush()->clear(); + $entityManager->update($account); $this->addFlash('success', 'Double authentification désactivée.'); @@ -100,7 +100,8 @@ class AccountAdminController extends AdminController Request $request, UserRepository $repository, TokenGeneratorInterface $tokenGenerator, - UserPasswordEncoderInterface $encoder + UserPasswordEncoderInterface $encoder, + EntityManager $entityManager ): Response { $account = $this->getUser(); $csrfToken = $request->request->get('_csrf_token'); @@ -129,10 +130,7 @@ class AccountAdminController extends AdminController ->setConfirmationToken($tokenGenerator->generateToken()) ; - $entityManager = $this->getDoctrine()->getManager(); - $entityManager->persist($account); - $entityManager->flush(); - $entityManager->clear(); + $entityManager->update($account); $this->addFlash('success', 'Mot de passe modifié !'); diff --git a/src/Controller/Auth/AuthController.php b/src/Controller/Auth/AuthController.php index 8d98b91..ce4ec79 100644 --- a/src/Controller/Auth/AuthController.php +++ b/src/Controller/Auth/AuthController.php @@ -71,7 +71,7 @@ class AuthController extends AbstractController $account->setConfirmationToken($tokenGenerator->generateToken()); $account->setPasswordRequestedAt(new \DateTime('now')); - $entityManager->update($account)->flush()->clear(); + $entityManager->update($account); $eventDispatcher->dispatch(new PasswordRequestEvent($account), PasswordRequestEvent::EVENT); $emailSent = true; @@ -135,7 +135,7 @@ class AuthController extends AbstractController ->setPasswordRequestedAt(new \DateTime('now')) ; - $entityManager->update($account)->flush()->clear(); + $entityManager->update($account); $passwordUpdated = true; } diff --git a/src/Controller/Blog/CategoryAdminController.php b/src/Controller/Blog/CategoryAdminController.php index d9fc9bd..4d81499 100644 --- a/src/Controller/Blog/CategoryAdminController.php +++ b/src/Controller/Blog/CategoryAdminController.php @@ -42,7 +42,7 @@ class CategoryAdminController extends AdminController $form->handleRequest($request); if ($form->isValid()) { - $entityManager->create($entity)->flush()->clear(); + $entityManager->create($entity); $this->addFlash('success', 'Donnée enregistrée.'); return $this->redirectToRoute('admin_blog_category_edit', [ @@ -54,6 +54,7 @@ class CategoryAdminController extends AdminController return $this->render('blog/category_admin/new.html.twig', [ 'form' => $form->createView(), + 'entity' => $entity, ]); } @@ -68,7 +69,7 @@ class CategoryAdminController extends AdminController $form->handleRequest($request); if ($form->isValid()) { - $entityManager->update($entity)->flush()->clear(); + $entityManager->update($entity); $this->addFlash('success', 'Donnée enregistrée.'); return $this->redirectToRoute('admin_blog_category_edit', [ @@ -107,7 +108,7 @@ class CategoryAdminController extends AdminController public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response { if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) { - $entityManager->delete($entity)->flush()->clear(); + $entityManager->delete($entity); $this->addFlash('success', 'Données supprimées.'); } diff --git a/src/Controller/Blog/PostAdminController.php b/src/Controller/Blog/PostAdminController.php index c07e7bf..677be20 100644 --- a/src/Controller/Blog/PostAdminController.php +++ b/src/Controller/Blog/PostAdminController.php @@ -3,6 +3,14 @@ namespace App\Controller\Blog; use App\Controller\Admin\AdminController; +use App\Entity\Blog\Post as Entity; +use App\Factory\Blog\PostFactory as EntityFactory; +use App\Form\Blog\PostType as EntityType; +use App\Manager\EntityManager; +use App\Repository\Blog\PostRepositoryQuery; +use App\Repository\Blog\PostRepositoryQuery as RepositoryQuery; +use App\Form\FileUploadHandler; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; @@ -12,14 +20,104 @@ use Symfony\Component\Routing\Annotation\Route; class PostAdminController extends AdminController { /** - * @Route("/", name="admin_blog_post_index") + * @Route("/{page}", name="admin_blog_post_index", requirements={"page": "\d+"}) */ - public function index(): Response + public function index(int $page = 1, RepositoryQuery $query, Request $request): Response { + $pager = $query->paginate($page); + return $this->render('blog/post_admin/index.html.twig', [ + 'pager' => $pager, ]); } + /** + * @Route("/new", name="admin_blog_post_new") + */ + public function new(EntityFactory $factory, EntityManager $entityManager, Request $request): Response + { + $entity = $factory->create($this->getUser()); + $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_blog_post_edit', [ + 'entity' => $entity->getId(), + ]); + } + $this->addFlash('warning', 'Le formulaire est invalide.'); + } + + return $this->render('blog/post_admin/new.html.twig', [ + 'form' => $form->createView(), + 'entity' => $entity, + ]); + } + + /** + * @Route("/edit/{entity}", name="admin_blog_post_edit") + */ + public function edit(Entity $entity, EntityManager $entityManager, FileUploadHandler $fileUpload, Request $request): Response + { + $form = $this->createForm(EntityType::class, $entity); + + if ($request->isMethod('POST')) { + $form->handleRequest($request); + + if ($form->isValid()) { + $fileUpload->handleForm( + $form->get('image')->getData(), + 'uploads/post/'.date('Y'), + function ($filename) use ($entity) { + $entity->setImage('post/'.date('Y').'/'.$filename); + } + ); + + $entityManager->update($entity); + $this->addFlash('success', 'Donnée enregistrée.'); + + return $this->redirectToRoute('admin_blog_post_edit', [ + 'entity' => $entity->getId(), + ]); + } + $this->addFlash('warning', 'Le formulaire est invalide.'); + } + + return $this->render('blog/post_admin/edit.html.twig', [ + 'form' => $form->createView(), + 'entity' => $entity, + ]); + } + + /** + * @Route("/show/{entity}", name="admin_blog_post_show") + */ + public function show(Entity $entity, PostRepositoryQuery $postQuery): Response + { + return $this->render('blog/post_admin/show.html.twig', [ + 'entity' => $entity, + ]); + } + + /** + * @Route("/delete/{entity}", name="admin_blog_post_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ées.'); + } + + return $this->redirectToRoute('admin_blog_post_index'); + } + public function getSection(): string { return 'blog_post'; diff --git a/src/Entity/Blog/Category.php b/src/Entity/Blog/Category.php index 4324f31..a2ab1ff 100644 --- a/src/Entity/Blog/Category.php +++ b/src/Entity/Blog/Category.php @@ -10,6 +10,7 @@ use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity(repositoryClass=CategoryRepository::class) + * @ORM\HasLifecycleCallbacks */ class Category implements EntityInterface { diff --git a/src/Entity/Blog/Post.php b/src/Entity/Blog/Post.php index 27d463d..8daafd2 100644 --- a/src/Entity/Blog/Post.php +++ b/src/Entity/Blog/Post.php @@ -12,6 +12,7 @@ use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity(repositoryClass=PostRepository::class) + * @ORM\HasLifecycleCallbacks */ class Post implements EntityInterface { @@ -153,8 +154,12 @@ class Post implements EntityInterface return $this->image; } - public function setImage(?string $image): self + public function setImage(?string $image, bool $force = false): self { + if (false === $force && null === $image) { + return $this; + } + $this->image = $image; return $this; diff --git a/src/Entity/User.php b/src/Entity/User.php index 8d03289..1afabed 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -9,11 +9,11 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Scheb\TwoFactorBundle\Model\Google\TwoFactorInterface; -use Scheb\TwoFactorBundle\Model\Totp\TotpConfiguration; use Symfony\Component\Security\Core\User\UserInterface; /** * @ORM\Entity(repositoryClass=UserRepository::class) + * @ORM\HasLifecycleCallbacks * @ORM\Table(name="`user`") */ class User implements UserInterface, TwoFactorInterface, EntityInterface @@ -191,16 +191,6 @@ class User implements UserInterface, TwoFactorInterface, EntityInterface return null !== $this->getTotpSecret(); } - public function getTotpAuthenticationUsername(): string - { - return $this->getEmail(); - } - - public function getTotpAuthenticationConfiguration(): TotpConfigurationInterface - { - return new TotpConfiguration($this->totpSecret, TotpConfiguration::ALGORITHM_SHA1, 30, 6); - } - public function isGoogleAuthenticatorEnabled(): bool { return $this->isTotpAuthenticationEnabled(); diff --git a/src/EventSuscriber/BlogPostEventSubscriber.php b/src/EventSuscriber/BlogPostEventSubscriber.php new file mode 100644 index 0000000..297f433 --- /dev/null +++ b/src/EventSuscriber/BlogPostEventSubscriber.php @@ -0,0 +1,78 @@ + + */ +class BlogPostEventSubscriber implements EventSubscriberInterface +{ + protected Filesystem $filesystem; + protected PostRepositoryQuery $query; + + public function __construct(Filesystem $filesystem, PostRepositoryQuery $query) + { + $this->filesystem = $filesystem; + $this->query = $query; + } + + public static function getSubscribedEvents() + { + return [ + EntityManagerEvent::UPDATE_EVENT => 'onUpdate', + EntityManagerEvent::DELETE_EVENT => 'onDelete', + ]; + } + + public function support(EntityInterface $entity) + { + return $entity instanceof Post; + } + + public function onUpdate(EntityManagerEvent $event) + { + if (!$this->support($event->getEntity())) { + return; + } + + $this->removeOrphanUploads(); + } + + public function onDelete(EntityManagerEvent $event) + { + if (!$this->support($event->getEntity())) { + return; + } + + $this->removeOrphanUploads(); + } + + protected function removeOrphanUploads() + { + $finder = new Finder(); + $finder->files()->in('uploads/post'); + + foreach ($finder as $file) { + $image = str_replace('uploads/', '', $file->getPathname()); + + $post = $this->query->create() + ->where('.image = :image') + ->setParameter(':image', $image) + ->findOne(); + + if (null === $post) { + $this->filesystem->remove($file->getRealPath()); + } + } + } +} diff --git a/src/EventSuscriber/EntityManagerEventSubscriber.php b/src/EventSuscriber/EntityManagerEventSubscriber.php index 6044a30..ef819f1 100644 --- a/src/EventSuscriber/EntityManagerEventSubscriber.php +++ b/src/EventSuscriber/EntityManagerEventSubscriber.php @@ -6,7 +6,7 @@ use App\Event\EntityManager\EntityManagerEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** - * class EventListener. + * class EntityManagerEventSubscriber. * * @author Simon Vieille */ diff --git a/src/Factory/Blog/PostFactory.php b/src/Factory/Blog/PostFactory.php new file mode 100644 index 0000000..d1e31ca --- /dev/null +++ b/src/Factory/Blog/PostFactory.php @@ -0,0 +1,25 @@ + + */ +class PostFactory +{ + public function create(?User $author = null): Post + { + $entity = new Post(); + + $entity + ->setAuthor($author) + ->setStatus(0); + + return $entity; + } +} diff --git a/src/Form/Blog/PostType.php b/src/Form/Blog/PostType.php new file mode 100644 index 0000000..a788d7e --- /dev/null +++ b/src/Form/Blog/PostType.php @@ -0,0 +1,208 @@ +add( + 'title', + TextType::class, + [ + 'label' => 'Titre', + 'required' => true, + 'attr' => [ + ], + 'constraints' => [ + new NotBlank(), + ], + ] + ); + + $builder->add( + 'subTitle', + TextareaType::class, + [ + 'label' => 'Sous-titre', + 'required' => false, + 'attr' => [ + 'rows' => 5, + ], + 'constraints' => [ + ], + ] + ); + + $builder->add( + 'metaDescription', + TextType::class, + [ + 'label' => 'Meta description', + 'required' => false, + 'attr' => [ + ], + 'constraints' => [ + ], + ] + ); + + $builder->add( + 'content', + TextareaType::class, + [ + 'label' => 'Contenu', + 'required' => false, + 'attr' => [ + 'data-tinymce' => '', + 'rows' => 20, + ], + 'constraints' => [ + ], + ] + ); + + $builder->add( + 'slug', + TextType::class, + [ + 'label' => 'Slug', + 'required' => false, + 'help' => 'Laisser vide pour une génération automatique', + 'attr' => [ + ], + 'constraints' => [ + ], + ] + ); + + $builder->add( + 'categories', + EntityType::class, + [ + 'label' => 'Catégories', + 'class' => Category::class, + 'choice_label' => 'title', + 'required' => false, + 'multiple' => true, + 'attr' => [ + 'data-jschoice' => '', + ], + 'query_builder' => function (EntityRepository $repo) { + return $repo->createQueryBuilder('a') + ->orderBy('a.title', 'ASC') + ; + }, + 'constraints' => [ + ], + ] + ); + + $builder->add( + 'status', + ChoiceType::class, + [ + 'label' => 'Statut', + 'required' => true, + 'choices' => [ + 'Brouillon' => 0, + 'Publié' => 1, + ], + 'attr' => [ + ], + 'constraints' => [ + new NotBlank(), + ], + ] + ); + + $builder->add( + 'imageCaption', + TextType::class, + [ + 'label' => 'Titre de l\'image', + 'required' => false, + 'attr' => [ + ], + 'constraints' => [ + ], + ] + ); + + $builder->add( + 'publishedAt', + DateType::class, + [ + 'label' => 'Date de publication', + 'required' => false, + 'html5' => true, + 'widget' => 'single_text', + 'attr' => [ + ], + 'constraints' => [ + new Date(), + ], + ] + ); + + $builder->add( + 'author', + EntityType::class, + [ + 'label' => 'Auteur', + 'class' => User::class, + 'choice_label' => 'displayName', + 'required' => true, + 'attr' => [ + ], + 'query_builder' => function (EntityRepository $repo) { + return $repo->createQueryBuilder('u') + ->orderBy('u.displayName', 'ASC') + ; + }, + 'constraints' => [ + new NotBlank(), + ], + ] + ); + + $builder->add( + 'image', + FileType::class, + [ + 'label' => 'Image', + 'required' => false, + 'data_class' => null, + 'attr' => [ + ], + 'constraints' => [ + new Image(), + ], + ] + ); + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => Post::class, + ]); + } +} diff --git a/src/Form/FileUploadHandler.php b/src/Form/FileUploadHandler.php new file mode 100644 index 0000000..940ea28 --- /dev/null +++ b/src/Form/FileUploadHandler.php @@ -0,0 +1,28 @@ + + */ +class FileUploadHandler +{ + public function handleForm(?UploadedFile $uploadedFile, string $path, callable $afterUploadCallback): void + { + if (null === $uploadedFile) { + return; + } + + $originalFilename = pathinfo($uploadedFile->getClientOriginalName(), PATHINFO_FILENAME); + $safeFilename = transliterator_transliterate('Any-Latin; Latin-ASCII; [^A-Za-z0-9_] remove; Lower()', $originalFilename); + $filename = date('Ymd-his').$safeFilename.'.'.$uploadedFile->guessExtension(); + + $uploadedFile->move($path, $filename); + + $afterUploadCallback($filename); + } +} diff --git a/src/Manager/EntityManager.php b/src/Manager/EntityManager.php index a841dbd..8216aab 100644 --- a/src/Manager/EntityManager.php +++ b/src/Manager/EntityManager.php @@ -44,6 +44,8 @@ class EntityManager public function delete(EntityInterface $entity): self { $this->entityManager->remove($entity); + $this->flush(); + $this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::DELETE_EVENT); return $this; @@ -66,5 +68,6 @@ class EntityManager protected function persist(EntityInterface $entity) { $this->entityManager->persist($entity); + $this->flush(); } } diff --git a/templates/blog/category_admin/edit.html.twig b/templates/blog/category_admin/edit.html.twig index 503b8ea..a469cfa 100644 --- a/templates/blog/category_admin/edit.html.twig +++ b/templates/blog/category_admin/edit.html.twig @@ -3,8 +3,8 @@ {% block body %}
-
-

{{ entity.title }}

+
+

{{ entity.title }}

@@ -38,7 +38,7 @@
-
+
diff --git a/templates/blog/category_admin/index.html.twig b/templates/blog/category_admin/index.html.twig index 27b9c49..1f6b0a3 100644 --- a/templates/blog/category_admin/index.html.twig +++ b/templates/blog/category_admin/index.html.twig @@ -1,10 +1,10 @@ {% extends 'admin/layout.html.twig' %} {% block body %} -
+
-
-

Catégories

+
+

Catégories

@@ -35,7 +35,7 @@ - + {{ item.title }} diff --git a/templates/blog/category_admin/new.html.twig b/templates/blog/category_admin/new.html.twig index c8eb21b..682b25b 100644 --- a/templates/blog/category_admin/new.html.twig +++ b/templates/blog/category_admin/new.html.twig @@ -3,8 +3,8 @@ {% block body %}
-
-

Nouvelle catégorie

+
+

Nouvelle catégorie

@@ -25,7 +25,7 @@
- +
diff --git a/templates/blog/category_admin/show.html.twig b/templates/blog/category_admin/show.html.twig index c7e3779..c80ad38 100644 --- a/templates/blog/category_admin/show.html.twig +++ b/templates/blog/category_admin/show.html.twig @@ -3,8 +3,8 @@ {% block body %}
-
-

{{ entity.title }}

+
+

{{ entity.title }}

@@ -28,17 +28,17 @@
  • - Titre
    + Titre {{ entity.title }}
  • - Sous-titre
    + Sous-titre {{ entity.subTitle }}
  • - URL
    + URL {{ absolute_url('/' ~ entity.slug) }}
  • @@ -61,7 +61,9 @@ {% for item in posts %} - {{ item.post }} + + {{ item.title }} + {% else %} diff --git a/templates/blog/post_admin/_form.html.twig b/templates/blog/post_admin/_form.html.twig new file mode 100644 index 0000000..684bf52 --- /dev/null +++ b/templates/blog/post_admin/_form.html.twig @@ -0,0 +1,36 @@ +
    +
    +
    + {% for item in ['title', 'subTitle', 'categories', 'metaDescription', 'slug'] %} +
    + {{ form_row(form[item]) }} +
    + {% endfor %} +
    +
    +
    +
    + {% for item in ['content'] %} +
    + {{ form_row(form[item]) }} +
    + {% endfor %} +
    +
    +
    +
    + {% for item in ['image', 'imageCaption', 'status', 'publishedAt', 'author'] %} +
    + {{ form_row(form[item]) }} + + {% if item == 'image' %} + {% if entity.image %} + + {% endif %} + {% endif %} +
    + {% endfor %} +
    +
    +
    + diff --git a/templates/blog/post_admin/edit.html.twig b/templates/blog/post_admin/edit.html.twig new file mode 100644 index 0000000..d2d4d4d --- /dev/null +++ b/templates/blog/post_admin/edit.html.twig @@ -0,0 +1,57 @@ +{% extends 'admin/layout.html.twig' %} + +{% block body %} +
    +
    +
    +

    {{ entity.title }}

    +
    + +
    +
    + + + Retour à la liste + + + + Voir + + + + + + +
    +
    +
    +
    + + +
    +
    +
    + {{ include('blog/post_admin/_form.html.twig') }} +
    +
    +
    + + {{ form_rest(form) }} + + +
    + + +
    +{% endblock %} diff --git a/templates/blog/post_admin/index.html.twig b/templates/blog/post_admin/index.html.twig index 10f369e..23e0506 100644 --- a/templates/blog/post_admin/index.html.twig +++ b/templates/blog/post_admin/index.html.twig @@ -1,32 +1,23 @@ {% extends 'admin/layout.html.twig' %} {% block body %} -
    +
    -
    -

    Articles

    +
    +

    Articles

    - +
    -
    - -
    + {{ knp_pagination_render(pager) }}
    @@ -37,37 +28,76 @@ - - - {% for item in range(1, 20) %} - - + {% for item in pager %} + {% set edit = path('admin_blog_post_edit', {entity: item.id}) %} + {% set show = path('admin_blog_post_show', {entity: item.id}) %} - Titre de l'article {{ item }}
    - Dans Nom de la catégorie par Mark - - - - - - {% endfor %} + + + + + + + {% else %} + + + + {% endfor %}
    Statut Actions
    - + +
    - - - 27/03/2021 09:10 - - - - - - -
    + {% if item.image %} + {% set image = asset('uploads/' ~ item.image) %} + {% else %} + {% set image = asset('build/images/no-image.png') %} + {% endif %} + + + + + {{ item.title }} + + + {% set categories = [] %} + + {% for category in item.categories %} + {% set url = path('admin_blog_category_show', {entity: category.id}) %} + {% set categories = categories|merge(['' ~ category.title ~ '']) %} + {% endfor %} + + Dans {{ categories|join(', ')|raw }} par {{ item.author.displayName }} + + + + {{ item.updatedAt|date('d/m/Y H:i') }} + + + {% set map = { + 0: ['warning', 'Brouillon'], + 1: ['success', 'Publié'], + } %} + + + + + + + +
    + + +
    +
    +
    + +
    +
    + Aucun résultat +
    +
    {% endblock %} diff --git a/templates/blog/post_admin/new.html.twig b/templates/blog/post_admin/new.html.twig new file mode 100644 index 0000000..dd684d1 --- /dev/null +++ b/templates/blog/post_admin/new.html.twig @@ -0,0 +1,39 @@ +{% extends 'admin/layout.html.twig' %} + +{% block body %} +
    +
    +
    +

    Nouvel article

    +
    + +
    +
    + + + + Retour à la liste + + + +
    +
    +
    +
    + +
    +
    +
    +
    + {{ include('blog/post_admin/_form.html.twig') }} +
    +
    +
    + + {{ form_rest(form) }} +
    +{% endblock %} diff --git a/templates/blog/post_admin/show.html.twig b/templates/blog/post_admin/show.html.twig new file mode 100644 index 0000000..733e00f --- /dev/null +++ b/templates/blog/post_admin/show.html.twig @@ -0,0 +1,106 @@ +{% extends 'admin/layout.html.twig' %} + +{% block body %} +
    +
    +
    +

    {{ entity.title }}

    +
    + + +
    +
    + +
    +
    +
      +
    • + Titre + + {{ entity.title }} +
    • +
    • + Sous-titre + + {{ entity.subTitle }} +
    • +
    • + Catégories + + {% for category in entity.categories %} + {{ category.title }} + {% endfor %} +
    • +
    • + URL + + {{ absolute_url('/' ~ entity.slug) }} +
    • +
    • + Meta description + + {{ entity.metaDescription }} +
    • +
    +
    +
    +
    Contenu
    + + {{ entity.content|raw|nl2br }} +
    +
    +
      +
    • + Image + + {% if entity.image %} +
      + {{ entity.imageCaption }} + +
      + {{ entity.imageCaption }} +
      +
      + {% else %} + - + {% endif %} +
    • + +
    • + Statut + + {% if entity.status == 0 %} + Brouillon + {% else %} + Publié + {% endif %} +
    • +
    • + Date de publication + + {{ entity.publishedAt ? entity.publishedAt|date('d/m/Y H:i') : '-' }} +
    • +
    • + Auteur + + + {{ entity.author.displayName }} + +
    • +
    +
    +
    +{% endblock %}