<?php
class layout extends common
{

    private $core;

    public function __construct(core $core)
    {
        parent::__construct();
        $this->core = $core;
    }

    /**
     * Génère le label d'un item de menu selon son type
     * @param string $pageId ID de la page
     * @return string Label HTML
     */
    private function getMenuLabel($pageId)
    {
        switch ($this->getData(['page', $pageId, 'typeMenu'])) {
            case '':
                return $this->getData(['page', $pageId, 'shortTitle']);
            case 'text':
                return $this->getData(['page', $pageId, 'shortTitle']);
            case 'icon':
                if ($this->getData(['page', $pageId, 'iconUrl']) != '') {
                    return '<img alt="' . $this->getData(['page', $pageId, 'shortTitle']) . '" src="' . helper::baseUrl(false) . self::FILE_DIR . 'source/' . $this->getData(['page', $pageId, 'iconUrl']) . '" />';
                } else {
                    return $this->getData(['page', $pageId, 'shortTitle']);
                }
            case 'icontitle':
                if ($this->getData(['page', $pageId, 'iconUrl']) != '') {
                    return '<img alt="' . $this->getData(['page', $pageId, 'shortTitle']) . '" src="' . helper::baseUrl(false) . self::FILE_DIR . 'source/' . $this->getData(['page', $pageId, 'iconUrl']) . '" data-tippy-content="' . $this->getData(['page', $pageId, 'shortTitle']) . '"/>';
                } else {
                    return $this->getData(['page', $pageId, 'shortTitle']);
                }
            case 'icontext':
                if ($this->getData(['page', $pageId, 'iconUrl']) != '') {
                    return '<img alt="' . $this->getData(['page', $pageId, 'shortTitle']) . '" src="' . helper::baseUrl(false) . self::FILE_DIR . 'source/' . $this->getData(['page', $pageId, 'iconUrl']) . '" />' . $this->getData(['page', $pageId, 'shortTitle']);
                } else {
                    return $this->getData(['page', $pageId, 'shortTitle']);
                }
            default:
                return $this->getData(['page', $pageId, 'shortTitle']);
        }
    }

    /**
     * Affiche le consentement aux cookies
     */
    public function showCookies()
    {
        // La gestion des cookies est externalisée
        if ($this->getData(['config', 'cookieConsent']) === false) {
            return;
        }
        // Le cookie est déjà validé
        if ($this->getInput('ZWII_COOKIE_CONSENT') === 'true') {
            return;
        }
        $item = '<div id="cookieConsent">';
        // Bouton de fermeture
        $item .= '<div class="cookieClose">';
        $item .= template::ico('cancel');
        $item .= '</div>';
        // Texte de la popup
        $item .= '<h3>' . $this->getData(['locale', 'cookies', 'titleLabel']) . '</h3>';
        $item .= '<p>' . $this->getData(['locale', 'cookies', 'mainLabel']) . '</p>';
        // Formulaire de réponse
        if (
            $this->getData(['locale', 'homePageId']) === $this->getUrl(0)
        ) {
            $item .= '<form method="POST" action="' . helper::baseUrl(false) . '" id="cookieForm">';
        } else {
            $item .= '<form method="POST" action="' . helper::baseUrl(true) . $this->getUrl() . '" id="cookieForm">';
        }
        $item .= '<br><br>';
        $item .= '<input type="submit" id="cookieConsentConfirm" value="' . $this->getData(['locale', 'cookies', 'buttonValidLabel']) . '">';
        $item .= '</form>';
        // mentions légales si la page est définie
        $legalPage = $this->getData(['locale', 'legalPageId']);
        if ($legalPage !== 'none') {
            $item .= '<p><a href="' . helper::baseUrl() . $legalPage . '">' . $this->getData(['locale', 'cookies', 'linkLegalLabel']) . '</a></p>';
        }
        $item .= '</div>';
        echo $item;
    }

    /**
     * Formate le contenu de la page selon les gabarits
     * @param Page par defaut
     */
    public function showMain()
    {
        echo '<main><section>';
        // Récupérer la config de la page courante
        $blocks = is_null($this->getData(['page', $this->getUrl(0), 'block'])) ? '12' : $this->getData(['page', $this->getUrl(0), 'block']);
        $blocks = explode('-', $blocks);
        // Initialiser
        $blockleft = '';
        $blockright = '';
        switch (sizeof($blocks)) {
            case 1: // une colonne
                $content = 'col' . $blocks[0];
                break;
            case 2: // 2 blocs
                if ($blocks[0] < $blocks[1]) { // détermine la position de la colonne
                    $blockleft = 'col' . $blocks[0];
                    $content = 'col' . $blocks[1];
                } else {
                    $content = 'col' . $blocks[0];
                    $blockright = 'col' . $blocks[1];
                }
                break;
            case 3: // 3 blocs
                $blockleft = 'col' . $blocks[0];
                $content = 'col' . $blocks[1];
                $blockright = 'col' . $blocks[2];
        }
        // Toujours en pleine page pour la configuration des modules et l'édition des pages sauf l'affichage d'un article de blog
        $pattern = ['config', 'edit', 'add', 'comment', 'data', 'option', 'theme', 'comment', 'article', 'data', 'gallery', 'update', 'users', 'validate'];
        if (
            (sizeof($blocks) === 1 ||
                in_array($this->getUrl(1), $pattern))
        ) { // Pleine page en mode configuration
            if (
                ($this->getData(['page', $this->getUrl(0), 'navLeft']) === 'top'
                    || $this->getData(['page', $this->getUrl(0), 'navRight']) === 'top')
                && in_array($this->getUrl(1), $pattern) === false
            ) {
                $this->showNavButtons('top');
            }
            $this->showContent();
            if (
                ($this->getData(['page', $this->getUrl(0), 'navLeft']) === 'bottom'
                    || $this->getData(['page', $this->getUrl(0), 'navRight']) === 'bottom')
                && in_array($this->getUrl(1), $pattern) === false
            ) {
                $this->showNavButtons('bottom');
            }
        } else {
            echo '<div class="row siteContainer">';
            /**
             * Barre gauche
             */
            if ($blockleft !== '') {
                echo '<div class="' . $blockleft . '" id="contentLeft"><aside>';
                // Détermine si le menu est présent
                if ($this->getData(['page', $this->getData(['page', $this->getUrl(0), 'barLeft']), 'displayMenu']) === 'none') {
                    // Pas de menu
                    echo $this->core->output['contentLeft'];
                } else {
                    // $mark contient 0 le menu est positionné à la fin du contenu
                    $contentLeft = str_replace('[]', '[MENU]', $this->core->output['contentLeft']);
                    $contentLeft = str_replace('[menu]', '[MENU]', $contentLeft);
                    $mark = strrpos($contentLeft, '[MENU]') !== false ? strrpos($contentLeft, '[MENU]') : strlen($contentLeft);
                    echo substr($contentLeft, 0, $mark);
                    echo '<div id="menuSideLeft">';
                    echo $this->showMenuSide($this->getData(['page', $this->getData(['page', $this->getUrl(0), 'barLeft']), 'displayMenu']) === 'parents' ? false : true);
                    echo '</div>';
                    echo substr($contentLeft, $mark + 6, strlen($contentLeft));
                }
                echo "</aside></div>";
            }
            /**
             * Contenu de page
             */
            echo '<div class="' . $content . '" id="contentSite">';
            $this->showNavButtons('top');
            $this->showContent();
            $this->showNavButtons('bottom');
            echo '</div>';
            /**
             * Barre droite
             */
            if ($blockright !== '') {
                echo '<div class="' . $blockright . '" id="contentRight"><aside>';
                // Détermine si le menu est présent
                if ($this->getData(['page', $this->getData(['page', $this->getUrl(0), 'barRight']), 'displayMenu']) === 'none') {
                    // Pas de menu
                    echo $this->core->output['contentRight'];
                } else {
                    // $mark contient 0 le menu est positionné à la fin du contenu
                    $contentRight = str_replace('[]', '[MENU]', $this->core->output['contentRight']);
                    $contentRight = str_replace('[menu]', '[MENU]', $contentRight);
                    $mark = strrpos($contentRight, '[MENU]') !== false ? strrpos($contentRight, '[MENU]') : strlen($contentRight);
                    echo substr($contentRight, 0, $mark);
                    echo '<div id="menuSideRight">';
                    echo $this->showMenuSide($this->getData(['page', $this->getData(['page', $this->getUrl(0), 'barRight']), 'displayMenu']) === 'parents' ? false : true);
                    echo '</div>';
                    echo substr($contentRight, $mark + 6, strlen($contentRight));
                }
                echo '</aside></div>';
            }
            echo '</div>';
        }
        echo '</section></main>';
    }

    /**
     * Affiche le contenu
     * @param Page par défaut
     */
    public function showContent()
    {

        if (
            $this->core->output['title']
            and ($this->getData(['page', $this->getUrl(0)]) === null
                or $this->getData(['page', $this->getUrl(0), 'hideTitle']) === false
                or $this->getUrl(1) === 'config'
            )
        ) {
            echo '<h1 id="sectionTitle">' . $this->core->output['title'] . '</h1>';
        }

        echo $this->core->output['content'];
    }

    /**
     * Affiche le pied de page
     */
    public function showFooter()
    {
        // Déterminer la position
        $positionFixed = '';
        if (
            $this->getData(['theme', 'footer', 'position']) === 'site'
            // Affiche toujours le pied de page pour l'édition du thème
            or ($this->getData(['theme', 'footer', 'position']) === 'hide'
                and $this->getUrl(0) === 'theme'
            )
        ) {
            $position = 'site';
        } else {
            $position = 'body';
            if ($this->getData(['theme', 'footer', 'fixed']) === true) {
                $positionFixed = ' footerbodyFixed';
            }
            // Sortir de la division précédente
            echo '</div>';
        }

        echo $this->getData(['theme', 'footer', 'position']) === 'hide' ? '<footer class="displayNone">' : '<footer>';
        echo ($position === 'site') ? '<div class="container"><div class="row" id="footersite">' : '<div class="container-large' . $positionFixed . '"><div class="row" id="footerbody">';
        /**
         * Calcule la dimension des blocs selon la configuration
         */
        switch ($this->getData(['theme', 'footer', 'template'])) {
            case '1':
                $class['left'] = "displayNone";
                $class['center'] = "col12";
                $class['right'] = "displayNone";
                break;
            case '2':
                $class['left'] = "col6";
                $class['center'] = "displayNone";
                $class['right'] = "col6";
                break;
            case '3':
                $class['left'] = "col4";
                $class['center'] = "col4";
                $class['right'] = "col4";
                break;
            case '4':
                $class['left'] = "col12";
                $class['center'] = "col12";
                $class['right'] = "col12";
                break;
        }
        /**
         * Affiche les blocs
         */
        echo '<div class="' . $class['left'] . '" id="footer' . $position . 'Left">';
        if ($this->getData(['theme', 'footer', 'textPosition']) === 'left') {
            $this->showFooterText();
        }
        if ($this->getData(['theme', 'footer', 'socialsPosition']) === 'left') {
            $this->showSocials();
        }
        if ($this->getData(['theme', 'footer', 'copyrightPosition']) === 'left') {
            $this->showCopyright();
        }
        echo '</div>';
        echo '<div class="' . $class['center'] . '" id="footer' . $position . 'Center">';
        if ($this->getData(['theme', 'footer', 'textPosition']) === 'center') {
            $this->showFooterText();
        }
        if ($this->getData(['theme', 'footer', 'socialsPosition']) === 'center') {
            $this->showSocials();
        }
        if ($this->getData(['theme', 'footer', 'copyrightPosition']) === 'center') {
            $this->showCopyright();
        }
        echo '</div>';
        echo '<div class="' . $class['right'] . '" id="footer' . $position . 'Right">';
        if ($this->getData(['theme', 'footer', 'textPosition']) === 'right') {
            $this->showFooterText();
        }
        if ($this->getData(['theme', 'footer', 'socialsPosition']) === 'right') {
            $this->showSocials();
        }
        if ($this->getData(['theme', 'footer', 'copyrightPosition']) === 'right') {
            $this->showCopyright();
        }
        echo '</div>';

        // Fermeture du conteneur
        echo '</div></div>';
        echo '</footer>';
    }

    /**
     * Affiche le texte du footer
     */
    private function showFooterText()
    {
        if ($footerText = $this->getData(['theme', 'footer', 'text']) or $this->getUrl(0) === 'theme') {
            echo '<div id="footerText">' . $footerText . '</div>';
        }
    }

    /**
     * Affiche le copyright
     */
    private function showCopyright()
    {
        // Ouverture Bloc copyright
        $items = '<div id="footerCopyright">';
        $items .= '<span id="footerFontCopyright">';
        // Affichage de motorisé par
        $items .= '<span id="footerDisplayCopyright" ';
        $items .= $this->getData(['theme', 'footer', 'displayCopyright']) === false ? 'class="displayNone"' : '';
        $label = empty($this->getData(['locale', 'poweredPageLabel'])) ? 'Motorisé par' : $this->getData(['locale', 'poweredPageLabel']);
        $items .= '><wbr>&nbsp;' . $label . '&nbsp;</span>';
        // Toujours afficher le nom du CMS
        $items .= '<span id="footerZwiiCMS">';
        $items .= '<a href="https://zwiicms.fr/" onclick="window.open(this.href);return false" >ZwiiCMS</a>';
        $items .= '</span>';
        // Affichage du numéro de version
        $items .= '<span id="footerDisplayVersion"';
        $items .= $this->getData(['theme', 'footer', 'displayVersion']) === false ? ' class="displayNone"' : '';
        $items .= '><wbr>&nbsp;' . common::ZWII_VERSION;
        $items .= '</span>';
        // Affichage du sitemap
        $items .= '<span id="footerDisplaySiteMap"';
        $items .= $this->getData(['theme', 'footer', 'displaySiteMap']) === false ? ' class="displayNone"' : '';
        $label = ($this->getData(['locale', 'sitemapPageLabel']) === 'none') ? 'Plan du site' : $this->getData(['locale', 'sitemapPageLabel']);
        $items .= '><wbr>&nbsp;|&nbsp;<a href="' . helper::baseUrl() . 'sitemap"  >' . $label . '</a>';
        $items .= '</span>';
        // Affichage du module de recherche
        $items .= '<span id="footerDisplaySearch"';
        $items .= $this->getData(['theme', 'footer', 'displaySearch']) === false ? ' class="displayNone" >' : '>';
        $label = empty($this->getData(['locale', 'searchPageLabel'])) ? 'Rechercher' : $this->getData(['locale', 'searchPageLabel']);
        if ($this->getData(['locale', 'searchPageId']) !== 'none') {
            $items .= '<wbr>&nbsp;|&nbsp;<a href="' . helper::baseUrl() . $this->getData(['locale', 'searchPageId']) . '"  >' . $label . '</a>';
        }
        $items .= '</span>';
        // Affichage des mentions légales
        $items .= '<span id="footerDisplayLegal"';
        $items .= $this->getData(['theme', 'footer', 'displayLegal']) === false ? ' class="displayNone" >' : '>';
        $label = empty($this->getData(['locale', 'legalPageLabel'])) ? 'Mentions Légales' : $this->getData(['locale', 'legalPageLabel']);
        if ($this->getData(['locale', 'legalPageId']) !== 'none') {
            $items .= '<wbr>&nbsp;|&nbsp;<a href="' . helper::baseUrl() . $this->getData(['locale', 'legalPageId']) . '"  >' . $label . '</a>';
        }
        $items .= '</span>';
        // Affichage de la gestion des cookies
        $items .= '<span id="footerDisplayCookie"';
        $items .= ($this->getData(['config', 'cookieConsent']) === true && $this->getData(['theme', 'footer', 'displayCookie']) === true) ? '>' : ' class="displayNone" >';
        $label = empty($this->getData(['locale', 'cookies', 'cookiesFooterText'])) ? 'Cookies' : $this->getData(['locale', 'cookies', 'cookiesFooterText']);
        $items .= '<wbr>&nbsp;|&nbsp;<a href="javascript:void(0)" id="footerLinkCookie">' . $label . '</a>';
        $items .= '</span>';
        // Affichage du lien de connexion
        if (
            ($this->getData(['theme', 'footer', 'loginLink'])
                and $this->isConnected() === false
            )
            or $this->getUrl(0) === 'theme'
        ) {
            $items .= '<span id="footerLoginLink" ' .
                ($this->getUrl(0) === 'theme' ? 'class="displayNone">' : '>') .
                '<wbr>&nbsp;|&nbsp;<wbr>' .
                template::ico('login', [
                    'href' => helper::baseUrl() . 'user/login/' . strip_tags(str_replace('/', '_', $this->getUrl())),
                    'attr' => 'rel="nofollow"',
                    'help' => 'Connexion'
                ]) . '</span>';
        }
        // Affichage de la barre de membre simple
        if (
            $this->getUser('role') >= self::ROLE_MEMBER && $this->getUser('role') < self::ROLE_ADMIN
            && $this->getData(['theme', 'footer', 'memberBar']) === true
        ) {
            $items .= '<span id="footerDisplayMemberAccount"';
            $items .= $this->getData(['theme', 'footer', 'displaymemberAccount']) === false ? ' class="displayNone">' : '>';
            $items .= '<wbr>&nbsp;|&nbsp;';
            if (
                $this->getUser('permission', 'filemanager') === true
            ) {
                $items .= '<wbr>' . template::ico('folder', [
                    'href' => helper::baseUrl(false) . 'core/vendor/filemanager/dialog.php?type=0&akey=' . md5_file(self::DATA_DIR . 'core.json') . '&lang=' . $this->getData(['user', $this->getUser('id'), 'language']),
                    'margin' => 'all',
                    'attr' => 'data-lity',
                    'help' => 'Fichiers du site'
                ]);
            }
            if (
                $this->getUser('permission', 'user', 'edit') === true
            ) {
                $items .= '<wbr>' . template::ico('user', [
                    'margin' => 'all',
                    'help' => 'Mon compte',
                    'href' => helper::baseUrl() . 'user/edit/' . $this->getUser('id')
                ]);
            }
            $items .= '<wbr>' . template::ico('logout', [
                'margin' => 'all',
                'help' => 'Déconnecter',
                'href' => helper::baseUrl() . 'user/logout'
            ]);
            $items .= '</span>';
        }
        // Fermeture du bloc copyright
        $items .= '</span></div>';
        echo $items;
    }


    /**
     * Affiche les réseaux sociaux
     */
    private function showSocials()
    {
        $socials = '';
        foreach ($this->getData(['config', 'social']) as $socialName => $socialId) {
            switch ($socialName) {
                case 'facebookId':
                    $socialUrl = 'https://www.facebook.com/';
                    $title = 'Facebook';
                    break;
                case 'linkedinId':
                    $socialUrl = 'https://fr.linkedin.com/in/';
                    $title = 'Linkedin';
                    break;
                case 'instagramId':
                    $socialUrl = 'https://www.instagram.com/';
                    $title = 'Instagram';
                    break;
                case 'pinterestId':
                    $socialUrl = 'https://pinterest.com/';
                    $title = 'Pinterest';
                    break;
                case 'twitterId':
                    $socialUrl = 'https://twitter.com/';
                    $title = 'Twitter';
                    break;
                case 'youtubeId':
                    $socialUrl = 'https://www.youtube.com/channel/';
                    $title = 'Chaîne YouTube';
                    break;
                case 'youtubeUserId':
                    $socialUrl = 'https://www.youtube.com/user/';
                    $title = 'YouTube';
                    break;
                case 'githubId':
                    $socialUrl = 'https://www.github.com/';
                    $title = 'Github';
                    break;
                case 'redditId':
                    $socialUrl = 'https://www.reddit.com/user/';
                    $title = 'Reddit';
                    break;
                case 'twitchId':
                    $socialUrl = 'https://www.twitch.tv/';
                    $title = 'Twitch';
                    break;
                case 'vimeoId':
                    $socialUrl = 'https://vimeo.com/';
                    $title = 'Vimeo';
                    break;
                case 'steamId':
                    $socialUrl = 'https://steamcommunity.com/id/';
                    $title = 'Steam';
                    break;
                default:
                    $socialUrl = '';
            }
            if ($socialId !== '') {
                $socials .= '<a href="' . $socialUrl . $socialId . '" onclick="window.open(this.href);return false" data-tippy-content="' . $title . '" alt="' . $title . '">' . template::ico(substr(str_replace('User', '', $socialName), 0, -2)) . '</a>';
            }
        }
        if ($socials !== '') {
            echo '<div id="footerSocials">' . $socials . '</div>';
        }
    }



    /**
     * Affiche le favicon
     */
    public function showFavicon()
    {
        // Light scheme
        $favicon = $this->getData(['config', 'favicon']);
        if (
            $favicon &&
            file_exists(self::FILE_DIR . 'source/' . $favicon)
        ) {
            echo '<link rel="shortcut icon" media="(prefers-color-scheme:light)" href="' . helper::baseUrl(false) . self::FILE_DIR . 'source/' . $favicon . '">';
        } else {
            echo '<link rel="shortcut icon" media="(prefers-color-scheme:light)"  href="' . helper::baseUrl(false) . 'core/vendor/zwiico/ico/favicon.ico">';
        }
        // Dark scheme
        $faviconDark = $this->getData(['config', 'faviconDark']);
        if (
            !empty($faviconDark) &&
            file_exists(self::FILE_DIR . 'source/' . $faviconDark)
        ) {
            echo '<link rel="shortcut icon" media="(prefers-color-scheme:dark)" href="' . helper::baseUrl(false) . self::FILE_DIR . 'source/' . $faviconDark . '">';
            echo '<script src="' . helper::baseUrl(false) . 'core/vendor/favicon-switcher/favicon-switcher.js" crossorigin="anonymous"></script>';
        }
    }


    /**
     * Affiche le menu
     */
    public function showMenu()
    {
        // Met en forme les items du menu
        $itemsLeft = $this->formatMenu(false);

        // Menu extra
        $itemsRight = $this->formatMenu(true);
        // Lien de connexion
        if (
            ($this->getData(['theme', 'menu', 'loginLink'])
                and $this->isConnected() === false
            )
            or $this->getUrl(0) === 'theme'
        ) {
            $itemsRight .= '<li id="menuLoginLink" ' . ($this->getUrl(0) === 'theme' ? 'class="displayNone"' : '') . '>' .
                template::ico('login', [
                    'href' => helper::baseUrl() . 'user/login/' . strip_tags(str_replace('/', '_', $this->getUrl())),
                    'help' => "Connexion"
                ]) .
                '</li>';
        }
        // Commandes pour les membres simples
        if (
            $this->getUser('role') === self::ROLE_MEMBER
            && $this->getData(['theme', 'menu', 'memberBar']) === true
        ) {
            if (
                $this->getUser('role') >= self::ROLE_MEMBER &&
                $this->getUser('permission', 'filemanager') === true
            ) {
                $itemsRight .= '<li>' . template::ico('folder', [
                    'href' => helper::baseUrl(false) . 'core/vendor/filemanager/dialog.php?type=0&akey=' . md5_file(self::DATA_DIR . 'core.json') . '&lang=' . $this->getData(['user', $this->getUser('id'), 'language']),
                    'attr' => 'data-lity',
                    'help' => 'Fichiers du site'
                ]) . '</li>';
            }
            if (
                $this->getUser('permission', 'user', 'edit') === true
            ) {
                $itemsRight .= '<li>' . template::ico('user', [
                    'help' => 'Mon compte',
                    'margin' => 'right',
                    'href' => helper::baseUrl() . 'user/edit/' . $this->getUser('id')
                ]) . '</li>';
            }
            $itemsRight .= '<li>' .
                template::ico('logout', [
                    'help' => 'Déconnecter',
                    'href' => helper::baseUrl() . 'user/logout',
                    'id' => 'barLogout'
                ]) . '</li>';
        }
        // Retourne les items du menu
        echo '<ul class="navMain" id="menuLeft">' . $itemsLeft . '</ul><ul class="navMain" id="menuRight">' . $itemsRight;
        // Drapeau les langues
        foreach (self::$languages as $key => $value) {
            if (is_dir(self::DATA_DIR . $key)) {
                $t[] = $this->showi18n($key);
            }
        }
        // Pas de drapeau si la langue est unique
        if (count($t) > 1) {
            foreach ($t as $key) {
                echo $key;
            }
        }
        echo '</ul>';
    }

    /**
     * Cette fonction est appelée par showMenu
     * Elle permet de générer le menu selon qu'il s'agisse du menu principal ou du petit menu
     *  @param $menu bool false pour le menu principal, true pour le petit menu
     */
    /**
     * Construit récursivement les enfants d'un menu
     * @param array $childrenPageIds IDs des enfants
     * @param int $depth Profondeur actuelle (commence à 1)
     * @param int $maxDepth Profondeur maximale
     * @param string $parentId ID du parent pour la classe CSS
     * @param string $currentPageId ID de la page courante
     * @return string HTML du menu
     */
    private function buildChildrenRecursive($childrenPageIds, $depth, $maxDepth, $parentId, $currentPageId)
    {
        $items = '';
        
        // Arrêter la récursion si profondeur max atteinte
        if ($depth >= $maxDepth) {
            return $items;
        }
        
        foreach ($childrenPageIds as $childKey) {
            // Ne pas afficher les pages de rôle supérieur
            if ($this->getData(['page', $childKey, 'role']) > $this->getUser('role')) {
                continue;
            }
            
            // Propriétés de l'item
            $active = ($childKey === $currentPageId) ? 'active ' : '';
            $targetBlank = $this->getData(['page', $childKey, 'targetBlank']) ? ' target="_blank"' : '';
            
            // Vérifier s'il y a des enfants pour ce nœud en parcourant toutes les pages
            $grandChildren = [];
            $allPages = $this->getData(['page']);
            if (is_array($allPages)) {
                // Créer un tableau temporaire avec les enfants et leur position
                $tempChildren = [];
                foreach ($allPages as $pageId => $pageData) {
                    // Si cette page a comme parent $childKey, c'est un enfant
                    if (isset($pageData['parentPageId']) && $pageData['parentPageId'] === $childKey) {
                        // Vérifier les droits d'accès
                        if ($pageData['role'] <= $this->getUser('role')) {
                            $position = isset($pageData['position']) ? $pageData['position'] : 0;
                            $tempChildren[$pageId] = $position;
                        }
                    }
                }
                // Trier par position
                asort($tempChildren);
                $grandChildren = array_keys($tempChildren);
            }
            
            // Un élément a des enfants affichables seulement si :
            // 1. Il a des enfants visibles
            // 2. Les enfants ne sont pas cachés
            // 3. On n'a pas atteint la profondeur maximale (depth + 1 <= maxDepth)
            $hasChildren = !empty($grandChildren) 
                && $this->getData(['page', $childKey, 'hideMenuChildren']) === false
                && ($depth + 1 <= $maxDepth);
            
            // Classe CSS pour indiquer la présence d'enfants et la profondeur
            $itemClass = 'navDepth' . $depth . ($hasChildren ? ' navParent' : '');
            
            // Mise en page du sous-item
            $items .= '<li id="' . $childKey . '" class="' . $itemClass . '">';
            
            if (
                ($this->getData(['page', $childKey, 'disable']) === true and
                    $this->isConnected() === false) or ($this->getData(['page', $childKey, 'disable']) === true and
                    $this->isConnected() === true and
                    $this->getUser('role') < self::ROLE_EDITOR)
            ) {
                $pageUrl = ($this->getData(['locale', 'homePageId']) === $this->getUrl(0)) ? helper::baseUrl(false) : helper::baseUrl() . $this->getUrl(0);
                $items .= '<a class="navLevel' . $depth . '" href="' . $pageUrl . '">';
            } else {
                $pageUrl = ($this->getData(['locale', 'homePageId']) === $childKey) ? helper::baseUrl(false) : helper::baseUrl() . $childKey;
                $items .= '<a class="' . $active . 'navLevel' . $depth . ' ' . $parentId . '" href="' . $pageUrl . '"' . $targetBlank . '>';
            }

            // Type de menu (texte, icône, etc.)
            $items .= $this->getMenuLabel($childKey);
            
            // Afficher l'icône de déroulement si enfants visibles
            if ($hasChildren) {
                // Indicateur systématiquement orienté vers le bas
                $items .= template::ico('down', ['margin' => 'left']);
            }
            
            $items .= '</a>';
            
            // Récursion pour les enfants de ce nœud
            if ($hasChildren) {
                $nextDepth = $depth + 1;
                $childrenHtml = $this->buildChildrenRecursive($grandChildren, $nextDepth, $maxDepth, $childKey, $currentPageId);
                // Générer le <ul> seulement s'il y a du contenu
                if (!empty($childrenHtml)) {
                    $cssClass = 'navSub navSubmenu' . $nextDepth;
                    $items .= '<ul class="' . $cssClass . '">';
                    $items .= $childrenHtml;
                    $items .= '</ul>';
                }
            }
            
            $items .= '</li>';
        }
        
        return $items;
    }

    private function formatMenu($extra = false)
    {
        $items = '';
        $currentPageId = $this->getData(['page', $this->getUrl(0)]) ? $this->getUrl(0) : $this->getUrl(2);
        $maxDepth = self::MENU_DEPTH;

        foreach ($this->getHierarchy() as $parentPageId => $childrenPageIds) {
             // Vérifier que c'est vraiment une page parente (parentPageId vide)
            if (
                $this->getData(['page', $parentPageId, 'parentPageId']) !== ''
            ) {
                continue;
            }
            // Ne pas afficher les pages du rôle supérieur
            if (
                $this->getData(['page', $parentPageId, 'role']) > $this->getUser('role')
            ) {
                continue;
            }
            // Menu extra ou standard
            if (
                // Absence de la position extra, la page est toujours affichée à gauche.
                ($this->getData(['page', $parentPageId, 'extraPosition']) !== NULL || $extra === true) &&
                $this->getData(['page', $parentPageId, 'extraPosition']) !== $extra
            ) {
                continue;
            }
            // Propriétés de l'item
            $active = ($parentPageId === $currentPageId or in_array($currentPageId, $childrenPageIds)) ? 'active ' : '';
            $targetBlank = $this->getData(['page', $parentPageId, 'targetBlank']) ? ' target="_blank"' : '';
            // Mise en page de l'item
            $items .= '<li id="' . $parentPageId . '">';

            if (
                ($this->getData(['page', $parentPageId, 'disable']) === true and
                    $this->isConnected() === false) or ($this->getData(['page', $parentPageId, 'disable']) === true and
                    $this->isConnected() === true and
                    $this->getUser('role') < self::ROLE_EDITOR)
            ) {
                $pageUrl = ($this->getData(['locale', 'homePageId']) === $this->getUrl(0)) ? helper::baseUrl(false) : helper::baseUrl() . $this->getUrl(0);
                $items .= '<a href="' . $pageUrl . '">';
            } else {
                $pageUrl = ($this->getData(['locale', 'homePageId']) === $parentPageId) ? helper::baseUrl(false) : helper::baseUrl() . $parentPageId;
                $items .= '<a class="' . $active . '" href="' . $pageUrl . '"' . $targetBlank . '>';
            }

            // Type de menu (texte, icône, etc.)
            $items .= $this->getMenuLabel($parentPageId);
            // Vérifier s'il y a au moins un enfant visible et si les enfants ne sont pas cachés
            $hasVisibleChildren = false;
            foreach ($childrenPageIds as $childKey) {
                // Vérifier si l'enfant est accessible pour l'utilisateur
                if ($this->getData(['page', $childKey, 'role']) <= $this->getUser('role')) {
                    $hasVisibleChildren = true;
                    break;
                }
            }
            // Afficher l'icône de déroulement si enfants visibles
            if (
                $hasVisibleChildren &&
                $this->getdata(['page', $parentPageId, 'hideMenuChildren']) === false
            ) {
                $items .= template::ico('down', ['margin' => 'left']);
            }
            // ------------------------------------------------
            $items .= '</a>';
            
            // Ajouter la classe navDepth0 et navParent si l'élément a des enfants
            if ($hasVisibleChildren && $this->getdata(['page', $parentPageId, 'hideMenuChildren']) === false) {
                // Ajouter les classes au <li> parent déjà créé en modifiant la chaîne
                $items = preg_replace(
                    '/<li id="' . preg_quote($parentPageId, '/') . '">/',
                    '<li id="' . $parentPageId . '" class="navDepth0 navParent">',
                    $items
                );
            }
            
            if (
                $this->getdata(['page', $parentPageId, 'hideMenuChildren']) === true ||
                empty($childrenPageIds)
            ) {
                continue;
            }
            // Récursion pour les enfants
            $childrenHtml = $this->buildChildrenRecursive($childrenPageIds, 1, $maxDepth, $parentPageId, $currentPageId);
            // Générer le <ul> seulement s'il y a du contenu
            if (!empty($childrenHtml)) {
                $items .= '<ul class="navSub navSubmenu1">';
                $items .= $childrenHtml;
                $items .= '</ul>';
            }
        }
        return ($items);
    }


    /**
     * Générer un menu pour la barre latérale
     * Uniquement texte
     * @param $onlyChildren n'affiche les sous-pages de la page actuelle
     */
    private function showMenuSide($onlyChildren = null)
    {
        // Met en forme les items du menu
        $items = '';
        // Nom de la page courante
        $currentPageId = $this->getData(['page', $this->getUrl(0)]) ? $this->getUrl(0) : $this->getUrl(2);
        // Nom de la page parente
        $currentParentPageId = $this->getData(['page', $currentPageId, 'parentPageId']);
        $maxDepth = self::MENU_DEPTH;

        if ($onlyChildren === true) {
            if (empty($currentParentPageId)) {
                $filterCurrentPageId = $currentPageId;
            } else {
                $filterCurrentPageId = $currentParentPageId;
            }
        } else {
            $items .= '<ul class="menuSide">';
        }

        foreach ($this->getHierarchy() as $parentPageId => $childrenPageIds) {
            // Ne pas afficher les entrées masquées
            if ($this->getData(['page', $parentPageId, 'hideMenuSide']) === true) {
                continue;
            }
            // Ne pas afficher les pages avec position 0 (pages orphelines)
            if ($this->getData(['page', $parentPageId, 'position']) === 0) {
                continue;
            }
            // Filtre actif et nom de la page parente courante différente, on sort de la boucle
            if ($onlyChildren === true && $parentPageId !== $filterCurrentPageId) {
                continue;
            }
            // Propriétés de l'item
            $active = ($parentPageId === $currentPageId or in_array($currentPageId, $childrenPageIds)) ? ' class="active"' : '';
            $targetBlank = $this->getData(['page', $parentPageId, 'targetBlank']) ? ' target="_blank" ' : '';
            // Mise en page de l'item;
            // Ne pas afficher le parent d'une sous-page quand l'option est sélectionnée.
            if ($onlyChildren === false) {
                $items .= '<li class="menuSideChild">';
                if (
                    $this->getData(['page', $parentPageId, 'disable']) === true
                    and $this->isConnected() === false
                ) {
                    $items .= '<a href="' . $this->getUrl(1) . '">';
                } else {
                    $items .= '<a href="' . helper::baseUrl() . $parentPageId . '"' . $targetBlank . $active . '>';
                }
                $items .= $this->getData(['page', $parentPageId, 'shortTitle']);
                $items .= '</a>';
            }
            
            // Appeler la méthode récursive pour les enfants
            if (!empty($childrenPageIds)) {
                $itemsChildren = $this->buildMenuSideChildrenRecursive($childrenPageIds, 1, $maxDepth, $currentPageId);
                if (!empty($itemsChildren)) {
                    $items .= '<ul class="menuSideChild">';
                    $items .= $itemsChildren;
                    $items .= '</ul>';
                }
            }
            
            if ($onlyChildren === false && empty($childrenPageIds)) {
                $items .= '</li>';
            }
        }
        if ($onlyChildren === false) {
            $items .= '</ul>';
        }
        // Retourne les items du menu
        echo $items;
    }

    /**
     * Construit récursivement les enfants du menu latéral
     * @param array $childrenPageIds IDs des enfants
     * @param int $depth Profondeur actuelle (commence à 1)
     * @param int $maxDepth Profondeur maximale
     * @param string $currentPageId ID de la page courante
     * @return string HTML du menu
     */
    private function buildMenuSideChildrenRecursive($childrenPageIds, $depth, $maxDepth, $currentPageId)
    {
        $items = '';
        
        // Arrêter la récursion si profondeur max atteinte
        if ($depth >= $maxDepth) {
            return $items;
        }
        
        foreach ($childrenPageIds as $childKey) {
            // Passer les entrées masquées
            if ($this->getData(['page', $childKey, 'hideMenuSide']) === true) {
                continue;
            }
            // Ne pas afficher les pages avec position 0 (pages orphelines)
            if ($this->getData(['page', $childKey, 'position']) === 0) {
                continue;
            }

            // Propriétés de l'item
            $active = ($childKey === $currentPageId) ? ' class="active"' : '';
            $targetBlank = $this->getData(['page', $childKey, 'targetBlank']) ? ' target="_blank"' : '';
            
            // Vérifier s'il y a des enfants pour ce nœud
            $grandChildren = [];
            $allPages = $this->getData(['page']);
            if (is_array($allPages)) {
                $tempChildren = [];
                foreach ($allPages as $pageId => $pageData) {
                    if (isset($pageData['parentPageId']) && $pageData['parentPageId'] === $childKey) {
                        // Ne pas ajouter les pages masquées dans le menu latéral
                        if (isset($pageData['hideMenuSide']) && $pageData['hideMenuSide'] === true) {
                            continue;
                        }
                        // Ne pas ajouter les pages avec position 0
                        $position = isset($pageData['position']) ? $pageData['position'] : 0;
                        if ($position === 0) {
                            continue;
                        }
                        $tempChildren[$pageId] = $position;
                    }
                }
                asort($tempChildren);
                $grandChildren = array_keys($tempChildren);
            }
            
            // Mise en page du sous-item avec indentation
            $indent = '&#9492; ';
            $items .= '<li class="menuSideChild">';

            if (
                $this->getData(['page', $childKey, 'disable']) === true
                and $this->isConnected() === false
            ) {
                $items .= '<a href="' . $this->getUrl(1) . '">' . $indent;
            } else {
                $items .= '<a href="' . helper::baseUrl() . $childKey . '"' . $targetBlank . $active . '>' . $indent;
            }

            $items .= $this->getData(['page', $childKey, 'shortTitle']);
            $items .= '</a>';
            
            // Récursion pour les enfants de ce nœud
            if (!empty($grandChildren) && ($depth + 1 <= $maxDepth)) {
                $nextDepth = $depth + 1;
                $childrenHtml = $this->buildMenuSideChildrenRecursive($grandChildren, $nextDepth, $maxDepth, $currentPageId);
                if (!empty($childrenHtml)) {
                    $items .= '<ul class="menuSideChild">';
                    $items .= $childrenHtml;
                    $items .= '</ul>';
                }
            }
            
            $items .= '</li>';
        }
        
        return $items;
    }

    /**
     * Affiche le meta titre
     */
    public function showMetaTitle()
    {
        echo '<title>' . $this->core->output['metaTitle'] . '</title>';
        echo '<meta property="og:title" content="' . $this->core->output['metaTitle'] . '" />';
        if (
            $this->getData(['locale', 'homePageId']) === $this->getUrl(0)
        ) {
            echo '<link rel="canonical" href="' . helper::baseUrl(false) . '" />';
        } else {
            echo '<link rel="canonical" href="' . helper::baseUrl(true) . $this->getUrl() . '" />';
        }
    }

    /**
     * Affiche la meta description
     */
    public function showMetaDescription()
    {
        echo '<meta name="description" content="' . $this->core->output['metaDescription'] . '" />';
        echo '<meta property="og:description" content="' . $this->core->output['metaDescription'] . '" />';
    }

    /**
     * Affiche le meta type
     */
    public function showMetaType()
    {
        echo '<meta property="og:type" content="website" />';
    }

    /**
     * Affiche la meta image (site screenshot)
     */
    public function showMetaImage()
    {
        $imagePath = self::FILE_DIR . 'source/' . $this->getData(['config', 'seo', 'openGraphImage']);
        if (
            $this->getData(['config', 'seo', 'openGraphImage'])
            && file_exists($imagePath)
        ) {
            $typeMime = exif_imagetype($imagePath);
            switch ($typeMime) {
                case IMAGETYPE_JPEG:
                    $typeMime = 'image/jpeg';
                    break;
                case IMAGETYPE_PNG:
                    $typeMime = 'image/png';
                    break;
                default:
                    // Type incorrect
                    return;
            }
            $imageSize = getimagesize($imagePath);
            $wide = $imageSize[0];
            $height = $imageSize[1];
            //Sortie
            $items = '<meta property="og:image" content="' . helper::baseUrl(false) . self::FILE_DIR . 'source/' . $this->getData(['config', 'seo', 'openGraphImage']) . '" />';
            $items .= '<meta property="og:image:type" content="' . $typeMime . '" />';
            $items .= '<meta property="og:image:width" content="' . $wide . '" />';
            $items .= '<meta property="og:image:height" content="' . $height . '" />';
            echo $items;
        }
    }

    /**
     * Affiche la notification
     */
    public function showNotification()
    {
        if (common::$importNotices) {
            $notification = common::$importNotices[0];
            $notificationClass = 'notificationSuccess';
        }
        if (common::$inputNotices) {
            $notification = 'Impossible de soumettre le formulaire, car il contient des erreurs';
            $notificationClass = 'notificationError';
        }
        if (common::$coreNotices) {
            $notification = sprintf('%s <p> | ', helper::translate('Restauration des bases de données absentes'));
            foreach (common::$coreNotices as $item)
                $notification .= $item . ' | ';
            $notificationClass = 'notificationError';
        } elseif (empty($_SESSION['ZWII_NOTIFICATION_SUCCESS']) === false) {
            $notification = $_SESSION['ZWII_NOTIFICATION_SUCCESS'];
            $notificationClass = 'notificationSuccess';
            unset($_SESSION['ZWII_NOTIFICATION_SUCCESS']);
        } elseif (empty($_SESSION['ZWII_NOTIFICATION_ERROR']) === false) {
            $notification = $_SESSION['ZWII_NOTIFICATION_ERROR'];
            $notificationClass = 'notificationError';
            unset($_SESSION['ZWII_NOTIFICATION_ERROR']);
        } elseif (empty($_SESSION['ZWII_NOTIFICATION_OTHER']) === false) {
            $notification = $_SESSION['ZWII_NOTIFICATION_OTHER'];
            $notificationClass = 'notificationOther';
            unset($_SESSION['ZWII_NOTIFICATION_OTHER']);
        }
        if (isset($notification) and isset($notificationClass)) {
            echo '<div id="notification" class="' . $notificationClass . '">' . $notification . '<span id="notificationClose">' . template::ico('cancel') . '<!----></span><div id="notificationProgress"></div></div>';
        }
    }

    /**
     * Affiche la barre de membre
     */
    public function showBar()
    {
        if ($this->isConnected() === true) {
            // Items de gauche
            $leftItems = '';
            // Sélecteur de langues
            if ($this->getUser('role') >= self::ROLE_EDITOR) {
                $leftItem = '';
                foreach (self::$languages as $key => $value) {
                    if (is_dir(self::DATA_DIR . $key)) {
                        $location = helper::baseUrl() . 'language/content/' . $key;
                        $leftItem .= '<option name="' . $key . '" value="' . $location . '" ' . ($key === self::$siteContent ? 'selected' : '') . '>' . $value . '</option>';
                    }
                }
                $leftItems .= '<li><select id="barSelectLanguage" >';
                $leftItems .= $leftItem;
                $leftItems .= '</select></li>';
            }
            if ($this->getUser('role') >= self::ROLE_ADMIN) {
                $leftItems .= '<li>' . template::ico('flag', [
                    'help' => 'Langues',
                    'href' => helper::baseUrl() . 'language'
                ]) . '</li>';
            }

            // Liste des pages
            if ($this->getUser('role') >= self::ROLE_EDITOR) {
                $leftItems .= '<li><select id="barSelectPage">';
                $leftItems .= '<option value="">' . helper::translate('Pages du site') . '</option>';
                $leftItems .= '<optgroup label="' . helper::translate('Pages orphelines') . '">';
                $orpheline = true;
                $currentPageId = $this->getData(['page', $this->getUrl(0)]) ? $this->getUrl(0) : $this->getUrl(2);
                foreach ($this->getHierarchy(null, false) as $parentPageId => $childrenPageIds) {
                    if (
                        $this->getData(['page', $parentPageId, 'position']) !== 0 &&
                        $orpheline
                    ) {
                        $orpheline = false;
                        $leftItems .= '<optgroup label="' . helper::translate('Pages dans le menu') . '">';
                    }
                    // Exclure les barres latérales de cette boucle
                    if ($this->getData(['page', $parentPageId, 'block']) === 'bar') {
                        continue;
                    }
                    // Exclure les pages qui sont déjà des enfants d'autres pages
                    $isChild = false;
                    foreach ($this->getHierarchy(null, false) as $potentialParentId => $potentialChildren) {
                        if (in_array($parentPageId, $potentialChildren)) {
                            $isChild = true;
                            break;
                        }
                    }
                    if ($isChild) {
                        continue;
                    }
                    // Exclure les barres
                    if ($this->getData(['page', $parentPageId, 'block']) !== 'bar') {
                        $leftItems .= '<option value="' .
                            helper::baseUrl() .
                            $parentPageId . '"' .
                            ($parentPageId === $currentPageId ? ' selected' : false) .
                            ' class="' .
                            ($this->getData(['page', $parentPageId, 'disable']) === true ? 'pageInactive' : '') .
                            ($this->getData(['page', $parentPageId, 'position']) === 0 ? ' pageHidden' : '') .
                            '">' .
                            $this->getData(['page', $parentPageId, 'shortTitle']) .
                            '</option>';
                        
                        // Afficher les enfants avec indentation hiérarchique
                        $this->addChildrenToSelect($leftItems, $childrenPageIds, $currentPageId, 1);
                    }
                }
                $leftItems .= '</optgroup' >
                    // Afficher les barres
                    $leftItems .= '<optgroup label="' . helper::translate('Barres latérales') . '">';
                foreach ($this->getHierarchy(null, false, true) as $parentPageId => $childrenPageIds) {
                    $leftItems .= '<option value="' . helper::baseUrl() . $parentPageId . '"' . ($parentPageId === $currentPageId ? ' selected' : false) . '>' . $this->getData(['page', $parentPageId, 'shortTitle']) . '</option>';
                    
                    // Pour les barres latérales, afficher les enfants avec simple indentation (pas de récursion)
                    foreach ($childrenPageIds as $childKey) {
                        $leftItems .= '<option value="' . helper::baseUrl() . $childKey . '"' . ($childKey === $currentPageId ? ' selected' : false) . '>&nbsp;&nbsp;&nbsp;&nbsp;' . $this->getData(['page', $childKey, 'shortTitle']) . '</option>';
                    }
                }
                $leftItems .= '</optgroup>';
                $leftItems .= '</select></li>';
                // Bouton Ajouter une page
                if ($this->getUser('permission', 'page', 'add')) {
                    $leftItems .= '<li>' . template::ico('plus', [
                        'href' => helper::baseUrl() . 'page/add/' . self::$siteContent,
                        'help' => 'Nouvelle page ou barre latérale'
                    ]) . '</li>';
                }
                if (
                    // Sur un module de page qui autorise le bouton de modification de la page
                    $this->core->output['showBarEditButton']
                    // Sur une page sans module
                    or $this->getData(['page', $this->getUrl(0), 'moduleId']) === ''
                    // Sur une page avec un module invalide
                    or (empty($this->getData(['page', $this->getUrl(0), 'moduleId']))  === false
                        and class_exists($this->getData(['page', $this->getUrl(0), 'moduleId'])) === false
                    )
                    // Sur une page d'accueil
                    or $this->getUrl(0) === ''
                ) {
                    // Bouton Editer une page
                    if (
                        $this->getUser('permission', 'page', 'edit')
                        and $this->geturl(1) !== 'edit'
                    ) {
                        $leftItems .= '<li>' . template::ico('pencil', [
                            'href' => helper::baseUrl() . 'page/edit/' . $this->getUrl(0) . '/' . self::$siteContent,
                            'help' => 'Éditer la page'
                        ]) . '</li>';
                    }
                    // Bouton Editer le module d'une page
                    if (
                        $this->getUser('permission', 'page', 'module')
                        and $this->geturl(1) !== 'edit'
                        and $this->getData(['page', $this->getUrl(0), 'moduleId'])
                        and class_exists($this->getData(['page', $this->getUrl(0), 'moduleId'])) === true
                        and $this->getUser('permission',$this->getData(['page', $this->getUrl(0), 'moduleId']), 'config') === true
                    ) {
                        $leftItems .= '<li>' . template::ico('gear', [
                            'href' => helper::baseUrl() . $this->getUrl(0) . '/config',
                            'help' => 'Module de la page'
                        ]) . '</li>';
                    }
                    // Bouton dupliquer une page
                    if (
                        $this->getUser('permission', 'page', 'duplicate')
                        and $this->geturl(1) !== 'edit'
                    ) {
                        $leftItems .= '<li>' . template::ico('clone', [
                            'href' => helper::baseUrl() . 'page/duplicate/' . $this->getUrl(0) . '/' . self::$siteContent,
                            'help' => 'Dupliquer la page'
                        ])
                            . '</li>';
                    }
                    // Bouton Effacer une page
                    if (
                        $this->getUser('permission', 'page', 'delete')
                        and $this->geturl(1) !== 'edit'

                    ) {
                        $leftItems .= '<li>' . template::ico('trash', [
                            'href' => helper::baseUrl() . 'page/delete/' . $this->getUrl(0) . '/' . self::$siteContent,
                            'help' => 'Supprimer la page',
                            'id' => 'pageDelete'
                        ])
                            . '</li>';
                    }
                }
            }
            // Items de droite
            $rightItems = '';
            if (
                $this->getUser('role') >= self::ROLE_EDITOR
                && $this->getUser(
                    'permission',
                    'filemanager'
                )
            ) {
                $rightItems .= '<li>' . template::ico('folder', [
                    'help' => 'Fichiers',
                    'href' => helper::baseUrl(false) . 'core/vendor/filemanager/dialog.php?type=0&akey=' . md5_file(self::DATA_DIR . 'core.json') . '&lang=' . $this->getData(['user', $this->getUser('id'), 'language']),
                    'attr' => 'data-lity'
                ]) . '</li>';
            }
            if ($this->getUser('role') >= self::ROLE_ADMIN) {
                $rightItems .= '<li>' . template::ico('brush', [
                    'help' => 'Thème',
                    'href' => helper::baseUrl() . 'theme'
                ]) . '</li>';
                $rightItems .= '<li>' . template::ico('users', [
                    'help' => 'Utilisateurs',
                    'href' => helper::baseUrl() . 'user'
                ]) . '</li>';
                if ($this->getData(['core', 'updateModuleAvailable'])) {
                    $rightItems .= '<li>' . template::ico('puzzle colorRed', [
                        'help' => 'Modules',
                        'href' => helper::baseUrl() . 'plugin'
                    ]) . '</li>';
                } else {
                    $rightItems .= '<li>' . template::ico('puzzle', [
                        'help' => 'Modules',
                        'href' => helper::baseUrl() . 'plugin'
                    ]) . '</li>';
                }
                $rightItems .= '<li>' . template::ico('cog-alt', [
                    'help' => 'Configuration',
                    'href' => helper::baseUrl() . 'config'
                ]) . '</li>';
                // Mise à jour automatique
                $today = mktime(0, 0, 0);
                $checkUpdate = $this->getData(['core', 'lastAutoUpdate']);
                // Recherche d'une mise à jour si active, si une mise à jour n'est pas déjà disponible et le délai journalier est dépassé.
                if (
                    $this->getData(['config', 'autoUpdate'])
                ) {
                    if (
                        $today > $checkUpdate + $this->getData(['config', 'autoUpdateDelay', 86400])
                    ) {
                        // Dernier auto controle
                        $this->setData(['core', 'lastAutoUpdate', $today], false);
                        if (
                            helper::checkNewVersion(common::ZWII_UPDATE_CHANNEL)
                        ) {
                            $this->setData(['core', 'updateAvailable', true], false);
                        }

                        // Modules installés
                        $infoModules = helper::getModules();
                        // Recherche de mise à jour des modules
                        $store = plugin::getStore();
                        if (is_array($store)) {
                            // Parcourir les données des modules du store
                            foreach ($store as $key => $value) {
                                if (empty($key)) {
                                    continue;
                                }
                                // Mise à jour d'un module
                                // Le module est installé et une mise à jour est en ligne
                                if (
                                    isset($infoModules[$key])
                                    &&
                                    version_compare($infoModules[$key]['version'], $value['version'], '<')
                                ) {
                                    $this->setData(['core', 'updateModuleAvailable', true], false);
                                }
                            }
                        }
                        // Sauvegarde la base manuellement
                        $this->saveDB('core');
                    }
                }
                // Afficher le bouton : Mise à jour détectée + activée
                if ($this->getData(['core', 'updateAvailable'])) {
                    $rightItems .= '<li><a href="' . helper::baseUrl() . 'install/update" data-tippy-content="Mettre à jour Zwii ' . common::ZWII_VERSION . ' vers ' . helper::getOnlineVersion(common::ZWII_UPDATE_CHANNEL) . '">' . template::ico('update colorRed') . '</a></li>';
                }
            }

            // Boutons depuis le rôle éditeur
            if (
                $this->getUser('role') >= self::ROLE_EDITOR
                && $this->getUser('permission', 'user', 'edit')

            ) {
                $rightItems .= '<li><a href="' . helper::baseUrl() . 'user/edit/' . $this->getUser('id') .
                    '" data-tippy-content="' . helper::translate('Configurer mon compte') . '">' .
                    template::ico('user', ['margin' => 'right']) . '<span id="displayUsername">' . $this->getUser('firstname') . ' ' . $this->getUser('lastname') .
                    '</span></a></li>';
            }
            $rightItems .= '<li>' . template::ico('logout', [
                'help' => 'Déconnecter',
                'href' => helper::baseUrl() . 'user/logout',
                'id' => 'barLogout'
            ]) . '</li>';
            // Barre de membre
            echo '<div id="bar"><ul id="barLeft">' . $leftItems . '</ul><ul id="barRight">' . $rightItems . '</ul></div>';
        }
    }

    /**
     * Affiche le script
     */
    public function showScript()
    {
        ob_start();
        require 'core/core.js.php';
        $coreScript = ob_get_clean();
        $inlineScript = '';
        if ($this->core->output['inlineScript']) {
            $inlineScript = implode($this->core->output['inlineScript']);
        }
        echo '<script defer>' . helper::minifyJs($coreScript . $this->core->output['script']) . '</script>';
        echo '<script defer>' . helper::minifyJs(htmlspecialchars_decode($inlineScript)) . '</script>';
    }

    /**
     * Affiche le style
     */
    public function showStyle()
    {
        // Import des styles liés à la page
        if ($this->core->output['style']) {
            echo '<base href="' . helper::baseUrl(true) . '">';
            if (strpos($this->core->output['style'], 'admin.css') >= 1) {
                echo '<link rel="stylesheet" href="' . self::DATA_DIR . 'admin.css?' . md5_file(self::DATA_DIR . 'admin.css') . '">';
            }
            echo '<style type="text/css">' . helper::minifyCss($this->core->output['style']) . '</style>';
        }
    }

    /**
     * Affiche le style interne des pages
     */
    public function showInlineStyle()
    {
        // Import des styles liés à la page
        if ($this->core->output['inlineStyle']) {
            foreach ($this->core->output['inlineStyle'] as $style) {
                if ($style) {
                    echo '<style type="text/css">' . helper::minifyCss(htmlspecialchars_decode($style)) . '</style>';
                }

            }
        }
    }

    /**
     * Importe les polices de caractères
     */
    public function showFonts()
    {
        // Import des fontes liées au thème
        if (file_exists(self::DATA_DIR . 'font/font.html')) {
            include_once(self::DATA_DIR . 'font/font.html');
        }
    }

    /**
     * Affiche l'import des librairies
     */
    public function showVendor(): void
    {
        // Variables partagées
        $vars = 'var baseUrl = ' . json_encode(helper::baseUrl(false)) . ';';
        $vars .= 'var baseUrlQs = ' . json_encode(helper::baseUrl()) . ';';
        // Liste des fontes disponibles pour TinyMCE (nom=font-family)
        $fontFormats = [];
        foreach (['websafe', 'imported', 'files'] as $typeFont) {
            $fontsSource = $this->getData(['font', $typeFont]);
            if (is_array($fontsSource)) {
                foreach ($fontsSource as $fontValue) {
                    $fontFormats[] = $fontValue['name'] . '=' . $fontValue['font-family'];
                }
            }
        }
        $vars .= 'var tinymceFontFormats = ' . json_encode(implode(';', $fontFormats)) . ';';
        if (
            $this->isConnected() === true
            and $this->getUser('role') >= self::ROLE_EDITOR
        ) {
            $vars .= 'var privateKey = ' . json_encode(md5_file(self::DATA_DIR . 'core.json')) . ';';
        }
        echo '<script defer >' . helper::minifyJs($vars) . '</script>';
        // Librairies
        $moduleId = $this->getData(['page', $this->getUrl(0), 'moduleId']);
        foreach ($this->core->output['vendor'] as $vendorName) {
            // Coeur
            if (file_exists('core/vendor/' . $vendorName . '/inc.json')) {
                $vendorPath = 'core/vendor/' . $vendorName . '/';
            }
            // Module
            elseif (
                $moduleId
                and in_array($moduleId, self::$coreModuleIds) === false
                and file_exists(self::MODULE_DIR . $moduleId . '/vendor/' . $vendorName . '/inc.json')
            ) {
                $vendorPath = self::MODULE_DIR . $moduleId . '/vendor/' . $vendorName . '/';
            }
            // Sinon continue
            else {
                continue;
            }
            // Détermine le type d'import en fonction de l'extension de la librairie
            $vendorFiles = json_decode(file_get_contents($vendorPath . 'inc.json'));
            foreach ($vendorFiles as $vendorFile) {
                switch (pathinfo($vendorFile, PATHINFO_EXTENSION)) {
                    case 'css':
                        // Force le rechargement lors d'une mise à jour du jeu d'icônes
                        $reload = $vendorPath === 'core/vendor/zwiico/'
                            ? '?' . md5_file('core/vendor/zwiico/css/zwiico-codes.css')
                            : '';
                        echo '<link rel="stylesheet" href="' . helper::baseUrl(false) . $vendorPath . $vendorFile . $reload . '">';
                        break;
                    case 'js':
                        echo '<script src="' . helper::baseUrl(false) . $vendorPath . $vendorFile . '"></script>';
                        break;
                }
            }
        }
    }
    /**
     * Affiche le cadre avec les drapeaux sélectionnés
     */
    public function showi18n($lang)
    {
        if (
            (isset($_SESSION['ZWII_SITE_CONTENT'])
                and $_SESSION['ZWII_SITE_CONTENT'] === $lang
            )
        ) {
            $select = ' class="i18nFlagSelected" ';
        } else {
            $select = ' class="i18nFlag" ';
        }

        $items = '<li>';
        $items .= '<a href="' . helper::baseUrl() . 'language/content/' . $lang . '"><img ' . $select . ' alt="' . self::$languages[$lang] . '" src="' . helper::baseUrl(false) . 'core/vendor/i18n/png/' . $lang . '.png"/></a>';
        $items .= '</li>';
        return $items;
    }

    // Affiche une icône de navigation
    // @param $position string 'top' or 'bottom
    public function showNavButtons($position)
    {
        // Boutons par défaut
        $leftButton = 'left';
        $rightButton = 'right-dir';

        // Déterminer la hiérarchie des pages
        $hierarchy = array();
        foreach ($this->getHierarchy() as $parentKey => $parentValue) {
            $hierarchy[] = $parentKey;
            foreach ($parentValue as $childKey) {
                $hierarchy[] = $childKey;
            }
        }
        // Parcourir la hiérarchie et rechercher les éléments avant et après
        $elementToFind = $this->getUrl(0);

        // Trouver la clé de l'élément recherché
        $key = array_search($elementToFind, $hierarchy);

        $previousPage = null;
        $nextPage = null;

        if ($key !== false) {
            // Trouver l'élément précédent
            $previousKey = ($key > 0) ? $key - 1 : null;
            $previousValue = ($previousKey !== null) ? $hierarchy[$previousKey] : null;

            // Trouver l'élément suivant
            $nextKey = ($key < count($hierarchy) - 1) ? $key + 1 : null;
            $nextValue = ($nextKey !== null) ? $hierarchy[$nextKey] : null;

            $previousPage = $previousValue;
            $nextPage = $nextValue;
        }

        // Jeux d'icônes sinon celui par défaut
        if ($this->getData(['page', $this->getUrl(0), 'navTemplate'])) {
            $leftButton = self::$navIconTemplate[$this->getData(['page', $this->getUrl(0), 'navTemplate'])]['left'];
            $rightButton = self::$navIconTemplate[$this->getData(['page', $this->getUrl(0), 'navTemplate'])]['right'];
        }

        $items = '<div class="navButton">';
        $items .= '<div class="row">';
        $items .= '<div class="col1">';
        if (
            $previousPage !== null && $this->getData(['page', $this->getUrl(0), 'navLeft']) === $position
        ) {
            $items .= template::button('navPreviousButtonLeft', [
                'href' => helper::baseUrl() . $previousPage,
                'value' => template::ico($leftButton)
            ]);
        }
        $items .= '</div>';
        $items .= '<div class="col1 offset10">';
        if ($nextPage !== null && $this->getData(['page', $this->getUrl(0), 'navRight']) === $position) {
            $items .= template::button('navNextButtonRight', [
                'href' => helper::baseUrl() . $nextPage,
                'value' => template::ico($rightButton)
            ]);
        }
        $items .= '</div></div></div>';
        echo $items;
    }

    /**
     * Ajoute récursivement les enfants au sélecteur de pages
     * @param string &$leftItems Référence au HTML du sélecteur
     * @param array $childrenPageIds IDs des enfants
     * @param string $currentPageId ID de la page courante
     * @param int $depth Profondeur actuelle pour l'indentation
     */
    private function addChildrenToSelect(&$leftItems, $childrenPageIds, $currentPageId, $depth = 1)
    {
        $maxDepth = self::MENU_DEPTH;
        
        if ($depth >= $maxDepth) {
            return;
        }
        
        foreach ($childrenPageIds as $childKey) {
            // Récupérer les enfants directement en cherchant les pages avec ce parent
            $grandChildren = [];
            foreach ($this->getData(['page']) as $pageId => $pageData) {
                if ($pageData['parentPageId'] === $childKey) {
                    $grandChildren[] = $pageId;
                }
            }
            
            $leftItems .= '<option value="'
                . helper::baseUrl()
                . $childKey . '"'
                . ($childKey === $currentPageId ? ' selected' : false)
                . ' class="'
                . ($this->getData(['page', $childKey, 'disable']) === true ? 'pageInactive' : '')
                . ($this->getData(['page', $childKey, 'position']) === 0 ? 'pageHidden' : '')
                . '">'
                . str_repeat('&nbsp;', ($depth - 1) * 4) . '&#9492; '
                . $this->getData(['page', $childKey, 'shortTitle'])
                . '</option>';
            
            // Récursion pour les enfants de cet enfant
            if (!empty($grandChildren)) {
                $this->addChildrenToSelect($leftItems, $grandChildren, $currentPageId, $depth + 1);
            }
        }
    }
}