Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ parameters:
site_articles:
nom: 'Articles'
niveau: 'ROLE_SITE'
url : '/admin/site/articles'
extra_routes:
- admin_site_articles_list
- admin_site_articles_add
- admin_site_articles_edit
forum:
nom: 'Évènements'
niveau: 'ROLE_FORUM'
Expand Down
20 changes: 20 additions & 0 deletions app/config/routing/admin_site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,23 @@ admin_site_rubriques_delete:
defaults: {_controller: AppBundle\Controller\Admin\Site\DeleteRubriqueAction}
requirements:
id: '\d+'

admin_site_articles_list:
path: /articles
defaults: {_controller: AppBundle\Controller\Admin\Site\Article\ListArticlesAction}

admin_site_articles_add:
path: /articles/add
defaults: {_controller: AppBundle\Controller\Admin\Site\Article\AddArticleAction}

admin_site_articles_edit:
path: /articles/edit/{id}
defaults: {_controller: AppBundle\Controller\Admin\Site\Article\EditArticleAction}
requirements:
id: '\d+'

admin_site_articles_delete:
path: /articles/delete/{id}/{token}
defaults: {_controller: AppBundle\Controller\Admin\Site\Article\DeleteArticleAction}
requirements:
id: '\d+'
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace AppBundle\Controller\Admin\Site\Article;

use Afup\Site\Logger\DbLoggerTrait;
use AppBundle\Site\Form\ArticleType;
use AppBundle\Site\Model\Article;
use AppBundle\Site\Model\Repository\ArticleRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

final class AddArticleAction extends AbstractController
{
use DbLoggerTrait;

public function __construct(
private readonly ArticleRepository $articleRepository,
) {}

public function __invoke(Request $request): Response
{
$article = new Article();
$form = $this->createForm(ArticleType::class, $article);

$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
$this->articleRepository->save($article);
$this->log('Ajout de l\'article ' . $article->getTitle());
$this->addFlash('notice', 'L\'article ' . $article->getTitle() . ' a été ajouté');
return $this->redirectToRoute('admin_site_articles_list', [
'filter' => $article->getTitle(),
]);
}

return $this->render('admin/site/article_form.html.twig', [
'form' => $form->createView(),
'formTitle' => 'Ajouter un article',
'submitLabel' => 'Ajouter',
'article' => $article,
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace AppBundle\Controller\Admin\Site\Article;

use Afup\Site\Logger\DbLoggerTrait;
use AppBundle\Site\Model\Repository\ArticleRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;

class DeleteArticleAction extends AbstractController
{
use DbLoggerTrait;

public function __construct(
private ArticleRepository $articleRepository,
private CsrfTokenManagerInterface $csrfTokenManager,
) {}

public function __invoke(int $id, string $token): RedirectResponse
{
if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken('article_delete', $token))) {
$this->addFlash('error', 'Token invalide');
return $this->redirectToRoute('admin_site_articles_list');
}
$article = $this->articleRepository->get($id);
$name = $article->getTitle();
$this->articleRepository->delete($article);
$this->log('Suppression de l\'article ' . $name);
$this->addFlash('notice', 'L\'article ' . $name . ' a été supprimé');
return $this->redirectToRoute('admin_site_articles_list');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace AppBundle\Controller\Admin\Site\Article;

use Afup\Site\Logger\DbLoggerTrait;
use AppBundle\Site\Form\ArticleType;
use AppBundle\Site\Model\Repository\ArticleRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

final class EditArticleAction extends AbstractController
{
use DbLoggerTrait;

public function __construct(
private readonly ArticleRepository $articleRepository,
) {}

public function __invoke(int $id, Request $request): Response
{
$article = $this->articleRepository->get($id);
$form = $this->createForm(ArticleType::class, $article);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->articleRepository->save($article);
$this->log('Modification de l\'article ' . $article->getTitle());
$this->addFlash('notice', 'L\'article ' . $article->getTitle() . ' a été modifié');
return $this->redirectToRoute('admin_site_articles_list', [
'filter' => $article->getTitle(),
]);
}

return $this->render('admin/site/article_form.html.twig', [
'form' => $form->createView(),
'article' => $article,
'formTitle' => 'Modifier un article',
'submitLabel' => 'Modifier',
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace AppBundle\Controller\Admin\Site\Article;

use AppBundle\Site\Model\Repository\ArticleRepository;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Twig\Environment;

class ListArticlesAction
{
public function __construct(
private readonly ArticleRepository $articleRepository,
private readonly Environment $twig,
) {}

public function __invoke(Request $request): Response
{
$fields = ['date', 'titre', 'etat'];

$sort = $request->query->get('sort', 'date');
if (in_array($sort, $fields) === false) {
$sort = 'date';
}
$direction = $request->query->get('direction', 'desc');
$filter = $request->query->get('filter', '');
$articles = $this->articleRepository->getAllArticlesWithCategoryAndTheme($sort, $direction, $filter);

return new Response($this->twig->render('admin/site/article_list.html.twig', [
'articles' => $articles,
'filter' => $filter,
'sort' => $sort,
'direction' => $direction,
]));
}
}
189 changes: 189 additions & 0 deletions sources/AppBundle/Site/Form/ArticleType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<?php

declare(strict_types=1);

namespace AppBundle\Site\Form;

use Afup\Site\Corporate\Article;
use AppBundle\Association\Model\Repository\UserRepository;
use AppBundle\Event\Model\Repository\EventRepository;
use AppBundle\Site\Model\Repository\RubriqueRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints as Assert;

class ArticleType extends AbstractType
{
public const POSITIONS_RUBRIQUES = 9;

public function __construct(
private readonly RubriqueRepository $rubriqueRepository,
private readonly UserRepository $userRepository,
private readonly EventRepository $eventRepository,
) {}

public function buildForm(FormBuilderInterface $builder, array $options): void
{
$users = [];
foreach ($this->userRepository->getAll() as $user) {
$users[$user->getFirstName() . ' ' . $user->getLastName()] = $user->getId();
}
$positions = [];
for ($i = self::POSITIONS_RUBRIQUES ; $i >= -(self::POSITIONS_RUBRIQUES); $i--) {
$positions[$i] = $i;
}
$rubriques = [];
foreach ($this->rubriqueRepository->getAll() as $rubrique) {
$rubriques[$rubrique->getNom()] = $rubrique->getId();
}

$events = [];
foreach ($this->eventRepository->getAll() as $event) {
$events[$event->getTitle()] = $event->getId();
}

/** @var \AppBundle\Site\Model\Article|null $article */
$article = $builder->getData();
$textareaCssClass = 'simplemde';
if ($article !== null && $article->usesMarkdown() === false) {
$textareaCssClass = 'tinymce';
}

$builder
->add('title', TextType::class, [
'label' => 'Titre de l\'article',
'required' => true,
'attr' => [
'maxlength' => 255,
'size' => 60,
],
'constraints' => [
new Assert\Length(['max' => 255]),
new Assert\NotBlank(),
new Assert\Type('string'),
],
])
->add('leadParagraph', TextareaType::class, [
'label' => 'Chapeau',
'required' => false,
'attr' => [
'maxlength' => 255,
'cols' => 42,
'rows' => 10,
'class' => $textareaCssClass,
],
'constraints' => [
new Assert\Length(['max' => 255]),
new Assert\Type('string'),
],
])
->add('content', TextareaType::class, [
'label' => 'Contenu',
'required' => false,
'attr' => [
'cols' => 42,
'rows' => 20,
'class' => $textareaCssClass,
],
'constraints' => [
new Assert\NotBlank(),
new Assert\Type('string'),
],
])
->add('path', TextType::class, [
'required' => true,
'label' => 'Raccourci',
'attr' => [
'maxlength' => 255,
'size' => 60,
],
'constraints' => [
new Assert\Length(['max' => 255]),
new Assert\NotBlank(),
new Assert\Type('string'),
new Assert\Regex('/(\s)/', 'Ne doit pas contenir d\'espaces', null, false),
],
])
->add('contentType', HiddenType::class, [
'required' => true,
])
->add('rubricId', ChoiceType::class, [
'required' => false,
'label' => 'Rubrique',
'choices' => $rubriques,
'constraints' => [
new Assert\Type("integer"),
],
])
->add('authorId', ChoiceType::class, [
'required' => false,
'label' => 'Auteur',
'choices' => $users,
'constraints' => [
new Assert\Type("integer"),
],
])
->add('publishedAt', DateTimeType::class, [
'required' => false,
'html5' => true,
'label' => 'Date',
'input' => 'timestamp',
'widget' => 'single_text',
// 'format' => 'yyyy-MM-dd HH:ii:ss',
'years' => range(2001, date('Y')),
'attr' => [
'style' => 'display: flex;',
],
'constraints' => [
new Assert\Type("datetime"),
],
])
->add('position', ChoiceType::class, [
'required' => false,
'label' => 'Position',
'choices' => $positions,
'translation_domain' => false,
'placeholder' => false,
'constraints' => [
new Assert\Type("integer"),
],
])
->add('state', ChoiceType::class, [
'label' => 'Etat',
'required' => false,
'choices' => [
'Hors ligne' => -1,
'En attente' => 0,
'En ligne' => 1,
],
'placeholder' => false,
'constraints' => [
new Assert\Type("integer"),
],
])
->add('theme', ChoiceType::class, [
'label' => 'Thème',
'required' => false,
'choices' => array_flip(Article::getThemesLabels()),
'constraints' => [
new Assert\Type("integer"),
],
])
->add('eventId', ChoiceType::class, [
'label' => 'Evênement',
'required' => false,
'choices' => $events,
'constraints' => [
new Assert\Type("integer"),
],
])
;
$builder->get('publishedAt')->addModelTransformer(new DateTimeToTimestampTransformer());
}
}
Loading