<?php
namespace App\Controller\Website;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\File\UploadedFile;
final class ClassicHomeController extends AbstractController
{
private function isAllowedCmsImageUpload(UploadedFile $file): bool
{
$allowedMimes = ['image/png', 'image/jpeg', 'image/webp'];
$allowedExts = ['png', 'jpg', 'jpeg', 'webp'];
$mimeCandidates = array_filter([
$file->getClientMimeType(),
]);
foreach ($mimeCandidates as $mime) {
if (in_array(strtolower((string) $mime), $allowedMimes, true)) {
return true;
}
}
$extCandidates = array_filter([
$file->getClientOriginalExtension(),
]);
foreach ($extCandidates as $ext) {
if (in_array(strtolower((string) $ext), $allowedExts, true)) {
return true;
}
}
return false;
}
private function safeUploadExtension(UploadedFile $file, string $default = 'bin'): string
{
$allowedExts = ['png', 'jpg', 'jpeg', 'webp'];
$clientExt = strtolower((string) $file->getClientOriginalExtension());
if (in_array($clientExt, $allowedExts, true)) {
return $clientExt;
}
return $default;
}
private function latestByPrefix(string $prefix, string $fallback): string
{
$publicDir = $this->getParameter('kernel.project_dir') . '/public';
$dir = $publicDir . '/uploads/cms/misc';
if (!is_dir($dir)) {
return $fallback;
}
$files = glob($dir . '/' . $prefix . '*.{png,jpg,jpeg,webp}', GLOB_BRACE);
if (!$files) {
return $fallback;
}
usort($files, static fn($a, $b) => filemtime($b) <=> filemtime($a));
$abs = $files[0];
$rel = ltrim(str_replace($publicDir, '', $abs), '/');
return $rel ?: $fallback;
}
private function publicAssetExists(string $path): bool
{
$path = trim($path);
if ($path === '') {
return false;
}
if (preg_match('#^https?://#i', $path)) {
return true;
}
$parsedPath = parse_url($path, PHP_URL_PATH);
if (!is_string($parsedPath) || $parsedPath === '') {
return false;
}
$relative = ltrim($parsedPath, '/');
if ($relative === '') {
return false;
}
return is_file($this->getParameter('kernel.project_dir') . '/public/' . $relative);
}
private function resolveKpiImage(string $key, string $prefix, string $fallback): string
{
$stored = $this->readCms($key, '');
if ($this->publicAssetExists($stored)) {
return ltrim((string) parse_url($stored, PHP_URL_PATH), '/');
}
$latest = $this->latestByPrefix($prefix, '');
if ($this->publicAssetExists($latest)) {
return ltrim((string) parse_url($latest, PHP_URL_PATH), '/');
}
return $fallback;
}
private function latestHeroBg(string $key, string $prefix, string $fallback): string
{
$stored = $this->readCms($key, '');
if ($this->publicAssetExists($stored)) {
return ltrim((string) parse_url($stored, PHP_URL_PATH), '/');
}
$publicDir = $this->getParameter('kernel.project_dir') . '/public';
$dir = $publicDir . '/uploads/cms/misc';
if (!is_dir($dir)) {
return $fallback;
}
$pattern = $dir . '/' . $prefix . '*.{png,jpg,jpeg,webp}';
$files = glob($pattern, GLOB_BRACE);
if (!$files) {
return $fallback;
}
usort($files, static fn($a, $b) => filemtime($b) <=> filemtime($a));
$abs = $files[0];
$rel = ltrim(str_replace($publicDir, '', $abs), '/');
return $rel ?: $fallback;
}
private function readCms(string $key, string $default = ''): string
{
$base = $this->getParameter('kernel.project_dir') . '/public/cms';
$file = $base . '/' . $key . '.txt';
if (is_file($file) && is_readable($file)) {
$c = @file_get_contents($file);
$c = $c === false ? '' : trim($c);
if ($c !== '') {
return $c;
}
}
return $default;
}
private function writeCms(string $key, string $text): bool
{
$base = $this->getParameter('kernel.project_dir') . '/public/cms';
if (!is_dir($base) && !@mkdir($base, 0775, true) && !is_dir($base)) {
return false;
}
$file = $base . '/' . $key . '.txt';
$ok = @file_put_contents($file, $text) !== false;
if ($ok) { @chmod($file, 0664); }
return $ok;
}
#[Route(path: '/', name: 'homepage', methods: ['GET'])]
public function index(): Response
{
// ------------------ Defaults ------------------
$defaultCatalogueText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.';
// ------------------ Read dynamic texts ------------------
$catalogue_text_left = $this->readCms('catalogue_text_left', $defaultCatalogueText);
$text_action_formation_card1 = $this->readCms('text_action_formation_card1', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.');
$text_action_formation_card2 = $this->readCms('text_action_formation_card2', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.');
$text_action_formation_card3 = $this->readCms('text_action_formation_card3', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.');
$text_actualite_du_moment_p1 = $this->readCms('text_actualite_du_moment_p1', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.');
// ------------------ HERO------------------
$defaultRibbonText = 'JEUDI 5 ET VENDREDI 6 DÉCEMBRE 2024 · HÔTEL ARAWAK · GOSIER, GUADELOUPE';
//dd($this->readCms('hero_slide1_link', '#'));
$hero = [
'slide1' => [
'bg' => $this->latestHeroBg('hero_slide1_bg', 'hero1-', 'default'),
'kicker' => $this->readCms('hero_slide1_kicker', 'Colloque professionnel'),
'title' => $this->readCms('hero_slide1_title', "Prendre en compte les besoins du patient<br/>ou de la personne accompagnée"),
'link' => $this->readCms('hero_slide1_link', '#'),
'cta' => $this->readCms('hero_slide1_cta', "Je m'inscris"),
'ribbon' => $this->readCms('hero_slide1_ribbon', $defaultRibbonText),
],
'slide2' => [
'bg' => $this->latestHeroBg('hero_slide2_bg', 'hero2-', 'default'),
'kicker' => $this->readCms('hero_slide2_kicker', 'Nouvelles sessions'),
'title' => $this->readCms('hero_slide2_title', "Perfectionnez vos compétences<br/>avec nos modules 2025"),
'cta' => $this->readCms('hero_slide2_cta', "Je m'inscris"),
'link' => $this->readCms('hero_slide2_link', '#'),
'ribbon' => $this->readCms('hero_slide2_ribbon', $defaultRibbonText),
],
'slide3' => [
'bg' => $this->latestHeroBg('hero_slide3_bg', 'hero3-', 'default'),
'kicker' => $this->readCms('hero_slide3_kicker', 'Nouvelles sessions'),
'title' => $this->readCms('hero_slide3_title', "Perfectionnez vos compétences<br/>avec nos modules 2025"),
'cta' => $this->readCms('hero_slide3_cta', "Je m'inscris"),
'ribbon' => $this->readCms('hero_slide3_ribbon', $defaultRibbonText),
'link' => $this->readCms('hero_slide3_link', '#'),
],
'slide4' => [
'bg' => $this->latestHeroBg('hero_slide4_bg', 'hero4-', 'default'),
'kicker' => $this->readCms('hero_slide4_kicker', 'Nouvelles sessions'),
'title' => $this->readCms('hero_slide4_title', "Perfectionnez vos compétences<br/>avec nos modules 2025"),
'cta' => $this->readCms('hero_slide4_cta', "Je m'inscris"),
'ribbon' => $this->readCms('hero_slide4_ribbon', $defaultRibbonText),
'link' => $this->readCms('hero_slide4_link', '#'),
],
'slide5' => [
'bg' => $this->latestHeroBg('hero_slide5_bg', 'hero5-', 'default'),
'kicker' => $this->readCms('hero_slide5_kicker', 'Nouvelles sessions'),
'title' => $this->readCms('hero_slide5_title', "Perfectionnez vos compétences<br/>avec nos modules 2025"),
'cta' => $this->readCms('hero_slide5_cta', "Je m'inscris"),
'ribbon' => $this->readCms('hero_slide5_ribbon', $defaultRibbonText),
'link' => $this->readCms('hero_slide5_link', '#'),
],
'slide6' => [
'bg' => $this->latestHeroBg('hero_slide6_bg', 'hero6-', 'default'),
'kicker' => $this->readCms('hero_slide6_kicker', 'Nouvelles sessions'),
'title' => $this->readCms('hero_slide6_title', "Perfectionnez vos compétences<br/>avec nos modules 2025"),
'cta' => $this->readCms('hero_slide6_cta', "Je m'inscris"),
'ribbon' => $this->readCms('hero_slide6_ribbon', $defaultRibbonText),
'link' => $this->readCms('hero_slide6_link', '#'),
],
'slide7' => [
'bg' => $this->latestHeroBg('hero_slide7_bg', 'hero7-', 'default'),
'kicker' => $this->readCms('hero_slide7_kicker', 'Nouvelles sessions'),
'title' => $this->readCms('hero_slide7_title', "Perfectionnez vos compétences<br/>avec nos modules 2025"),
'cta' => $this->readCms('hero_slide7_cta', "Je m'inscris"),
'ribbon' => $this->readCms('hero_slide7_ribbon', $defaultRibbonText),
'link' => $this->readCms('hero_slide7_link', '#'),
],
'slide8' => [
'bg' => $this->latestHeroBg('hero_slide8_bg', 'hero8-', 'default'),
'kicker' => $this->readCms('hero_slide8_kicker', 'Nouvelles sessions'),
'title' => $this->readCms('hero_slide8_title', "Perfectionnez vos compétences<br/>avec nos modules 2025"),
'cta' => $this->readCms('hero_slide8_cta', "Je m'inscris"),
'ribbon' => $this->readCms('hero_slide8_ribbon', $defaultRibbonText),
'link' => $this->readCms('hero_slide8_link', '#'),
],
'slide9' => [
'bg' => $this->latestHeroBg('hero_slide9_bg', 'hero9-', 'default'),
'kicker' => $this->readCms('hero_slide9_kicker', 'Nouvelles sessions'),
'title' => $this->readCms('hero_slide9_title', "Perfectionnez vos compétences<br/>avec nos modules 2025"),
'cta' => $this->readCms('hero_slide9_cta', "Je m'inscris"),
'ribbon' => $this->readCms('hero_slide9_ribbon', $defaultRibbonText),
'link' => $this->readCms('hero_slide9_link', '#'),
],
'slide10' => [
'bg' => $this->latestHeroBg('hero_slide10_bg', 'hero10-', 'default'),
'kicker' => $this->readCms('hero_slide10_kicker', 'Nouvelles sessions'),
'title' => $this->readCms('hero_slide10_title', "Perfectionnez vos compétences<br/>avec nos modules 2025"),
'cta' => $this->readCms('hero_slide10_cta', "Je m'inscris"),
'ribbon' => $this->readCms('hero_slide10_ribbon', $defaultRibbonText),
'link' => $this->readCms('hero_slide10_link', '#'),
],
];
// ------------------ CATALOGUE ------------------
$catalogue = [
'title' => $this->readCms('catalogue_title', 'DÉCOUVREZ NOTRE NOUVEAU CATALOGUE DE CONSEIL ET DE FORMATION 2025'),
'text' => $catalogue_text_left,
'btn_more' => $this->readCms('catalogue_btn_more', 'Voir plus'),
'btn_cta' => $this->readCms('catalogue_btn_cta', "Je m'inscris"),
'image' => 'assets/img/pexels-thirdman-5684445.png',
'vtitle' => $this->readCms('catalogue_vtitle', 'CATALOGUE'),
'vsubtitle' => $this->readCms('catalogue_vsubtitle', 'DE CONSEIL ET DE CONSULTATION'),
'year' => $this->readCms('catalogue_year', '2025'),
'badge' => $this->readCms('catalogue_badge', 'VOTRE PARTENAIRE CONSEIL ET PRESTATION EN<br>GUADELOUPE, MARTINIQUE & GUYANE'),
];
// ------------------ COMPÉTENCES ------------------
$competences = [
'title' => $this->readCms('competences_title', 'Nos domaines de compétences'),
'explorer' => $this->readCms('competences_cta', 'Explorer'),
'items' => [
['label' => 'Productivité et éfficacité professionnelle', 'icon' => 'assets/img/icons/stonks.png'],
['label' => 'Élaboration et conduite de projet', 'icon' => 'assets/img/icons/eos-icons_content-lifecycle-management.png'],
['label' => 'Équipe de cadres et managers', 'icon' => 'assets/img/icons/ri_team-fill.png'],
['label' => 'Santé au travail', 'icon' => 'assets/img/icons/mage_heart-health-fill.png'],
['label' => 'Gestion globale des risques, des crises & développement durable', 'icon' => 'assets/img/icons/graph.png'],
['label' => 'Management des ressources humaines', 'icon' => 'assets/img/icons/carbon_id-management.png'],
['label' => "Mobilisation & cohésion d'équipes", 'icon' => 'assets/img/icons/hugeicons_agreement-01.png'],
['label' => 'Organisation sociale & médico-sociale', 'icon' => 'assets/img/icons/ion_share-social-sharp.png'],
['label' => 'Évolution et transition professionnelle', 'icon' => 'assets/img/icons/ph_plant-bold.png'],
],
];
// ------------------ PRESTATIONS (3 cards) ------------------
$prestations = [
[
'img' => $this->readCms('presta_card1_img', 'assets/img/pexels-thirdman-5684445.png'),
'title' => $this->readCms('presta_card1_title', 'Action de formation inter'),
'text' => $text_action_formation_card1,
'cta' => $this->readCms('presta_card1_cta', 'Voir plus'),
'tone' => 'tone-a',
],
[
'img' => $this->readCms('presta_card2_img', 'assets/img/pexels-kindelmedia-7979405.png'),
'title' => $this->readCms('presta_card2_title', 'Action de formation intra'),
'text' => $text_action_formation_card2,
'cta' => $this->readCms('presta_card2_cta', 'Voir plus'),
'tone' => 'tone-b',
],
[
'img' => $this->readCms('presta_card3_img', 'assets/img/pexels-kindelmedia-7979435.png'),
'title' => $this->readCms('presta_card3_title', 'Conseil & accompagnement'),
'text' => $text_action_formation_card3,
'cta' => $this->readCms('presta_card3_cta', 'Voir plus'),
'tone' => 'tone-c',
],
];
// ------------------ ACTU — TEXTS + IMAGES ------------------
$actu = [
'kicker' => $this->readCms('actu_kicker', 'Actualités du moment'),
'title' => $this->readCms('actu_title', 'Titre de l’actualité'),
'p1' => $this->readCms('text_actualite_du_moment_p1', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'),
'p2' => $this->readCms('text_actualite_du_moment_p2', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'),
'cta' => $this->readCms('actu_cta', 'Découvrir'),
'pill1' => $this->readCms('actu_pill1_label', 'Mieux Gérer'),
'bg_img' => $this->readCms('actu_bg_img', 'assets/img/pexels-shkrabaanthony-5816297.png'),
'pill1_img' => $this->readCms('actu_pill1_img', 'assets/img/pexels-thirdman-5684445.png'),
'pill2_img' => $this->readCms('actu_pill2_img', 'assets/img/pexels-kindelmedia-7979405.png'),
'pill3_img' => $this->readCms('actu_pill3_img', 'assets/img/img conseiller.png'),
];
// ------------------ TRUST (logos) ------------------
$trust = [
'kicker' => $this->readCms('trust_kicker', 'ILS NOUS FONT CONFIANCE !'),
'sub' => $this->readCms('trust_sub', 'Labels & certification'),
'logos' => [
'assets/img/icons/2560px-La_Poste_logo.png',
'assets/img/icons/2560px-La_Poste_logo.png',
'assets/img/icons/2560px-La_Poste_logo.png',
'assets/img/icons/2560px-La_Poste_logo.png',
'assets/img/icons/2560px-La_Poste_logo.png',
],
];
// ------------------ CHIFFRES CLÉS ------------------
$kpis = [
'title' => $this->readCms('kpis_title', 'Nos chiffres clés !'),
'items' => [
[
'img' => $this->resolveKpiImage('kpis_img1', 'c1-', 'assets/img/pexels-fauxels-3182831.png'),
'value' => $this->readCms('chiffre_cles_card1_value', '95%'),
'label' => $this->readCms('chiffre_cles_card1_label', 'Taux de satisfaction pour l’année en cours'),
],
[
'img' => $this->resolveKpiImage('kpis_img2', 'c2-', 'assets/img/pexels-growthgal-3719037.png'),
'value' => $this->readCms('chiffre_cles_card2_value', '95%'),
'label' => $this->readCms('chiffre_cles_card2_label', 'Personnes certifiées de la formation'),
],
[
'img' => $this->resolveKpiImage('kpis_img3', 'c3-', 'assets/img/pexels-shkrabaanthony-5292192.png'),
'value' => $this->readCms('chiffre_cles_card3_value', '67%'),
'label' => $this->readCms('chiffre_cles_card3_label', 'Des demandes de contacts traitées'),
],
[
'img' => $this->resolveKpiImage('kpis_img4', 'c4-', 'assets/img/pexels-shkrabaanthony-5292192.png'),
'value' => $this->readCms('chiffre_cles_card4_value', '27%'),
'label' => $this->readCms('chiffre_cles_card4_label', 'Des demandes de contacts traitées'),
],
[
'img' => $this->resolveKpiImage('kpis_img5', 'c5-', 'assets/img/pexels-shkrabaanthony-5292192.png'),
'value' => $this->readCms('chiffre_cles_card5_value', '27%'),
'label' => $this->readCms('chiffre_cles_card5_label', 'Des demandes de contacts traitées'),
],
],
];
// ------------------ Render ------------------
return $this->render('home/index.html.twig', [
'title' => 'Accueil - ACOA',
// legacy vars
'catalogue_text_left' => $catalogue_text_left,
'text_action_formation_card1' => $text_action_formation_card1,
'text_action_formation_card2' => $text_action_formation_card2,
'text_action_formation_card3' => $text_action_formation_card3,
'text_actualite_du_moment_p1' => $text_actualite_du_moment_p1,
'text_actualite_du_moment_p2' => $actu['p2'],
'chiffre_cles_card1_value' => $kpis['items'][0]['value'],
'chiffre_cles_card1_label' => $kpis['items'][0]['label'],
'chiffre_cles_card2_value' => $kpis['items'][1]['value'],
'chiffre_cles_card2_label' => $kpis['items'][1]['label'],
'chiffre_cles_card3_value' => $kpis['items'][2]['value'],
'chiffre_cles_card3_label' => $kpis['items'][2]['label'],
// structured vars
'hero' => $hero,
'catalogue' => $catalogue,
'competences' => $competences,
'prestations' => $prestations,
'actu' => $actu,
'trust' => $trust,
'kpis' => $kpis,
]);
}
// ------------------ Text & image endpoints ------------------
#[Route('/cms/text', name: 'cms_text_update', methods: ['POST'])]
public function updateText(Request $request): JsonResponse
{
$data = json_decode($request->getContent(), true) ?? [];
$key = preg_replace('/[^a-z0-9_]/i', '', (string)($data['key'] ?? ''));
$text = trim((string)($data['text'] ?? ''));
if ($key === '' || $text === '') {
return new JsonResponse(['ok' => false, 'error' => 'Paramètres invalides'], 422);
}
$allowed = [
'hero_slide1_kicker','hero_slide1_title','hero_slide1_cta','hero_slide1_bg','hero_slide1_ribbon','hero_slide1_link',
'hero_slide2_kicker','hero_slide2_title','hero_slide2_cta','hero_slide2_bg','hero_slide2_ribbon','hero_slide2_link',
'hero_slide3_kicker','hero_slide3_title','hero_slide3_cta','hero_slide3_bg','hero_slide3_ribbon','hero_slide3_link',
'hero_slide4_kicker','hero_slide4_title','hero_slide4_cta','hero_slide4_bg','hero_slide4_ribbon','hero_slide4_link',
'hero_slide5_kicker','hero_slide5_title','hero_slide5_cta','hero_slide5_bg','hero_slide5_ribbon','hero_slide5_link',
'hero_slide6_kicker','hero_slide6_title','hero_slide6_cta','hero_slide6_bg','hero_slide6_ribbon','hero_slide6_link',
'hero_slide7_kicker','hero_slide7_title','hero_slide7_cta','hero_slide7_bg','hero_slide7_ribbon','hero_slide7_link',
'hero_slide8_kicker','hero_slide8_title','hero_slide8_cta','hero_slide8_bg','hero_slide8_ribbon','hero_slide8_link',
'hero_slide9_kicker','hero_slide9_title','hero_slide9_cta','hero_slide9_bg','hero_slide9_ribbon','hero_slide9_link',
'hero_slide10_kicker','hero_slide10_title','hero_slide10_cta','hero_slide10_bg','hero_slide10_ribbon','hero_slide10_link',
'catalogue_title','catalogue_text_left','catalogue_btn_more','catalogue_btn_cta',
'catalogue_vtitle','catalogue_vsubtitle','catalogue_year','catalogue_badge',
'presta_card1_title','presta_card1_cta','presta_card1_img',
'presta_card2_title','presta_card2_cta','presta_card2_img',
'presta_card3_title','presta_card3_cta','presta_card3_img',
'text_action_formation_card1','text_action_formation_card2','text_action_formation_card3',
'actu_kicker','actu_title','text_actualite_du_moment_p1','text_actualite_du_moment_p2',
'actu_cta','actu_pill1_label',
'actu_bg_img','actu_pill1_img','actu_pill2_img','actu_pill3_img',
'trust_kicker','trust_sub',
'kpis_title',
'kpis_img1','kpis_img2','kpis_img3','kpis_img4','kpis_img5',
'chiffre_cles_card1_value','chiffre_cles_card1_label',
'chiffre_cles_card2_value','chiffre_cles_card2_label',
'chiffre_cles_card3_value','chiffre_cles_card3_label',
'chiffre_cles_card4_value','chiffre_cles_card4_label',
'chiffre_cles_card5_value','chiffre_cles_card5_label',
];
if (!in_array($key, $allowed, true)) {
return new JsonResponse(['ok' => false, 'error' => 'Clé non autorisée'], 403);
}
if (!$this->writeCms($key, $text)) {
return new JsonResponse(['ok' => false, 'error' => "Écriture impossible"], 500);
}
return new JsonResponse(['ok' => true, 'key' => $key, 'text' => $text]);
}
#[Route('/cms/media', name: 'cms_page_media_upload', methods: ['POST'])]
public function uploadMedia(Request $request): JsonResponse
{
/** @var UploadedFile|null $file */
$file = $request->files->get('file');
$key = preg_replace('/[^a-z0-9_]/i', '', (string)$request->request->get('key', ''));
if (!$file instanceof UploadedFile || $key === '') {
return new JsonResponse(['ok' => false, 'error' => 'Fichier ou clé manquants'], 422);
}
$allowedKeys = [
'hero_slide1_bg','hero_slide2_bg','hero_slide3_bg','hero_slide4_bg','hero_slide5_bg',
'hero_slide6_bg','hero_slide7_bg','hero_slide8_bg','hero_slide9_bg','hero_slide10_bg',
'presta_card1_img','presta_card2_img','presta_card3_img',
'actu_bg_img','actu_pill1_img','actu_pill2_img','actu_pill3_img',
'kpis_img1','kpis_img2','kpis_img3','kpis_img4','kpis_img5',
];
if (!in_array($key, $allowedKeys, true)) {
return new JsonResponse(['ok' => false, 'error' => 'Clé non autorisée'], 403);
}
if (!$this->isAllowedCmsImageUpload($file)) {
return new JsonResponse(['ok' => false, 'error' => 'Format non supporté (png, jpg, webp)'], 415);
}
$projectDir = $this->getParameter('kernel.project_dir');
$subdir = 'uploads/cms/' . date('Y/m');
$targetDir = $projectDir . '/public/' . $subdir;
if (!is_dir($targetDir) && !@mkdir($targetDir, 0775, true) && !is_dir($targetDir)) {
return new JsonResponse(['ok' => false, 'error' => 'Impossible de créer le dossier'], 500);
}
$ext = $this->safeUploadExtension($file);
$originalName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
$sanitizedName = preg_replace('/[^a-z0-9]+/i', '-', (string) $originalName);
$sanitizedName = trim((string) $sanitizedName, '-');
$basename = $sanitizedName !== ''
? strtolower($sanitizedName)
: 'img-' . date('Ymd-His') . '-' . substr(sha1(uniqid('', true)), 0, 8);
$filename = $basename . '.' . $ext;
try {
$file->move($targetDir, $filename);
@chmod($targetDir . '/' . $filename, 0664);
} catch (\Throwable $e) {
return new JsonResponse([
'ok' => false,
'error' => 'Upload échoué',
'detail' => $e->getMessage(),
'targetDir' => $targetDir,
], 500);
}
$relativePath = $subdir . '/' . $filename;
if (!$this->writeCms($key, $relativePath)) {
return new JsonResponse(['ok' => false, 'error' => 'Upload enregistré mais liaison CMS impossible'], 500);
}
return new JsonResponse(['ok' => true, 'path' => $relativePath, 'key' => $key]);
}
// ------------------ (Optional) legacy route kept for backward compatibility ------------------
#[Route('/cms/catalogue-text', name: 'catalogue_text_update', methods: ['POST'])]
public function updateCatalogueTextLegacy(Request $request): JsonResponse
{
$data = json_decode($request->getContent(), true);
$text = trim((string)($data['text'] ?? ''));
if ($text === '') {
return new JsonResponse(['ok' => false, 'error' => 'Texte vide'], 422);
}
if (!$this->writeCms('catalogue_text_left', $text)) {
return new JsonResponse(['ok' => false, 'error' => "Impossible d'écrire le fichier"], 500);
}
return new JsonResponse(['ok' => true, 'text' => $text]);
}
}