From 46614bc06f81ef134e710568aeb50bb28fe7be1e Mon Sep 17 00:00:00 2001 From: Deltacms Date: Tue, 13 Jun 2023 14:53:28 +0200 Subject: [PATCH] =?UTF-8?q?am=C3=A9liorations=20menu=20petit=20et=20grand?= =?UTF-8?q?=20=C3=A9cran?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/core.js.php | 52 +- core/core.js_ok.php | 647 ++++ core/core.php | 32 +- core/core_ok.php | 3374 +++++++++++++++++ core/layout/mediaqueries.css | 16 +- core/layout/mediaqueries_ok.css | 335 ++ .../statislite/view/config/config.help.html | 4 +- .../view/config/config.help_en.html | 4 +- 8 files changed, 4412 insertions(+), 52 deletions(-) create mode 100644 core/core.js_ok.php create mode 100644 core/core_ok.php create mode 100644 core/layout/mediaqueries_ok.css diff --git a/core/core.js.php b/core/core.js.php index ddfa79d..93013d4 100644 --- a/core/core.js.php +++ b/core/core.js.php @@ -602,38 +602,28 @@ $(document).ready(function(){ * après appui sur l'icône plus / minus * substitution des icônes down, plus, minus */ - if($(window).width() < 800) { - if( $("nav #menu ul li span").hasClass('zwiico-down') ) { - $("nav #menu ul li span").removeClass('zwiico-down').addClass('zwiico-plus'); + $(window).on("resize", function() { + if($(window).width() < 800) { + $("nav #menu ul li span").click(function() { + // id de la page parent + var parentId = $(this).parents().attr("id"); + var select = "ul#_"+parentId+".navSub"; + var select2 = "nav #menu ul li #" + parentId + " span"; + if( $(select).css("z-index") === "-1" ) { + $(select).css("z-index","1"); + $(select).css("opacity","1"); + $(select).css("padding-left","20px"); + $(select).css("position","static"); + $(select2).removeClass('zwiico-plus').addClass('zwiico-minus'); + } else { + $(select).css("z-index","-1"); + $(select).css("opacity","0"); + $(select).css("position","absolute"); + $(select2).removeClass('zwiico-minus').addClass('zwiico-plus'); + } + }); } - $("nav #menu ul li span").click(function() { - // id de la page parent - var parentId = $(this).parents().attr("id"); - var select = "ul#_"+parentId+".navSub"; - if( $(select).css("z-index") === "-1" ) { - $(select).css("z-index","1"); - $(select).css("opacity","1"); - $(select).css("padding-left","20px"); - $(select).css("position","static"); - } else { - $(select).css("z-index","-1"); - $(select).css("opacity","0"); - $(select).css("position","absolute"); - } - // Modification de l'icône plus ou minus - var select = "nav #menu ul li #" + parentId + " span"; - if ( $(select).hasClass('zwiico-plus') ) { - $(select).removeClass('zwiico-plus').addClass('zwiico-minus'); - } - else { - $(select).removeClass('zwiico-minus').addClass('zwiico-plus'); - }; - }); - } else { - if( $("nav #menu ul li span").hasClass('zwiico-plus') ) { - $("nav #menu ul li span").removeClass('zwiico-plus').addClass('zwiico-down'); - } - } + }).trigger("resize"); /* Suppression du décalage vertical de la bannière en petit écran * si menu burger fixe et bannière dans le site et bannière visible diff --git a/core/core.js_ok.php b/core/core.js_ok.php new file mode 100644 index 0000000..ddfa79d --- /dev/null +++ b/core/core.js_ok.php @@ -0,0 +1,647 @@ +/** + * This file is part of DeltaCMS. + * For full copyright and license information, please see the LICENSE + * file that was distributed with this source code. + * @author Sylvain Lelièvre + * @copyright Copyright (C) 2021, Sylvain Lelièvre + * @license GNU General Public License, version 3 + * @link https://deltacms.fr/ + * + * Delta was created from version 11.2.00.24 of ZwiiCMS + * @author Rémi Jean + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2021, Frédéric Tempez + */ + +var core = {}; + +/** + * Crée un message d'alerte + */ +core.alert = function(text) { + var lightbox = lity(function($) { + return $("
") + .addClass("lightbox") + .append( + $("").text(text), + $("
") + .addClass("lightboxButtons") + .append( + $("") + .addClass("button") + .text("Ok") + .on("click", function() { + lightbox.close(); + }) + ) + ) + }(jQuery)); + // Validation de la lightbox avec le bouton entrée + $(document).on("keyup", function(event) { + if(event.keyCode === 13) { + lightbox.close(); + } + }); + return false; +}; + +/** + * Génère des variations d'une couleur + */ +core.colorVariants = function(rgba) { + rgba = rgba.match(/\(+(.*)\)/); + rgba = rgba[1].split(", "); + return { + "normal": "rgba(" + rgba[0] + "," + rgba[1] + "," + rgba[2] + "," + rgba[3] + ")", + "darken": "rgba(" + Math.max(0, rgba[0] - 15) + "," + Math.max(0, rgba[1] - 15) + "," + Math.max(0, rgba[2] - 15) + "," + rgba[3] + ")", + "veryDarken": "rgba(" + Math.max(0, rgba[0] - 20) + "," + Math.max(0, rgba[1] - 20) + "," + Math.max(0, rgba[2] - 20) + "," + rgba[3] + ")", + //"text": core.relativeLuminanceW3C(rgba) > .22 ? "inherit" : "white" + "text": core.relativeLuminanceW3C(rgba) > .22 ? "#222" : "#DDD" + }; +}; + +/** + * Crée un message de confirmation + */ +core.confirm = function(text, yesCallback, noCallback) { + var lightbox = lity(function($) { + return $("
") + .addClass("lightbox") + .append( + $("").text(text), + $("
") + .addClass("lightboxButtons") + .append( + $("") + .addClass("button grey") + .text(textConfirmNo) + .on("click", function() { + lightbox.options('button', true); + lightbox.close(); + if(typeof noCallback !== "undefined") { + noCallback(); + } + }), + $("") + .addClass("button") + .text(textConfirmYes) + .on("click", function() { + lightbox.options('button', true); + lightbox.close(); + if(typeof yesCallback !== "undefined") { + yesCallback(); + } + }) + ) + ) + }(jQuery)); + // Callback lors d'un clic sur le fond et sur la croix de fermeture + lightbox.options('button', false); + $(document).on('lity:close', function(event, instance) { + if( + instance.options('button') === false + && typeof noCallback !== "undefined" + ) { + noCallback(); + } + }); + // Validation de la lightbox avec le bouton entrée + $(document).on("keyup", function(event) { + if(event.keyCode === 13) { + lightbox.close(); + if(typeof yesCallback !== "undefined") { + yesCallback(); + } + } + }); + return false; +}; + +/** + * Scripts à exécuter en dernier + */ +core.end = function() { + /** + * Modifications non enregistrées du formulaire + */ + var formDOM = $("form"); + // Ignore : + // - TinyMCE car il gère lui même le message + // - Les champs avec data-no-dirty + var inputsDOM = formDOM.find("input:not([data-no-dirty]), select:not([data-no-dirty]), textarea:not(.editorWysiwyg):not([data-no-dirty])"); + var inputSerialize = inputsDOM.serialize(); + $(window).on("beforeunload", function() { + if(inputsDOM.serialize() !== inputSerialize) { + return "Les modifications que vous avez apportées ne seront peut-être pas enregistrées."; + } + }); + formDOM.submit(function() { + $(window).off("beforeunload"); + }); +}; +$(function() { + core.end(); +}); + +/** + * Ajoute une notice + */ +core.noticeAdd = function(id, notice) { + $("#" + id + "Notice").text(notice).removeClass("displayNone"); + $("#" + id).addClass("notice"); +}; + +/** + * Supprime une notice + */ +core.noticeRemove = function(id) { + $("#" + id + "Notice").text("").addClass("displayNone"); + $("#" + id).removeClass("notice"); +}; + +/** + * Scripts à exécuter en premier + */ +core.start = function() { + /** + * Remonter en haut au clic sur le bouton + */ + var backToTopDOM = $("#backToTop"); + backToTopDOM.on("click", function() { + $("body, html").animate({scrollTop: 0}, "400"); + }); + /** + * Affiche / Cache le bouton pour remonter en haut + */ + $(window).on("scroll", function() { + if($(this).scrollTop() > 200) { + backToTopDOM.fadeIn(); + } + else { + backToTopDOM.fadeOut(); + } + }); + /** + * Cache les notifications + */ + var notificationTimer; + $("#notification") + .on("mouseenter", function() { + clearTimeout(notificationTimer); + $("#notificationProgress") + .stop() + .width("100%"); + }) + .on("mouseleave", function() { + // Disparition de la notification + notificationTimer = setTimeout(function() { + $("#notification").fadeOut(); + }, 3000); + // Barre de progression + $("#notificationProgress").animate({ + "width": "0%" + }, 3000, "linear"); + }) + .trigger("mouseleave"); + $("#notificationClose").on("click", function() { + clearTimeout(notificationTimer); + $("#notification").fadeOut(); + $("#notificationProgress").stop(); + }); + + /** + * Traitement du formulaire cookies + */ + $("#cookieForm").submit(function(event){ + + // Varables des cookies + var samesite = "samesite=lax"; + var getUrl = window.location; + var domain = "domain=" + getUrl.hostname; + // var path = "path=" + getUrl.pathname.split('/')[1]; + var e = new Date(); + e.setFullYear(e.getFullYear() + 1); + var expires = "expires=" + e.toUTCString(); + + // Crée le cookie d'acceptation Cookies Externes (tiers) si le message n'est pas vide + var messageCookieExt = "getData(['locale', 'cookies', 'cookiesExtText']);?>"; + // le message de consentement des cookies externes est défini en configuration, afficher la checkbox d'acceptation + if( messageCookieExt.length > 0){ + // Traitement du retour de la checkbox + if ($("#cookiesExt").is(":checked")) { + // L'URL du serveur faut TRUE + //document.cookie = "DELTA_COOKIE_EXT_CONSENT=true;" + domain + ";" + path + ";" + samesite + ";" + expires; + document.cookie = "DELTA_COOKIE_EXT_CONSENT=true;" + domain + ";" + samesite + ";" + expires; + } else { + //document.cookie = "DELTA_COOKIE_EXT_CONSENT=false;" + domain + ";" + path + ";" + samesite + ";" + expires; + document.cookie = "DELTA_COOKIE_EXT_CONSENT=false;" + domain + ";" + samesite + ";" + expires; + } + + } + + // Stocke le cookie d'acceptation + //document.cookie = "DELTA_COOKIE_CONSENT=true;" + domain + ";" + path + ";" + samesite + ";" + expires; + document.cookie = "DELTA_COOKIE_CONSENT=true;" + domain + ";" + samesite + ";" + expires; + }); + + + /** + * Fermeture de la popup des cookies + */ + $("#cookieConsent .cookieClose").on("click", function() { + $('#cookieConsent').addClass("displayNone"); + }); + + /** + * Commande de gestion des cookies dans le footer + */ + + $("#footerLinkCookie").on("click", function() { + $("#cookieConsent").removeClass("displayNone"); + }); + + /** + * Affiche / Cache le menu en mode responsive + */ + var menuDOM = $("#menu"); + $("#burgerIcon").on("click", function() { + menuDOM.slideToggle(); + }); + $(window).on("resize", function() { + if($(window).width() > 799) { + menuDOM.css("display", ""); + } + }); + + /** + * Choix de page dans la barre de membre + */ + $("#barSelectPage").on("change", function() { + var pageUrl = $(this).val(); + if(pageUrl) { + $(location).attr("href", pageUrl); + } + }); + /** + * Champs d'upload de fichiers + */ + // Mise à jour de l'affichage des champs d'upload + $(".inputFileHidden").on("change", function() { + var inputFileHiddenDOM = $(this); + var fileName = inputFileHiddenDOM.val(); + if(fileName === "") { + fileName = textSelectFile; + $(inputFileHiddenDOM).addClass("disabled"); + } + else { + $(inputFileHiddenDOM).removeClass("disabled"); + } + inputFileHiddenDOM.parent().find(".inputFileLabel").text(fileName); + }).trigger("change"); + // Suppression du fichier contenu dans le champ + $(".inputFileDelete").on("click", function() { + $(this).parents(".inputWrapper").find(".inputFileHidden").val("").trigger("change"); + }); + // Suppression de la date contenu dans le champ + $(".inputDateDelete").on("click", function() { + $(this).parents(".inputWrapper").find(".datepicker").val("").trigger("change"); + }); + // Confirmation de mise à jour + $("#barUpdate").on("click", function() { + return core.confirm( textUpdating, function() { + $(location).attr("href", $("#barUpdate").attr("href")); + }); + }); + // Confirmation de déconnexion + $("#barLogout").on("click", function() { + return core.confirm( textLogout, function() { + $(location).attr("href", $("#barLogout").attr("href")); + }); + }); + /** + * Bloque la multi-soumission des boutons + */ + $("form").on("submit", function() { + $(this).find(".uniqueSubmission") + .addClass("disabled") + .prop("disabled", true) + .empty() + .append( + $("").addClass("zwiico-spin animate-spin") + ) + }); + /** + * Check adresse email + */ + $("[type=email]").on("change", function() { + var _this = $(this); + var pattern = /^([a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+(\.[a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)*|"((([ \t]*\r\n)?[ \t]+)?([\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*(([ \t]*\r\n)?[ \t]+)?")@(([a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.)+([a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.?$/i; + if(pattern.test(_this.val())) { + core.noticeRemove(_this.attr("id")); + } + else { + core.noticeAdd(_this.attr("id"), textCheckMail); + } + }); + + /** + * Iframes et vidéos responsives + */ + var elementDOM = $("iframe, video, embed, object"); + // Calcul du ratio et suppression de la hauteur / largeur des iframes + elementDOM.each(function() { + var _this = $(this); + _this + .data("ratio", _this.height() / _this.width()) + .data("maxwidth", _this.width()) + .removeAttr("width height"); + }); + // Prend la largeur du parent et détermine la hauteur à l'aide du ratio lors du resize de la fenêtre + $(window).on("resize", function() { + elementDOM.each(function() { + var _this = $(this); + var width = _this.parent().first().width(); + if (width > _this.data("maxwidth")){ width = _this.data("maxwidth");} + _this + .width(width) + .height(width * _this.data("ratio")); + }); + }).trigger("resize"); + + /* + * Header responsive + */ + $(window).on("resize", function() { + var responsive = "getdata(['theme','header','imageContainer']);?>"; + var feature = "getdata(['theme','header','feature']);?>"; + if ( (responsive === "cover" || responsive === "contain") && feature !== "feature" ) { + var widthpx = "getdata(['theme','site','width']);?>"; + var width = widthpx.substr(0,widthpx.length-2); + var heightpx = "getdata(['theme','header','height']);?>"; + var height = heightpx.substr(0,heightpx.length-2); + var ratio = width / height; + var feature = "getdata(['theme','header','feature']);?>"; + if ( ($(window).width() / ratio) <= height) { + $("header").height( $(window).width() / ratio ); + $("header").css("line-height", $(window).width() / ratio + "px"); + } + } + }).trigger("resize"); + +}; + + +core.start(); + +/** + * Confirmation de suppression + */ +$("#pageDelete").on("click", function() { + var _this = $(this); + return core.confirm( textPageDelete, function() { + $(location).attr("href", _this.attr("href")); + }); +}); + +/** + * Calcul de la luminance relative d'une couleur + */ +core.relativeLuminanceW3C = function(rgba) { + // Conversion en sRGB + var RsRGB = rgba[0] / 255; + var GsRGB = rgba[1] / 255; + var BsRGB = rgba[2] / 255; + // Ajout de la transparence + var RsRGBA = rgba[3] * RsRGB + (1 - rgba[3]); + var GsRGBA = rgba[3] * GsRGB + (1 - rgba[3]); + var BsRGBA = rgba[3] * BsRGB + (1 - rgba[3]); + // Calcul de la luminance + var R = (RsRGBA <= .03928) ? RsRGBA / 12.92 : Math.pow((RsRGBA + .055) / 1.055, 2.4); + var G = (GsRGBA <= .03928) ? GsRGBA / 12.92 : Math.pow((GsRGBA + .055) / 1.055, 2.4); + var B = (BsRGBA <= .03928) ? BsRGBA / 12.92 : Math.pow((BsRGBA + .055) / 1.055, 2.4); + return .2126 * R + .7152 * G + .0722 * B; +}; + + + +$(document).ready(function(){ + /** + * Affiche le sous-menu quand il est sticky + */ + $("nav").mouseenter(function(){ + $("#navfixedlogout .navSub").css({ 'pointer-events' : 'auto' }); + $("#navfixedconnected .navSub").css({ 'pointer-events' : 'auto' }); + }); + $("nav").mouseleave(function(){ + $("#navfixedlogout .navSub").css({ 'pointer-events' : 'none' }); + $("#navfixedconnected .navSub").css({ 'pointer-events' : 'none' }); + }); + + /** + * Chargement paresseux des images et des iframes + */ + $("img,picture,iframe").attr("loading","lazy"); + + /** + * Effet accordéon + */ + $('.accordion').each(function(e) { + // on stocke l'accordéon dans une variable locale + var accordion = $(this); + // on récupère la valeur data-speed si elle existe + var toggleSpeed = accordion.attr('data-speed') || 100; + + // fonction pour afficher un élément + function open(item, speed) { + // on récupère tous les éléments, on enlève l'élément actif de ce résultat, et on les cache + accordion.find('.accordion-item').not(item).removeClass('active') + .find('.accordion-content').slideUp(speed); + // on affiche l'élément actif + item.addClass('active') + .find('.accordion-content').slideDown(speed); + } + function close(item, speed) { + accordion.find('.accordion-item').removeClass('active') + .find('.accordion-content').slideUp(speed); + } + + // on initialise l'accordéon, sans animation + open(accordion.find('.active:first'), 0); + + // au clic sur un titre... + accordion.on('click', '.accordion-title', function(ev) { + ev.preventDefault(); + // Masquer l'élément déjà actif + if ($(this).closest('.accordion-item').hasClass('active')) { + close($(this).closest('.accordion-item'), toggleSpeed); + } else { + // ...on lance l'affichage de l'élément, avec animation + open($(this).closest('.accordion-item'), toggleSpeed); + } + }); + }); + + /** + * Icône du Menu Burger, couleur du bandeau burger et position du menu + */ + $("#burgerIcon").click(function() { + var changeIcon = $('#burgerIcon').children("span"); + var bgColor = "getData(['theme', 'menu', 'burgerBannerColor']) ;?>"; + var bgColorOpaque = bgColor.replace(/[^,]+(?=\))/, '1'); + if ( $(changeIcon).hasClass('zwiico-menu') ) { + $(changeIcon).removeClass('zwiico-menu').addClass('zwiico-cancel'); + $("nav #toggle").css("background-color",bgColorOpaque); + } + else { + $(changeIcon).addClass('zwiico-menu'); + $("nav #toggle").css("background-color",bgColor); + }; + }); + + /** + * Active le système d'aide interne + * + */ + + $(".buttonHelp").click(function() { + $(".helpDisplayContent").slideToggle(); + /** + if( $(".buttonHelp").css('opacity') > '0.75'){ + $(".buttonHelp").css('opacity','0.5'); + } + else{ + $(".buttonHelp").css('opacity','1'); + } + */ + }); + + $(".helpDisplayContent").click(function() { + $(".helpDisplayContent").slideToggle(); + }); + + /** + * Remove ID Facebook from URL + */ + if(/^\?fbclid=/.test(location.search)) + location.replace(location.href.replace(/\?fbclid.+/, "")); + + /** + * No translate Lity close + */ + $(document).on('lity:ready', function(event, instance) { + $('.lity-close').addClass('notranslate'); + }); + + /** + * Bouton screenshot + */ + var dataURL = {}; + $('#screenshot').click(function() { + html2canvas(document.querySelector("#main_screenshot")).then(canvas => { + dataURL = canvas.toDataURL('image/jpeg', 0.1); + console.log( dataURL); + $.ajax({ + type: "POST", + contentType:"application/x-www-form-urlencoded", + url: "core/vendor/screenshot/screenshot.php", + data: { + image: dataURL + }, + dataType: "html" + }); + }); + }); + + /* + * Largeur minimale des onglets principaux du menu et largeur du sous-menu égale à la largeur de l'onglet parent + * sauf en petit écran + */ + $(window).on("resize", function() { + if( $(window).width() > 799 ){ + if( typeof parentPage !== "undefined" ){ + var page=[]; + if( 'getData(['theme', 'menu', 'minWidthParentOrAll']); ?>' === ''){ + page = parentPage; + // suppression d'un sous-menu depuis le dernier enregistrement de theme.css + $.each(allPage, function(index, value) { + // si la page n'est pas parent on repositionne min-width à auto + if( parentPage.includes( value ) === false ) $('nav li .' + value).css('min-width', 'auto'); + }); + } else{ + page = allPage; + } + $.each(page, function(index, value) { + $('nav li .' + value).css('min-width', 'getData(['theme', 'menu', 'minWidthTab']); ?>'); + $('nav li ul li .'+value).css('width', $('.'+value).css('width')); + $('nav li.' + value).css('text-align', 'left'); + }); + } + } + }).trigger("resize"); + + /* Compteur de liens cliqués + * Fonctionne avec download_counter.php + * Les liens comptabilisés doivent avoir la class="clicked_link_count" + * Envoi au fichier download_counter.php la donnée url + */ + getData(['config', 'statislite', 'enable']) && is_file('site/data/statislite/module/download_counter/download_counter.php' ) ) { ?> + $('.clicked_link_count').on('click', function(event) { + // Récupérer le chemin vers le fichier + var filePath = $(this).attr('href'); + // Envoyer une requête AJAX pour enregistrer le téléchargement + $.ajax({ + type: 'POST', + url: '/site/data/statislite/module/download_counter/download_counter.php', + data: {'url': filePath}, + }); + }); + + + /* Affichage / masquage des items du sous-menu + * après appui sur l'icône plus / minus + * substitution des icônes down, plus, minus + */ + if($(window).width() < 800) { + if( $("nav #menu ul li span").hasClass('zwiico-down') ) { + $("nav #menu ul li span").removeClass('zwiico-down').addClass('zwiico-plus'); + } + $("nav #menu ul li span").click(function() { + // id de la page parent + var parentId = $(this).parents().attr("id"); + var select = "ul#_"+parentId+".navSub"; + if( $(select).css("z-index") === "-1" ) { + $(select).css("z-index","1"); + $(select).css("opacity","1"); + $(select).css("padding-left","20px"); + $(select).css("position","static"); + } else { + $(select).css("z-index","-1"); + $(select).css("opacity","0"); + $(select).css("position","absolute"); + } + // Modification de l'icône plus ou minus + var select = "nav #menu ul li #" + parentId + " span"; + if ( $(select).hasClass('zwiico-plus') ) { + $(select).removeClass('zwiico-plus').addClass('zwiico-minus'); + } + else { + $(select).removeClass('zwiico-minus').addClass('zwiico-plus'); + }; + }); + } else { + if( $("nav #menu ul li span").hasClass('zwiico-plus') ) { + $("nav #menu ul li span").removeClass('zwiico-plus').addClass('zwiico-down'); + } + } + + /* Suppression du décalage vertical de la bannière en petit écran + * si menu burger fixe et bannière dans le site et bannière visible + */ + if($(window).width() < 800) { + getData(['theme','menu', 'burgerFixed'])=== true && $this->getData(['theme','header', 'position'])=== 'site' && $this->getData(['theme','header', 'tinyHidden'])=== false && ( $this->getData(['theme','header', 'homePageOnly'])=== false || $this->getUrl(0) === $this->getData(['locale','homePageId']) ) ){ ?> + $("#site.container").css("padding-top","0"); + + } + +}); diff --git a/core/core.php b/core/core.php index 88e7a89..0863f23 100644 --- a/core/core.php +++ b/core/core.php @@ -1775,17 +1775,31 @@ class common { getHierarchy() as $parentPageId => $childrenPageIds) { - // Passer les entrées masquées // Propriétés de l'item $active = ($parentPageId === $currentPageId OR in_array($currentPageId, $childrenPageIds)) ? 'active ' : ''; $targetBlank = $this->getData(['page', $parentPageId, 'targetBlank']) ? ' target="_blank"' : ''; + // Cas où les pages enfants enfant sont toutes masquées dans le menu + // ne pas afficher de symbole lorsqu'il n'y a rien à afficher + $totalChild = 0; + $disableChild = 0; + foreach($childrenPageIds as $childKey) { + $totalChild += 1; + if( $this->getData(['page', $childKey, 'disable']) === true ) $disableChild +=1; + } + $iconSubExistLargeScreen=''; + $iconSubExistSmallScreen=''; + if($childrenPageIds && $disableChild !== $totalChild && $this->getdata(['page',$parentPageId,'hideMenuChildren']) === false) { + $iconSubExistLargeScreen= ''; + $iconSubExistSmallScreen= ''; + + } // Mise en page de l'item $itemsLeft .= '
  • '; if ( ( $this->getData(['page',$parentPageId,'disable']) === true AND $this->getUser('password') !== $this->getInput('DELTA_USER_PASSWORD')) OR ( $this->getData(['page',$parentPageId,'disable']) === true AND $this->getUser('password') === $this->getInput('DELTA_USER_PASSWORD')AND $this->getUser('group') < self::GROUP_EDITOR )) { $pageUrl = ($this->getData(['locale', 'homePageId']) === $this->getUrl(0)) ? helper::baseUrl(false) : helper::baseUrl() . $this->getUrl(0); - $itemsLeft .= '
    '; + $itemsLeft .= '
    '; } else { $pageUrl = ($this->getData(['locale', 'homePageId']) === $parentPageId) ? helper::baseUrl(false) : helper::baseUrl() . $parentPageId; $itemsLeft .= '
    '; - // Cas où les pages enfants enfant sont toutes masquées dans le menu - // ne pas afficher de symbole lorsqu'il n'y a rien à afficher - $totalChild = 0; - $disableChild = 0; - foreach($childrenPageIds as $childKey) { - $totalChild += 1; - if( $this->getData(['page', $childKey, 'disable']) === true ) $disableChild +=1; - } - if($childrenPageIds && $disableChild !== $totalChild && $this->getdata(['page',$parentPageId,'hideMenuChildren']) === false) { - $itemsLeft .= template::ico('down', 'left'); - } - // ------------------------------------------------ + $itemsLeft .= $iconSubExistSmallScreen; $itemsLeft .= '
    '; if ($this->getdata(['page',$parentPageId,'hideMenuChildren']) === true || diff --git a/core/core_ok.php b/core/core_ok.php new file mode 100644 index 0000000..88e7a89 --- /dev/null +++ b/core/core_ok.php @@ -0,0 +1,3374 @@ + + * @copyright Copyright (C) 2021, Sylvain Lelièvre + * @license GNU General Public License, version 3 + * @link https://deltacms.fr/ + * + * Delta was created from version 11.2.00.24 of ZwiiCMS + * @author Rémi Jean + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2021, Frédéric Tempez + */ + +class common { + + const DISPLAY_RAW = 0; + const DISPLAY_JSON = 1; + const DISPLAY_RSS = 2; + const DISPLAY_LAYOUT_BLANK = 3; + const DISPLAY_LAYOUT_MAIN = 4; + const DISPLAY_LAYOUT_LIGHT = 5; + const GROUP_BANNED = -1; + const GROUP_VISITOR = 0; + const GROUP_MEMBER = 1; + const GROUP_EDITOR = 2; + const GROUP_MODERATOR = 3; + const GROUP_ADMIN = 4; + const SIGNATURE_ID = 1; + const SIGNATURE_PSEUDO = 2; + const SIGNATURE_FIRSTLASTNAME = 3; + const SIGNATURE_LASTFIRSTNAME = 4; + // Dossier de travail + const BACKUP_DIR = 'site/backup/'; + const DATA_DIR = 'site/data/'; + const FILE_DIR = 'site/file/'; + const TEMP_DIR = 'site/tmp/'; + + // Miniatures de la galerie + const THUMBS_SEPARATOR = 'mini_'; + const THUMBS_WIDTH = 640; + + // Contrôle d'édition temps maxi en secondes avant déconnexion 30 minutes + const ACCESS_TIMER = 1800; + + // Numéro de version + const DELTA_UPDATE_URL = 'https://update.deltacms.fr/master/'; + const DELTA_VERSION = '4.5.01'; + const DELTA_UPDATE_CHANNEL = "v4"; + + public static $actions = []; + public static $coreModuleIds = [ + 'config', + 'install', + 'maintenance', + 'page', + 'sitemap', + 'theme', + 'user', + 'translate', + 'addon' + ]; + public static $accessList = [ + 'user', + 'theme', + 'config', + 'edit', + 'config', + 'translate' + ]; + public static $accessExclude = [ + 'login', + 'logout' + ]; + private $data = []; + private $hierarchy = [ + 'all' => [], + 'visible' => [], + 'bar' => [] + ]; + private $input = [ + '_COOKIE' => [], + '_POST' => [] + ]; + public static $inputBefore = []; + public static $inputNotices = []; + public static $importNotices = []; + public static $captchaNotices = []; + public static $coreNotices = []; + public $output = [ + 'access' => true, + 'content' => '', + 'contentLeft' => '', + 'contentRight' => '', + 'display' => self::DISPLAY_LAYOUT_MAIN, + 'metaDescription' => '', + 'metaTitle' => '', + 'notification' => '', + 'redirect' => '', + 'script' => '', + 'showBarEditButton' => false, + 'showPageContent' => false, + 'state' => false, + 'style' => '', + 'title' => null, // Null car un titre peut être vide + // chargement des scripts et des styles dans head + 'vendor' => [ + 'jquery', + //'normalize', chargé par main.php avant common.css + 'lity', + 'filemanager', + 'tippy', + 'zwiico', + 'imagemap', + 'simplelightbox', + 'swiper' + ], + // chargement des styles dans head + 'vendorCss' => [], + // chargement des scripts dans body + 'vendorJsBody' => [], + 'view' => '' + ]; + // Tableaux dynamiques des vendor triés + public static $cssVendorPath = []; + public static $jsHeadVendorPath = []; + public static $jsBodyVendorPath = []; + // Langues proposées, conserver ces 5 variables $i18nList... + public static $i18nList = [ + 'fr' => 'Français (fr)', + 'de' => 'Allemand (de)', + 'en' => 'Anglais (en)', + 'da' => 'Danois (da)', + 'es' => 'Espagnol (es)', + 'fi' => 'Finnois (fi)', + 'el' => 'Grec (el)', + 'it' => 'Italien (it)', + 'ga' => 'Irlandais (ga)', + 'nl' => 'Néerlandais (nl)', + 'pt' => 'Portugais (pt)', + 'sv' => 'Suédois (sv)', + 'br' => 'Breton (br)', + 'ca' => 'Catalan (ca)', + 'co' => 'Corse (co)', + 'eu' => 'Basque (eu)', + 'none' => 'Autre langue' + ]; + public static $i18nList_es = [ + 'fr' => 'Francés (fr)', + 'de' => 'Alemán (de)', + 'en' => 'Inglés (en)', + 'da' => 'Danés (da)', + 'es' => 'Español (es)', + 'fi' => 'Finés (fi)', + 'el' => 'Griego (el)', + 'it' => 'Italiano (it)', + 'ga' => 'Irlandés (ga)', + 'nl' => 'Holandés (nl)', + 'pt' => 'Portugués (pt)', + 'sv' => 'Sueco (sv)', + 'br' => 'Bretón (br)', + 'ca' => 'Catalán (ca)', + 'co' => 'Córcega (co)', + 'eu' => 'Euskera (eu)', + 'none' => 'Otro idioma' + ]; + public static $i18nList_en = [ + 'en' => 'English (en)', + 'fr' => 'French (fr)', + 'de' => 'German (de)', + 'es' => 'Spanish (es)', + 'da' => 'Danish (da)', + 'fi' => 'Finnish (fi)', + 'el' => 'Greek (el)', + 'it' => 'Italian (it)', + 'ga' => 'Irish (ga)', + 'nl' => 'Dutch (nl)', + 'pt' => 'Portuguese (pt)', + 'sv' => 'Swedish (sv)', + 'br' => 'Breton (br)', + 'ca' => 'Catalan (ca)', + 'co' => 'Corsican (co)', + 'eu' => 'Basque (eu)', + 'none' => 'Other language' + ]; + public static $i18nList_admin = [ + 'fr' => 'Français (fr)', + 'en' => 'English (en)', + 'es' => 'Español (es)' + ]; + public static $i18nList_int = [ + 'fr' => 'Français (fr)', + 'da' => 'Dansk (da)', + 'de' => 'Deutsch (de)', + 'el' => 'Ελληνική (el)', + 'en' => 'English (en)', + 'es' => 'Español (es)', + 'ga' => 'Gaeilge (ga)', + 'it' => 'Italiano (it)', + 'nl' => 'Nederlands (nl)', + 'pt' => 'Português (pt)', + 'fi' => 'Suomalainen (fi)', + 'sv' => 'Svenska (sv)', + 'br' => 'Brezhoneg (br)', + 'ca' => 'Català (ca)', + 'co' => 'Corsu (co)', + 'eu' => 'Euskara (eu)', + 'none' => 'Other language (?)' + ]; + // Langues non prises en charge par la traduction automatique + public static $i18nListSiteOnly = [ + 'br' => 'Breton (br)' + ]; + // Langue courante + public static $i18n; + public static $timezone; + private $url = ''; + // Données de site + private $user = []; + private $core = []; + private $config = []; + private $fonts =[]; + // Dossier localisé + private $page = []; + private $module = []; + private $locale = []; + + // Descripteur de données Entrées / Sorties + // Liste ici tous les fichiers de données + private $dataFiles = [ + 'config' => '', + 'page' => '', + 'module' => '', + 'core' => '', + 'user' => '', + 'theme' => '', + 'admin' => '', + 'blacklist' => '', + 'locale' => '', + 'fonts' => '', + 'session' =>'' + ]; + + /** + * Constructeur commun + */ + public function __construct() { + + // Extraction des données http + if(isset($_POST)) { + $this->input['_POST'] = $_POST; + } + if(isset($_COOKIE)) { + $this->input['_COOKIE'] = $_COOKIE; + } + + // Déterminer la langue sélectionnée pour le chargement des fichiers de données + if (isset($this->input['_COOKIE']['DELTA_I18N_SITE']) + ) { + self::$i18n = $this->input['_COOKIE']['DELTA_I18N_SITE']; + setlocale (LC_TIME, self::$i18n . '_' . strtoupper (self::$i18n) ); + + } else { + self::$i18n = 'base'; + } + + // 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 base + foreach ($this->dataFiles as $stageId => $item) { + $folder = $this->dataPath ($stageId, self::$i18n); + if (file_exists($folder . $stageId .'.json') === false) { + $this->initData($stageId, self::$i18n); + common::$coreNotices [] = $stageId ; + } + } + + // Utilisateur connecté + if($this->user === []) { + $this->user = $this->getData(['user', $this->getInput('DELTA_USER_ID')]); + } + + /** + * Discrimination humain robot pour shuntage des Captchas + * Initialisation à 'bot' ou 'human' en fonction des données $_SERVER : à développer ! + */ + if( !isset( $_SESSION['humanBot'] )){ + $_SESSION['humanBot'] = 'bot'; + if( !empty($_SERVER['HTTP_ACCEPT_LANGUAGE']) ) $_SESSION['humanBot'] = 'human'; + } + + /** + * Traduction du site par script + * Traduction par auto-détection de la langue du navigateur + * - Exclure la traduction manuelle + * - La langue du navigateur est lisible + * - L'auto-détection est active + */ + + if ( $this->getData(['config', 'i18n', 'enable']) === true + AND $this->getData(['config', 'i18n','scriptGoogle']) === true + AND $this->getData(['config', 'i18n','autoDetect']) === true + AND $this->getInput('DELTA_I18N_SITE') !== '' + AND !empty($_SERVER['HTTP_ACCEPT_LANGUAGE']) ) + { + /** + * Le cookie est prioritaire sur le navigateur + * la traduction est celle de la langue du drapeau + **/ + if ( $this->getInput('DELTA_I18N_SCRIPT') !== substr($_SERVER["HTTP_ACCEPT_LANGUAGE"],0,2 ) ) { + setrawcookie('googtrans', '/'.$this->getData(['config', 'i18n', 'langBase']).'/'.substr( $_SERVER["HTTP_ACCEPT_LANGUAGE"],0,2 ), time() + 3600, helper::baseUrl(false, false)); + } else { + // Langue du drapeau si elle est définie + if ( $this->getInput('DELTA_I18N_SCRIPT') !== '' ) { + // Paramètre du script + setrawcookie("googtrans", '/'.$this->getData(['config', 'i18n', 'langBase']). '/'. $this->getInput('DELTA_I18N_SCRIPT') , time() + 3600, helper::baseUrl(false,false)); + } + } + } + + // Construit la liste des pages parents/enfants + if($this->hierarchy['all'] === []) { + $pages = helper::arrayCollumn($this->getData(['page']), 'position', 'SORT_ASC'); + // Parents + foreach($pages as $pageId => $pagePosition) { + if( + // Page parent + $this->getData(['page', $pageId, 'parentPageId']) === "" + // Ignore les pages dont l'utilisateur n'a pas accès + AND ( + $this->getData(['page', $pageId, 'group']) === self::GROUP_VISITOR + OR ( + $this->getUser('password') === $this->getInput('DELTA_USER_PASSWORD') + AND $this->getUser('group') >= $this->getData(['page', $pageId, 'group']) + ) + ) + ) { + if($pagePosition !== 0) { + $this->hierarchy['visible'][$pageId] = []; + } + if($this->getData(['page', $pageId, 'block']) === 'bar') { + $this->hierarchy['bar'][$pageId] = []; + } + $this->hierarchy['all'][$pageId] = []; + } + } + // Enfants + foreach($pages as $pageId => $pagePosition) { + if( + // Page parent + $parentId = $this->getData(['page', $pageId, 'parentPageId']) + // Ignore les pages dont l'utilisateur n'a pas accès + AND ( + ( + $this->getData(['page', $pageId, 'group']) === self::GROUP_VISITOR + AND $this->getData(['page', $parentId, 'group']) === self::GROUP_VISITOR + ) + OR ( + $this->getUser('password') === $this->getInput('DELTA_USER_PASSWORD') + AND $this->getUser('group') >= $this->getData(['page', $parentId, 'group']) + AND $this->getUser('group') >= $this->getData(['page', $pageId, 'group']) + ) + ) + ) { + if($pagePosition !== 0) { + $this->hierarchy['visible'][$parentId][] = $pageId; + } + if($this->getData(['page', $pageId, 'block']) === 'bar') { + $this->hierarchy['bar'][$pageId] = []; + } + $this->hierarchy['all'][$parentId][] = $pageId; + } + } + } + // Construit l'url + if($this->url === '') { + if($url = $_SERVER['QUERY_STRING']) { + $this->url = $url; + } + else { + $this->url = $this->getData(['locale', 'homePageId']); + } + } + + // Mise à jour des données core + if( $this->getData(['core', 'dataVersion']) !== intval(str_replace('.','',self::DELTA_VERSION))) include( 'core/include/update.inc.php'); + + // Données de proxy + $proxy = $this->getData(['config','proxyType']) . $this->getData(['config','proxyUrl']) . ':' . $this->getData(['config','proxyPort']); + if (!empty($this->getData(['config','proxyUrl'])) && + !empty($this->getData(['config','proxyPort'])) ) { + $context = array( + 'http' => array( + 'proxy' => $proxy, + 'request_fulluri' => true, + 'verify_peer' => false, + 'verify_peer_name' => false, + ), + "ssl"=>array( + "verify_peer"=>false, + "verify_peer_name"=>false + ) + ); + stream_context_set_default($context); + } + + } + + /** + * Ajoute les valeurs en sortie + * @param array $output Valeurs en sortie + */ + public function addOutput($output) { + $this->output = array_merge($this->output, $output); + } + + /** + * Ajoute une notice de champ obligatoire + * @param string $key Clef du champ + */ + public function addRequiredInputNotices($key) { + // Lexique + include('./core/lang/'. $this->getData(['config', 'i18n', 'langAdmin']) . '/lex_core.php'); + // La clef est un tableau + if(preg_match('#\[(.*)\]#', $key, $secondKey)) { + $firstKey = explode('[', $key)[0]; + $secondKey = $secondKey[1]; + if(empty($this->input['_POST'][$firstKey][$secondKey])) { + common::$inputNotices[$firstKey . '_' . $secondKey] = $text['core']['addRequiredInputNotices'][0]; + } + } + // La clef est une chaine + elseif(empty($this->input['_POST'][$key])) { + common::$inputNotices[$key] = $text['core']['addRequiredInputNotices'][0]; + } + } + + /** + * Check du token CSRF (true = bo + */ + public function checkCSRF() { + return ((empty($_POST['csrf']) OR hash_equals($_SESSION['csrf'], $_POST['csrf']) === false) === false); + } + + /** + * Supprime des données + * @param array $keys Clé(s) des données + */ + public function deleteData($keys) { + // Descripteur + $db = $this->dataFiles[$keys[0]]; + // Aiguillage + switch(count($keys)) { + case 1: + $db->delete($keys[0], true); + break; + case 2: + $db->delete($keys[0].'.'.$keys[1],true); + break; + case 3: + $db->delete($keys[0].'.'.$keys[1].'.'.$keys[2], true); + break; + case 4: + $db->delete($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3], true); + break; + case 5: + $db->delete($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4], true); + break; + case 6: + $db->delete($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5], true); + break; + case 7: + $db->delete($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5].'.'.$keys[6], true); + break; + } + } + + /** + * Accède aux données + * @param array $keys Clé(s) des données + * @return mixed + */ + public function getData($keys = []) { + + if (count($keys) >= 1) { + /** + * Lecture directe + */ + $db = $this->dataFiles[$keys[0]]; + switch(count($keys)) { + case 1: + $tempData = $db->get($keys[0]); + break; + case 2: + $tempData = $db->get($keys[0].'.'.$keys[1]); + break; + case 3: + $tempData = $db->get($keys[0].'.'.$keys[1].'.'.$keys[2]); + break; + case 4: + $tempData = $db->get($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3]); + break; + case 5: + $tempData = $db->get($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4]); + break; + case 6: + $tempData = $db->get($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5]); + break; + case 7: + $tempData = $db->get($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5].'.'.$keys[6]); + break; + case 8: + $tempData = $db->get($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5].'.'.$keys[6].'.'.$keys[7]); + break; + } + return $tempData; + } + } + + /** + * Lire les données de la page + * @param string pageId + * @param string langue + * @param return contenu de la page + */ + public function getPage($page, $lang) { + + // Le nom de la ressource et le fichier de contenu sont définis : + if ( + $this->getData(['page', $page, 'content']) !== '' + && file_exists(self::DATA_DIR . $lang . '/content/' . $this->getData(['page', $page, 'content'])) + && is_file(self::DATA_DIR . $lang . '/content/' . $this->getData(['page', $page, 'content'])) + ) { + return file_get_contents(self::DATA_DIR . $lang . '/content/' . $this->getData(['page', $page, 'content'])); + } else { + return 'Aucun contenu trouvé.'; + } + + } + + /** + * Ecrire les données de la page + * @param string pageId + * @param string contenu de la page + * @param return nombre d'octets écrits ou erreur + */ + public function setPage($page, $value, $lang) { + + return file_put_contents(self::DATA_DIR . $lang . '/content/' . $page . '.html', $value); + + } + + /** + * Effacer les données de la page + * @param string pageId + * @param return statut de l'effacement + */ + public function deletePage($page, $lang) { + + return unlink(self::DATA_DIR . $lang . '/content/' . $this->getData(['page', $page, 'content'])); + + } + + /** + * Sauvegarde des données + * @param array $keys Clé(s) des données + */ + public function setData($keys = []) { + // Pas d'enregistrement lorsqu'une notice est présente ou tableau transmis vide + if (!empty(self::$inputNotices) + OR empty($keys)) { + return false; + } + + // Empêcher la sauvegarde d'une donnée nulle. + if (gettype($keys[count($keys) -1]) === NULL) { + return false; + } + + // Descripteur + $db = $this->dataFiles[$keys[0]]; + + // Aiguillage + switch(count($keys)) { + case 2: + $db->set($keys[0],$keys[1], true); + break; + case 3: + $db->set($keys[0].'.'.$keys[1],$keys[2], true); + break; + case 4: + $db->set($keys[0].'.'.$keys[1].'.'.$keys[2],$keys[3], true); + break; + case 5: + $db->set($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3],$keys[4], true); + break; + case 6: + $db->set($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4],$keys[5], true); + break; + case 7: + $db->set($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5],$keys[6], true); + break; + case 8: + $db->set($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5].'.'.$keys[6],$keys[7], true ); + break; + } + return true; + } + + /** + * Initialisation des données + * @param array $module : nom du module à générer + * choix valides : core config user theme page module + */ + public function initData($module, $lang = 'base', $sampleSite = false) { + + // Tableau avec les données vierges + require_once('core/module/install/ressource/defaultdata.php'); + + // Stockage dans un sous-dossier localisé + if (!file_exists(self::DATA_DIR . $lang)) { + mkdir (self::DATA_DIR .$lang, 0755); + } + $db = $this->dataFiles[$module]; + if ($sampleSite === true) { + $db->set($module,init::$siteData[$module]); + } else { + $db->set($module,init::$defaultData[$module]); + } + $db->save; + + // Dossier des pages + if (!is_dir(self::DATA_DIR . $lang . '/content')) { + mkdir(self::DATA_DIR . $lang . '/content', 0755); + } + // Créer le jeu de pages du site de test + if ($module === 'page' ) { + // Site de test ou page simple + if ($sampleSite === true) { + foreach(init::$siteContent as $key => $value) { + // Creation du contenu de la page + if (!empty($this->getData(['page', $key, 'content'])) ) { + file_put_contents(self::DATA_DIR . $lang . '/content/' . $this->getData(['page', $key, 'content']), $value); + } + } + } else { + // Créer la page d'accueil + file_put_contents(self::DATA_DIR . $lang . '/content/' . 'accueil.html', '

    Contenu de votre nouvelle page.

    '); + } + } + } + + /* + * Dummy function + * Compatibilité des modules avec v8 et v9 + */ + public function saveData() { + return; + } + + /** + * Accède à la liste des pages parents et de leurs enfants + * @param int $parentId Id de la page parent + * @param bool $onlyVisible Affiche seulement les pages visibles + * @param bool $onlyBlock Affiche seulement les pages de type barre + * @return array + */ + public function getHierarchy($parentId = null, $onlyVisible = true, $onlyBlock = false) { + $hierarchy = $onlyVisible ? $this->hierarchy['visible'] : $this->hierarchy['all']; + $hierarchy = $onlyBlock ? $this->hierarchy['bar'] : $hierarchy; + // Enfants d'un parent + if($parentId) { + if(array_key_exists($parentId, $hierarchy)) { + return $hierarchy[$parentId]; + } + else { + return []; + } + } + // Parents et leurs enfants + else { + return $hierarchy; + } + } + + /** + * Accède à une valeur des variables http (ordre de recherche en l'absence de type : _COOKIE, _POST) + * @param string $key Clé de la valeur + * @param int $filter Filtre à appliquer à la valeur + * @param bool $required Champ requis + * @return mixed + */ + public function getInput($key, $filter = helper::FILTER_STRING_SHORT, $required = false) { + // La clef est un tableau + if(preg_match('#\[(.*)\]#', $key, $secondKey)) { + $firstKey = explode('[', $key)[0]; + $secondKey = $secondKey[1]; + foreach($this->input as $type => $values) { + // Champ obligatoire + if($required) { + $this->addRequiredInputNotices($key); + } + // Check de l'existence + // Également utile pour les checkbox qui ne retournent rien lorsqu'elles ne sont pas cochées + if( + array_key_exists($firstKey, $values) + AND array_key_exists($secondKey, $values[$firstKey]) + ) { + // Retourne la valeur filtrée + if($filter) { + return helper::filter($this->input[$type][$firstKey][$secondKey], $filter); + } + // Retourne la valeur + else { + return $this->input[$type][$firstKey][$secondKey]; + } + } + } + } + // La clef est une chaîne + else { + foreach($this->input as $type => $values) { + // Champ obligatoire + if($required) { + $this->addRequiredInputNotices($key); + } + // Check de l'existence + // Également utile pour les checkbox qui ne retournent rien lorsqu'elles ne sont pas cochées + if(array_key_exists($key, $values)) { + // Retourne la valeur filtrée + if($filter) { + return helper::filter($this->input[$type][$key], $filter); + } + // Retourne la valeur + else { + return $this->input[$type][$key]; + } + } + } + } + // Sinon retourne null + return helper::filter(null, $filter); + } + + /** + * Accède à une partie l'url ou à l'url complète + * @param int $key Clé de l'url + * @return string|null + */ + public function getUrl($key = null) { + // Url complète + if($key === null) { + return $this->url; + } + // Une partie de l'url + else { + $url = explode('/', $this->url); + return array_key_exists($key, $url) ? $url[$key] : null; + } + } + + /** + * Accède à l'utilisateur connecté + * @param int $key Clé de la valeur + * @return string|null + */ + public function getUser($key) { + if(is_array($this->user) === false) { + return false; + } + elseif($key === 'id') { + return $this->getInput('DELTA_USER_ID'); + } + elseif(array_key_exists($key, $this->user)) { + return $this->user[$key]; + } + else { + return false; + } + } + + /** + * Check qu'une valeur est transmise par la méthode _POST + * @return bool + */ + public function isPost() { + return ($this->checkCSRF() AND $this->input['_POST'] !== []); + } + + + /** + * Génère un fichier json avec la liste des pages + * + */ + public function pages2Json() { + // Sauve la liste des pages pour TinyMCE + $parents = []; + $rewrite = (helper::checkRewrite()) ? '' : '?'; + // Boucle de recherche des pages actives + foreach($this->getHierarchy(null,false,false) as $parentId => $childIds) { + $children = []; + // Exclure les barres + if ($this->getData(['page', $parentId, 'block']) !== 'bar' ) { + // Boucler sur les enfants et récupérer le tableau children avec la liste des enfants + foreach($childIds as $childId) { + $children [] = [ 'title' => ' » '. html_entity_decode($this->getData(['page', $childId, 'shortTitle']), ENT_QUOTES) , + 'value'=> $rewrite.$childId + ]; + } + // Traitement + if (empty($childIds)) { + // Pas d'enfant, uniquement l'entrée du parent + $parents [] = ['title' => html_entity_decode($this->getData(['page', $parentId, 'shortTitle']), ENT_QUOTES) , + 'value'=> $rewrite.$parentId + ]; + } else { + // Des enfants, on ajoute la page parent en premier + array_unshift ($children , ['title' => html_entity_decode($this->getData(['page', $parentId, 'shortTitle']), ENT_QUOTES) , + 'value'=> $rewrite.$parentId + ]); + // puis on ajoute les enfants au parent + $parents [] = ['title' => html_entity_decode($this->getData(['page', $parentId, 'shortTitle']), ENT_QUOTES) , + 'value'=> $rewrite.$parentId , + 'menu' => $children + ]; + } + } + } + // Sitemap et Search + $children = []; + $children [] = ['title'=>'Rechercher dans le site', + 'value'=>$rewrite.'search' + ]; + $children [] = ['title'=>'Plan du site', + 'value'=>$rewrite.'sitemap' + ]; + $parents [] = ['title' => 'Pages spéciales', + 'value' => '#', + 'menu' => $children + ]; + + // Enregistrement : 3 tentatives + for($i = 0; $i < 3; $i++) { + if (file_put_contents ('core/vendor/tinymce/link_list.json', json_encode($parents), LOCK_EX) !== false) { + break; + } + // Pause de 10 millisecondes + usleep(10000); + } + } + + /** + * Retourne un chemin localisé pour l'enregistrement des données + * @param $stageId nom du module + * @param $lang langue des pages + * @return string du dossier à créer + */ + public function dataPath($id, $lang) { + // Sauf pour les pages et les modules + if ($id === 'page' || + $id === 'module' || + $id === 'locale' ) { + $folder = self::DATA_DIR . $lang . '/' ; + } else { + $folder = self::DATA_DIR; + } + return ($folder); + } + + + /** + * Génère un fichier sitemap.xml + * https://github.com/icamys/php-sitemap-generator + * $command valeurs possible + * all : génère un site map complet + * Sinon contient id de la page à créer + */ + + 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); + + // will create also compressed (gzipped) sitemap : option buguée + // $sitemap->enableCompression(); + + // determine how many urls should be put into one file + // according to standard protocol 50000 is maximum value (see http://www.sitemaps.org/protocol.html) + $sitemap->setMaxUrlsPerSitemap(50000); + + // sitemap file name + $sitemap->setSitemapFileName( 'sitemap.xml') ; + + + // Set the sitemap index file name + $sitemap->setSitemapIndexFileName( 'sitemap-index.xml'); + + $datetime = new DateTime(date('c')); + $datetime->format(DateTime::ATOM); // Updated ISO8601 + + if ($this->getData(['config','seo', 'robots']) === true) { + foreach($this->getHierarchy(null, null, null) as $parentPageId => $childrenPageIds) { + // Exclure les barres,les pages non publiques et les pages orphelines + if ($this->getData(['page',$parentPageId,'group']) !== 0 || + $this->getData(['page', $parentPageId, 'block']) === 'bar' || + $this->getData(['page', $parentPageId, 'position']) === 0 ) { + continue; + } + // Page désactivée, traiter les sous-pages sans prendre en compte la page parente. + if ($this->getData(['page', $parentPageId, 'disable']) !== true ) { + // Cas de la page d'accueil ne pas dupliquer l'URL + $pageId = ($parentPageId !== $this->getData(['locale', 'homePageId'])) ? $parentPageId : ''; + $sitemap->addUrl ('/' . $pageId, $datetime); + } + // Articles du blog + if ($this->getData(['page', $parentPageId, 'moduleId']) === 'blog' && + !empty($this->getData(['module',$parentPageId])) ) { + foreach($this->getData(['module',$parentPageId,'posts']) as $articleId => $article) { + if($this->getData(['module',$parentPageId,'posts',$articleId,'state']) === true) { + $date = $this->getData(['module',$parentPageId,'posts',$articleId,'publishedOn']); + $sitemap->addUrl('/' . $parentPageId . '/' . $articleId , new DateTime("@{$date}",new DateTimeZone($timezone))); + } + } + } + // Sous-pages + foreach($childrenPageIds as $childKey) { + if ($this->getData(['page',$childKey,'group']) !== 0 || $this->getData(['page', $childKey, 'disable']) === true) { + continue; + } + // Cas de la page d'accueil ne pas dupliquer l'URL + $pageId = ($childKey !== $this->getData(['locale', 'homePageId'])) ? $childKey : ''; + $sitemap->addUrl('/' . $childKey,$datetime); + + // La sous-page est un blog + if ($this->getData(['page', $childKey, 'moduleId']) === 'blog' && + !empty($this->getData(['module',$childKey])) ) { + foreach($this->getData(['module',$childKey,'posts']) as $articleId => $article) { + if($this->getData(['module',$childKey,'posts',$articleId,'state']) === true) { + $date = $this->getData(['module',$childKey,'posts',$articleId,'publishedOn']); + $sitemap->addUrl( '/' . $childKey . '/' . $articleId , new DateTime("@{$date}",new DateTimeZone($timezone))); + } + } + } + } + + } + } + else{ + $sitemap->addUrl ('/', $datetime); + } + + // Flush all stored urls from memory to the disk and close all necessary tags. + $sitemap->flush(); + + // Move flushed files to their final location. Compress if the option is enabled. + $sitemap->finalize(); + + // Update robots.txt file in output directory + + if ($this->getData(['config','seo', 'robots']) === true) { + if(file_exists('robots.txt')) unlink('robots.txt'); + $sitemap->updateRobots(); + } else { + file_put_contents('robots.txt','User-agent: *' . PHP_EOL . 'Disallow: /'); + } + + // Submit your sitemaps to Google, Yahoo, Bing and Ask.com + if (empty ($this->getData(['config','proxyType']) . $this->getData(['config','proxyUrl']) . ':' . $this->getData(['config','proxyPort'])) ) { + $sitemap->submitSitemap(); + } + + return(file_exists('sitemap.xml') && file_exists('robots.txt')); + + } + + /* + * Création d'une miniature + * Fonction utilisée lors de la mise à jour d'une version 9 à une version 10 + * @param string $src image source + * @param string $dest image destination + * @param integer $desired_width largeur demandée + */ + function makeThumb($src, $dest, $desired_width) { + // Vérifier l'existence du dossier de destination. + $fileInfo = pathinfo($dest); + if (!is_dir($fileInfo['dirname'])) { + mkdir($fileInfo['dirname'], 0755, true); + } + $source_image = ''; + // Type d'image + switch( $fileInfo['extension']) { + case 'jpeg': + case 'jpg': + $source_image = imagecreatefromjpeg($src); + break; + case 'png': + $source_image = imagecreatefrompng($src); + break; + case 'gif': + $source_image = imagecreatefromgif($src); + break; + case 'webp': + $source_image = imagecreatefromwebp($src); + break; + } + // Image valide + if ($source_image) { + $width = imagesx($source_image); + $height = imagesy($source_image); + /* find the "desired height" of this thumbnail, relative to the desired width */ + $desired_height = floor($height * ($desired_width / $width)); + /* create a new, "virtual" image */ + $virtual_image = imagecreatetruecolor($desired_width, $desired_height); + /* copy source image at a resized size */ + imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height); + switch(mime_content_type($src) ) { + case 'image/jpeg': + return (imagejpeg($virtual_image, $dest)); + break; + case 'image/png': + return (imagepng($virtual_image, $dest)); + break; + case 'image/gif': + return (imagegif($virtual_image, $dest)); + break; + case 'image/webp': + return (imagewebp($virtual_image, $dest)); + break; + } + } else { + return (false); + } + } + + + /** + * Envoi un mail + * @param string|array $to Destinataire + * @param string $subject Sujet + * @param string $content Contenu + * @return bool + */ + public function sendMail($to, $subject, $content, $replyTo = null, $file_name = '') { + // Layout + ob_start(); + include 'core/layout/mail.php'; + $layout = ob_get_clean(); + $mail = new PHPMailer\PHPMailer\PHPMailer; + $mail->CharSet = 'UTF-8'; + // Langage par défaut : en + if( $this->getData(['config', 'i18n', 'langAdmin']) === 'fr')$mail->setLanguage('fr', 'core/class/phpmailer/phpmailer.lang-fr.php'); + // Mail + try{ + // Paramètres SMTP + if ($this->getdata(['config','smtp','enable'])) { + //$mail->SMTPDebug = PHPMailer\PHPMailer\SMTP::DEBUG_SERVER; + $mail->isSMTP(); + $mail->SMTPAutoTLS = false; + $mail->Host = $this->getdata(['config','smtp','host']); + $mail->Port = (int) $this->getdata(['config','smtp','port']); + if ($this->getData(['config','smtp','auth'])) { + $mail->Username = $this->getData(['config','smtp','username']); + $mail->Password = helper::decrypt($this->getData(['config','smtp','username']),$this->getData(['config','smtp','password'])); + $mail->SMTPAuth = $this->getData(['config','smtp','auth']); + $mail->SMTPSecure = $this->getData(['config','smtp','secure']); + $mail->setFrom($this->getData(['config','smtp','username'])); + if (is_null($replyTo)) { + $mail->addReplyTo($this->getData(['config','smtp','username'])); + } else { + $mail->addReplyTo($replyTo); + } + } + // Fin SMTP + } else { + $host = str_replace('www.', '', $_SERVER['HTTP_HOST']); + $mail->setFrom('no-reply@' . $host, $this->getData(['locale', 'title'])); + if (is_null($replyTo)) { + $mail->addReplyTo('no-reply@' . $host, $this->getData(['locale', 'title'])); + } else { + $mail->addReplyTo($replyTo); + } + } + if(is_array($to)) { + foreach($to as $userMail) { + $mail->addAddress($userMail); + } + } + else { + $mail->addAddress($to); + } + $mail->isHTML(true); + $mail->Subject = $subject; + $mail->Body = $layout; + $mail->AltBody = strip_tags($content); + if($file_name !== '') $mail->addAttachment( self::FILE_DIR.'uploads/'.$file_name); + + if($mail->send()) { + return true; + } + else { + return $mail->ErrorInfo; + } + } catch (Exception $e) { + echo $e->errorMessage(); + } catch (\Exception $e) { + echo $e->getMessage(); + } + } + + + + /** + * Effacer un dossier non vide. + * @param string URL du dossier à supprimer + */ + public function removeDir ( $path ) { + foreach ( new DirectoryIterator($path) as $item ) { + if ( $item->isFile() ) @unlink($item->getRealPath()); + if ( !$item->isDot() && $item->isDir() ) $this->removeDir($item->getRealPath()); + } + return ( rmdir($path) ); + } + + + /* + * Copie récursive de dossiers + * @param string $src dossier source + * @param string $dst dossier destination + * @return bool + */ + public function copyDir($src, $dst) { + // Ouvrir le dossier source + $dir = opendir($src); + // Créer le dossier de destination + if (!is_dir($dst)) + $success = mkdir($dst, 0755, true); + else + $success = true; + + // Boucler dans le dossier source en l'absence d'échec de lecture écriture + while( $success + AND $file = readdir($dir) ) { + if (( $file != '.' ) && ( $file != '..' )) { + if ( is_dir($src . '/' . $file) ){ + // Appel récursif des sous-dossiers + $success = $this->copyDir($src . '/' . $file, $dst . '/' . $file); + } + else { + $success = copy($src . '/' . $file, $dst . '/' . $file); + } + } + } + closedir($dir); + return $success; + } + + + /** + * Génère une archive d'un dossier et des sous-dossiers + * @param string fileName path et nom de l'archive + * @param string folder path à zipper + * @param array filter dossiers à exclure + */ + public function makeZip ($fileName, $folder, $filter ) { + $zip = new ZipArchive(); + $zip->open($fileName, ZipArchive::CREATE | ZipArchive::OVERWRITE); + //$directory = 'site/'; + $files = new RecursiveIteratorIterator( + new RecursiveCallbackFilterIterator( + new RecursiveDirectoryIterator( + $folder, + RecursiveDirectoryIterator::SKIP_DOTS + ), + function ($fileInfo, $key, $iterator) use ($filter) { + return $fileInfo->isFile() || !in_array($fileInfo->getBaseName(), $filter); + } + ) + ); + foreach ($files as $name => $file) { + if (!$file->isDir()) { + $filePath = $file->getRealPath(); + $relativePath = substr($filePath, strlen(realpath($folder)) + 1); + $zip->addFile($filePath, $relativePath); + } + } + $zip->close(); + } + + + /** + * Affiche le consentement aux cookies + */ + public function showCookies() { + + // Gestion des cookies intégrée + if ($this->getData(['config', 'cookieConsent']) === true ) + { + // Détermine si le bloc doit être affiché selon la validité du cookie + // L'URL du serveur faut TRUE + $item = '
    getInput('DELTA_COOKIE_CONSENT') !== 'true' ? '>' : ' class="displayNone">'; + // Image titre et bouton de fermeture + $item .= '
    '; + $item .= '
    '; + $item .= '
    '.$this->getData(['locale', 'cookies', 'cookiesTitleText']) . '
    '; + $item .= '
    '. template::ico('cancel') .'
    '; + $item .= '
    '; + // Texte de la popup + $item .= '

    ' . $this->getData(['locale', 'cookies', 'cookiesDeltaText']) . '

    '; + // Formulaire de réponse + $item .= '
    '; + $cookieExt = $this->getData(['locale', 'cookies', 'cookiesExtText']); + $stateCookieExt = $this->getInput('DELTA_COOKIE_EXT_CONSENT') === 'true' ? 'checked="checked"' : ''; + if( $cookieExt !== null AND $cookieExt !== '' ) { + $item .= '

    ' . $this->getData(['locale', 'cookies', 'cookiesExtText']) . '

    '; + $item .= ''; + $item .= ''; + } + $item .= '
    '; + $item .= ''; + $item .= '
    '; + // mentions légales si la page est définie + $legalPage = $this->getData(['locale', 'legalPageId']); + if ($legalPage !== 'none') { + $item .= '

    ' . $this->getData(['locale', 'cookies', 'cookiesLinkMlText']) . '

    '; + } + $item .= '
    '; + echo $item; + } + + } + + /** + * Formate le contenu de la page selon les gabarits + * @param Page par defaut + */ + public function showSection() { + echo '
    '; + // Récupérer la config de la page courante + $blocks = []; + if( null !== $this->getData(['page',$this->getUrl(0),'block'])) $blocks = explode('-',$this->getData(['page',$this->getUrl(0),'block'])); + // Initialiser + $content = ""; + $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]; + } + // Page pleine pour la configuration des modules et l'édition des pages sauf l'affichage d'un article de blog + $pattern = ['config','edit','add','comment','data']; + if ((sizeof($blocks) === 1 || + in_array($this->getUrl(1),$pattern) ) + ) { // Pleine page en mode configuration + $this->showContent(); + if (file_exists(self::DATA_DIR . 'body.inc.html')) { + include( self::DATA_DIR . 'body.inc.html'); + } + if($this->getData(['config', 'statislite', 'enable'])){ + if(is_dir("./module/statislite")) include "./module/statislite/include/stat.php"; + } + } else { + echo '
    '; + /** + * Barre gauche + */ + if ($blockleft !== "") { + echo '
    "; + } + /** + * Contenu de page + */ + echo '
    '; + $this->showContent(); + if (file_exists(self::DATA_DIR . 'body.inc.html')) { + include(self::DATA_DIR . 'body.inc.html'); + } + if($this->getData(['config', 'statislite', 'enable'])){ + if(is_dir("./module/statislite")) include "./module/statislite/include/stat.php"; + } + echo '
    '; + /** + * Barre droite + */ + if ($blockright !== "") { + echo '
    '; + } + echo '
    '; + } + echo '
    '; + } + + /** + * Affiche le contenu + * @param Page par défaut + */ + public function showContent() { + if( + $this->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 '

    ' . $this->output['title'] . '

    '; + } + + echo $this->output['content']; + + /** + * Affiche les crédits, conditions requis : + * La traduction automatique est active et La fonction est activée. + */ + if ( $this->getData(['config', 'i18n', 'enable']) === true + AND $this->getData(['config', 'i18n','scriptGoogle']) === true + AND $this->getData(['config', 'i18n','showCredits']) === true + AND + // et la traduction n'est pas manuelle + ( $this->getInput('DELTA_I18N_SCRIPT') + AND $this->getData(['config', 'i18n', $this->getInput('DELTA_I18N_SCRIPT')]) === 'script' + ) + ) + { + echo ''; + } + } + + /** + * 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 '
    '; + } + + echo $this->getData(['theme', 'footer', 'position']) === 'hide' ? '