<?php
namespace App\Controller\Client;
use App\Entity\Contact;
use App\Entity\ContactCreationSociete;
use App\Entity\ContactInvestissementImmo;
use App\Entity\ContactInvestissementImmoProperty;
use App\Entity\ContactInvestissementImmoPropertyCharge;
use App\Entity\EmailBlacklist;
use App\Form\ContactCreationSocieteType;
use App\Form\ContactInvestissementImmoType;
use App\Services\MoneticoService;
use App\Services\QueriesV2Manager;
use Psr\Log\LoggerInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Twig\Environment;
use App\Form\ContactType;
use App\Services\WebSite;
use App\Form\MiniBookType;
use App\Entity\ApplyRequest;
use App\Form\NewsletterType;
use App\Entity\AccountingFirm;
use App\Form\EbookDownloadType;
use App\Form\CompanyCreationType;
use App\Form\AnnouncementApplyType;
use App\Repository\AliasRepository;
use App\Services\SendContactEmail as CE;
use App\Repository\AccountingFirmRepository;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\HttpFoundation\Response;
use App\Controller\Client\ClientWidgetController;
use App\Entity\ContactProfession;
use App\Form\ContactProfessionType;
use App\Form\ContactCustomType;
use App\Form\ContactEtreRappeleType;
use App\Repository\DynDataRepository;
use App\Repository\AnnouncementRepository;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use App\Services\AccountingFirmResolver;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\RateLimiter\RateLimiterFactory;
abstract class AbstractClientController extends AbstractController
{
protected $accountingFirm;
protected $templateBase;
protected $mailer;
protected $request;
protected $redirect;
protected $formview;
protected $formsend;
protected $formlivreview;
protected $form_send_livre;
protected $formcompanyview;
protected $form_send_company;
protected $formprofessionview;
protected $formetrerappeleview;
protected $form_send_profession;
protected $form_send_etreRappele;
protected $formnewsletterview;
protected $form_send_newsletter;
protected $formminibookview;
protected $form_send_minibook;
protected $formrecrutementview;
protected $form_send_recrutement;
protected $formprevicreationview;
protected $form_send_previ_creation;
protected $formpreviimmoview;
protected $form_send_previ_immo;
protected $formcustomview;
protected $form_send_custom;
protected $CE;
protected $twig;
protected $site;
protected $dynData;
protected $dynDataArray;
protected $dynDataText;
protected $announcementMain;
protected $announcementSpontanee;
protected $serviceQMV2;
protected $moneticoService;
protected $theHost;
protected $logger;
protected $cache;
protected $contactFormByIpLimiter;
public function __construct(RequestStack $request, AccountingFirmRepository $accountingFirmRepository, AliasRepository $aliasrepository, MailerInterface $mailer, CE $CE, Environment $twig, WebSite $site, AnnouncementRepository $announcementRepository, DynDataRepository $dynDataRepository, QueriesV2Manager $serviceQMV2, MoneticoService $moneticoService, LoggerInterface $logger, AccountingFirmResolver $accountingFirmResolver, CacheInterface $cache, RateLimiterFactory $contactFormByIpLimiter)
{
$this->moneticoService = $moneticoService;
$this->logger = $logger;
$this->cache = $cache;
$this->contactFormByIpLimiter = $contactFormByIpLimiter;
$theHost = $request->getCurrentRequest()->getHost();
$theHost = str_replace('www.', '', $theHost);
if (preg_match('/preview/', $request->getCurrentRequest()->get('_route'))) {
$theHost = $request->getCurrentRequest()->attributes->get('host');
}
// $theHost = 'cabinet-za.de';
// ✅ UTILISATION DU RESOLVER CENTRALISÉ - évite la duplication avec le Voter
$this->accountingFirm = $accountingFirmResolver->resolve();
// Gestion de la redirection si alias ou host non trouvé
if (is_null($this->accountingFirm)) {
$alias = $aliasrepository->findOneBy([
'name' => $theHost
]);
if (!is_null($alias)) {
$acID = $alias->getParameter()->getAccountingFirm()->getId();
$this->accountingFirm = $accountingFirmRepository->findOneBy([
'id' => $acID
]);
$this->redirect = $this->accountingFirm->getHost();
} else {
$this->redirect = 404;
}
}
$theHost = $this->accountingFirm->getHost();
$this->theHost = $theHost;
// ✅ OPTIMISATION : Cache des DynData avec relations hydratées
// On récupère directement depuis le repository (pas de cache d'entités Doctrine)
// car les relations lazy-loaded ne survivent pas à la sérialisation du cache
$this->dynData = $dynDataRepository->findAll();
// Filtrer par type - on garde les entités complètes avec relations hydratées
$this->dynDataArray = array_values(array_filter($this->dynData, fn($d) => $d->getType() === 'ARRAY'));
$this->dynDataText = array_values(array_filter($this->dynData, fn($d) => $d->getType() === 'TEXT'));
if (!empty($this->accountingFirm)) {
$this->announcementMain = $announcementRepository->findMyAnnouncementMain($this->accountingFirm);
$this->announcementSpontanee = $announcementRepository->findMyAnnouncementsSpontanee($this->accountingFirm);
}
$this->templateBase = '/' . $theHost . '/templates/';
$this->request = $request;
$this->mailer = $mailer;
$this->formsend = false;
$this->form_send_livre = false;
$this->form_send_company = false;
$this->form_send_newsletter = false;
$this->form_send_minibook = false;
$this->form_send_recrutement = false;
$this->form_send_custom = false;
$this->form_send_previ_creation = false;
$this->form_send_previ_immo = false;
$this->CE = $CE;
$this->twig = $twig;
$this->site = $site;
$this->serviceQMV2 = $serviceQMV2;
}
protected function render(string $view, array $parameters = [], Response $response = null, array $return = []): Response
{
// ✅ OPTIMISATION : Cache du fichier entrypoints.json (évite I/O disque + parsing JSON)
// Le cache sera automatiquement invalidé au prochain déploiement
$build = $this->cache->get('build_entrypoints', function() {
$filePath = $this->getParameter('kernel.project_dir') . '/public/build/entrypoints.json';
return json_decode(file_get_contents($filePath), true);
});
$this->twig->addGlobal('build', $build);
$this->twig->addGlobal('serverActuUrl', $this->site->getServerActuUrl());
// set redirection
if (!is_null($this->redirect)) {
if ($this->redirect == 404) {
return new Response("", 404);
die();
} else {
$this->redirect = "https://" . $this->redirect;
}
}
// Access from actualite-du-mois.php
if ($this->request && $this->request->getCurrentRequest() && $this->request->getCurrentRequest()->getBaseUrl() == "/actualite-du-mois.php") {
if ($this->redirectRouteV2($this->request->getCurrentRequest())) {
$redirect = $this->redirectRouteV2($this->request->getCurrentRequest());
$redirect = str_replace("actualite-du-mois.php", "", $redirect);
$redirect = str_replace("//", "/", $redirect);
} else {
$redirect = "/actualite-du-mois";
}
if (empty($this->redirect)) {
$this->redirect = "https://" . $this->request->getCurrentRequest()->getHttpHost() . $redirect;
} else {
$this->redirect = $this->redirect . $redirect;
}
}
// do redirection
if (!is_null($this->redirect)) {
return new RedirectResponse($this->redirect);
die();
}
// SETUP
$entityManager = $this->getDoctrine()->getManager();
// Contact
$form = $this->createForm(ContactType::class, null, ['cabinet' => $this->accountingFirm]);
$request = $this->get('request_stack')->getCurrentRequest();
$form->get('form_timestamp')->setData(time());
$form->get('route')->setData($request->attributes->get('_route'));
if ($request->attributes->get('_route') == 'previewprescripteur_client' || $request->attributes->get('_route') == 'prescripteur_client') {
$form->get('id_prescriber')->setData($request->attributes->get('id'));
}
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// Rate limiting par IP : max 2 soumissions par période de 24h
$limiter = $this->contactFormByIpLimiter->create($request->getClientIp());
if (!$limiter->consume(1)->isAccepted()) {
$form->addError(new FormError('Trop de tentatives. Vous avez déjà soumis ce formulaire plusieurs fois aujourd\'hui. Merci de réessayer demain ou de nous contacter directement par téléphone.'));
} else {
$name = $form->get('name')->getData();
$email = $form->get('email')->getData();
$message = $form->get('message')->getData();
$fiveMinutesAgo = new \DateTime('-5 minutes');
$existingContact = $this->getDoctrine()->getRepository(Contact::class)->findRecentContactByName($name, $fiveMinutesAgo);
$submittedAt = (int) $form->get('form_timestamp')->getData();
$now = time();
$elapsedSeconds = $now - $submittedAt;
// Anti-spam : formulaire soumis trop vite
if ($elapsedSeconds < 10) {
// Ajout à la blacklist
$blacklistRepo = $this->getDoctrine()->getRepository(EmailBlacklist::class);
$entityManager = $this->getDoctrine()->getManager();
$blacklistedEmail = $blacklistRepo->findByEmail($email);
if (!$blacklistedEmail) {
$blacklistedEmail = new EmailBlacklist();
$blacklistedEmail->setEmail($email);
$blacklistedEmail->setName($name);
$blacklistedEmail->setBlockCount(1);
} else {
$blacklistedEmail->incrementBlockCount();
}
$entityManager->persist($blacklistedEmail);
$entityManager->flush();
}
// Détection message spam type "ClbNCSjWDTZMonBownuySkg"
if ($this->isGibberishMessage($message) || $this->isSpamContent($message)) {
$blacklistRepo = $this->getDoctrine()->getRepository(EmailBlacklist::class);
$entityManager = $this->getDoctrine()->getManager();
$blacklistedEmail = $blacklistRepo->findByEmail($email);
if (!$blacklistedEmail) {
$blacklistedEmail = new EmailBlacklist();
$blacklistedEmail->setEmail($email);
$blacklistedEmail->setName($name);
$blacklistedEmail->setBlockCount(1);
} else {
$blacklistedEmail->incrementBlockCount();
}
$entityManager->persist($blacklistedEmail);
$entityManager->flush();
}
// Vérifier si l'email est dans la blacklist
$blacklistRepo = $this->getDoctrine()->getRepository(EmailBlacklist::class);
if (!($blacklistRepo->isEmailBlacklisted($email) || $blacklistRepo->isEmailBlacklisted($name)) && !$existingContact && !$this->isSpamContent($message)) {
$this->CE->sendContactEmail($form);
} elseif ($blacklistRepo->isEmailBlacklisted($email) || $blacklistRepo->isEmailBlacklisted($name)) {
// Incrémenter le compteur de tentatives
$blacklistedEmail = $blacklistRepo->findByEmail($email);
if ($blacklistedEmail) {
$blacklistedEmail->incrementBlockCount();
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($blacklistedEmail);
$entityManager->flush();
}
}
$this->formsend = true;
} // end else rate limiter
}
$this->formview = $form->createView();
// Contact Custom
$form_custom = $this->createForm(ContactCustomType::class, null, ['cabinet' => $this->accountingFirm]);
$form_custom->handleRequest($request);
$this->formcustomview = $form_custom->createView();
if ($form_custom->isSubmitted() && $form_custom->isValid()) {
$name = $form_custom->get('name')->getData();
$email = $form_custom->get('email')->getData();
$message = $form_custom->get('message')->getData();
// Vérifier si l'email est dans la blacklist
$blacklistRepo = $this->getDoctrine()->getRepository(EmailBlacklist::class);
if (!($blacklistRepo->isEmailBlacklisted($email) || $blacklistRepo->isEmailBlacklisted($name)) && stripos($message, "free") === false) {
$this->CE->sendContactCustomEmail($form_custom);
} elseif ($blacklistRepo->isEmailBlacklisted($email) || $blacklistRepo->isEmailBlacklisted($name)) {
// Incrémenter le compteur de tentatives
$blacklistedEmail = $blacklistRepo->findByEmail($email);
if ($blacklistedEmail) {
$blacklistedEmail->incrementBlockCount();
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($blacklistedEmail);
$entityManager->flush();
}
}
$this->form_send_custom = true;
}
// EbookDownload
$form_livre = $this->createForm(EbookDownloadType::class);
$form_livre->handleRequest($request);
$this->formlivreview = $form_livre->createView();
if ($form_livre->isSubmitted() && $form_livre->isValid()) {
$this->CE->sendEbookEmail($form_livre);
$this->form_send_livre = true;
}
// company
$form_company = $this->createForm(CompanyCreationType::class, null, ['cabinet' => $this->accountingFirm]);
$form_company->handleRequest($request);
$this->formcompanyview = $form_company->createView();
if ($form_company->isSubmitted() && $form_company->isValid()) {
$name = $form_company->get('name')->getData();
$email = $form_company->get('email')->getData();
// Vérifier si l'email est dans la blacklist
$blacklistRepo = $this->getDoctrine()->getRepository(EmailBlacklist::class);
if (!($blacklistRepo->isEmailBlacklisted($email) || $blacklistRepo->isEmailBlacklisted($name))) {
$this->CE->sendCompanyEmail($form_company);
} elseif ($blacklistRepo->isEmailBlacklisted($email) || $blacklistRepo->isEmailBlacklisted($name)) {
// Incrémenter le compteur de tentatives
$blacklistedEmail = $blacklistRepo->findByEmail($email);
if ($blacklistedEmail) {
$blacklistedEmail->incrementBlockCount();
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($blacklistedEmail);
$entityManager->flush();
}
}
$this->form_send_company = true;
}
// company
$form_profession = $this->createForm(ContactProfessionType::class, null, ['cabinet' => $this->accountingFirm]);
$form_profession->handleRequest($request);
$this->formprofessionview = $form_profession->createView();
if ($form_profession->isSubmitted() && $form_profession->isValid()) {
$name = $form_profession->get('name')->getData();
$email = $form_profession->get('email')->getData();
$message = $form_profession->get('message')->getData();
// Vérifier si l'email est dans la blacklist
$blacklistRepo = $this->getDoctrine()->getRepository(EmailBlacklist::class);
if (!($blacklistRepo->isEmailBlacklisted($email) || $blacklistRepo->isEmailBlacklisted($name)) && stripos($message, "free") === false) {
$this->CE->sendContactProfessionEmail($form_profession);
} elseif ($blacklistRepo->isEmailBlacklisted($email) || $blacklistRepo->isEmailBlacklisted($name)) {
// Incrémenter le compteur de tentatives
$blacklistedEmail = $blacklistRepo->findByEmail($email);
if ($blacklistedEmail) {
$blacklistedEmail->incrementBlockCount();
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($blacklistedEmail);
$entityManager->flush();
}
}
$this->form_send_profession = true;
}
// etre rappele
$form_etre_rappele = $this->createForm(ContactEtreRappeleType::class, null, ['cabinet' => $this->accountingFirm]);
$form_etre_rappele->handleRequest($request);
$this->formetrerappeleview = $form_etre_rappele->createView();
if ($form_etre_rappele->isSubmitted() && $form_etre_rappele->isValid())
{
$name = $form_etre_rappele->get('name')->getData();
$email = $form_etre_rappele->get('email')->getData();
// Vérifier si l'email est dans la blacklist
$blacklistRepo = $this->getDoctrine()->getRepository(EmailBlacklist::class);
if (!($blacklistRepo->isEmailBlacklisted($email) || $blacklistRepo->isEmailBlacklisted($name))) {
$this->CE->sendContactEtreRappeleEmail($form_etre_rappele);
} elseif ($blacklistRepo->isEmailBlacklisted($email) || $blacklistRepo->isEmailBlacklisted($name)) {
// Incrémenter le compteur de tentatives
$blacklistedEmail = $blacklistRepo->findByEmail($email);
if ($blacklistedEmail) {
$blacklistedEmail->incrementBlockCount();
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($blacklistedEmail);
$entityManager->flush();
}
}
$this->form_send_etreRappele = true;
}
// previ creation
$logFile = $this->getParameter('kernel.project_dir') . '/var/log/monprevi.log';
if (!is_dir(dirname($logFile))) {
@mkdir(dirname($logFile), 0775, true);
}
$log = function (string $message) use ($logFile) {
@file_put_contents(
$logFile,
sprintf("[%s] %s\n", (new \DateTimeImmutable())->format('Y-m-d H:i:s'), $message),
FILE_APPEND
);
};
if ($this->accountingFirm->getId() === 301 || $this->accountingFirm->getId() === 320) {
$log("Init Previ Creation: host={$this->theHost}, cabinet_id=" . ($this->accountingFirm ? $this->accountingFirm->getId() : 'null'));
}
try {
$contactCreationSociete = new ContactCreationSociete();
$contactCreationSociete->setCabinet($this->accountingFirm);
$contactCreationSociete->setCreatedAt(new \DateTimeImmutable('now'));
$form_previcreation = $this->createForm(ContactCreationSocieteType::class, $contactCreationSociete, ['cabinet' => $this->accountingFirm]);
$this->formprevicreationview = $form_previcreation->createView();
if ($this->accountingFirm->getId() === 301) {
$log("Previ Creation : Formulaire affiché");
}
if ($request->isMethod('POST') && $request->request->has($form_previcreation->getName())) {
$log("Previ Creation POST detecté pour form=" . $form_previcreation->getName());
$form_previcreation->handleRequest($request);
if ($form_previcreation->isSubmitted() && $form_previcreation->isValid()) {
$log("Previ Creation formulaire soumis et valide");
$startActivityAt = $form_previcreation->get('startActivityAt')->getData();
$fiscalYearEndAt = $form_previcreation->get('fiscalYearEndAt')->getData();
$log(sprintf(
"Previ Creation dates reçues: startActivityAt=%s, fiscalYearEndAt=%s",
$startActivityAt ? $startActivityAt->format('d/m/Y') : 'null',
$fiscalYearEndAt ? $fiscalYearEndAt->format('d/m/Y') : 'null'
));
$contactCreationSociete->setStartActivityAt($startActivityAt);
$contactCreationSociete->setFiscalYearEndAt($fiscalYearEndAt);
$name = $form_previcreation->get('name')->getData();
$email = $form_previcreation->get('email')->getData();
$log("Previ Creation nom reçu: {$name} {$email}");
// Vérifier si l'email est dans la blacklist
$blacklistRepo = $this->getDoctrine()->getRepository(EmailBlacklist::class);
if (!($blacklistRepo->isEmailBlacklisted($email) || $blacklistRepo->isEmailBlacklisted($name))) {
$log("Blacklist check passed for email: {$email}");
try {
$entityManager->persist($contactCreationSociete);
$entityManager->flush();
} catch (\Throwable $e) {
$this->logger->error("❌ Erreur lors du persist/flush Previ Creation", [
'message' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'form_data_class' => get_class($form_previcreation->getData()),
'form_valid' => $form_previcreation->isValid(),
'form_errors' => (string) $form_previcreation->getErrors(true, false),
]);
throw $e;
}
$log("Previ Creation entité persistée id=" . $contactCreationSociete->getId());
$contactCreationSociete->setReference('PREVI_' . $contactCreationSociete->getId());
$entityManager->persist($contactCreationSociete);
$entityManager->flush();
$log("Previ Creation référence définie=" . $contactCreationSociete->getReference());
if ($contactCreationSociete->getPaymentMethod() === 'cb') {
$parameters = $this->accountingFirm->getParameters();
$price = ($contactCreationSociete->getCategory() == "Prévisionnel complet") ? "359,00" : "259,00";
$urlRetour = $this->generateUrl('monetico_return_client', ['id' => $contactCreationSociete->getId()], UrlGeneratorInterface::ABSOLUTE_URL);
$urlNotif = $this->generateUrl('monetico_notification', [], UrlGeneratorInterface::ABSOLUTE_URL);
if (str_contains($this->request->getCurrentRequest()->get('_route'), 'preview')) {
$urlRetour = $this->generateUrl('previewmonetico_return_client', ['host' => $this->theHost, 'id' => $contactCreationSociete->getId()], UrlGeneratorInterface::ABSOLUTE_URL);
$urlNotif = $this->generateUrl('previewmonetico_notification', ['host' => $this->theHost], UrlGeneratorInterface::ABSOLUTE_URL);
}
$log("Previ Creation paiement CB: price={$price}, reference=" . $contactCreationSociete->getReference() . ", url_retour={$urlRetour}, url_notif={$urlNotif}");
$params = [
'tpe' => $parameters->getMoneticoTpe(),
'societe' => $parameters->getMoneticoSociete(),
'hmac_key' => $parameters->getMoneticoCle(),
'url_retour' => $urlRetour,
'url_notif' => $urlNotif,
'montant' => $price,
'reference' => $contactCreationSociete->getReference(),
'email' => $contactCreationSociete->getEmail(),
'name' => $contactCreationSociete->getName(),
'firstname' => $contactCreationSociete->getFirstname(),
'address' => $contactCreationSociete->getSocietyAddress(),
'zipCode' => $contactCreationSociete->getSocietyZipCode(),
'city' => $contactCreationSociete->getSocietyCity(),
];
$log("Previ Creation génération du formulaire de paiement Monetico");
return new Response($this->moneticoService->generatePaymentForm($params));
}
$this->CE->sendPreviCreation($contactCreationSociete);
$this->form_send_previ_creation = true;
$log("Previ Creation email envoyé, référence=" . $contactCreationSociete->getReference());
$urlRetour = $this->generateUrl('monetico_return_client', ['id' => $contactCreationSociete->getId()], UrlGeneratorInterface::ABSOLUTE_URL);
if (str_contains($this->request->getCurrentRequest()->get('_route'), 'preview')) {
$urlRetour = $this->generateUrl('previewmonetico_return_client', ['host' => $this->theHost, 'id' => $contactCreationSociete->getId()], UrlGeneratorInterface::ABSOLUTE_URL);
}
$urlRetour .= "?reference=" . $contactCreationSociete->getReference();
$urlRetour .= "&code-retour=paiement";
$log("Previ Creation redirection vers {$urlRetour}");
return new RedirectResponse($urlRetour);
} elseif ($blacklistRepo->isEmailBlacklisted($email) || $blacklistRepo->isEmailBlacklisted($name)) {
$blacklistedEmail = $blacklistRepo->findByEmail($email);
if ($blacklistedEmail) {
$blacklistedEmail->incrementBlockCount();
$entityManager->persist($blacklistedEmail);
$entityManager->flush();
}
$log("Previ Creation email blacklisté: {$email}");
}
} else {
$log("Previ Creation formulaire non valide ou non soumis");
$this->logger->error("Previ Creation formulaire non valide ou non soumis", [
'form_data_class' => get_class($form_previcreation->getData()),
'form_valid' => $form_previcreation->isValid(),
'form_errors' => (string) $form_previcreation->getErrors(true, false),
]);
}
}
} catch (\Throwable $e) {
// ⚙️ On tente de récupérer le contenu du formulaire, même s'il est invalide
$formData = [];
try {
if (isset($form_previcreation) && $form_previcreation->isSubmitted()) {
// on extrait les données brutes + erreurs
$formData = [
'data' => $form_previcreation->getData(),
'submitted_data' => $request->request->all($form_previcreation->getName()),
'errors' => (string) $form_previcreation->getErrors(true, false),
];
}
} catch (\Throwable $innerE) {
// on logue aussi si jamais même la récupération échoue (par sécurité)
$this->logger->warning('Impossible d’extraire les données du formulaire dans le catch', [
'inner_message' => $innerE->getMessage(),
]);
}
// 🧾 Log complet de l’exception
$this->logger->error('❌ Exception non interceptée dans Previ Creation', [
'message' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'request_uri' => $request->getRequestUri(),
'form_data' => $formData,
]);
// 🔁 selon ton besoin :
throw $e; // pour laisser Symfony gérer l'erreur (page 500)
}
// previ immo
if ($this->accountingFirm->getId() === 301) {
$log("Init Previ Immo: host={$this->theHost}, cabinet_id=" . ($this->accountingFirm ? $this->accountingFirm->getId() : 'null'));
}
try {
$contactInvestissementImmo = new ContactInvestissementImmo();
$contactInvestissementImmo->setAccountingFirm($this->accountingFirm);
$contactInvestissementImmo->setNumberProperties(1);
$contactInvestissementImmo->setCreatedAt(new \DateTimeImmutable('now'));
$form_previimmo = $this->createForm(ContactInvestissementImmoType::class, $contactInvestissementImmo, ['cabinet' => $this->accountingFirm]);
$this->formpreviimmoview = $form_previimmo->createView();
if ($this->accountingFirm->getId() === 301) {
$log("Previ Immo : Formulaire affiché");
}
if ($request->isMethod('POST') && $request->request->has($form_previimmo->getName())) {
$log("Previ Immo POST détecté pour form=" . $form_previimmo->getName());
$form_previimmo->handleRequest($request);
if ($form_previimmo->isSubmitted() && $form_previimmo->isValid()) {
$log("Previ Immo formulaire soumis et valide");
$name = $form_previimmo->get('name')->getData();
$email = $form_previimmo->get('email')->getData();
$log("Previ Immo nom reçu: {$name} {$email}");
$blacklistRepo = $this->getDoctrine()->getRepository(EmailBlacklist::class);
if (!($blacklistRepo->isEmailBlacklisted($email) || $blacklistRepo->isEmailBlacklisted($name))) {
$propertiesImmoData = $form_previimmo->get('propertiesImmo')->getData();
$propertiesChargesData = $form_previimmo->get('propertiesCharges')->getData();
$propertiesRentData = $form_previimmo->get('propertiesRent')->getData();
$log("Previ Immo propriétés reçues: nb=" . (is_iterable($propertiesImmoData) ? count($propertiesImmoData) : 0));
foreach ($propertiesImmoData as $index => $propertyData) {
$log("Previ Immo propriété index={$index} création");
$property = new ContactInvestissementImmoProperty();
$property->setSurface($propertyData['surface'] ?? '');
$property->setBiens($propertyData['biens'] ?? '');
$property->setRents($propertiesRentData[$index]['rent'] ?? '');
$property->setContactInvestissementImmo($contactInvestissementImmo);
if (isset($propertiesChargesData[$index]['charges']) && is_iterable($propertiesChargesData[$index]['charges'])) {
$log("Previ Immo propriété index={$index} charges nb=" . count($propertiesChargesData[$index]['charges']));
foreach ($propertiesChargesData[$index]['charges'] as $chargeData) {
$charge = new ContactInvestissementImmoPropertyCharge();
$charge->setName($chargeData['name'] ?? '');
$charge->setAmount($chargeData['amount'] ?? '');
$charge->setFrequency($chargeData['periodicite'] ?? '');
$charge->setContactInvestissementImmoProperty($property);
$entityManager->persist($charge);
$property->addCharge($charge);
}
}
$entityManager->persist($property);
$contactInvestissementImmo->getProperties()->add($property);
}
try {
$entityManager->persist($contactInvestissementImmo);
$entityManager->flush();
} catch (\Throwable $e) {
$this->logger->error("❌ Erreur lors du persist/flush Previ Immo", [
'message' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'form_data_class' => get_class($form_previimmo->getData()),
'form_valid' => $form_previimmo->isValid(),
'form_errors' => (string) $form_previimmo->getErrors(true, false),
]);
throw $e;
}
$log("Previ Immo entité persistée id=" . $contactInvestissementImmo->getId());
$contactInvestissementImmo->setReference('PREVI_IMMO_' . $contactInvestissementImmo->getId());
$entityManager->persist($contactInvestissementImmo);
$entityManager->flush();
$log("Previ Immo référence définie=" . $contactInvestissementImmo->getReference());
if ($contactInvestissementImmo->getPaymentMethod() === 'cb') {
$parameters = $this->accountingFirm->getParameters();
$price = "259,00";
$urlRetour = $this->generateUrl('monetico_return_client', ['id' => $contactInvestissementImmo->getId()], UrlGeneratorInterface::ABSOLUTE_URL);
$urlNotif = $this->generateUrl('monetico_notification', [], UrlGeneratorInterface::ABSOLUTE_URL);
if (str_contains($this->request->getCurrentRequest()->get('_route'), 'preview')) {
$urlRetour = $this->generateUrl('previewmonetico_return_client', ['host' => $this->theHost, 'id' => $contactInvestissementImmo->getId()], UrlGeneratorInterface::ABSOLUTE_URL);
$urlNotif = $this->generateUrl('previewmonetico_notification', ['host' => $this->theHost], UrlGeneratorInterface::ABSOLUTE_URL);
}
$log("Previ Immo paiement CB: price={$price}, reference=" . $contactInvestissementImmo->getReference());
$params = [
'tpe' => $parameters->getMoneticoTpe(),
'societe' => $parameters->getMoneticoSociete(),
'hmac_key' => $parameters->getMoneticoCle(),
'url_retour' => $urlRetour,
'url_notif' => $urlNotif,
'montant' => $price,
'reference' => $contactInvestissementImmo->getReference(),
'email' => $contactInvestissementImmo->getEmail(),
'name' => $contactInvestissementImmo->getName(),
'firstname' => $contactInvestissementImmo->getFirstname(),
'address' => $contactInvestissementImmo->getAdress(),
'zipCode' => $contactInvestissementImmo->getZipCode(),
'city' => $contactInvestissementImmo->getCity(),
];
$log("Previ Immo génération du formulaire de paiement Monetico");
return new Response($this->moneticoService->generatePaymentForm($params));
}
$this->CE->sendPreviImmo($contactInvestissementImmo);
$this->form_send_previ_immo = true;
$log("Previ Immo email envoyé, référence=" . $contactInvestissementImmo->getReference());
$urlRetour = $this->generateUrl('monetico_return_client', ['id' => $contactInvestissementImmo->getId()], UrlGeneratorInterface::ABSOLUTE_URL);
if (str_contains($this->request->getCurrentRequest()->get('_route'), 'preview')) {
$urlRetour = $this->generateUrl('previewmonetico_return_client', ['host' => $this->theHost, 'id' => $contactInvestissementImmo->getId()], UrlGeneratorInterface::ABSOLUTE_URL);
}
$urlRetour .= "?reference=" . $contactInvestissementImmo->getReference();
$urlRetour .= "&code-retour=paiement";
$log("Previ Immo redirection vers {$urlRetour}");
return new RedirectResponse($urlRetour);
} else {
$blacklistedEmail = $blacklistRepo->findByEmail($email);
if ($blacklistedEmail) {
$blacklistedEmail->incrementBlockCount();
$entityManager->persist($blacklistedEmail);
$entityManager->flush();
}
$log("Previ Immo email blacklisté: {$email}");
}
} else {
$log("Previ Immo formulaire non valide ou non soumis");
}
}
} catch (\Throwable $e) {
// 🧾 Récupération sécurisée du contenu du formulaire
$formData = [];
try {
if (isset($form_previimmo) && $form_previimmo->isSubmitted()) {
$formData = [
'data' => $form_previimmo->getData(),
'submitted_data' => $request->request->all($form_previimmo->getName()),
'errors' => (string) $form_previimmo->getErrors(true, false),
];
}
} catch (\Throwable $innerE) {
$this->logger->warning('Impossible d’extraire les données du formulaire Previ Immo', [
'inner_message' => $innerE->getMessage(),
]);
}
// 🔥 Log complet de l’exception
$this->logger->error('❌ Exception non interceptée dans Previ Immo', [
'message' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'request_uri' => $request->getRequestUri(),
'form_data' => $formData,
]);
return new Response("Une erreur interne est survenue", 500);
}
// newsletter
$form_newsletter = $this->createForm(NewsletterType::class, null, ['cabinet' => $this->accountingFirm]);
$form_newsletter->handleRequest($request);
$this->formnewsletterview = $form_newsletter->createView();
if ($form_newsletter->isSubmitted() && $form_newsletter->isValid()) {
$name = $form_newsletter->get('name')->getData();
$email = $form_newsletter->get('email')->getData();
// Vérifier si l'email est dans la blacklist
$blacklistRepo = $this->getDoctrine()->getRepository(EmailBlacklist::class);
if (!($blacklistRepo->isEmailBlacklisted($email) || $blacklistRepo->isEmailBlacklisted($name))) {
$this->CE->sendNewsletterEmail($form_newsletter);
} elseif ($blacklistRepo->isEmailBlacklisted($email) || $blacklistRepo->isEmailBlacklisted($name)) {
// Incrémenter le compteur de tentatives
$blacklistedEmail = $blacklistRepo->findByEmail($email);
if ($blacklistedEmail) {
$blacklistedEmail->incrementBlockCount();
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($blacklistedEmail);
$entityManager->flush();
}
}
$this->form_send_newsletter = true;
}
// minibook
$form_minibook = $this->createForm(MiniBookType::class);
$form_minibook->handleRequest($request);
$this->formminibookview = $form_minibook->createView();
if ($form_minibook->isSubmitted() && $form_minibook->isValid()) {
$this->CE->sendMiniEbookEmail($form_minibook);
$this->form_send_minibook = true;
}
// recrutement
$form_recrutement = $this->createForm(AnnouncementApplyType::class, null, ['cabinet' => $this->accountingFirm]);
$form_recrutement->handleRequest($request);
$this->formrecrutementview = $form_recrutement->createView();
if ($form_recrutement->isSubmitted() && $form_recrutement->isValid()) {
$this->CE->sendRecrutementEmail($form_recrutement);
$this->form_send_recrutement = true;
}
// Setup return data
$parameters['accountingFirm'] = $parameters['cabinet'] = $this->accountingFirm;
$parameters['parameters'] = $this->accountingFirm->getParameters();
$parameters['form'] = $this->formview;
$parameters['form_send'] = $this->formsend;
$parameters['form_livre'] = $this->formlivreview;
$parameters['form_send_livre'] = $this->form_send_livre;
$parameters['form_company'] = $this->formcompanyview;
$parameters['form_send_company'] = $this->form_send_company;
$parameters['form_newsletter'] = $this->formnewsletterview;
$parameters['form_send_newsletter'] = $this->form_send_newsletter;
$parameters['form_minibook'] = $this->formminibookview;
$parameters['form_send_minibook'] = $this->form_send_minibook;
$parameters['form_recrutement'] = $this->formrecrutementview;
$parameters['form_send_recrutement'] = $this->form_send_recrutement;
$parameters['form_profession'] = $this->formprofessionview;
$parameters['form_send_profession'] = $this->form_send_profession;
$parameters['form_etre_rappele'] = $this->formetrerappeleview;
$parameters['form_send_etre_rappele'] = $this->form_send_etreRappele;
$parameters['form_custom'] = $this->formcustomview;
$parameters['form_send_custom'] = $this->form_send_custom;
$parameters['form_previ_creation'] = $this->formprevicreationview;
$parameters['form_send_previ_creation'] = $this->form_send_previ_creation;
$parameters['form_previ_immo'] = $this->formpreviimmoview;
$parameters['form_send_previ_immo'] = $this->form_send_previ_immo;
if (isset($this->announcementMain[0])) {
$parameters['announcementMain'] = $this->announcementMain[0];
}
if (isset($this->announcementSpontanee[0])) {
$parameters['announcementSpontanee'] = $this->announcementSpontanee[0];
}
$parameters["DynData"] = [];
// ✅ OPTIMISATION : Éviter count() dans la boucle et appels répétés aux getters
// Utilisation de foreach au lieu de for avec count()
foreach ($this->dynDataText as $dynDataItem) {
$name = $dynDataItem->getName();
$key = str_replace(' ', '-', strtolower($name));
$parameters["DynData"][$key] = [
'name' => $name,
'data' => $dynDataItem->getData(),
'type' => $dynDataItem->getType(),
];
}
foreach ($this->dynDataArray as $dynDataItem) {
$name = $dynDataItem->getName();
$dataItems = $dynDataItem->getDataItems();
$dataItemsArray = [];
foreach ($dataItems as $item) {
$dataItemsArray[] = [
'titre' => $item->getTitle(),
'data' => $item->getData(),
];
}
$key = str_replace(' ', '-', strtolower($name));
$parameters["DynData"][$key] = [
'name' => $name,
'data' => $dataItemsArray,
'type' => $dynDataItem->getType(),
];
}
$host = $this->request->getCurrentRequest()->getHost();
$isPreview = str_contains($this->request->getCurrentRequest()->get('_route') ?? '', 'preview');
if (!$isPreview && in_array($host, ['prod.lagence.expert', 'frontdev.lagence.expert', '127.0.0.1'])) {
return parent::render($this->templateBase . $view, $parameters, $response);
} else if ($this->accountingFirm->getParameters()->isMaintenance()) {
return parent::render("/maintenance.html.twig", $parameters, $response);
} else if ($this->accountingFirm->getParameters()->isConstruction()) {
return parent::render("/maintenance.html.twig", $parameters, $response);
} else {
return parent::render($this->templateBase . $view, $parameters, $response);
}
}
private function isGibberishMessage(string $message): bool
{
$message = trim($message);
// Trop court = on laisse passer
if (mb_strlen($message) < 10) {
return false;
}
// Aucun espace → très suspect
if (strpos($message, ' ') === false) {
// Uniquement lettres/chiffres
if (preg_match('/^[a-zA-Z0-9]+$/', $message)) {
return true;
}
}
// Ratio voyelles / longueur
preg_match_all('/[aeiouyAEIOUY]/', $message, $matches);
$vowelCount = count($matches[0]);
$length = mb_strlen($message);
if ($vowelCount / $length < 0.2) {
return true;
}
return false;
}
private function isSpamContent(string $message): bool
{
if (mb_strlen(trim($message)) < 10) {
return false;
}
// Mots-clés spam (casino, crypto, etc.)
$spamKeywords = [
'free',
'casino', 'poker', 'slots', 'jackpot', 'gambling',
'crypto', 'bitcoin', 'btc', 'ethereum', 'nft', 'binance', 'cryptomonnaie',
'forex', 'trading', 'investment opportunity', 'make money',
'viagra', 'cialis', 'pharma',
'click here', 'cliquez ici', 'buy now', 'achetez maintenant',
'paid to write', 'writing job', 'work from home', 'earn money', 'job quiz',
'passive income', 'side hustle', 'full training', 'no experience',
'make money online', 'extra income', 'financial freedom', 'be your own boss',
];
$messageLower = mb_strtolower($message);
foreach ($spamKeywords as $keyword) {
if (str_contains($messageLower, $keyword)) {
return true;
}
}
// Liens dans le message (http:// ou https:// uniquement — www. et .com seuls génèrent trop de faux positifs)
if (preg_match('/https?:\/\//i', $message)) {
return true;
}
// Majorité de mots en majuscules (plus de 50% des mots de 3+ lettres)
preg_match_all('/\b[A-Z]{3,}\b/', $message, $capsWords);
preg_match_all('/\b\w{3,}\b/', $message, $allWords);
if (!empty($allWords[0]) && count($capsWords[0]) / count($allWords[0]) > 0.5) {
return true;
}
// Répétition excessive d'un même caractère (5+ fois de suite)
if (preg_match('/(.)\1{4,}/', $message)) {
return true;
}
// Longue séquence de chiffres (12+ consécutifs — évite les numéros de téléphone à 10 chiffres)
if (preg_match('/\d{12,}/', $message)) {
return true;
}
return false;
}
protected function redirectRouteV2(Request $request, string $prefix = null): ?string
{
$redirect = null;
$p = $request->getRequestUri();
$pos = strpos($p, '?');
if ($pos !== false) {
$p = substr($p, 0, $pos + 1) . str_replace('?', '&', substr($p, $pos + 1));
}
$route = parse_url($p);
if (isset($route['query'])) {
parse_str($route['query'], $query);
$page = isset($query['p']) ? $query['p'] : null;
$id = isset($query['id']) ? $query['id'] : null;
$date = isset($query['date']) ? $query['date'] : null;
$search = isset($query['q']) ? $query['q'] : null;
if (!empty($page) && !is_null($page)) {
switch ($page) {
case 'transformation-digitale.php':
if (!empty($id) && !is_null($id)) {
$redirect = $this->generateUrl('client_widget_transformation_digitale_show', ['id' => $id, 'slug' => 'actualite']);
} else {
$redirect = $this->generateUrl('client_widget_transformation_digitale_noid');
}
break;
case 'calendrier-fiscal.php':
if (!empty($id) && !is_null($id)) {
$redirect = $this->generateUrl('client_widget_news_fisc_show', ['id' => $id]);
}
break;
case 'actualites-calendrier-fiscal.php':
if (!empty($date) && !is_null($date)) {
$redirect = $this->generateUrl('client_widget_news_fisc_list_show', ['date' => $date]);
}
break;
case 'actualite.php':
if (!empty($id) && !is_null($id)) {
$redirect = $this->generateUrl('client_widget_news_juri_show', ['id' => $id, 'slug' => 'actualite']);
}
break;
case 'bien-etre.php':
if (!empty($id) && !is_null($id)) {
$redirect = $this->generateUrl('client_widget_bien_etre_show', ['id' => $id, 'slug' => 'actualite']);
} else {
$redirect = $this->generateUrl('client_widget_bien_etre_noid');
}
break;
case 'actualite-cabinet.php':
if (!empty($id) && !is_null($id)) {
$redirect = $this->generateUrl('client_widget_news_cabinet_show', ['id' => $id, 'slug' => 'actualite']);
}
break;
case 'recherche.php':
if (!empty($search) && !is_null($search)) {
$redirect = $this->generateUrl('client_widget_recherche', ['q' => $search]);
}
break;
case 'podcast.php':
if (!empty($id) && !is_null($id)) {
$redirect = $this->generateUrl('client_widget_podcast_detail', ['id' => $id]);
} else {
$redirect = $this->generateUrl('client_widget_podcast_detail', ['id' => "list"]);
}
break;
case 'podcast-latest.php':
$podcats_latest = $this->serviceQMV2->getPodcastLatest();
$redirect = $this->generateUrl('client_widget_podcast_detail', ['id' => $podcats_latest[0]->getId()]);
break;
case 'infographie-rse.php':
if (!empty($id) && !is_null($id)) {
$redirect = $this->generateUrl('client_widget_infographie_rse_detail', ['id' => $id]);
} else {
$redirect = $this->generateUrl('client_widget_infographie_rse_detail_noid');
}
break;
case 'participation.php':
$redirect = $this->generateUrl('client_widget_participation_pal');
break;
case 'journal.php':
if (!empty($id) && !is_null($id)) {
$redirect = $this->generateUrl('client_widget_journal_show', ['id' => $id]);
} else {
$redirect = $this->generateUrl('client_widget_journal_noid');
}
break;
case 'aide.php':
if (!empty($id) && !is_null($id)) {
$redirect = $this->generateUrl('client_widget_aide_show', ['id' => $id]);
} else {
$redirect = $this->generateUrl('client_widget_aide_noid');
}
break;
case 'calendrier-juridique.php':
if ($date) {
$redirect = $this->generateUrl('client_widget_news_fisc_list_show', ['date' => $date]);
} else {
$redirect = $this->generateUrl('client_widget_calendrier_show');
}
break;
default:
// NOPE
break;
}
}
}
return $redirect ? $prefix . $redirect : $redirect;
}
}