✨ feat(tuto): Ajoute gestion des tutoriels avec vidéos et PDFs.
This commit is contained in:
32
migrations/Version20251001081120.php
Normal file
32
migrations/Version20251001081120.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20251001081120 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE esy_web_tuto ADD type VARCHAR(255) NOT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('ALTER TABLE esy_web_tuto DROP type');
|
||||
}
|
||||
}
|
||||
32
migrations/Version20251001081226.php
Normal file
32
migrations/Version20251001081226.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20251001081226 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE esy_web_tuto ADD type_file VARCHAR(255) NOT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('ALTER TABLE esy_web_tuto DROP type_file');
|
||||
}
|
||||
}
|
||||
@@ -16,49 +16,113 @@ use Vich\UploaderBundle\Templating\Helper\UploaderHelper;
|
||||
|
||||
class TutoController extends AbstractController
|
||||
{
|
||||
#[Route(path: '/artemis/esyweb/tuto/add', name: 'artemis_esyweb_tuto_add', methods: ['GET', 'POST'])]
|
||||
public function tutosAdd(Request $request,LoggerService $loggerService,EsyWebTutoRepository $esyWebTutoRepository,EntityManagerInterface $entityManager)
|
||||
{
|
||||
$tuto = new EsyWebTuto();
|
||||
$tuto->setPos($esyWebTutoRepository->count());
|
||||
$tuto->setUpdateAt(new \DateTimeImmutable());
|
||||
$form = $this->createForm(TutoType::class,$tuto);
|
||||
$form->handleRequest($request);
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
|
||||
$entityManager->persist($tuto);
|
||||
$entityManager->flush();
|
||||
|
||||
$loggerService->log("SUCCESS","Crée un tutoriel");
|
||||
$this->addFlash("success","Tutoriel crée un tutoriel");
|
||||
return $this->redirectToRoute('artemis_esyweb_tuto');
|
||||
}
|
||||
return $this->render('artemis/esyweb/tuto_add.twig',[
|
||||
'form' => $form->createView(),
|
||||
]);
|
||||
}
|
||||
#[Route(path: '/artemis/esyweb/tuto', name: 'artemis_esyweb_tuto', methods: ['GET', 'POST'])]
|
||||
public function tutos(KernelInterface $kernel,Request $request,UploaderHelper $uploaderHelper,LoggerService $loggerService,EsyWebTutoRepository $esyWebTutoRepository,EntityManagerInterface $entityManager)
|
||||
{
|
||||
|
||||
if($request->query->has('idDelete')) {
|
||||
$ndd = $esyWebTutoRepository->find($request->query->get('idDelete'));
|
||||
$entityManager->remove($ndd);
|
||||
$entityManager->flush();
|
||||
$loggerService->log("DELETE","Suppression d'un tutoriel");
|
||||
$this->addFlash("success","Tutoriel bien supprimer");
|
||||
return $this->redirectToRoute('artemis_esyweb_tuto');
|
||||
}
|
||||
|
||||
if($request->query->has('idView')) {
|
||||
$ndd = $esyWebTutoRepository->find($request->query->get('idView'));
|
||||
$path = $uploaderHelper->asset($ndd,'file');
|
||||
$response = new BinaryFileResponse($kernel->getProjectDir()."/public".$path);
|
||||
return $response;
|
||||
}
|
||||
$categories =[
|
||||
'dashboard',
|
||||
'custom_graphics',
|
||||
'custom_colors',
|
||||
'custom_colors_degrade',
|
||||
'custom_fonts',
|
||||
'custom_fonts_custom',
|
||||
'custom_fontSize',
|
||||
'custom_crops',
|
||||
'custom_shape',
|
||||
'custom_files',
|
||||
'navbar',
|
||||
'menu',
|
||||
'content_management',
|
||||
'footer',
|
||||
'internal_page',
|
||||
'modules_txtAdmin',
|
||||
'modules_slider',
|
||||
'modules_parallax',
|
||||
'modules_pushForward',
|
||||
'modules_bouton',
|
||||
'modules_title',
|
||||
'modules_event',
|
||||
'modules_news',
|
||||
'modules_partenair',
|
||||
'modules_faq',
|
||||
'modules_form',
|
||||
'modules_socialNetwork',
|
||||
'modules_maps',
|
||||
'modules_newsletter',
|
||||
'modules_counter',
|
||||
'modules_cover',
|
||||
'modules_notif',
|
||||
'modules_photo',
|
||||
'modules_photoLibrary',
|
||||
'modules_video',
|
||||
'modules_videoLibrary',
|
||||
'modules_cloud',
|
||||
'modules_pdfViewer',
|
||||
'modules_sound',
|
||||
'modules_trustpilot',
|
||||
'modules_securedHoliday',
|
||||
'modules_googleAdsense',
|
||||
'modules_iframe',
|
||||
'setting',
|
||||
'setting_link',
|
||||
'setting_legal',
|
||||
'setting_ext',
|
||||
'setting_langue',
|
||||
'setting_security',
|
||||
'customer',
|
||||
'administrator',
|
||||
'customer_group',
|
||||
'customer_settings',
|
||||
];
|
||||
|
||||
|
||||
|
||||
return $this->render('artemis/esyweb/tuto.twig',[
|
||||
'tutos' => $esyWebTutoRepository->findAll(),
|
||||
'categories' => $categories
|
||||
]);
|
||||
}
|
||||
#[Route(path: '/artemis/esyweb/tuto/{type}', name: 'artemis_esyweb_tuto_view', methods: ['GET', 'POST'])]
|
||||
public function tutoView(string $type,Request $request,EsyWebTutoRepository $esyWebTutoRepository,EntityManagerInterface $entityManager,LoggerService $loggerService)
|
||||
{
|
||||
$tutos = $esyWebTutoRepository->findBy(['type' => $type,'typeFile'=>'pdf'],['pos'=>'asc']);
|
||||
$videos = $esyWebTutoRepository->findBy(['type' => $type,'typeFile'=>'video'],['pos'=>'asc']);
|
||||
|
||||
|
||||
if($request->query->has('idDelete')) {
|
||||
$v = $esyWebTutoRepository->find($request->query->get('idDelete'));
|
||||
$entityManager->remove($v);
|
||||
$entityManager->flush();
|
||||
$loggerService->log('DELETE',"Suppression effectuée");
|
||||
$this->addFlash('success','Suppression effectuée');
|
||||
return $this->redirectToRoute('artemis_esyweb_tuto_view',['type'=>$type]);
|
||||
}
|
||||
|
||||
$tuto = new EsyWebTuto();
|
||||
$tuto->setType($type);
|
||||
$form = $this->createForm(TutoType::class,$tuto);
|
||||
$form->handleRequest($request);
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
if($tuto->getFile()->getMimeType() == "application/pdf"){
|
||||
$tuto->setTypeFile("pdf");
|
||||
$tuto->setPos($esyWebTutoRepository->count(['type' => $type,'typeFile'=>'pdf']));
|
||||
} else {
|
||||
$tuto->setTypeFile("video");
|
||||
$tuto->setPos($esyWebTutoRepository->count(['type' => $type,'typeFile'=>'video']));
|
||||
}
|
||||
$tuto->setUpdateAt(new \DateTimeImmutable());
|
||||
$entityManager->persist($tuto);
|
||||
$entityManager->flush();
|
||||
$loggerService->log('ADD','Ajout tutoriels');
|
||||
return $this->redirectToRoute('artemis_esyweb_tuto_view',['type'=>$type]);
|
||||
}
|
||||
return $this->render('artemis/esyweb/tuto_view.twig',[
|
||||
'tutoPdf' => $tutos,
|
||||
'tutoVideos' => $videos,
|
||||
'type' => $type,
|
||||
'form' => $form->createView()
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\Account;
|
||||
use App\Entity\AccountResetPasswordRequest;
|
||||
use App\Form\RequestPasswordConfirmType;
|
||||
use App\Form\RequestPasswordRequestType;
|
||||
use App\Service\Mailer\Mailer;
|
||||
use App\Service\ResetPassword\Event\ResetPasswordConfirmEvent;
|
||||
use App\Service\ResetPassword\Event\ResetPasswordEvent;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
use Twig\Environment;
|
||||
|
||||
class TutoController extends AbstractController
|
||||
{
|
||||
|
||||
#[Route(path: '/tuto',name: 'app_tuto',methods: ['GET', 'POST'])]
|
||||
public function tuto(): Response
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,12 @@ class EsyWebTuto
|
||||
#[ORM\Column]
|
||||
private ?int $pos = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $type = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $typeFile = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
@@ -180,4 +186,28 @@ class EsyWebTuto
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getType(): ?string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function setType(string $type): static
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTypeFile(): ?string
|
||||
{
|
||||
return $this->typeFile;
|
||||
}
|
||||
|
||||
public function setTypeFile(string $typeFile): static
|
||||
{
|
||||
$this->typeFile = $typeFile;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ class TutoType extends AbstractType
|
||||
'required' => true,
|
||||
])
|
||||
->add('file',FileType::class,[
|
||||
'label' => 'Fichier du tutoriel (mp4)',
|
||||
'label' => 'Fichier du tutoriel (mp4 pdf)',
|
||||
'attr' => [
|
||||
'accept' => 'video/mp4'
|
||||
'accept' => 'video/mp4, .pdf'
|
||||
]
|
||||
]);
|
||||
|
||||
|
||||
@@ -4,38 +4,20 @@
|
||||
{% block content %}
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-3xl font-semibold text-gray-800 dark:text-gray-200">Liste des tutoriels</h2>
|
||||
<div>
|
||||
<a href="{{ path('artemis_esyweb_tuto_add') }}" class="px-4 py-2 bg-blue-600 text-white font-medium rounded-md shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900">
|
||||
+ Crée un tutoriel
|
||||
</a>
|
||||
<a target="_blank" href="{{ path('app_tuto') }}" class="px-4 py-2 bg-blue-600 text-white font-medium rounded-md shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900">
|
||||
Voir la page des tutoriels
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 w-full mx-auto bg-gray-800 rounded-lg shadow-lg overflow-x-auto">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||
{% for categorie in categories %}
|
||||
<div class="p-6 bg-gray-800 rounded-2xl shadow-xl border border-gray-700">
|
||||
<div class="flex items-center space-x-4">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-white">{{ categorie|trans }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6 flex justify-end">
|
||||
<a href="{{ path('artemis_esyweb_tuto_view',{type:categorie}) }}" class="px-4 py-2 bg-indigo-600 text-white text-sm font-medium rounded-xl hover:bg-indigo-700">Voir les tutoriels</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<table class="min-w-full divide-y divide-gray-700">
|
||||
<thead class="bg-gray-700">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider">Titre</th>
|
||||
<th scope="col" class="px-6 py-3 text-center text-xs font-medium uppercase tracking-wider">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-700">
|
||||
{% for tuto in tutos %}
|
||||
<tr class="hover:bg-gray-700 transition relative hover:bg-gray-700 transition">
|
||||
<td class="px-6 py-4 text-center text-sm w-80">{{ tuto.title }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-center w-20">
|
||||
<a href="{{ path('artemis_esyweb_tuto',{idView:tuto.id}) }}" target="_blank"
|
||||
class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded mr-2">Voir</a>
|
||||
<a data-turbo="false" is="confirm-modal" type="delete-esyweb-tuto"
|
||||
href="{{ path('artemis_esyweb_tuto',{idDelete:tuto.id}) }}"
|
||||
class="bg-red-600 hover:bg-red-700 text-white px-3 py-1 rounded">Supprimer</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
56
templates/artemis/esyweb/tuto_view.twig
Normal file
56
templates/artemis/esyweb/tuto_view.twig
Normal file
@@ -0,0 +1,56 @@
|
||||
{% extends 'artemis/base.twig' %}
|
||||
{% block title %}Tutoriel(s){% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="flex flex-col md:flex-row gap-8">
|
||||
<div class="md:w-1/2">
|
||||
<h2 class="text-xl font-semibold mb-4">Vidéos</h2>
|
||||
<ul class="divide-y divide-gray-300 dark:divide-gray-700 rounded border border-gray-300 dark:border-gray-700">
|
||||
{% for video in tutoVideos %}
|
||||
<li class="flex items-center justify-between px-4 py-3 hover:bg-gray-200 dark:hover:bg-gray-700 transition">
|
||||
<a class="text-blue-500 hover:underline truncate" href="{{ vich_uploader_asset(video,'file') }}" target="_blank" title="{{ video.title }}">
|
||||
{{ video.title }}
|
||||
</a>
|
||||
<form data-turbo="false" method="post" action="{{ path('artemis_esyweb_tuto_view', {'idDelete': video.id,type:type}) }}" onsubmit="return confirm('Confirmer la suppression ?');">
|
||||
<button type="submit" class="ml-4 px-3 py-1 text-white bg-red-600 hover:bg-red-700 rounded text-sm font-semibold transition">
|
||||
Supprimer
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="px-4 py-3 text-gray-500">Aucune vidéo disponible.</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="md:w-1/2">
|
||||
<h2 class="text-xl font-semibold mb-4">PDFs</h2>
|
||||
<ul class="divide-y divide-gray-300 dark:divide-gray-700 rounded border border-gray-300 dark:border-gray-700">
|
||||
{% for pdf in tutoPdf %}
|
||||
<li class="flex items-center justify-between px-4 py-3 hover:bg-gray-200 dark:hover:bg-gray-700 transition">
|
||||
<a class="text-blue-500 hover:underline truncate" href="{{ vich_uploader_asset(pdf,'file') }}" target="_blank" title="{{ pdf.title }}">
|
||||
{{ pdf.title }}
|
||||
</a>
|
||||
<form data-turbo="false" method="post" action="{{ path('artemis_esyweb_tuto_view', {'idDelete': pdf.id,type:type}) }}" onsubmit="return confirm('Confirmer la suppression ?');">
|
||||
<button type="submit" class="ml-4 px-3 py-1 text-white bg-red-600 hover:bg-red-700 rounded text-sm font-semibold transition">
|
||||
Supprimer
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="px-4 py-3 text-gray-500">Aucun PDF disponible.</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-10 p-6 bg-gray-100 dark:bg-gray-800 rounded-lg shadow w-full">
|
||||
{{ form_start(form) }}
|
||||
{{ form_row(form.title) }}
|
||||
{{ form_row(form.file) }}
|
||||
<button type="submit"
|
||||
class="px-6 py-2 bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 text-white rounded transition font-semibold mt-2">
|
||||
Upload
|
||||
</button>
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user