diff --git a/CHANGES.md b/CHANGES.md index 69b0b42c..bb49b6d0 100755 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,16 +1,17 @@ # Changelog ## Version 12.0.00 -### Modifications : - - - Module addon refondu et renommé plugin. - - Réorganisation des écrans pour éviter de mélanger des formulaires avec des champs d'information. +### Nouveautés : +- Module addon refondu et renommé plugin. +- Réorganisation des écrans pour éviter de mélanger des formulaires avec des champs d'information. +- Edition des pages : - Nouvelle présentation de l'édition des pages et de la configuration du site. - - Options de pages, feuille de style et script attachés à la page. - - Amélioration du thème admin de base, modifications du jeu d'icônes. - - Mise à jour automatisée, affichage de l'erreur en cas d'échec. - - Suppression du support de l'import à partir d'une version 9, y compris pour la restauration des sauvegardes. - - Optimisation du chargement des base de données, mise à jour des scripts jsonDB et dot. + - Feuille de style et script attachés à la page. +### Modifications : +- Amélioration du thème admin de base, modifications du jeu d'icônes. +- Mise à jour automatisée, affichage de l'erreur en cas d'échec. +- Suppression du support de l'import à partir d'une version 9, y compris pour la restauration des sauvegardes. +- Optimisation du chargement des base de données, mise à jour des scripts jsonDB et dot. ## Version 11.4.00 diff --git a/core/core.php b/core/core.php index 0441d5e4..34b21c62 100644 --- a/core/core.php +++ b/core/core.php @@ -46,7 +46,7 @@ class common { // Numéro de version const ZWII_UPDATE_URL = 'https://forge.chapril.org/ZwiiCMS-Team/update/raw/branch/master/'; - const ZWII_VERSION = '12.0.00_dev'; + const ZWII_VERSION = '12.0.00-dev'; const ZWII_UPDATE_CHANNEL = "test"; public static $actions = []; @@ -66,13 +66,14 @@ class common { 'theme', 'config', 'edit', - 'translate', - 'plugin' + 'config', + 'translate' ]; public static $accessExclude = [ 'login', 'logout' ]; + private $data = []; private $hierarchy = [ 'all' => [], 'visible' => [], @@ -164,6 +165,7 @@ class common { private $saveFlag = false; // Descripteur de données Entrées / Sorties + // Liste ici tous les fichiers de données private $dataFiles = [ 'admin' => '', 'blacklist' => '', @@ -175,6 +177,7 @@ class common { 'page' => '', 'theme' => '', 'user' => '' + ]; public static $fontsWebSafe = [ @@ -183,9 +186,14 @@ class common { 'font-family' => 'Arial, Helvetica, sans-serif', 'resource' => 'websafe' ], - 'arvo'=> [ - 'name' => 'Arvo', - 'font-family' => 'Arvo, sans-serif', + 'arial-black' => [ + 'name' => 'Arial Black', + 'font-family' => '\'Arial Black\', Gadget, sans-serif', + 'resource' => 'websafe' + ], + 'courrier' => [ + 'name' => 'Courier', + 'font-family' => 'Courier, \'Liberation Mono\', monospace', 'resource' => 'websafe' ], 'courrier-new' => [ @@ -208,21 +216,11 @@ class common { 'font-family' => 'Impact, Charcoal, sans-serif', 'resource' => 'websafe' ], - 'lora'=> [ - 'name' => 'Lora', - 'font-family' => 'Lora, serif', - 'resource' => 'websafe' - ], 'lucida' => [ 'name' => 'Lucida', 'font-family' => '\'Lucida Sans Unicode\', \'Lucida Grande\', sans-serif', 'resource' => 'websafe' ], - 'roboto'=> [ - 'name' => 'Roboto', - 'font-family' => 'Roboto, sans-serif', - 'resource' => 'websafe' - ], 'tahoma' => [ 'name' => 'Tahoma', 'font-family' => 'Tahoma, Geneva, sans-serif', @@ -274,6 +272,29 @@ class common { self::$i18n = 'fr'; } + // Instanciation de la classe des entrées / sorties + // Récupère les descripteurs + foreach ($this->dataFiles as $keys => $value) { + // Constructeur JsonDB + $this->dataFiles[$keys] = new \Prowebcraft\JsonDb([ + 'name' => $keys . '.json', + 'dir' => $this->dataPath ($keys, self::$i18n), + 'backup' => file_exists('site/data/.backup') + ]);; + } + + // Installation fraîche, initialisation des modules manquants + // La langue d'installation par défaut est fr + foreach ($this->dataFiles as $stageId => $item) { + $folder = $this->dataPath ($stageId, self::$i18n); + if ( file_exists($folder . $stageId .'.json') === false || + $this->getData([$stageId]) === NULL + ) { + $this->initData($stageId, self::$i18n); + common::$coreNotices [] = $stageId ; + } + } + // Utilisateur connecté if($this->user === []) { $this->user = $this->getData(['user', $this->getInput('ZWII_USER_ID')]); @@ -436,10 +457,6 @@ class common { * @param array $keys Clé(s) des données */ public function deleteData($keys) { - - // Récupère le descripteur dans le tableau dataFiles si absent - $this->connectData($keys[0]); - // Descripteur de la base $db = $this->dataFiles[$keys[0]]; // Initialisation de la requête par le nom de la base @@ -469,9 +486,6 @@ class common { return false; } - // Récupère le descripteur dans le tableau dataFiles si absent - $this->connectData($keys[0]); - // Initialisation du retour en cas d'erreur de descripteur $success = false; // Construire la requête dans la base inf à 1 retourner toute la base @@ -497,9 +511,6 @@ class common { */ public function getData($keys = []) { - // Récupère le descripteur dans le tableau dataFiles si absent - $this->connectData($keys[0]); - // Eviter une requete vide if (count($keys) >= 1) { // descripteur de la base @@ -565,9 +576,6 @@ class common { */ public function initData($module, $lang = 'fr', $sampleSite = false) { - // Récupère le descripteur dans le tableau dataFiles si absent - $this->connectData($module); - // Tableau avec les données vierges require_once('core/module/install/ressource/defaultdata.php'); @@ -741,36 +749,59 @@ class common { return ($this->checkCSRF() AND $this->input['_POST'] !== []); } - /** - * Initialise un fichier de données absent. - * Connecte la base de données si le descripteur est inconnu. + * Import des données de la version 9 + * Convertit un fichier de données data.json puis le renomme */ - private function connectData ($database) { - - // Installation fraîche, initialisation des modules manquants - // La langue d'installation par défaut est fr - $folder = $this->dataPath ($database, self::$i18n); - if ( file_exists($folder . $database .'.json') === false - ) { - $this->initData($database, self::$i18n); - common::$coreNotices [] = $database ; + public function importData($keepUsers = false) { + // Trois tentatives de lecture + for($i = 0; $i < 3; $i++) { + $tempData=json_decode(file_get_contents(self::DATA_DIR.'core.json'), true); + $tempTheme=json_decode(file_get_contents(self::DATA_DIR.'theme.json'), true); + if($tempData && $tempTheme) { + // Backup + rename (self::DATA_DIR.'core.json',self::DATA_DIR.'imported_core.json'); + rename (self::DATA_DIR.'theme.json',self::DATA_DIR.'imported_theme.json'); + break; + } + elseif($i === 2) { + throw new \ErrorException('Import des données impossible.'); + } + // Pause de 10 millisecondes + usleep(10000); } - // Instanciation de la classe des entrées / sorties - // Constructeur JsonDB - if (!is_object($this->dataFiles[$database]) ) { - $this->dataFiles[$database] = new \Prowebcraft\JsonDb([ - 'name' => $database . '.json', - 'dir' => $this->dataPath ($database, self::$i18n), - 'backup' => file_exists('site/data/.backup') - ]);; + // Dossier de langues + if (!file_exists(self::DATA_DIR . '/fr')) { + mkdir (self::DATA_DIR . '/fr', 0755); + } + + // Un seul fichier pour éviter les erreurs de sauvegarde des v9 + $tempData = array_merge($tempData,$tempTheme); + + // Ecriture des données + $this->setData(['config',$tempData['config']]); + $this->setData(['core',$tempData['core']]); + $this->setData(['page',$tempData['page']]); + $this->setData(['module',$tempData['module']]); + $this->setData(['theme',$tempData['theme']]); + + // Import des users sauvegardés si option active + if ($keepUsers === false + AND $tempData['user'] !== NULL) { + $this->setData(['user',$tempData['user']]); + } + + // Nettoyage du fichier de thème pour forcer une régénération + if (file_exists(self::DATA_DIR . '/theme.css')) { // On ne sait jamais + unlink (self::DATA_DIR . '/theme.css'); } } /** - * Génère la liste des pages pour le plugin Link de TinyMCE + * Génère un fichier json avec la liste des pages + * */ public function listPages() { // Sauve la liste des pages pour TinyMCE @@ -858,6 +889,8 @@ class common { public function createSitemap($command = "all") { + //require_once "core/vendor/sitemap/SitemapGenerator.php"; + $timezone = $this->getData(['config','timezone']); $outputDir = getcwd(); $sitemap = new \Icamys\SitemapGenerator\SitemapGenerator(helper::baseurl(false),$outputDir); @@ -1017,7 +1050,6 @@ class common { $layout = ob_get_clean(); $mail = new PHPMailer\PHPMailer\PHPMailer; $mail->CharSet = 'UTF-8'; - $mail->setLanguage('fr', 'core/class/phpmailer/phpmailer.lang-fr.php'); // Mail try{ // Paramètres SMTP @@ -1067,10 +1099,10 @@ class common { else { return $mail->ErrorInfo; } + } catch (phpmailerException $e) { + return $e->errorMessage(); } catch (Exception $e) { - echo $e->errorMessage(); - } catch (\Exception $e) { - echo $e->getMessage(); + return $e->getMessage(); } } @@ -1316,9 +1348,6 @@ class common { echo ''; echo substr($contentLeft,$mark+6,strlen($contentLeft)); } - // Feuille de style et script de la barre - echo '' ; - echo ''; echo ""; } /** @@ -1326,6 +1355,9 @@ class common { */ echo '
'; $this->showContent(); + if (file_exists(self::DATA_DIR . 'body.inc.html')) { + include(self::DATA_DIR . 'body.inc.html'); + } echo '
'; /** * Barre droite @@ -1347,9 +1379,6 @@ class common { echo ''; echo substr($contentRight,$mark+6,strlen($contentRight)); } - // Feuille de style et script de la barre - echo '' ; - echo ''; echo ''; } echo ''; @@ -2107,18 +2136,14 @@ class common { // Import des styles liés à la page if($this->output['style']) { echo ''; - // Import de la feuille de style des pages admin if (strpos($this->output['style'], 'admin.css') >= 1 ) { echo ''; } echo ''; } - // Import des fontes - if ( file_exists(self::DATA_DIR . 'fonts/fonts.html') ){ - include_once(self::DATA_DIR . 'fonts/fonts.html'); - } - if ( file_exists(self::DATA_DIR . 'fonts/fonts.css') ){ - echo ''; + // Import des fontes liées au thème + if (file_exists(self::DATA_DIR.'fonts/fonts.html')) { + include_once(self::DATA_DIR.'fonts/fonts.html'); } } @@ -2289,18 +2314,74 @@ class core extends common { // Version $css = '/*' . md5(json_encode($this->getData(['theme']))) . '*/'; + /** * Import des polices de caractères + * A partir du CDN + * ou dans le dossier site/file/source/fonts + * ou pas du tout si fonte webSafe */ - $f ['files'] = $this->getData(['fonts', 'files']); - $f ['imported'] = $this->getData(['fonts', 'imported']); - $f ['websafe'] = self::$fontsWebSafe; - // Construit un tableau avec leur ID et leur famille - foreach(['websafe', 'imported', 'files'] as $type) { - if (is_array($f[$type])) { - foreach ($f[$type] as $fontId => $fontValue ) { - $fonts[$fontId] = $fontValue['font-family']; + // Fonts disponibles + $fontsAvailable ['files'] = $this->getData(['fonts', 'files']); + $fontsAvailable ['imported'] = $this->getData(['fonts', 'imported']); + $fontsAvailable ['websafe'] = self::$fontsWebSafe; + + // Fontes installées + $fonts = [ $this->getData(['theme', 'text', 'font']), + $this->getData(['theme', 'title', 'font']), + $this->getData(['theme', 'header', 'font']), + $this->getData(['theme', 'menu', 'font']), + $this->getData(['theme', 'footer', 'font']) + ]; + // Suppression des polices identiques + $fonts = array_unique($fonts); + + /** + * Charge les fontes websafe + */ + $fontFile = ''; + foreach ($fonts as $fontId) { + if ( isset($fontsAvailable['websafe'][$fontId])) { + $fonts [$fontId] = $fontsAvailable['websafe'][$fontId]['font-family']; + } + } + + /** + * Chargement des polices en ligne dans un fichier fonts.html inclus dans main.php + */ + $fontFile = ''; + $gf = false; + foreach ($fonts as $fontId) { + if ( isset($fontsAvailable['imported'][$fontId])) { + $fontFile .= ''; + // Tableau pour la construction de la feuille de style + $fonts [$fontId] = $fontsAvailable['imported'][$fontId]['font-family']; + $gf = strpos($fontsAvailable['imported'][$fontId]['resource'], 'fonts.googleapis.com') === false ? $gf || false : $gf || true; + } + } + // Ajoute le préconnect des fontes Googles. + $fontFile = $gf ? '' . $fontFile + : $fontFile; + // Enregistre la personnalisation + file_put_contents(self::DATA_DIR.'fonts/fonts.html', $fontFile); + + /** + * Fontes installées localement + */ + foreach ($fonts as $fontId) { + // Validité du tableau : + if ( isset($fontsAvailable['files'][$fontId]) ) { + if (file_exists(self::DATA_DIR . 'fonts/' . $fontId) ) { + // Chargement de la police + //$formatFont = explode('.', self::DATA_DIR . 'fonts/' . $fontName); + $css .= '@font-face {font-family:"' . $fontsAvailable['files'][$fontId]['font-family'] . '";'; + $css .= 'src: url("' . helper::baseUrl(false) . self::DATA_DIR . 'fonts/' .$fontsAvailable['files'][$fontId]['resource'] . '");}'; + // Tableau pour la construction de la feuille de style + $fonts [$fontId] = $fontsAvailable['files'][$fontId]['font-family']; + } else { + // Le fichier de font n'est pas disponible, fonte par défaut + $fonts [$fontId] = 'verdana'; } } } @@ -2471,6 +2552,12 @@ class core extends common { $css .= '#footerText > p {text-align:' . $this->getData(['theme', 'footer', 'textAlign']) . '}'; $css .= '#footerCopyright{text-align:' . $this->getData(['theme', 'footer', 'copyrightAlign']) . '}'; + // Enregistre les fontes + if (!is_dir(self::DATA_DIR . 'fonts')) { + mkdir(self::DATA_DIR . 'fonts'); + } + file_put_contents(self::DATA_DIR . 'fonts/fonts.html', $fontFile); + // Enregistre la personnalisation file_put_contents(self::DATA_DIR.'theme.css', $css); @@ -2491,21 +2578,57 @@ class core extends common { /** * Import des polices de caractères + * A partir du CDN ou dans le dossier site/file/source/fonts */ + $fonts = [ $this->getData(['admin', 'fontText']), + $this->getData(['admin', 'fontTitle']), + ]; + // Suppression des polices identiques + $fonts = array_unique($fonts); - $f ['files'] = $this->getData(['fonts', 'files']); - $f ['imported'] = $this->getData(['fonts', 'imported']); - $f ['websafe'] = self::$fontsWebSafe; - // Construit un tableau avec leur ID et leur famille - foreach(['websafe', 'imported', 'files'] as $type) { - if (is_array($f[$type])) { - foreach ($f[$type] as $fontId => $fontValue ) { - $fonts[$fontId] = $fontValue['font-family']; - } + /** + * Charge les fontes websafe + */ + $fontFile = ''; + foreach ($fonts as $fontId) { + if ( isset($fontsAvailable['websafe'][$fontId])) { + $fonts [$fontId] = $fontsAvailable['websafe'][$fontId]['font-family']; } } + /** + * Chargement des polices en ligne dans un fichier fonts.html inclus dans main.php + */ + $fontFile = ''; + foreach ($fonts as $fontId) { + if ( isset($fontsAvailable['imported'][$fontId])) { + $fontFile .= ''; + // Tableau pour la construction de la feuille de style + $fonts [$fontId] = $fontsAvailable['imported'][$fontId]['font-family']; + } + } + // Enregistre la personnalisation + file_put_contents(self::DATA_DIR.'fonts/fonts.html', $fontFile); + /** + * Fontes installées localement + */ + foreach ($fonts as $fontId) { + // Validité du tableau : + if ( isset($fontsAvailable['files'][$fontId]) ) { + if (file_exists(self::DATA_DIR . 'fonts/' . $fontId) ) { + // Chargement de la police + //$formatFont = explode('.', self::DATA_DIR . 'fonts/' . $fontName); + $css .= '@font-face {font-family:"' . $fontsAvailable['files'][$fontId]['font-family'] . '";'; + $css .= 'src: url("' . helper::baseUrl(false) . self::DATA_DIR . 'fonts/' .$fontsAvailable['files'][$fontId]['resource'] . '");}'; + // Tableau pour la construction de la feuille de style + $fonts [$fontId] = $fontsAvailable['files'][$fontId]['font-family']; + } else { + // Le fichier de font n'est pas disponible, fonte par défaut + $fonts [$fontId] = 'verdana'; + } + } + } // Thème Administration $colors = helper::colorVariants($this->getData(['admin','backgroundColor'])); @@ -2699,17 +2822,19 @@ class core extends common { 'title' => $title, 'content' => $this->getPage($this->getUrl(0), self::$i18n) . // Concatène avec les paramètres avancés. - '' . - '', + $this->getData(['page', $this->getUrl(0), 'css']) . + $this->getData(['page', $this->getUrl(0), 'js']), 'metaDescription' => $this->getData(['page', $this->getUrl(0), 'metaDescription']), 'metaTitle' => $this->getData(['page', $this->getUrl(0), 'metaTitle']), 'typeMenu' => $this->getData(['page', $this->getUrl(0), 'typeMenu']), 'iconUrl' => $this->getData(['page', $this->getUrl(0), 'iconUrl']), 'disable' => $this->getData(['page', $this->getUrl(0), 'disable']), 'contentRight' => $this->getData(['page',$this->getUrl(0),'barRight']) + //file_get_contents(self::DATA_DIR . self::$i18n . '/content/' . $this->getData(['page', $this->getData(['page',$this->getUrl(0),'barRight']), 'content'])) ? $this->getPage($this->getData(['page',$this->getUrl(0),'barRight']), self::$i18n) : '', 'contentLeft' => $this->getData(['page',$this->getUrl(0),'barLeft']) + //file_get_contents(self::DATA_DIR . self::$i18n . '/content/' . $this->getData(['page', $this->getData(['page',$this->getUrl(0),'barLeft']), 'content'])) ? $this->getPage($this->getData(['page',$this->getUrl(0),'barLeft']), self::$i18n) : '' ]); @@ -2741,10 +2866,7 @@ class core extends common { : '' ]); //$pageContent = file_get_contents(self::DATA_DIR . self::$i18n . '/content/' . $this->getData(['page', $this->getUrl(0), 'content'])); - $pageContent = $this->getPage($this->getUrl(0), self::$i18n). - // Concatène avec les paramètres avancés. - '' . - ''; + $pageContent = $this->getPage($this->getUrl(0), self::$i18n); } else { $moduleId = $this->getUrl(0); @@ -2955,23 +3077,15 @@ class core extends common { 'content' => template::speech('La page ' . $accessInfo['pageId'] . ' est ouverte par l\'utilisateur ' . $accessInfo['userName'] . '') ]); } else { - // Redirige vers la page de connexion si page de gestion demandée - if ( $this->getData(['config', 'connect', 'redirectLogin']) === true - && in_array($this->geturl(0), self::$accessList) ) { - http_response_code(302); - header('Location:' . helper::baseUrl() . 'user/login/'); - exit(); + if ( $this->getData(['locale','page403']) !== 'none' + AND $this->getData(['page',$this->getData(['locale','page403'])])) + { + header('Location:' . helper::baseUrl() . $this->getData(['locale','page403'])); } else { - if ( $this->getData(['locale','page403']) !== 'none' - AND $this->getData(['page',$this->getData(['locale','page403'])])) - { - header('Location:' . helper::baseUrl() . $this->getData(['locale','page403'])); - } else { - $this->addOutput([ - 'title' => 'Accès interdit', - 'content' => template::speech('Vous n\'êtes pas autorisé à consulter cette page (erreur 403)') - ]); - } + $this->addOutput([ + 'title' => 'Accès interdit', + 'content' => template::speech('Vous n\'êtes pas autorisé à consulter cette page (erreur 403)') + ]); } } } elseif ($this->output['content'] === '') {