<?php

/**
 * This file is part of Zwii.
 *
 * For full copyright and license information, please see the LICENSE
 * file that was distributed with this source code.
 *
 * @author Rémi Jean <remi.jean@outlook.com>
 * @copyright Copyright (C) 2008-2018, Rémi Jean
 * @author Frédéric Tempez <frederic.tempez@outlook.com>
 * @copyright Copyright (C) 2018-2025, Frédéric Tempez
 * @license CC Attribution-NonCommercial-NoDerivatives 4.0 International 
 * @Copyright (C) 2026, Frédéric Tempez
 * @Licensed under the GNU General Public License v3.0 or later.
 * @link http://zwiicms.fr/
 */
class gallery extends common
{
	const VERSION = '6.0';
	const REALNAME = 'Galerie';
	const DATADIRECTORY = self::DATA_DIR . 'gallery/';
	const SORT_ASC = 'SORT_ASC';
	const SORT_DSC = 'SORT_DSC';
	const SORT_HAND = 'SORT_HAND';

	public static $directories = [];

	public static $firstPictures = [];

	public static $galleries = [];

	public static $galleriesId = [];

	public static $pictures = [];

	public static $picturesId = [];

	public static $thumbs = [];

	public static $config = [];

	public static $actions = [
		'config' => self::ROLE_EDITOR,
		'delete' => self::ROLE_EDITOR,
		'dirs' => self::ROLE_EDITOR,
		'sortGalleries' => self::ROLE_EDITOR,
		'sortPictures' => self::ROLE_EDITOR,
		'edit' => self::ROLE_EDITOR,
		'add' => self::ROLE_EDITOR,
		'theme' => self::ROLE_EDITOR,
		'option' => self::ROLE_EDITOR,
		'index' => self::ROLE_VISITOR
	];

	public static $sort = [
		self::SORT_ASC => 'Alphabétique ',
		self::SORT_DSC => 'Alphabétique inverse',
		self::SORT_HAND => 'Manuel'
	];

	public static $galleryThemeFlexAlign = [
		'flex-start' => 'À gauche',
		'center' => 'Au centre',
		'flex-end' => 'À droite',
		'space-around' => 'Distribué avec marges',
		'space-between' => 'Distribué sans marge',
	];

	public static $galleryThemeAlign = [
		'left' => 'À gauche',
		'center' => 'Au centre',
		'right' => 'À droite'
	];

	public static $galleryThemeSizeWidth = [
		'9em' => 'Très petite',
		'12em' => 'Petite',
		'15em' => 'Moyenne',
		'18em' => 'Grande',
		'21em' => 'Très grande',
		'100%' => 'Proportionnelle'
	];

	public static $galleryThemeSizeHeight = [
		'9em' => 'Très petite',
		'12em' => 'Petite',
		'15em' => 'Moyenne',
		'18em' => 'Grande',
		'21em' => 'Très grande'
	];

	public static $galleryThemeLegendHeight = [
		'.125em' => 'Très petite',
		'.25em' => 'Petite',
		'.375em' => 'Moyenne',
		'.5em' => 'Grande',
		'.625em' => 'Très grande'
	];

	public static $galleryThemeBorder = [
		'0em' => 'Aucune',
		'.1em' => 'Très fine',
		'.3em' => 'Fine',
		'.5em' => 'Moyenne',
		'.7em' => 'Epaisse',
		'.9em' => 'Très épaisse'
	];

	public static $galleryThemeOpacity = [
		'1' => 'Aucun ',
		'.9' => 'Très Discrète',
		'.8' => 'Discrète',
		'.7' => 'Moyenne',
		'.6' => 'Forte',
		'.5' => 'Très forte'
	];

	public static $galleryThemeMargin = [
		'0em' => 'Aucune',
		'.1em' => 'Très petite',
		'.3em' => 'Petite',
		'.5em' => 'Moyenne',
		'.7em' => 'Grande',
		'.9em' => 'Très grande'
	];

	public static $galleryThemeRadius = [
		'0em' => 'Aucun',
		'.3em' => 'Très léger',
		'.6em' => 'Léger',
		'.9em' => 'Moyen',
		'1.2em' => 'Important',
		'1.5em' => 'Très important'
	];

	public static $galleryThemeShadows = [
		'0px' => 'Aucune',
		'1px 1px 5px' => 'Très légère',
		'1px 1px 10px' => 'Légère',
		'1px 1px 15px' => 'Moyenne',
		'1px 1px 25px' => 'Importante',
		'1px 1px 50px' => 'Très importante'
	];

	public static $galleryOptionBackPosition = [
		'none' => 'Masqué',
		'top' => 'Au-dessus',
		'both' => 'Au-dessus et en dessous',
		'bottom' => 'En dessous',
	];

	public static $galleryOptionBackAlign = [
		'left' => 'A gauche',
		'center' => 'Au centre',
		'right' => 'A droite',
	];

	public static $formOptionSelect = '';

	/**
	 * Constructeur : initialise les données du module dès l'instanciation.
	 */
	public function __construct()
	{
		parent::__construct();
		$this->update();
	}

	/**
	 * Mise à jour du module
	 * Appelée par les fonctions index et config
	 */
	private function update()
	{
		// Initialisation du module, créer les données si elles sont manquantes.
		$this->init();

		$versionData = $this->getData(['module', $this->getUrl(0), 'config', 'versionData']);
		// Mise à jour 3.1
		if (version_compare($versionData, '3.1', '<')) {
			if (is_dir(self::DATADIRECTORY . 'pages/')) {
				// Déplacer les données du dossier Pages
				$this->copyDir(self::DATADIRECTORY . 'pages/' . $this->getUrl(0), self::DATADIRECTORY . $this->getUrl(0));
				$this->deleteDir(self::DATADIRECTORY . 'pages/');
				$style = $this->getData(['module', $this->getUrl(0), 'theme', 'style']);
				$this->setData(['module', $this->getUrl(0), 'theme', 'style', str_replace('pages/', '', $style)]);
			}
			// Mettre à jour la version
			$this->setData(['module', $this->getUrl(0), 'config', 'versionData', '3.1']);
		}
	}

	/**
	 * Initialisation séparée des éléments absents
	 * Thème
	 * Config
	 * Content
	 */
	private function init()
	{
		// Mise à jour d'une version inférieure, la gallery existe mais pas la variable content
		if (
			$this->getData(['module', $this->getUrl(0)]) &&
			$this->getData(['module', $this->getUrl(0), 'content']) === NULL
		) {
			// Changement de l'arborescence dans module.json
			$data = $this->getData(['module', $this->getUrl(0)]);
			$this->deleteData(['module', $this->getUrl(0)]);
			$this->setData(['module', $this->getUrl(0), 'content', $data]);

			// Effacer les fichiers CSS de l'ancienne version
			if (file_exists('module/gallery/view/index/index.css')) {
				unlink('module/gallery/view/index/index.css');
			}
			if (file_exists('module/gallery/view/gallery/gallery.css')) {
				unlink('module/gallery/view/gallery/gallery.css');
			}
			// Stockage des données du thème de la gallery existant
			if (is_array($this->getData(['theme', 'gallery']))) {
				$data = $this->getData(['theme', 'gallery']);
				$this->deleteData(['theme', 'gallery']);
				$this->setData(['module', $this->getUrl(0), 'theme', $data]);
				// Nom de la feuille de style
				$this->setData(['module', $this->getUrl(0), 'theme', 'style', self::DATADIRECTORY . $this->getUrl(0) . '/theme.css']);
			}
		}

		// Variable commune
		$fileCSS = self::DATADIRECTORY . $this->getUrl(0) . '/theme.css';

		// Check la présence des données de thème
		if ($this->getData(['module', $this->getUrl(0), 'theme']) === null) {
			require_once ('module/gallery/ressource/defaultdata.php');
			$this->setData(['module', $this->getUrl(0), 'theme', theme::$defaultTheme]);
			// Nom de la feuille de style
			$this->setData(['module', $this->getUrl(0), 'theme', 'style', $fileCSS]);
		}

		// Check la présence de la feuille de style
		if (!file_exists(self::DATADIRECTORY . $this->getUrl(0) . '/theme.css')) {
			// Dossier de l'instance
			if (!is_dir(self::DATADIRECTORY . $this->getUrl(0))) {
				mkdir(self::DATADIRECTORY . $this->getUrl(0), 0755, true);
			}
			// Générer la feuille de CSS
			$content = file_get_contents('module/gallery/ressource/vartheme.css');
			$themeCss = file_get_contents('module/gallery/ressource/theme.css');

			// Injection des variables
			$content = str_replace('#thumbAlign#', $this->getData(['module', $this->getUrl(0), 'theme', 'thumbAlign']), $content);
			$content = str_replace('#thumbWidth#', $this->getData(['module', $this->getUrl(0), 'theme', 'thumbWidth']), $content);
			$content = str_replace('#thumbHeight#', $this->getData(['module', $this->getUrl(0), 'theme', 'thumbHeight']), $content);
			$content = str_replace('#thumbMargin#', $this->getData(['module', $this->getUrl(0), 'theme', 'thumbMargin']), $content);
			$content = str_replace('#thumbBorder#', $this->getData(['module', $this->getUrl(0), 'theme', 'thumbBorder']), $content);
			$content = str_replace('#thumbBorderColor#', $this->getData(['module', $this->getUrl(0), 'theme', 'thumbBorderColor']), $content);
			$content = str_replace('#thumbOpacity#', $this->getData(['module', $this->getUrl(0), 'theme', 'thumbOpacity']), $content);
			$content = str_replace('#thumbShadows#', $this->getData(['module', $this->getUrl(0), 'theme', 'thumbShadows']), $content);
			$content = str_replace('#thumbShadowsColor#', $this->getData(['module', $this->getUrl(0), 'theme', 'thumbShadowsColor']), $content);
			$content = str_replace('#thumbRadius#', $this->getData(['module', $this->getUrl(0), 'theme', 'thumbRadius']), $content);
			$content = str_replace('#legendAlign#', $this->getData(['module', $this->getUrl(0), 'theme', 'legendAlign']), $content);
			$content = str_replace('#legendHeight#', $this->getData(['module', $this->getUrl(0), 'theme', 'legendHeight']), $content);
			$content = str_replace('#legendTextColor#', $this->getData(['module', $this->getUrl(0), 'theme', 'legendTextColor']), $content);
			$content = str_replace('#legendBgColor#', $this->getData(['module', $this->getUrl(0), 'theme', 'legendBgColor']), $content);

			// Ecriture de la feuille de style
			file_put_contents(self::DATADIRECTORY . $this->getUrl(0) . '/theme.css', $content . $themeCss);
			// Nom de la feuille de style
			$this->setData(['module', $this->getUrl(0), 'theme', 'style', $fileCSS]);
		}

		// Check la présence de la config

		if (
			is_null($this->getData(['module', $this->getUrl(0), 'config', 'showUniqueGallery'])) ||
			is_null($this->getData(['module', $this->getUrl(0), 'config', 'backPosition'])) ||
			is_null($this->getData(['module', $this->getUrl(0), 'config', 'backAlign'])) ||
			is_null($this->getData(['module', $this->getUrl(0), 'config', 'versionData']))
		) {
			require_once ('module/gallery/ressource/defaultdata.php');
			$this->setData(['module', $this->getUrl(0), 'config', theme::$defaultData]);
		}

		// Contenu vide de la galerie
		if (!is_array($this->getData(['module', $this->getUrl(0), 'content']))) {
			$this->setData(['module', $this->getUrl(0), 'content', array()]);
		}
	}

	/**
	 * Tri de la liste des galeries
	 */
	public function sortGalleries()
	{
		if (isset($_POST['response'])) {
			$data = explode('&', $_POST['response']);
			$data = str_replace('galleryTable%5B%5D=', '', $data);
			for ($i = 0; $i < count($data); $i++) {
				$this->setData([
					'module',
					$this->getUrl(0),
					'content',
					$data[$i],
					[
						'config' => [
							'name' => $this->getData(['module', $this->getUrl(0), 'content', $data[$i], 'config', 'name']),
							'directory' => $this->getData(['module', $this->getUrl(0), 'content', $data[$i], 'config', 'directory']),
							'homePicture' => $this->getData(['module', $this->getUrl(0), 'content', $data[$i], 'config', 'homePicture']),
							'sort' => $this->getData(['module', $this->getUrl(0), 'content', $data[$i], 'config', 'sort']),
							'position' => $i,
							'fullScreen' => $this->getData(['module', $this->getUrl(0), 'content', $data[$i], 'config', 'fullScreen']),
							'showPageContent' => $this->getData(['module', $this->getUrl(0), 'content', $data[$i], 'config', 'showPageContent'])
						],
						'legend' => $this->getData(['module', $this->getUrl(0), 'content', $data[$i], 'legend']),
						'position' => $this->getData(['module', $this->getUrl(0), 'content', $data[$i], 'position'])
					]
				]);
			}
		}
	}

	/**
	 * Tri de la liste des images
	 */
	public function sortPictures()
	{
		if (isset($_POST['response'])) {
			$galleryName = $_POST['gallery'];
			$data = explode('&', $_POST['response']);

			// Appliquer str_replace à chaque élément du tableau
			$data = array_map(function ($item) {
				return str_replace('galleryTable%5B%5D=', '', $item);
			}, $data);
			echo $data;
			// Créer un tableau avec les positions correctes
			$positions = array();
			foreach ($data as $index => $value) {
				$positions[$value] = $index;
			}

			// Sauvegarder
			$this->setData([
				'module',
				$this->getUrl(0),
				'content',
				$galleryName,
				[
					'config' => [
						'name' => $this->getData(['module', $this->getUrl(0), 'content', $galleryName, 'config', 'name']),
						'directory' => $this->getData(['module', $this->getUrl(0), 'content', $galleryName, 'config', 'directory']),
						'homePicture' => $this->getData(['module', $this->getUrl(0), 'content', $galleryName, 'config', 'homePicture']),
						'sort' => $this->getData(['module', $this->getUrl(0), 'content', $galleryName, 'config', 'sort']),
						'position' => $this->getData(['module', $this->getUrl(0), 'content', $galleryName, 'config', 'position']),
						'fullScreen' => $this->getData(['module', $this->getUrl(0), 'content', $galleryName, 'config', 'fullScreen'])
					],
					'legend' => $this->getData(['module', $this->getUrl(0), 'content', $galleryName, 'legend']),
					'position' => $positions,
				]
			]);
			// Tests à supprimer
			$data = ($this->getData([
				'module',
				$this->getUrl(0),
				'content',
				$galleryName
			]));
		}
	}

	/**
	 * Configuration
	 */
	public function config()
	{
		// Affichage de la galerie triée
		$g = $this->getData(['module', $this->getUrl(0), 'content']);
		$p = helper::arrayColumn(helper::arrayColumn($g, 'config'), 'position');
		asort($p, SORT_NUMERIC);
		$galleries = [];
		foreach ($p as $positionId => $item) {
			$galleries[$positionId] = $g[$positionId];
		}
		// Traitement de l'affichage
		if ($galleries) {
			foreach ($galleries as $galleryId => $gallery) {
				// Erreur dossier vide
				if (is_dir($gallery['config']['directory'])) {
					if (count(scandir($gallery['config']['directory'])) === 2) {
						$gallery['config']['directory'] = '<span class="galleryConfigError">' . $gallery['config']['directory'] . ' (dossier vide)</span>';
					}
				}
				// Erreur dossier supprimé
				else {
					$gallery['config']['directory'] = '<span class="galleryConfigError">' . $gallery['config']['directory'] . ' (dossier introuvable)</span>';
				}
				// Met en forme le tableau
				self::$galleries[] = [
					// $gallery['config']['position']++,
					$gallery['config']['name'],
					$gallery['config']['directory'],
					template::button('galleryConfigEdit' . $galleryId, [
						'href' => helper::baseUrl() . $this->getUrl(0) . '/edit/' . $galleryId,
						'value' => template::ico('pencil'),
						'help' => 'Configuration de la galerie '
					]),
					template::button('galleryConfigDelete' . $galleryId, [
						'class' => 'galleryConfigDelete buttonRed',
						'href' => helper::baseUrl() . $this->getUrl(0) . '/delete/' . $galleryId,
						'value' => template::ico('trash'),
						'help' => 'Supprimer cette galerie'
					])
				];
				// Tableau des id des galleries pour le drag and drop
				self::$galleriesId[] = $galleryId;
			}
		}
		// Valeurs en sortie
		$this->addOutput([
			'title' => helper::translate('Configuration des galeries'),
			'view' => 'config',
			'vendor' => [
				'tablednd'
			]
		]);
	}

	/**
	 * Ajout d'une galerie
	 */
	public function add()
	{
		// Soumission du formulaire d'ajout d'une galerie
		if ($this->getUser('permission', __CLASS__, __FUNCTION__) === true && $this->isPost()) {
			$galleryId = $this->getInput('galleryAddName', null, true);
			$success = false;
			if ($galleryId) {
				$galleryId = helper::increment($this->getInput('galleryAddName', helper::FILTER_ID, true), (array) $this->getData(['module', $this->getUrl(0), 'content']));
				$homePicture = '';
				$legends = [];
				$positions = [];
				
				// définir une vignette par défaut
				$directory = $this->getInput('galleryAddDirectory', helper::FILTER_STRING_SHORT, true);
				
				// Vérifier si le répertoire existe et est accessible
				if (!is_dir($directory) || !is_readable($directory)) {
					self::$inputNotices['galleryAddDirectory'] = 'Le dossier est invalide ou inaccessible';
					$success = false;
				} else {
					try {
						$iterator = new DirectoryIterator($directory);
						$i = 0;
						// Parcourir les images de la galerie
						foreach ($iterator as $fileInfos) {
							if ($fileInfos->isDot() === false && $fileInfos->isFile() && @getimagesize($fileInfos->getPathname())) {
								$thumbPath = str_replace('source', 'thumb', $fileInfos->getPath());
								$cleanFileName = strtolower(preg_replace('/[^a-zA-Z0-9._-]/', '', $fileInfos->getFilename()));
								
								// Créer la miniature si manquante
								if (!file_exists($thumbPath . '/' . self::THUMBS_SEPARATOR . $cleanFileName)) {
									$this->makeThumb(
										$fileInfos->getPathname(),
										null,  // Chemin de destination généré automatiquement
										self::THUMBS_WIDTH
									);
								}
								
								// Initialisation des légendes et du numéro d'ordre
								$fileNameId = str_replace('.', '', $fileInfos->getFilename());
								if (!empty($fileNameId)) {
									$legends[$fileNameId] = '';
									$positions[$fileNameId] = $i++;
									
									// La première image sera la vignette
									if ($i === 1) {
										$homePicture = $cleanFileName;
									}
								}
							}
						}
						
						// Vérifier si le dossier contient des images
						if ($i > 0) {
							$this->setData([
								'module',
								$this->getUrl(0),
								'content',
								$galleryId,
								[
									'config' => [
										'name' => $this->getInput('galleryAddName'),
										'directory' => $directory,
										'homePicture' => $homePicture,
										'sort' => $this->getInput('galleryAddSort'),
										'position' => count($this->getData(['module', $this->getUrl(0), 'content'])) + 1,
										'fullScreen' => $this->getInput('galleryAddFullscreen', helper::FILTER_BOOLEAN),
										'showPageContent' => $this->getInput('galleryAddShowPageContent', helper::FILTER_BOOLEAN)
									],
									'legend' => $legends,
									'position' => $positions,
								]
							]);
							$success = true;
						} else {
							self::$inputNotices['galleryAddDirectory'] = 'Le dossier sélectionné ne contient aucune image';
							$success = false;
						}
					} catch (Exception $e) {
						self::$inputNotices['galleryAddDirectory'] = 'Erreur lors de la lecture du dossier : ' . $e->getMessage();
						$success = false;
					}
				}
			}

			if ($success) {
				// Valeurs en sortie
				$this->addOutput([
					'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config',
					'notification' => helper::translate('Galerie ajoutée'),
					'state' => true
				]);
			} else {
				// Valeurs en sortie
				$this->addOutput([
					'title' => helper::translate("Création d'une galerie"),
					'view' => 'add'
				]);
			}
		} else {
			// Valeurs en sortie
			$this->addOutput([
				'title' => helper::translate("Création d'une galerie"),
				'view' => 'add'
			]);
		}
	}

	/**
	 * Suppression
	 */
	public function delete()
	{
		// La galerie n'existe pas
		if (
			$this->getUser('permission', __CLASS__, __FUNCTION__) !== true ||
			$this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2)]) === null
		) {
			// Valeurs en sortie
			$this->addOutput([
				'access' => false
			]);
		}
		// Suppression
		else {
			$this->deleteData(['module', $this->getUrl(0), 'content', $this->getUrl(2)]);
			// Valeurs en sortie
			$this->addOutput([
				'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config',
				'notification' => helper::translate('Galerie effacée'),
				'state' => true
			]);
		}
	}

	/**
	 * Liste des dossiers
	 */
	public function dirs()
	{
		// Déterminer le $rootPath à explorer
		$rootPath = $this->getUserPath($this->getUser('id'));
		// Liste des dossiers
		$sharePath = helper::getSubdirectories($rootPath);
		// Tri des dossiers sur la clé
		ksort($sharePath);
		$sharePath = array_flip($sharePath);
		// Parcours des dossiers
		foreach ($sharePath as $path => $name) {
			$sharePath[$path] = [
				'name' => $name,
				'path' => $path
			];
		}
		// Valeurs en sortie
		$this->addOutput([
			'display' => self::DISPLAY_JSON,
			'content' => array_values($sharePath)
		]);
	}

	/**
	 * Édition
	 */
	public function edit()
	{
		// Soumission du formulaire
		if (
			$this->getUser('permission', __CLASS__, __FUNCTION__) === true &&
			$this->isPost()
		) {
			// légendes
			foreach ((array) $this->getInput('legend', null) as $file => $legend) {
				// Image de couverture par défaut si non définie
				$homePicture = $file;
				$file = str_replace('.', '', $file);
				$legends[$file] = helper::filter($legend, helper::FILTER_STRING_SHORT);
			}
			// Photo de la page de garde de l'album définie dans form
			if (is_array($this->getInput('homePicture', null))) {
				// Extrait la couverture sélectionnée
				$homePicture = array_keys($this->getInput('homePicture', null))[0];
			}
			// Sauvegarder
			$this->setData([
				'module',
				$this->getUrl(0),
				'content',
				$this->getUrl(2),
				[
					'config' => [
						'homePicture' => $homePicture,
						// Données mises à jour par les options
						'name' => $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2), 'config', 'name']),
						'directory' => $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2), 'config', 'directory']),
						'sort' => $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2), 'config', 'sort']),
						'position' => $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2), 'config', 'position']),
						'fullScreen' => $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2), 'config', 'fullScreen']),
						'showPageContent' => $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2), 'config', 'showPageContent'])
					],
					'legend' => $legends,
					'position' => $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2), 'position'])
				]
			]);
			// Valeurs en sortie
			$this->addOutput([
				'redirect' => helper::baseUrl() . $this->getUrl(0) . '/edit/' . $this->getUrl(2),
				'notification' => helper::translate('Modifications enregistrées'),
				'state' => true
			]);
		}
		// La galerie n'existe pas
		if ($this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2)]) === null) {
			// Valeurs en sortie
			$this->addOutput([
				'access' => false
			]);
		}
		// La galerie existe
		else {
			// Met en forme le tableau
			$directory = $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2), 'config', 'directory']);
			if (is_dir($directory)) {
				$iterator = new DirectoryIterator($directory);
				foreach ($iterator as $fileInfos) {
					if ($fileInfos->isDot() === false and $fileInfos->isFile() and @getimagesize($fileInfos->getPathname())) {
						// Créer la miniature RFM si manquante
						$thumbPath = $this->getThumb($fileInfos->getPathname());
						if (!file_exists($thumbPath)) {
							$this->makeThumb(
								$fileInfos->getPathname(),
								null,  // Chemin de destination généré automatiquement
								122
							);
						}
						self::$pictures[str_replace('.', '', $fileInfos->getFilename())] = [
							$this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2), 'position', str_replace('.', '', $fileInfos->getFilename())]) + 1,
							$fileInfos->getFilename(),
							template::checkbox('homePicture[' . $fileInfos->getFilename() . ']', true, '', [
								'checked' => $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2), 'config', 'homePicture']) === $fileInfos->getFilename() ? true : false,
								'class' => 'homePicture'
							]),
							template::text('legend[' . $fileInfos->getFilename() . ']', [
								'value' => $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2), 'legend', str_replace('.', '', $fileInfos->getFilename())])
							]),
							'<a href="' . $this->getThumb($fileInfos->getPathname(), '') . '" rel="data-lity" data-lity=""><img src="' . $this->getThumb($fileInfos->getPathname()) . '"></a>'
						];
						self::$picturesId[str_replace('.', '', $fileInfos->getFilename())] = str_replace('.', '', $fileInfos->getFilename());
					}
				}
				// Tri des images
				switch ($this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2), 'config', 'sort'])) {
					case self::SORT_HAND:
						// Récupération du tableau des positions
						$positions = $this->getData([
							'module',
							$this->getUrl(0),
							'content',
							$this->getUrl(2),
							'position'
						]);

						// Tri de self::$pictures selon les positions
						uasort(self::$pictures, function ($a, $b) use ($positions) {
							// Les clés sont obtenues par str_replace('.', '', filename)
							$keyA = str_replace('.', '', $a[1]);
							$keyB = str_replace('.', '', $b[1]);

							// Vérification de l'existence des clés dans le tableau positions
							$posA = isset($positions[$keyA]) ? $positions[$keyA] : PHP_INT_MAX;
							$posB = isset($positions[$keyB]) ? $positions[$keyB] : PHP_INT_MAX;

							// Comparaison des positions
							return $posA <=> $posB;
						});
						break;
					case self::SORT_ASC:
						ksort(self::$pictures, SORT_NATURAL);
						sort(self::$picturesId, SORT_NATURAL);
						break;
					case self::SORT_DSC:
						krsort(self::$pictures, SORT_NATURAL);
						rsort(self::$picturesId, SORT_NATURAL);
						break;
				}
			}
			// Valeurs en sortie
			$this->addOutput([
				'title' => sprintf(helper::translate('Configuration de la galerie %s '), $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(2), 'config', 'name'])),
				'view' => 'edit',
				'vendor' => [
					'tablednd'
				]
			]);
		}
	}

	/**
	 * Accueil (deux affichages en un pour éviter une url à rallonge)
	 */
	public function index()
	{
		// Une seule galerie, bifurquer sur celle-ci
		$gallery = $this->getData(['module', $this->getUrl(0), 'config', 'showUniqueGallery']) === true &&
			count($this->getData(['module', $this->getUrl(0), 'content'])) === 1
				? array_key_first($this->getData(['module', $this->getUrl(0), 'content']))
				: $this->getUrl(1);
		if ($gallery) {
			// La galerie n'existe pas
			if ($this->getData(['module', $this->getUrl(0), 'content', $gallery]) === null) {
				// Valeurs en sortie
				$this->addOutput([
					'access' => false
				]);
			}
			// La galerie existe
			else {
				// Images de la galerie
				$directory = $this->getData(['module', $this->getUrl(0), 'content', $gallery, 'config', 'directory']);
				// Définir les options
				self::$config['homePicture'] = $this->getData(['module', $this->getUrl(0), 'content', $gallery, 'config', 'homePicture']);
				self::$config['fullScreen'] = $this->getData(['module', $this->getUrl(0), 'content', $gallery, 'config', 'fullScreen']) === true ? 'fullScreen' : '';
				self::$config['backPositionTop'] = ($this->getData(['module', $this->getUrl(0), 'config', 'backPosition']) === 'top' ||
						$this->getData(['module', $this->getUrl(0), 'config', 'backPosition']) === 'both')
					? ''
					: 'displayNone';
				self::$config['backPositionBottom'] = ($this->getData(['module', $this->getUrl(0), 'config', 'backPosition']) === 'bottom' ||
						$this->getData(['module', $this->getUrl(0), 'config', 'backPosition']) === 'both')
					? ''
					: 'displayNone';
				self::$config['backAlign'] = 'textAlign' . ucfirst($this->getData(['module', $this->getUrl(0), 'config', 'backAlign']));

				// Annulation de l'apparition des boutons si la galerie est unique
				self::$config['backPositionTop'] = ($this->getData(['module', $this->getUrl(0), 'config', 'showUniqueGallery']) === true &&
						count($this->getData(['module', $this->getUrl(0), 'content'])) === 1)
					? 'displayNone'
					: self::$config['backPositionTop'];
				self::$config['backPositionBottom'] = ($this->getData(['module', $this->getUrl(0), 'config', 'showUniqueGallery']) === true &&
						count($this->getData(['module', $this->getUrl(0), 'content'])) === 1)
					? 'displayNone'
					: self::$config['backPositionBottom'];

				if (is_dir($directory)) {
					$iterator = new DirectoryIterator($directory);
					foreach ($iterator as $fileInfos) {
						if ($fileInfos->isDot() === false and $fileInfos->isFile() and @getimagesize($fileInfos->getPathname())) {
							self::$pictures[$directory . '/' . $fileInfos->getFilename()] = $this->getData(['module', $this->getUrl(0), 'content', $gallery, 'legend', str_replace('.', '', $fileInfos->getFilename())]);
							$picturesSort[$directory . '/' . $fileInfos->getFilename()] = $this->getData(['module', $this->getUrl(0), 'content', $gallery, 'position', str_replace('.', '', $fileInfos->getFilename())]);
							// Créer la miniature si manquante
							if (!file_exists(str_replace('source', 'thumb', $fileInfos->getPath()) . '/' . self::THUMBS_SEPARATOR . strtolower($fileInfos->getFilename()))) {
								$this->makeThumb(
									$fileInfos->getPathname(),
									null,  // Chemin de destination généré automatiquement
									self::THUMBS_WIDTH
								);
							}
							// Définir la Miniature
							$thumbDir = rtrim(str_replace('source', 'thumb', $directory), '/') . '/';
							self::$thumbs[$directory . '/' . $fileInfos->getFilename()] = file_exists($thumbDir . self::THUMBS_SEPARATOR . strtolower($fileInfos->getFilename()))
								? $thumbDir . self::THUMBS_SEPARATOR . strtolower($fileInfos->getFilename())
								: $thumbDir . strtolower($fileInfos->getFilename());
						}
					}
					// Tri des images
					switch ($this->getData(['module', $this->getUrl(0), 'content', $gallery, 'config', 'sort'])) {
						case self::SORT_HAND:
							asort($picturesSort);
							if ($picturesSort) {
								foreach ($picturesSort as $name => $position) {
									$temp[$name] = self::$pictures[$name];
								}
								self::$pictures = $temp;
								break;
							}
						case self::SORT_DSC:
							krsort(self::$pictures, SORT_NATURAL);
							break;
						case self::SORT_ASC:
						default:
							ksort(self::$pictures, SORT_NATURAL);
							break;
					}
				}
				// Affichage du template
				if (self::$pictures) {
					// Valeurs en sortie
					$this->addOutput([
						'showBarEditButton' => true,
						'title' => $this->getData(['module', $this->getUrl(0), 'content', $gallery, 'config', 'name']),
						'view' => 'gallery',
						'style' => file_exists($this->getData(['module', $this->getUrl(0), 'theme', 'style']))
							? $this->getData(['module', $this->getUrl(0), 'theme', 'style'])
							: '',
						'showPageContent' => $this->getData(['module', $this->getUrl(0), 'content', $gallery, 'config', 'showPageContent']),
					]);
				}
				// Pas d'image dans la galerie
				else {
					// Valeurs en sortie
					$this->addOutput([
						'access' => false
					]);
				}
			}
		}
		// Liste des galeries
		else {
			// Tri des galeries suivant l'ordre défini
			$g = $this->getData(['module', $this->getUrl(0), 'content']);
			$p = helper::arrayColumn(helper::arrayColumn($g, 'config'), 'position');
			asort($p, SORT_NUMERIC);
			$galleries = [];
			foreach ($p as $positionId => $item) {
				$galleries[$positionId] = $g[$positionId];
			}
			// Construire le tableau
			foreach ((array) $galleries as $galleryId => $gallery) {
				$galleryDir = rtrim($gallery['config']['directory'], '/');
				$homePicture = ltrim($gallery['config']['homePicture'], '/');
				if (!is_dir($galleryDir)) {
					continue;
				}

				$coverPath = '';
				$homePicturePath = $homePicture ? $galleryDir . '/' . $homePicture : '';

				if ($homePicturePath && is_file($homePicturePath) && @getimagesize($homePicturePath)) {
					$coverPath = $homePicturePath;
				} else {
					$iterator = new DirectoryIterator($galleryDir);
					foreach ($iterator as $fileInfos) {
						if ($fileInfos->isDot() || !$fileInfos->isFile()) {
							continue;
						}
						if (!@getimagesize($fileInfos->getPathname())) {
							continue;
						}
						$coverPath = $fileInfos->getPathname();
						break; // Première image trouvée, pas besoin de parcourir tout le dossier
					}
				}

				if (!$coverPath) {
					continue;
				}

				$thumbDir = str_replace('source', 'thumb', dirname($coverPath));
				$thumbBaseName = strtolower(basename($coverPath));
				$expectedThumb = $thumbDir . '/' . self::THUMBS_SEPARATOR . $thumbBaseName;
				$fallbackThumb = $thumbDir . '/' . $thumbBaseName;

				// Créer le dossier des miniatures si besoin pour éviter les chemins cassés
				if (!is_dir($thumbDir)) {
					mkdir($thumbDir, 0755, true);
				}

				if (!file_exists($expectedThumb)) {
					$this->makeThumb(
						$coverPath,
						null,
						self::THUMBS_WIDTH
					);
				}

				self::$galleries[$galleryId] = $gallery;
				self::$firstPictures[$galleryId] = file_exists($expectedThumb)
					? $expectedThumb
					: (file_exists($fallbackThumb) ? $fallbackThumb : $coverPath);
			}
			// Valeurs en sortie
			$this->addOutput([
				'showBarEditButton' => true,
				'showPageContent' => true,
				'view' => 'index',
				'style' => file_exists($this->getData(['module', $this->getUrl(0), 'theme', 'style']))
					? $this->getData(['module', $this->getUrl(0), 'theme', 'style'])
					: ''
			]);
		}
	}

	/**
	 * Thème de la galerie
	 */
	public function theme()
	{
		// Soumission du formulaire
		if (
			$this->getUser('permission', __CLASS__, __FUNCTION__) === true &&
			$this->isPost()
		) {
			// Dossier de l'instance
			if (!is_dir(self::DATADIRECTORY . $this->getUrl(0))) {
				mkdir(self::DATADIRECTORY . $this->getUrl(0), 0755, true);
			}
			$this->setData([
				'module',
				$this->getUrl(0),
				'theme',
				[
					'thumbAlign' => $this->getinput('galleryThemeThumbAlign', helper::FILTER_STRING_SHORT),
					'thumbWidth' => $this->getinput('galleryThemeThumbWidth', helper::FILTER_STRING_SHORT),
					'thumbHeight' => $this->getinput('galleryThemeThumbHeight', helper::FILTER_STRING_SHORT),
					'thumbMargin' => $this->getinput('galleryThemeThumbMargin', helper::FILTER_STRING_SHORT),
					'thumbBorder' => $this->getinput('galleryThemeThumbBorder', helper::FILTER_STRING_SHORT),
					'thumbBorderColor' => $this->getinput('galleryThemeThumbBorderColor', helper::FILTER_STRING_SHORT),
					'thumbOpacity' => $this->getinput('galleryThemeThumbOpacity', helper::FILTER_STRING_SHORT),
					'thumbShadows' => $this->getinput('galleryThemeThumbShadows', helper::FILTER_STRING_SHORT),
					'thumbShadowsColor' => $this->getinput('galleryThemeThumbShadowsColor', helper::FILTER_STRING_SHORT),
					'thumbRadius' => $this->getinput('galleryThemeThumbRadius', helper::FILTER_STRING_SHORT),
					'legendHeight' => $this->getinput('galleryThemeLegendHeight', helper::FILTER_STRING_SHORT),
					'legendAlign' => $this->getinput('galleryThemeLegendAlign', helper::FILTER_STRING_SHORT),
					'legendTextColor' => $this->getinput('galleryThemeLegendTextColor', helper::FILTER_STRING_SHORT),
					'legendBgColor' => $this->getinput('galleryThemeLegendBgColor', helper::FILTER_STRING_SHORT),
					'showUniqueGallery' => $this->getinput('galleryThemeShowUniqueGallery', helper::FILTER_BOOLEAN),
					'style' => self::DATADIRECTORY . $this->getUrl(0) . '/theme.css',
				]
			]);
			// Création des fichiers CSS
			$content = file_get_contents('module/gallery/ressource/vartheme.css');
			$themeCss = file_get_contents('module/gallery/ressource/theme.css');
			// Injection des variables
			$content = str_replace('#thumbAlign#', $this->getinput('galleryThemeThumbAlign'), $content);
			$content = str_replace('#thumbWidth#', $this->getinput('galleryThemeThumbWidth'), $content);
			$content = str_replace('#thumbHeight#', $this->getinput('galleryThemeThumbHeight'), $content);
			$content = str_replace('#thumbMargin#', $this->getinput('galleryThemeThumbMargin'), $content);
			$content = str_replace('#thumbBorder#', $this->getinput('galleryThemeThumbBorder'), $content);
			$content = str_replace('#thumbBorderColor#', $this->getinput('galleryThemeThumbBorderColor'), $content);
			$content = str_replace('#thumbOpacity#', $this->getinput('galleryThemeThumbOpacity'), $content);
			$content = str_replace('#thumbShadows#', $this->getinput('galleryThemeThumbShadows'), $content);
			$content = str_replace('#thumbShadowsColor#', $this->getinput('galleryThemeThumbShadowsColor'), $content);
			$content = str_replace('#thumbRadius#', $this->getinput('galleryThemeThumbRadius'), $content);
			$content = str_replace('#legendAlign#', $this->getinput('galleryThemeLegendAlign'), $content);
			$content = str_replace('#legendHeight#', $this->getinput('galleryThemeLegendHeight'), $content);
			$content = str_replace('#legendTextColor#', $this->getinput('galleryThemeLegendTextColor'), $content);
			$content = str_replace('#legendBgColor#', $this->getinput('galleryThemeLegendBgColor'), $content);
			$success = is_int(file_put_contents(self::DATADIRECTORY . $this->getUrl(0) . '/theme.css', $content . $themeCss));
			// Valeurs en sortie
			$this->addOutput([
				'redirect' => helper::baseUrl() . $this->getUrl() . '/theme',
				'notification' => $success ? 'Modifications enregistrées' : 'Modifications non enregistrées !',
				'state' => $success
			]);
		}
		// Valeurs en sortie
		$this->addOutput([
			'title' => helper::translate('Thème'),
			'view' => 'theme',
			'vendor' => [
				'tinycolorpicker'
			]
		]);
	}

	/**
	 * Option de configuration de la galerie
	 */
	public function option()
	{
		/** Options applicables à toutes les galeries du module */
		if ($this->getUrl(2) === 'galleries') {
			// Soumission du formulaire
			if (
				$this->getUser('permission', __CLASS__, __FUNCTION__) === true &&
				$this->isPost()
			) {
				// Sauver la configuration de la galerie
				$this->setData([
					'module',
					$this->getUrl(0),
					'config',
					[
						'showUniqueGallery' => $this->getinput('galleriesOptionShowUniqueGallery', helper::FILTER_BOOLEAN),
						'backPosition' => $this->getinput('galleryOptionBackPosition'),
						'backAlign' => $this->getinput('galleryOptionBackAlign'),
						'versionData' => $this->getData(['module', $this->getUrl(0), 'config', 'versionData']),
					]
				]);

				// Valeurs en sortie
				$this->addOutput([
					'redirect' => helper::baseUrl() . $this->getUrl() . '/option',
					'notification' => 'Modifications enregistrées',
					'state' => true
				]);
			}
			// Routage du formulaire
			self::$formOptionSelect = 'galleries';
			// Valeurs en sortie
			$this->addOutput([
				'title' => helper::translate('Options de configuration'),
				'view' => 'option'
			]);

			/**
			 * Enregistre les options de configuration de la galerie d'images sélectionnée
			 */
		} elseif ($this->getUrl(2) === 'gallery') {
			// Soumission du formulaire
			if (
				$this->getUser('permission', __CLASS__, __FUNCTION__) === true &&
				$this->isPost()
			) {
				// Le nom de la galerie est vide c'est le nom dans l'url qui est pris en compte
				$galleryId = !empty($this->getInput('galleryEditName')) ? $this->getInput('galleryEditName', helper::FILTER_ID, true) : $this->getUrl(3);

				// Sauvegarde des Valeurs non affectées par ce formulaire
				$homePicture = $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(3), 'config', 'homePicture']);
				$position = $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(3), 'config', 'position']);
				$legend = $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(3), 'legend']);
				$positions = $this->getData(['module', $this->getUrl(0), 'content', $this->getUrl(3), 'position']);

				// Le nom de la galerie n'est pas celui dans la BDD
				if ($galleryId !== $this->getUrl(3)) {
					// Incrémente le nouvel id de la galerie
					$galleryId = helper::increment($galleryId, $this->getData(['module', $this->getUrl(0), 'content']));
					// Supprime l'ancienne galerie
					$this->deleteData(['module', $this->getUrl(0), 'content', $this->getUrl(3)]);
				}
				// Sauvegarder
				if ($this->getInput('galleryEditName')) {
					$this->setData([
						'module',
						$this->getUrl(0),
						'content',
						$galleryId,
						[
							'config' => [
								'name' => $this->getInput('galleryEditName', helper::FILTER_STRING_SHORT, true),
								'directory' => $this->getInput('galleryEditDirectory', helper::FILTER_STRING_SHORT, true),
								'sort' => $this->getInput('galleryEditSort'),
								'fullScreen' => $this->getInput('galleryEditFullscreen', helper::FILTER_BOOLEAN),
								'showPageContent' => $this->getInput('galleryEditShowPageContent', helper::FILTER_BOOLEAN),
								// Données lues à partir du formulaire edit de la galerie avec la liste des images
								'homePicture' => $homePicture,
								'position' => $position
							],
							'legend' => $legend,
							'position' => $positions
						]
					]);
				}
				// Valeurs en sortie
				$this->addOutput([
					'redirect' => helper::baseUrl() . $this->getUrl(0) . '/edit/' . $galleryId,
					'notification' => helper::translate('Modifications enregistrées'),
					'state' => true
				]);
			}
			// Routage du formulaire
			self::$formOptionSelect = 'gallery';
			// Valeurs en sortie
			$this->addOutput([
				'title' => sprintf(helper::translate('Options de la galerie %s'), ucfirst($this->getUrl(3))),
				'view' => 'option'
			]);
		} else {
			// Valeurs en sortie par défaut
			$this->addOutput([
				'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config',
				'notification' => helper::translate('Action interdite')
			]);
		}
	}
}
