Source of file TICController.php

Size: 8,739 Bytes - Last Modified: 2023-11-16T22:56:02+01:00

/home/websites/teicee/packagist/site/phpdoc/conf/../vendor/teicee/core-bundle/src/Base/TICController.php

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
<?php
namespace TIC\CoreBundle\Base;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;

/**
 * Controlleur de base disposant de nombreuses méthodes utiles.
 * @see https://symfony.com/doc/current/controller.html
 */
abstract class TICController extends AbstractController
{
	// Initialisation automatique des propriétés à partir du nom de la classe courante
	use \TIC\CoreBundle\Traits\ContextProperties;   // ctxForm, ctxTrans...
	use \TIC\CoreBundle\Traits\ControllerRouting;   // redirectTo, redirectItem, redirectList...

	// ----------------------------------------------------------------- FORMS

	/**
	 * Raccourci pour créer le formulaire correspondant au controlleur.
	 *
	 * @param   mixed           $item
	 * @param   Request         $request
	 * @param   array           $options        Options du formulaire + facultatif "form_name" pour spécifier son nommage
	 * @return  FormInterface
	 */
	protected function getForm(mixed $data = null, ?Request $request = null, ?array $options = []): FormInterface
	{
#		$form = $this->createForm($this->ctxForm, $data, $options);
		
		// Génération du FormType (avec ma méthode createNamed sur présence de l'option "form_name")
		if (isset($options['form_name'])) {
			$name = $options['form_name']; unset($options['form_name']);
			$form = $this->container->get('form.factory')->createNamed($name, $this->ctxForm, $data, $options);
		} else {
			$form = $this->container->get('form.factory')->create($this->ctxForm, $data, $options);
		}
		
		// Application des données de la requête (si soumission et $request transmis)
		if ($request !== null) $form->handleRequest($request);
		
		return $form;
	}

	// ----------------------------------------------------------------- TRANSLATOR


	/**
	 * Raccourci vers la méthode trans du translator.
	 *
	 * @param   atring|array    $message        Clé de message à traduire (possibilité de traiter une liste)
	 * @param   array           $parameters     Liste de paramètres à fournir à la méthode du translator
	 * @return  string|array                    Chaine traduite (ou liste des chaines traduites)
	 */
	protected function trans(mixed $message, array $parameters = []): mixed
	{
		if (\is_array($message)) {
			foreach ($message as $key => $val) $message[$key] = $this->get('translator')->trans($val, $parameters);
			return $message;
		}
		return $this->get('translator')->trans($message, $parameters);
	}

	/**
	 *
	 */
	protected function mesg(string $mesg): string
	{
		return $this->ctxTrans . $mesg;
	}

	/**
	 * Retourne le nom (traduit, au singulier ou au pluriel) de l'objet du controlleur.
	 */
	protected function getItemLabel(bool $pluriel = false): string
	{
		return $this->trans(\sprintf('%s.names.%s', $this->ctxTrans, ($pluriel) ? 'plur' : 'sing'));
	}

	// ----------------------------------------------------------------- REDIRECT

	/**
	 * Ajout d'un message dans le FlashBag (avec redirection optionnelle).
	 *
	 * @param   string          $type           Contexte bootstrap : success | info | warning | danger
	 * @param   string|array    $message        Texte simple ou structure avec : title [text] [extra] [params]
	 * @param   mixed           $redirect       Boolean (liste/referer), Entité (ou id) ou autre route
	 * @return  RedirectResponse                Réponse HTTP avec redirection selon le paramètre $redirect
	 */
	protected function alert(string $type, mixed $message, mixed $redirect = null): ?RedirectResponse
	{
		$this->addFlash($type, $message);
		return $this->redirectTo($redirect);
	}

	// ----------------------------------------------------------------- DOWNLOAD

	/**
	 * Retourne les entêtes HTTP pour le téléchargement d'un fichier du serveur.
	 *
	 * @param   string          $filepath       Chemin complet sur le système de fichier
	 * @param   string          $filename       Nom à présenter pour le téléchargement (depuis $filepath par défaut)
	 * @param   bool            $inline         Faux pour forcer le téléchargement
	 * @param   bool|string     $mimetype       Type MIME (deviné par défaut, application/octet-stream si True)
	 * @return  array                           Liste d'entêtes HTTP pour une Response
	 */
	protected function fileHeaders(string $filepath, ?string $filename, $mimetype, ?bool $inline): array
	{
		if ($filepath === null)        throw new \Exception('alert.file.download.notexist');
		if (! \file_exists($filepath)) throw new \Exception('alert.file.download.notfound');
		if ($filename === null) $filename = \basename($filepath);
		
		if ($mimetype === null) $mimetype = ($inline) ? true : "application/octet-stream";
		if ($mimetype === true) {
			$fileinst = new \Symfony\Component\HttpFoundation\File\File($filepath);
			$mimetype = $fileinst->getMimeType();
		}
		$disposition = ($inline) ? "inline" : "attachment";
		
		return array(
			'Content-Type'         => $mimetype,
			'Content-Disposition'  => $disposition . '; filename="'. $filename .'"',
			'Content-Length'       => \filesize($filepath),
		);
	}

	/**
	 * Méthode utilitaire pour retourner un fichier en réponse d'une action.
	 *
	 * @param   string          $filepath       Chemin complet sur le système de fichier
	 * @param   string          $filename       Nom à présenter pour le téléchargement (depuis $filepath par défaut)
	 * @param   bool            $inline         Faux pour forcer le téléchargement
	 * @param   bool|string     $mimetype       Type MIME (deviné par défaut, application/octet-stream si True)
	 * @return  Response                        Réponse HTTP contenant les données du fichier
	 */
	protected function fileDownload(string $filepath, ?string $filename=null, ?bool $inline=false, $mimetype=null): Response
	{
		$headers = $this->fileHeaders($filepath, $filename, $mimetype, $inline);
		return new Response(\file_get_contents($filepath), 200, $headers);
	}

	/**
	 * Méthode utilitaire pour retourner un fichier en réponse d'une action (version stream avec option delete).
	 *
	 * @param   string          $filepath       Chemin complet sur le système de fichier
	 * @param   string          $filename       Nom à présenter pour le téléchargement (depuis $filepath par défaut)
	 * @param   bool            $inline         Faux pour forcer le téléchargement
	 * @param   bool|string     $mimetype       Type MIME (deviné par défaut, application/octet-stream si True)
	 * @param   bool            $delete         Suppression du fichier à la fin du téléchargement
	 * @return  StreamedResponse                Réponse HTTP avec callback pour transmission des données du fichier
	 */
	protected function fileStream(string $filepath, ?string $filename=null, ?bool $inline=false, $mimetype=null, ?bool $delete=false): StreamedResponse
	{
		$headers = $this->fileHeaders($filepath, $filename, $mimetype, $inline);
		return new StreamedResponse(function() use ($filepath, $delete) {
			\readfile($filepath);
			if ($delete === true) @\unlink($filepath);
		}, 200, $headers);
	}

	// ----------------------------------------------------------------- SECURITY

	/**
	 * Vérification de la validité d'un token CSRF dans la requête.
	 *
	 * @param   string          $name           Clé du token à vérifier dans la requête
	 * @param   Request         $request        Requête courante pour récupération de la valeur du token
	 * @return  bool
	 */
	protected function checkCSRF(string $name, ?Request $request = null): ?bool
	{
		if (empty($request)) return null;
		$token = $request->request->get('_token');
		if ($token === null) $token = $request->query->get('_token');
		return $this->isCsrfTokenValid($name, $token);
	}

	/**
	 * Vérification que l'utilisateur courant dispose de l'un des droits mentionnés.
	 *
	 * @param   string|array    $roles          Liste des rôles requis (test 'OR'), sans le préfixe 'ROLE_'
	 * @param   string|bool     $exception      Message pour lever une exception (True pour le message par défaut)
	 * @return  bool                            True si l'un des rôles est trouvé, sinon False (si $exception à False)
	 */
	protected function checkRole(mixed $roles, mixed $exception = false): bool
	{
		if (! \is_array($roles)) $roles = \explode(',', $roles);
		foreach ($roles as $role) {
			if ($this->isGranted('ROLE_' . $role)) return true;
		}
		if ($exception === false) return false;
		if ($exception === true) $exception = 'error.role.access_denied';
		throw $this->createAccessDeniedException($this->trans($exception));
	}

}