<?php
namespace App\Controller;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\RequestStack;
use Psr\Log\LoggerInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpKernel\KernelInterface;
// Security
use Symfony\Component\Security\Core\Security;
// PDF
use Knp\Snappy\Pdf;
// DateTime
use DateTime;
// Files
use Symfony\Component\String\Slugger\SluggerInterface;
// Controller
// Classes
use App\Entity\Utilisateur;
use App\Entity\Eleve;
use App\Entity\Enseignant;
use App\Entity\Enseignantcours;
use App\Entity\Cursus;
use App\Entity\Anneeuniversitaire;
use App\Entity\Semestre;
use App\Entity\Typecours;
use App\Entity\Qualification;
use App\Entity\Competence;
use App\Entity\Composante;
use App\Entity\Diplome;
use App\Entity\Note;
use App\Entity\Niveau;
use App\Entity\Fichier;
use App\Entity\Statutimport;
use App\Entity\Cours;
use App\Entity\Epreuve;
use App\Entity\Evaluation;
use App\Entity\Evaluationeleve;
use App\Entity\Inscription;
use App\Entity\Typeimport;
use App\Entity\Modelenotif;
use App\Entity\Parametre;
#[Route('/secured')]
class EclacController extends AbstractController
{
public string $versionapp = "v1.4.0";
public string $app_env;
public bool $app_public;
public string $php_version;
public string $projectDir;
public function __construct(
public Security $security,
private Pdf $knpSnappyPdf,
private EntityManagerInterface $entityManager,
private SluggerInterface $slugger,
private RequestStack $requestStack,
private UrlGeneratorInterface $urlGenerator,
private ParameterBagInterface $params,
private KernelInterface $kernel,
private LoggerInterface $compassLogger,
) {
$this->app_env = $params->get('app_env');
$this->app_public = $params->get('app_public');
$this->php_version = $params->get('php_version');
$this->projectDir = $kernel->getProjectDir();
if ($this->requestStack->getCurrentRequest() != null) {
if ($this->requestStack->getCurrentRequest()->hasSession()) {
$session = $this->requestStack->getSession();
// Variable session parametre
$parametre = $this->entityManager->getRepository(Parametre::class)->findOneBy(array(
'idparametre' => 1
));
$session->set('parametre', $parametre);
// Variable session élève DEMO
$jeanneDupont = $this->entityManager->getRepository(Eleve::class)->findOneBy(array(
'numeroidentification' => "DEMO"
));
if ($jeanneDupont != null) {
$session->set('jeanneDupont', true);
$session->set('jeanneDupontNom', $jeanneDupont->getPrenomeleve() . ' ' . $jeanneDupont->getNomeleve());
} else {
$session->set('jeanneDupont', false);
$session->set('jeanneDupontNom', null);
}
// Variable session élève DEMOFINALE
$jeanDupont = $this->entityManager->getRepository(Eleve::class)->findOneBy(array(
'numeroidentification' => "DEMOFINALE"
));
if ($jeanDupont != null) {
$session->set('jeanDupont', true);
$session->set('jeanDupontNom', $jeanDupont->getPrenomeleve() . ' ' . $jeanDupont->getNomeleve());
} else {
$session->set('jeanDupont', false);
$session->set('jeanDupontNom', null);
}
// Variable session anneeUniversitaire
$now = new DateTime('now');
if ($session->get('anneeUniversitaire') == null) {
$anneesuniversitaire = $entityManager->getRepository(Anneeuniversitaire::class)->findAll();
// On recherche si une année existe pour la date actuelle
$query = $entityManager
->createQuery(
'SELECT a
FROM App\Entity\Anneeuniversitaire a
WHERE a.datedebut <= :now
AND a.datefin >= :now'
)
->setParameter('now', $now);
$anneeUniversitaireQuery = $query->getResult();
// Si oui, on la définit en session
if (!empty($anneeUniversitaireQuery)) {
$session->set('anneeUniversitaire', $anneeUniversitaireQuery[0]);
} // Sinon on choisit la dernière année créée
else {
$session->set('anneeUniversitaire', $anneesuniversitaire[0]);
}
}
// Variable session cursus
if ($session->get('cursus') == null) {
$cursus = $entityManager->getRepository(Cursus::class)->findAll();
$query = $entityManager
->createQuery(
'SELECT c
FROM App\Entity\Cursus c
ORDER BY c.libellecursus ASC'
);
$cursus = $query->getResult();
$session->set('cursus', $cursus[0]);
}
$importsEnErreur = $this->entityManager->getRepository(Fichier::class)->findBy(array(
"statutimport" => 3,
));
if (!empty($importsEnErreur)) {
$session->set('importEnErreur', true);
} else {
$session->set('importEnErreur', false);
}
if ($parametre->isModemaintenance()) {
$session->set('modeMaintenance', true);
if ($session->get('droitadmin') != null) {
if (str_contains($this->requestStack->getMainRequest()->getRequestUri(), '/secured/')) {
if ($session->get('droitadmin') != true) {
if ($this->requestStack->getMainRequest()->attributes->get('_route') != "app_eclac_maintenance"
&& $this->requestStack->getMainRequest()->attributes->get('_route') != "app_eclac_secure-index"
&& str_contains($this->requestStack->getMainRequest()->getRequestUri(), '/secured/')) {
$response = new RedirectResponse($this->urlGenerator->generate('app_eclac_maintenance'));
$response->send();
exit;
}
}
}
}
} else {
$session->set('modeMaintenance', false);
}
if ($this->versionapp != $parametre->getVersionapp()) {
$session->set('afficheNavbarre', false);
$session->set('messageMaintenance', 'messages');
// On ne redirige que les utilisateurs déjà connectés : sinon, le formulaire de
// connexion (app_public_login) déclenche lui aussi cette redirection avant même
// d'avoir pu authentifier l'utilisateur, ce qui crée une boucle avec le SSO Entra
// (ré-authentification silencieuse -> maintenance -> ré-authentification -> ...)
if ($this->security->getUser() !== null
&& $this->requestStack->getMainRequest()->attributes->get('_route') != "app_eclac_maintenance") {
$session->set('afficheNavbarre', false);
$response = new RedirectResponse($this->urlGenerator->generate('app_eclac_maintenance'));
$response->send();
exit;
}
} else {
$session->set('afficheNavbarre', true);
$session->set('messageMaintenance', 'libelles');
}
}
}
}
#[Route('/maintenance', name: 'app_eclac_maintenance', methods: ['GET'])]
public function maintenance(Request $request): Response
{
return $this->render('eclac/maintenance.html.twig', []);
}
#[Route('/', name: 'app_eclac_secure-index', methods: ['GET'])]
public function secureIndex(Request $request): Response
{
// Variables communes
$entityManager = $this->entityManager;
$session = $request->getSession();
// On récupère l'utilisateur
$utilisateur = $entityManager->getRepository(Utilisateur::class)->findOneBy(array(
'idutilisateur' => $session->get('utilisateurconnecte')->getIdutilisateur()
));
// On regarde si la personne connectée est un élève
$eleve = $entityManager->getRepository(Eleve::class)->findOneBy(
array(
'utilisateur' => $utilisateur->getIdutilisateur()
)
);
if ($eleve != null) {
return $this->redirectToRoute('app_ecleleve_index', [], Response::HTTP_SEE_OTHER);
} else {
return $this->redirectToRoute('app_public_index', [], Response::HTTP_SEE_OTHER);
}
}
// Permet de se connecter à l'application
#[Route('/choix-espace/{switch}', name: 'app_eclac_choix-espace', methods: ['GET', 'POST'], defaults: ['switch' => 'switch'])]
public function choixEspace(Request $request, ?string $switch): Response
{
// La personne a besoin d'être identifiée pour accéder à la ressource
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED');
// Variables communes
$entityManager = $this->entityManager;
$session = $request->getSession();
// RAZ des variables
$session->set('droitadmin', false);
$session->set('utilisateurconnecte', null);
$eleve = null;
//echo "ON est la ";die();
// error_log($token->getUser()->getUserIdentifier());
/*
$utilisateurCAS = $this->security->getUser();
// On regarde si la personne connectée est un élève
$utilisateur = $entityManager->getRepository(Utilisateur::class)->findOneBy(
array(
'login' => $utilisateurCAS->getUserIdentifier()
)
);
*/
$utilisateurCAS = $this->security->getUser();
$identifier = $utilisateurCAS->getUserIdentifier();
// Entra renvoie un email (ex: ext-bruyas@em-lyon.com) ; CAS/LDAP renvoie déjà le login
$login = str_contains($identifier, '@')
? strtolower(explode('@', $identifier)[0])
: $identifier;
$utilisateur = $entityManager->getRepository(Utilisateur::class)->findOneBy(
array(
'login' => $login
)
);
if ($utilisateur == null) {
// Login non trouvé dans UTILISATEUR : on déconnecte l'utilisateur de Symfony. Sinon il reste
// authentifié et la page de connexion le renvoie ici en boucle (choix-espace -> index ->
// form_login -> choix-espace...). Il retrouve la page de connexion avec le message.
$this->logError("Connexion refusée, login non trouvé : " . $login . " (identifiant reçu : " . $identifier . ")", "connexion");
$this->container->get('security.token_storage')->setToken(null);
$session->invalidate();
// Après invalidate() : le message est stocké dans la nouvelle session
$this->addFlash('error', 'Votre compte (' . $login . ') n\'est pas autorisé dans ComPass. Contactez l\'administrateur.');
return $this->redirectToRoute('app_public_login', [], Response::HTTP_SEE_OTHER);
} else {
$session->set('utilisateurconnecte', $utilisateur);
// On regarde en priorité si la personne connectée est enseignant (si elle a aussi un profil élève, l'espace enseignant prime)
$enseignant = $entityManager->getRepository(Enseignant::class)->findOneBy(
array(
'utilisateur' => $utilisateur->getIdutilisateur()
)
);
if ($enseignant != null) {
$session->set('utilisateur', $enseignant);
$session->set('typeUtilisateur', 'Enseignant');
$session->set('enseignant', $enseignant);
// On vérifie si l'enseignant est un administrateur
if ($enseignant->isAdministrateur()) {
$session->set('droitadmin', true);
if($switch == 'switch') {
if ($session->get('vueAdministrateur') == true) {
$session->set('vueAdministrateur', false);
} else {
$session->set('vueAdministrateur', true);
}
}
} else {
$session->set('droitadmin', false);
}
return $this->redirectToRoute('app_eclenseignant_index', [], Response::HTTP_SEE_OTHER);
}
// Sinon on regarde si c'est un élève
else {
$eleve = $entityManager->getRepository(Eleve::class)->findOneBy(
array(
'utilisateur' => $utilisateur->getIdutilisateur()
)
);
if ($eleve != null) {
$session->set('utilisateur', $eleve);
$session->set('typeUtilisateur', 'Élève');
$session->set('droitadmin', false);
$session->set('vueAdministrateur', false);
return $this->redirectToRoute('app_ecleleve_index', [], Response::HTTP_SEE_OTHER);
}
// Sinon, il n'est ni enseignant, ni élève, il faut le déconnecter et contacter un administrateur
else {
return $this->redirectToRoute('app_logout', [], Response::HTTP_SEE_OTHER);
}
}
}
}
// Paramètres
#[Route('/parametre', name: 'app_eclac_parametre', methods: ['GET', 'POST'])]
public function parametre(Request $request): Response
{
// La personne a besoin d'être identifiée pour accéder à la ressource
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED');
// Variables communes
$entityManager = $this->entityManager;
$session = $request->getSession();
if ($session->get('vueAdministrateur') != true) {
$this->addFlash('error', 'Vous n\'avez pas les droits d\'accéder à cette ressource');
return $this->redirectToRoute('app_eclac_choix-espace', [], Response::HTTP_SEE_OTHER);
}
$parametre = $entityManager->getRepository(Parametre::class)->findOneBy(array(
'idparametre' => 1
));
if (isset($_POST['activeMaintenance'])) {
$parametre->setModemaintenance(true);
$entityManager->persist($parametre);
$entityManager->flush();
$session->set('modeMaintenance', true);
}
if (isset($_POST['desactiveMaintenance'])) {
$parametre->setModemaintenance(false);
$entityManager->persist($parametre);
$entityManager->flush();
$session->set('modeMaintenance', false);
}
$session->set('parametre', $parametre);
$notif = $entityManager->getRepository(Modelenotif::class)->findOneBy(array(
'idmodelenotif' => 1
));
return $this->renderForm('eclac/parametre.html.twig', [
'modelnotif' => $notif,
]);
}
public static function overrideFile(string $file, string $contenu): void
{
// On vérifie que le fichier existe
if (!file_exists($file)) {
touch($file);
}
// On vérifie que le fichier est accessible en écriture
if (is_writable($file)) {
// On vérifie qu'on arrive à ouvrir le fichier
if (!$fp = fopen($file, 'w+')) {
throw new \Exception();
}
// On vérifie qu'on arrive à écrire le yaml dans le fichier
if (fwrite($fp, $contenu) === false) {
throw new \Exception();
}
fclose($fp);
} else {
throw new \Exception();
}
}
// Permet de récupérer les composantes d'un élève
public function getComposantesEleve(Eleve $eleve, bool $all)
{
$entityManager = $this->entityManager;
// On récupère les inscriptions de l'élève
$inscriptions = $entityManager->getRepository(Inscription::class)->findBy(array(
"eleve" => $eleve
));
// Puis tous les cours auquel il est inscrit
$arrayIdcours = array();
foreach ($inscriptions as $inscription) {
if (!in_array($inscription->getCours()->getIdcours(), $arrayIdcours)) {
array_push($arrayIdcours, $inscription->getCours()->getIdcours());
}
}
$cours = $entityManager->getRepository(Cours::class)->findBy(array(
'idcours' => $arrayIdcours
));
// On récupère toutes les épreuves de ces cours
$epreuves = $entityManager->getRepository(Epreuve::class)->findBy(array('cours' => $cours));
// On récupère toutes les composantes de ces épreuves
$arrayIdcomposantes = array();
foreach ($epreuves as $epreuve) {
// Épreuve pas encore qualifiée (critères non importés) : pas de composante
if ($epreuve->getComposante() === null) {
continue;
}
if ($all) {
array_push($arrayIdcomposantes, $epreuve->getComposante()->getIdcomposante());
} else {
$arrayIdcomposantes[$epreuve->getComposante()->getCodecomposante()] = $epreuve->getComposante()->getIdcomposante();
}
}
$composantes = $entityManager->getRepository(Composante::class)->findBy(
array(
'idcomposante' => $arrayIdcomposantes,
),
array(
'codecomposante' => 'ASC',
'idcomposante' => 'ASC',
)
);
return $composantes;
}
// Rend propre une chaîne de caractère
public static function sanitizeString(string $string, bool $file = false): string
{
$array = [' ' => "_",
'.' => "_",
'\'' => "_",
'"' => "_",
'…' => "",
'«' => "",
'»' => "",
];
if ($file) {
$array = [' ' => "_",
'\'' => "_",
'"' => "_",
'…' => "",
'«' => "",
'»' => "",
];
}
return strtolower(
EclacController::replaceAccent(
strtr($string, $array)
)
);
}
// Remet le contenu d'un CSV en UTF-8 propre : les anciens exports du client étaient en
// Latin-1/Windows-1252, mais les exports récents sont déjà en UTF-8 (parfois avec BOM).
// utf8_encode() suppose à tort du Latin-1 : appliqué à un fichier déjà en UTF-8, il double-
// encode chaque octet non-ASCII (le BOM devient un artefact "" qui n'est plus reconnu comme
// BOM par le lecteur CSV et reste collé au nom de la première colonne, cassant sa détection).
public static function reencodeUtf8(string $contents): string
{
$contents = preg_replace('/^\xEF\xBB\xBF/', '', $contents);
if (!mb_check_encoding($contents, 'UTF-8')) {
$contents = mb_convert_encoding($contents, 'UTF-8', 'ISO-8859-1');
}
return $contents;
}
// Décrit pourquoi l'écriture d'un fichier d'import a échoué (droits, disque plein...),
// pour distinguer ce cas d'un vrai type d'import non reconnu dans les logs et messages d'erreur.
public static function diagnosticEcritureFichier(string $file): string
{
$dir = dirname($file);
$uid = function_exists('posix_geteuid') ? posix_geteuid() : null;
$user = function_exists('posix_getpwuid') && $uid !== null ? (posix_getpwuid($uid)['name'] ?? 'inconnu') : get_current_user();
return sprintf(
"Échec écriture %s | uid=%s user=%s | fichier: exists=%s writable=%s perms=%s owner=%s | dossier: exists=%s writable=%s perms=%s | disque libre=%s octets",
$file,
$uid ?? 'n/a',
$user,
file_exists($file) ? 'oui' : 'non',
is_writable($file) ? 'oui' : 'non',
file_exists($file) ? substr(sprintf('%o', fileperms($file)), -4) : 'n/a',
file_exists($file) && function_exists('posix_getpwuid') ? (posix_getpwuid(fileowner($file))['name'] ?? fileowner($file)) : 'n/a',
is_dir($dir) ? 'oui' : 'non',
is_writable($dir) ? 'oui' : 'non',
is_dir($dir) ? substr(sprintf('%o', fileperms($dir)), -4) : 'n/a',
(string) (disk_free_space($dir) ?: 'n/a')
);
}
// Remplace les accents
public static function replaceAccent(string $string): string
{
$unwanted_array = array(
'À' => 'A',
'Á' => 'A',
'Â' => 'A',
'Ã' => 'A',
'Ä' => 'A',
'Å' => 'A',
'Æ' => 'A',
'à' => 'a',
'á' => 'a',
'â' => 'a',
'ã' => 'a',
'ä' => 'a',
'å' => 'a',
'æ' => 'a',
'Ç' => 'C',
'ç' => 'c',
'È' => 'E',
'É' => 'E',
'Ê' => 'E',
'Ë' => 'E',
'è' => 'e',
'é' => 'e',
'ê' => 'e',
'ë' => 'e',
'Ì' => 'I',
'Í' => 'I',
'Î' => 'I',
'Ï' => 'I',
'ì' => 'i',
'í' => 'i',
'î' => 'i',
'ï' => 'i',
'Ñ' => 'N',
'ñ' => 'n',
'Ò' => 'O',
'Ó' => 'O',
'Ô' => 'O',
'Õ' => 'O',
'Ö' => 'O',
'Ø' => 'O',
'ð' => 'o',
'ò' => 'o',
'ó' => 'o',
'ô' => 'o',
'õ' => 'o',
'ö' => 'o',
'Š' => 'S',
'š' => 's',
'Ù' => 'U',
'Ú' => 'U',
'Û' => 'U',
'Ü' => 'U',
'ù' => 'u',
'ú' => 'u',
'û' => 'u',
'Ý' => 'Y',
'ý' => 'y',
'Ž' => 'Z',
'ž' => 'z',
'Þ' => 'B',
'ß' => 'Ss',
'þ' => 'b',
'ÿ' => 'y',
"\n" => ' '
);
$newString = strtr($string, $unwanted_array);
return $newString;
}
public function createTempDoc(string $path, string $docName): string
{
$now = date_format(new DateTime(), ('Ymd'));
$outputPathBase = "";
$file = $path . $docName;
if (file_exists($file)) {
// On génère un random
$random = "";
for ($i = 1; $i < 15; $i++) {
$random .= mt_rand(1, 10);
}
if (!is_dir($this->projectDir . "/public/docs/tmp/")) {
if (!mkdir($this->projectDir . "/public/docs/tmp/", 0775, true)) {
$this->addFlash('error', 'Erreur à la création du dossier');
}
}
$pathRoot = $this->projectDir;
$outputPath = "/public/docs/tmp/";
$outputFile = "temp-" . $now . "-" . $random . "-" . $docName;
$outputComplet = $pathRoot . $outputPath . $outputFile;
$outputPathBase = "/docs/tmp/" . $outputFile;
if (!copy($file, $outputComplet)) {
$this->addFlash('error', 'Document non trouvé');
}
} else {
$this->addFlash('error', 'Document non trouvé');
}
return $outputPathBase;
}
// Supprime les copies publiques de /docs/tmp/ plus anciennes que $ageMaxSecondes. Sans risque pour
// les liens : getCheminTempCertificat() recrée la copie à la demande depuis l'original.
public function purgeDocsTemp(int $ageMaxSecondes = 86400): int
{
$limite = time() - $ageMaxSecondes;
$nbSupprimes = 0;
foreach(glob($this->projectDir . "/public/docs/tmp/temp-*") ?: [] as $fichier)
{
if(is_file($fichier) && filemtime($fichier) < $limite && unlink($fichier))
{
$nbSupprimes++;
}
}
if($nbSupprimes > 0)
{
$this->logError($nbSupprimes . " fichier(s) temporaire(s) supprimé(s) de /docs/tmp/", "purgeDocsTemp");
}
return $nbSupprimes;
}
// Retourne le chemin public (dans /docs/tmp/) du certificat de ce diplôme, en recréant la copie
// temporaire à partir de l'original (docs/certificat-competence/, non accessible) si elle a été purgée.
// Retourne NULL si l'original n'existe pas.
public function getCheminTempCertificat(Diplome $diplome): ?string
{
$pathRoot = $this->projectDir . '/public';
if($diplome->getChemintemp() != NULL && file_exists($pathRoot . $diplome->getChemintemp()))
{
return $diplome->getChemintemp();
}
$cheminTemp = $this->createTempDoc($pathRoot . '/../docs/certificat-competence/', 'certif_competences_' . $diplome->getNumerodiplome() . '.pdf');
if($cheminTemp == "")
{
return NULL;
}
$diplome->setChemintemp($cheminTemp);
$this->entityManager->persist($diplome);
$this->entityManager->flush();
return $cheminTemp;
}
// Journal applicatif unique : logs/compass-AAAA-MM-JJ.log (canal Monolog "compass", voir monolog.yaml).
// $typeLog (ex. "cronPreImport") n'est plus un nom de fichier mais une étiquette en tête du message ;
// $contexte (ex. ['fichier' => 1068]) est ajouté en fin de ligne au format JSON.
// logError() était historiquement utilisé pour tous les messages : le niveau est déduit du texte
// tant que les appels n'ont pas été remplacés par logInfo()/logWarning().
public function logError(string $string, string $typeLog, array $contexte = []): void
{
$niveau = preg_match('/erreur|impossible|échou|echou|exception|introuvable|non trouvé|refus|rejet|orphelin|stoppé/iu', $string) ? 'error' : 'info';
$this->journaliser($niveau, $string, $typeLog, $contexte);
}
public function logWarning(string $string, string $typeLog, array $contexte = []): void
{
$this->journaliser('warning', $string, $typeLog, $contexte);
}
public function logInfo(string $string, string $typeLog, array $contexte = []): void
{
$this->journaliser('info', $string, $typeLog, $contexte);
}
private function journaliser(string $niveau, string $string, string $typeLog, array $contexte): void
{
if ($string == '') {
$string = "Message d'erreur non défini";
}
if ($typeLog == '') {
$typeLog = "NC";
}
$this->compassLogger->log($niveau, '[' . $typeLog . '] ' . $string, $contexte);
}
// Recrée l'utilisateur système (id=3) utilisé par les commandes cron si jamais il a été supprimé
public function ensureUtilisateurSysteme(): void
{
$conn = $this->entityManager->getConnection();
$conn->executeStatement(
"INSERT IGNORE INTO UTILISATEUR (idutilisateur, actifutilisateur, datecrea, datemaj, login, password, pers_id)
VALUES (3, 0, NOW(), NOW(), ?, NULL, NULL)",
['Automatique']
);
}
public static function dateToMySQL(string $date): DateTime
{
$tabDate = explode('/', $date);
$date = $tabDate[2].'-'.$tabDate[1].'-'.$tabDate[0];
$date = new DateTime(date('Y-m-d H:i:s', strtotime($date)));
return $date;
}
// Permet de générer un PDF à partir d'une vue twig
public function generatePDF(string $html, string $path, string $url): bool
{
$knpSnappyPdf = $this->knpSnappyPdf;
$parametre = $this->entityManager->getRepository(Parametre::class)->findOneBy(array(
"idparametre" => 1,
));
$header = $this->renderView('pdf/header' . $parametre->getNompdf() . '.html.twig', [
"url" => $url,
'parametre' => $parametre,
]);
$footer = $this->renderView('pdf/footer' . $parametre->getNompdf() . '.html.twig', [
'parametre' => $parametre,
]);
try {
$knpSnappyPdf->setOptions(
[
'page-size' => 'A4',
'margin-top' => '1.5in',
'margin-right' => '0in',
'margin-bottom' => '0.35in',
'margin-left' => '0in',
'header-html' => $header,
'footer-html' => $footer,
'disable-smart-shrinking' => true,
'enable-local-file-access' => true,
'enable-external-links' => true,
'enable-internal-links' => true,
'enable-javascript' => true,
'debug-javascript' => true,
// Le graphique (Chart.js, animations désactivées) est dessiné dès le chargement de la page :
// 200 ms suffisent. Le délai est payé à chaque certificat (il était de 2 s).
'javascript-delay' => 200,
'no-stop-slow-scripts' => true,
'encoding' => 'UTF-8',
'toc' => false,
'dpi' => 96,
'zoom' => 1,
'viewport-size' => '1920x1080',
]
)
->generateFromHtml($html, $path);
return true;
} catch (\Exception $e) {
$this->logError($e->getMessage(), "generateAllDegrees");
return false;
}
}
}