Compare commits

...

21 Commits

Author SHA1 Message Date
fredtempez 03b9ee2094 Ajout de script dans Tinymce 2021-01-03 15:09:53 +01:00
fredtempez 31981a39e6 Blog version 2021-01-02 17:21:38 +01:00
fredtempez 0e00d64620 Blog mail notification : nom de l'article plutôt que la page. 2021-01-02 17:16:29 +01:00
fredtempez a082bb130e Interdire le téléchargement des backup 2020-12-31 18:02:08 +01:00
fredtempez 7dd83edb4c change 2020-12-31 17:57:12 +01:00
fredtempez 6c2074ca70 Thème couleur texte bouton standard 2020-12-31 17:56:02 +01:00
fredtempez 1d9e6ddee4 Message blocage 2020-12-31 17:39:36 +01:00
fredtempez c57b176f20 change 2020-12-31 17:19:02 +01:00
fredtempez 92b78bc7a9 Merge branch '10313' of https://github.com/fredtempez/ZwiiCMS into 10313 2020-12-30 17:31:33 +01:00
fredtempez 1eeb8fcac7 change 2020-12-30 17:30:57 +01:00
fredtempez 4535c05dd2 change 2020-12-30 17:30:35 +01:00
fredtempez 1cf8ff5ca1 Message de bienvenue 2020-12-30 17:27:45 +01:00
fredtempez 258ebce4a1 RAZ données de blocage 2020-12-28 21:08:33 +01:00
fredtempez 8efafd9683 Coquille 2020-12-27 16:15:08 +01:00
fredtempez 47e6121567 Blog 212 notification par email, pas de nom de page 2020-12-27 10:56:23 +01:00
fredtempez 945f45609c Nouveau fichier json 2020-12-27 10:10:50 +01:00
fredtempez 1fc11e26b7 Version finale de la classe jsondb 2020-12-25 20:15:56 +01:00
fredtempez 23fdca6c52 gestion d'erreur par exception 2020-12-23 17:33:57 +01:00
fredtempez 72e12e17c0 nombre d'essais en cas de fichier verrouillé 2020-12-22 22:29:25 +01:00
fredtempez 311deabfb4 gtag anonymous 2020-12-22 22:15:02 +01:00
fredtempez 94e65e92aa verrouillage des fchiers ouverts 2020-12-22 22:01:10 +01:00
11 changed files with 444 additions and 387 deletions

9
.gitignore vendored
View File

@ -10,6 +10,8 @@ site/backup/*
site/data/*.json site/data/*.json
site/data/*.css site/data/*.css
site/data/fr/*.json site/data/fr/*.json
site/data/fr/*.back
site/data/*.back
# Fichiers uploadés # Fichiers uploadés
@ -36,9 +38,4 @@ site/data/journal.log
.DS_Store .DS_Store
site/.DS_Store site/.DS_Store
site/file/.DS_Store site/file/.DS_Store
site/tmp/5f7f5e998762c.png
site/tmp/5f7f5e9987628.png
site/tmp/5f7f5ea3e983b.png
site/tmp/5f7f5ea3e9837.png
site/tmp/5f7f5ea20d5eb.png
site/tmp/5f7f5ea20d5ee.png

View File

@ -1,5 +1,17 @@
# Changelog # Changelog
## Version 10.3.13
Modifications :
- Système de données (JsonDB) :
- Verrouillage des fichiers de données ouverts en écriture.
- Message d'erreur littéral.
- Sauvegarde des fichiers de données après un effacement et une écriture.
- Google Analytics, option d'anonymisation.
- Procédure de connexion : les erreurs de captcha sont comptabilisées comme des échecs, allégement des messages d'information.
- TinyMCE : ajout des scripts possibles.
Correction :
- Notification de commentaire, remplacement du nom de la page par le titre de l'article.
- Thème : couleur du texte au survol d'un bouton standard.
## Version 10.3.12 ## Version 10.3.12
Correction : Correction :
@ -23,7 +35,7 @@ Modification :
Corrections : Corrections :
- Configuration : persistance de l'ouverture des blocs. - Configuration : persistance de l'ouverture des blocs.
- Réinitialisation du mot de passe : - Réinitialisation du mot de passe :
- Remise à zéro du timer après renouvellement du mot de passe. - Remise à zéro du timer après renouvèlement du mot de passe.
- Affichage de le fenêtre "Nouveau mot de passe" allégée. - Affichage de le fenêtre "Nouveau mot de passe" allégée.
- Redirection sur la page d'accueil. - Redirection sur la page d'accueil.
- Modules news et blog : transparence icône RSS. - Modules news et blog : transparence icône RSS.
@ -40,7 +52,7 @@ Corrections :
## version 10.3.06 ## version 10.3.06
- Correction : - Correction :
- Edition de page avec module, le changement de mise en page désactive le bouton d'option du module. - Édition de page avec module, le changement de mise en page désactive le bouton d'option du module.
- Modification : - Modification :
- Modules News et Blog : ajout de l'option flux RSS. L'option est activée par défaut. - Modules News et Blog : ajout de l'option flux RSS. L'option est activée par défaut.
@ -100,7 +112,7 @@ Corrections :
## version 10.3.00 ## version 10.3.00
- Corrections : - Corrections :
- Bloquage de l'incrémentation de l'id de page lorsque deux pages ont le même nom. - Incrémentation de l'id de page bloquée lorsque deux pages ont le même nom.
- Login : l'option "Se souvenir de moi" est fonctionnelle. - Login : l'option "Se souvenir de moi" est fonctionnelle.
- Menu : déplacement de la classe "active". - Menu : déplacement de la classe "active".
- Le titre dans la configuration du module non affiché si le titre de la page est masqué. - Le titre dans la configuration du module non affiché si le titre de la page est masqué.
@ -117,7 +129,7 @@ Corrections :
- Configuration du site : - Configuration du site :
- Pages 403 (accès interdit), 404 (page introuvable) et site en maintenance personnalisables - Pages 403 (accès interdit), 404 (page introuvable) et site en maintenance personnalisables
- Sauvegarde du site dans une archive : animation d'attente avec message de confirmation ou d'erreur ; le nom de l'archive prend le nom du sous-domaine s'il existe. - Sauvegarde du site dans une archive : animation d'attente avec message de confirmation ou d'erreur ; le nom de l'archive prend le nom du sous-domaine s'il existe.
- Captcha : addition présentée en lettres sous la forme d'images, réponse en chiffres ; correction du nom de la fonction (capcha en captcha). - Captcha : addition présentée en lettres sous la forme d'images, réponse en chiffres ; correction du nom de la fonction (captcha en captcha).
- Page : - Page :
- Duplication d'une page. - Duplication d'une page.
- Mise à jour : - Mise à jour :
@ -156,7 +168,7 @@ Corrections :
## version 10.2.03 ## version 10.2.03
- Corrections : - Corrections :
- Les entrées de menu disposent d'une classe par groupe de parent en lieu et place des ids. - Les entrées de menu disposent d'une classe par groupe de parent en lieu et place des ids.
- Edition du compte de l'utilisateur, empêcher le préremplissage de l'ancien mot de passe. - Édition du compte de l'utilisateur, empêcher le pré-remplissage de l'ancien mot de passe.
- Reformulation du mail de confirmation d'inscription. - Reformulation du mail de confirmation d'inscription.
- Champ de sélection de fichiers, suppression de la couleur des URL lors d'un survol - Champ de sélection de fichiers, suppression de la couleur des URL lors d'un survol
- Modifications : - Modifications :
@ -173,7 +185,7 @@ Corrections :
- Optimisation et correction de l'algorithme de contrôle d'accès. - Optimisation et correction de l'algorithme de contrôle d'accès.
- Erreur des noms de champ barre des membres dans le pied de page. - Erreur des noms de champ barre des membres dans le pied de page.
- Génération de l'image tag, amélioration du code et du message d'erreur. - Génération de l'image tag, amélioration du code et du message d'erreur.
- Edition de page, erreur lors de la sélection d'une icône de menu. - Édition de page, erreur lors de la sélection d'une icône de menu.
- Problème lors de l'installation, impossibilité d'obtenir l'écran de configuration. - Problème lors de l'installation, impossibilité d'obtenir l'écran de configuration.
## version 10.2.00 ## version 10.2.00
@ -181,11 +193,11 @@ Corrections :
- jQuery v3.5.1 - jQuery v3.5.1
- Nouveautés : - Nouveautés :
- Gestion des accès concurrents : - Gestion des accès concurrents :
- deux utilisateurs ne peuvent accèder en modification à la même page du site ou de configuration - deux utilisateurs ne peuvent accéder en modification à la même page du site ou de configuration
- la connexion d'un utilisateur sur un autre poste ou navigateur déconnecte la session précédente. - la connexion d'un utilisateur sur un autre poste ou navigateur déconnecte la session précédente.
- sécurisation du login - sécurisation du login
- journalisation de l'utilisation du site - journalisation de l'utilisation du site
- Ecran de configuration et d'édition des pages, les blocs sont pliables et dépliables afin d'alléger l'occupation sur l'écran. Le statut des blocs (fermés ; ouverts) est persistante au cours de la session. - Écran de configuration et d'édition des pages, les blocs sont pliables et dépliables afin d'alléger l'occupation sur l'écran. Le statut des blocs (fermés ; ouverts) est persistante au cours de la session.
- Modifications : - Modifications :
- Thème, les sélecteurs de couleur affiche la valeur RGBa d'une couleur différente de celle de la sélection. - Thème, les sélecteurs de couleur affiche la valeur RGBa d'une couleur différente de celle de la sélection.
- Thème de l'administration, amélioration du rendu. - Thème de l'administration, amélioration du rendu.
@ -259,16 +271,16 @@ Corrections :
- Améliorations : - Améliorations :
- Architecture de stockage des données. - Architecture de stockage des données.
- Les données sont désormais stockées dans des fichiers distincts (core, config, theme, user, page et module). - Les données sont désormais stockées dans des fichiers distincts (core, config, theme, user, page et module).
- Les données relatives aux pages et aux modules sont stockées dans un dossier localisé fr par défaut en préaration de la version multilangues. - Les données relatives aux pages et aux modules sont stockées dans un dossier localisé fr par défaut en préparation de la version multi-langues.
- Gestion des données : - Gestion des données :
- Le système ne conserve plus en mémoire l'intégralité des données de site comme dans les versions précédentes. - Le système ne conserve plus en mémoire l'intégralité des données de site comme dans les versions précédentes.
- Les données du site sont chargées à la demande au lieu d'être lues dans leur intégralité. - Les données du site sont chargées à la demande au lieu d'être lues dans leur intégralité.
- Les mises à jour et effacements sont appliquées en direct sur le disque. - Les mises à jour et effacements sont appliquées en direct sur le disque.
- Modifications : - Modifications :
- Module gallery optimisé, tri dynamique, choix du thème. - Module gallery optimisé, tri dynamique, choix du thème.
- Module blog présentation optimisée avec options de position de l'image, la métadescription est le contenu de l'article. - Module blog présentation optimisée avec options de position de l'image, la méta-description est le contenu de l'article.
- Chargement paresseux des images. - Chargement paresseux des images.
- Edition de page : suppression de l'option d'ouverture dans une lity. - Édition de page : suppression de l'option d'ouverture dans une lity.
- Protection des données des modules en cas de changement lors de l'édition d'une page. - Protection des données des modules en cas de changement lors de l'édition d'une page.
Corrections de bug : Corrections de bug :
- Mise à jour automatique : procédure modifiée, désactivée si allow_url_fopen = off sur le serveur - Mise à jour automatique : procédure modifiée, désactivée si allow_url_fopen = off sur le serveur
@ -393,7 +405,7 @@ Corrections de bug :
- L'effet de couleur de fond personnalisé d'une page sélectionnée dans le menu est limité aux pages parents. - L'effet de couleur de fond personnalisé d'une page sélectionnée dans le menu est limité aux pages parents.
- Améliorations : - Améliorations :
- Affichage du contenu seul d'une page du site dans une popup Lity sans menu, bannière et pied de page. - Affichage du contenu seul d'une page du site dans une popup Lity sans menu, bannière et pied de page.
- Editeur de texte ; effet accordéon, les accordéons peuvent être tous refermés. - Éditeur de texte ; effet accordéon, les accordéons peuvent être tous refermés.
- Thème ; menu : lorsque le menu est réduit, le titre du site peut être inséré à la gauche du menu burger. - Thème ; menu : lorsque le menu est réduit, le titre du site peut être inséré à la gauche du menu burger.
## version 9.2.14 ## version 9.2.14
@ -425,7 +437,7 @@ Corrections de bug :
- Supprimer le forçage de l'affichage des médias à 100% - Supprimer le forçage de l'affichage des médias à 100%
- Activer le dimensionnement des médias - Activer le dimensionnement des médias
- Module Form : - Module Form :
- Etiquette de séparation - Étiquette de séparation
- Checkbox retourne un astérisque plutôt que 1 - Checkbox retourne un astérisque plutôt que 1
- Thème - Menu : - Thème - Menu :
- Couleur de fond de la page sélectionnée - Couleur de fond de la page sélectionnée
@ -435,7 +447,7 @@ Corrections de bug :
- Corrections : - Corrections :
- Marge du pied de page par défaut 5px - Marge du pied de page par défaut 5px
- Installation sans site exemple : suppression des barres latérales - Installation sans site exemple : suppression des barres latérales
- Edition de page : - Édition de page :
- Affichage de l'option Fil d'ariane alors que le titre est masqué. - Affichage de l'option Fil d'ariane alors que le titre est masqué.
- Page parente, l'option "ne pas afficher les pages enfants dans le menu horizontal" est incompatible avec une page désactivée : désactivation et masquage lorsque la page est désactivée. - Page parente, l'option "ne pas afficher les pages enfants dans le menu horizontal" est incompatible avec une page désactivée : désactivation et masquage lorsque la page est désactivée.
- Mauvais encodage des titres de pages perturbant l'affichage des caractères spéciaux ( ex: apostrophes ). - Mauvais encodage des titres de pages perturbant l'affichage des caractères spéciaux ( ex: apostrophes ).
@ -465,7 +477,7 @@ Corrections de bug :
## Version 9.2.08 ## Version 9.2.08
- Correction : - Correction :
- Edition de page : bug empêchant le paramétrage d'un module après un changement de gabarit. - Édition de page : bug empêchant le paramétrage d'un module après un changement de gabarit.
- Modification : - Modification :
- Aide de l'édition des pages - Aide de l'édition des pages
@ -505,7 +517,7 @@ Corrections de bug :
## Version 9.2.01 ## Version 9.2.01
- Corrections : - Corrections :
- Sauvegarde du thème : prise en compte du fichier custom.css - Sauvegarde du thème : prise en compte du fichier custom.css
- Edition de page : libellés - Édition de page : libellés
- Thème ; footer : marges du pied de page placé hors du site - Thème ; footer : marges du pied de page placé hors du site
- Thème ; footer : aperçu du texte personnalisé - Thème ; footer : aperçu du texte personnalisé
@ -571,7 +583,7 @@ Corrections de bug :
- Réécriture activée après chaque mise à jour auto. - Réécriture activée après chaque mise à jour auto.
- Modifications : - Modifications :
- Thème 100% fluide sans marge - Thème 100% fluide sans marge
- Ecran de smartphone (ex : iPhone 6) : adaptation de la barre d'administration : le username est masqué et la taille des icônes est augmentée - Écran de smartphone (ex : iPhone 6) : adaptation de la barre d'administration : le username est masqué et la taille des icônes est augmentée
- Chemins vers les données dans des constantes - Chemins vers les données dans des constantes
- Modèles de bannières de plusieurs dimensions - Modèles de bannières de plusieurs dimensions
- Hauteur de police par défaut 13px - Hauteur de police par défaut 13px
@ -661,7 +673,7 @@ Corrections de bug :
## Version 9.0.18 ## Version 9.0.18
- Correction : - Correction :
- Etat par défaut du numéro de version mal récupéré - État par défaut du numéro de version mal récupéré
## Version 9.0.17 ## Version 9.0.17
- Mises à jour : - Mises à jour :
@ -676,7 +688,7 @@ Corrections de bug :
## Version 9.0.16 ## Version 9.0.16
- Correction : - Correction :
- Nom de page constitué de caractères filtrés empchant la création d'un Id valide. - Nom de page constitué de caractères filtrés empêchant la création d'un Id valide.
- Module Gallery : bouton de fermeture sous Edge - Module Gallery : bouton de fermeture sous Edge
## Version 9.0.15 ## Version 9.0.15
@ -701,7 +713,7 @@ Corrections de bug :
## Version 9.0.12 ## Version 9.0.12
- Corrections : - Corrections :
- Configuration de Tippy pour l'utilisation de l'argument title dans les balises a et img. Data-tippy-content reste un argument reconnu - Configuration de Tippy pour l'utilisation de l'argument title dans les balises a et img. Data-tippy-content reste un argument reconnu
- Bug de la redirection lorsqu'un dossier porte le nom d'une page, le contrôle de cohérence est déplacé dans page. - Bug de la redirection lorsque un dossier porte le nom d'une page, le contrôle de cohérence est déplacé dans page.
## Version 9.0.11 ## Version 9.0.11
- Corrections : - Corrections :
@ -751,7 +763,7 @@ Corrections de bug :
- Thème : - Thème :
- nouvelle position du menu dans le site quand la bannière est au-dessus. - nouvelle position du menu dans le site quand la bannière est au-dessus.
- Simplification et ordre des libellés position du menu par rapport à la bannière - Simplification et ordre des libellés position du menu par rapport à la bannière
- Editeur de texte, scrolle lorsque l'éditeur est ouvert, la barre d'outil se colle sous la barre d'administration. - Éditeur de texte, scrolle lorsque l'éditeur est ouvert, la barre d'outil se colle sous la barre d'administration.
- TinyMCE : - TinyMCE :
- liste des pages du site dans la fenêtre des liens - liste des pages du site dans la fenêtre des liens
- option lightbox pour l'affichage d'images ou de liens - option lightbox pour l'affichage d'images ou de liens

View File

@ -1,6 +1,6 @@
![](https://img.shields.io/github/last-commit/fredtempez/ZwiiCMS/master) ![](https://img.shields.io/github/release-date/fredtempez/ZwiiCMS) ![](https://img.shields.io/github/last-commit/fredtempez/ZwiiCMS/master) ![](https://img.shields.io/github/release-date/fredtempez/ZwiiCMS)
# ZwiiCMS 10.3.12 # ZwiiCMS 10.3.13
Zwii est un CMS sans base de données (flat-file) qui permet de créer et gérer facilement un site web sans aucune connaissance en programmation. Zwii est un CMS sans base de données (flat-file) qui permet de créer et gérer facilement un site web sans aucune connaissance en programmation.

View File

@ -22,10 +22,10 @@ class JsonDb extends \Prowebcraft\Dot
public function __construct($config = []) public function __construct($config = [])
{ {
$this->config = array_merge([ $this->config = array_merge([
'name' => 'data.json', 'name' => 'data.json',
'backup' => 5, 'backup' => false,
'dir' => getcwd(), 'dir' => getcwd()
'template' => getcwd() . DIRECTORY_SEPARATOR . 'data.template.json' //'template' => getcwd() . DIRECTORY_SEPARATOR . 'data.template.json'
], $config); ], $config);
$this->loadData(); $this->loadData();
parent::__construct(); parent::__construct();
@ -106,10 +106,17 @@ class JsonDb extends \Prowebcraft\Dot
if (!file_exists($this->db)) { if (!file_exists($this->db)) {
return null; return null;
} else { } else {
$this->data = json_decode(file_get_contents($this->db), true); // 3 essais
for($i = 0; $i <3; $i++) {
if ($this->data = json_decode(@file_get_contents($this->db), true) ) {
break;
}
// Pause de 10 millisecondes
usleep(10000);
}
// Gestion de l'erreur
if (!$this->data === null) { if (!$this->data === null) {
throw new \InvalidArgumentException('Database file ' . $this->db exit ('JsonDB : Erreur de lecture du fichier de données ' . $this->db .'. Aucune donnée lisible, essayez dans quelques instants ou vérifiez le système de fichiers.');
. ' contains invalid json object. Please validate or remove file');
} }
} }
} }
@ -120,6 +127,29 @@ class JsonDb extends \Prowebcraft\Dot
* Saving to local database * Saving to local database
*/ */
public function save() { public function save() {
file_put_contents($this->db, json_encode($this->data, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT)); // Fichier inexistant, le créer
if ( !file_exists($this->db) ) {
touch($this->db);
}
// Backup file
if ($this->config['backup']) {
copy ($this->db, $this->db . '.back');
}
if ( is_writable($this->db) ) {
// 3 essais
for($i = 0; $i < 3; $i++) {
if( @file_put_contents($this->db, json_encode($this->data, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT|LOCK_EX)) !== false) {
break;
}
// Pause de 10 millisecondes
usleep(10000);
}
if ($i === 2) {
exit ('Jsondb : Erreur d\'écriture dans le fichier de données ' . $this->db . '. Vérifiez le système de fichiers.' );
}
} else {
exit ('Jsondb : Écriture interdite dans le fichier de données ' . $this->db .'. Vérifiez les permissions.' );
}
} }
} }

View File

@ -40,7 +40,7 @@ class common {
const ACCESS_TIMER = 1800; const ACCESS_TIMER = 1800;
// Numéro de version // Numéro de version
const ZWII_VERSION = '10.3.12'; const ZWII_VERSION = '10.3.13';
const ZWII_UPDATE_CHANNEL = "v10"; const ZWII_UPDATE_CHANNEL = "v10";
public static $actions = []; public static $actions = [];
@ -86,6 +86,7 @@ class common {
public static $inputBefore = []; public static $inputBefore = [];
public static $inputNotices = []; public static $inputNotices = [];
public static $importNotices = []; public static $importNotices = [];
public static $captchaNotices = [];
public static $coreNotices = []; public static $coreNotices = [];
public $output = [ public $output = [
'access' => true, 'access' => true,
@ -325,36 +326,30 @@ class common {
// Constructeur JsonDB // Constructeur JsonDB
$db = new \Prowebcraft\JsonDb([ $db = new \Prowebcraft\JsonDb([
'name' => $keys[0] . '.json', 'name' => $keys[0] . '.json',
'dir' => $folder 'dir' => $folder,
'backup' => true
]); ]);
switch(count($keys)) { switch(count($keys)) {
case 1: case 1:
$db->delete($keys[0]); $db->delete($keys[0], true);
$db->save();
break; break;
case 2: case 2:
$db->delete($keys[0].'.'.$keys[1]); $db->delete($keys[0].'.'.$keys[1],true);
$db->save();
break; break;
case 3: case 3:
$db->delete($keys[0].'.'.$keys[1].'.'.$keys[2]); $db->delete($keys[0].'.'.$keys[1].'.'.$keys[2], true);
$db->save();
break; break;
case 4: case 4:
$db->delete($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3]); $db->delete($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3], true);
$db->save();
break; break;
case 5: case 5:
$db->delete($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4]); $db->delete($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4], true);
$db->save();
break; break;
case 6: case 6:
$db->delete($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5]); $db->delete($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5], true);
$db->save();
break; break;
case 7: case 7:
$db->delete($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5].'.'.$keys[6]); $db->delete($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5].'.'.$keys[6], true);
$db->save();
break; break;
} }
} }
@ -617,7 +612,8 @@ class common {
$this->setData(['theme',$tempData['theme']]); $this->setData(['theme',$tempData['theme']]);
// Import des users sauvegardés si option active // Import des users sauvegardés si option active
if ($keepUsers === false) { if ($keepUsers === false
AND $tempData['user'] !== NULL) {
$this->setData(['user',$tempData['user']]); $this->setData(['user',$tempData['user']]);
} }
@ -965,33 +961,28 @@ class common {
// Constructeur JsonDB // Constructeur JsonDB
$db = new \Prowebcraft\JsonDb([ $db = new \Prowebcraft\JsonDb([
'name' => $keys[0] . '.json', 'name' => $keys[0] . '.json',
'dir' => $folder 'dir' => $folder,
'backup' => true
]); ]);
switch(count($keys)) { switch(count($keys)) {
case 2: case 2:
$db->set($keys[0],$keys[1]); $db->set($keys[0],$keys[1], true);
$db->save();
break; break;
case 3: case 3:
$db->set($keys[0].'.'.$keys[1],$keys[2]); $db->set($keys[0].'.'.$keys[1],$keys[2], true);
$db->save();
break; break;
case 4: case 4:
$db->set($keys[0].'.'.$keys[1].'.'.$keys[2],$keys[3]); $db->set($keys[0].'.'.$keys[1].'.'.$keys[2],$keys[3], true);
$db->save();
break; break;
case 5: case 5:
$db->set($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3],$keys[4]); $db->set($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3],$keys[4], true);
$db->save();
break; break;
case 6: case 6:
$db->set($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4],$keys[5]); $db->set($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4],$keys[5], true);
$db->save();
break; break;
case 7: case 7:
$db->set($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5],$keys[6]); $db->set($keys[0].'.'.$keys[1].'.'.$keys[2].'.'.$keys[3].'.'.$keys[4].'.'.$keys[5],$keys[6], true);
$db->save();
break; break;
} }
return true; return true;
@ -1591,7 +1582,7 @@ class core extends common {
// Site // Site
$colors = helper::colorVariants($this->getData(['theme', 'text', 'linkColor'])); $colors = helper::colorVariants($this->getData(['theme', 'text', 'linkColor']));
$css .= 'a{color:' . $colors['normal'] . '}'; $css .= 'a{color:' . $colors['normal'] . '}';
$css .= 'a:hover:not(.inputFile, button){color:' . $colors['darken'] . '}'; //$css .= 'a:hover:not(.inputFile, button){color:' . $colors['darken'] . '}';
$css .= 'body,.row > div{font-size:' . $this->getData(['theme', 'text', 'fontSize']) . '}'; $css .= 'body,.row > div{font-size:' . $this->getData(['theme', 'text', 'fontSize']) . '}';
$css .= 'body{color:' . $this->getData(['theme', 'text', 'textColor']) . '}'; $css .= 'body{color:' . $this->getData(['theme', 'text', 'textColor']) . '}';
$css .= 'select,input[type=\'password\'],input[type=\'email\'],input[type=\'text\'],.inputFile,select,textarea{color:' . $this->getData(['theme', 'text', 'textColor']) .';background-color:'.$this->getData(['theme', 'site', 'backgroundColor']).';}'; $css .= 'select,input[type=\'password\'],input[type=\'email\'],input[type=\'text\'],.inputFile,select,textarea{color:' . $this->getData(['theme', 'text', 'textColor']) .';background-color:'.$this->getData(['theme', 'site', 'backgroundColor']).';}';
@ -1878,7 +1869,8 @@ class core extends common {
} }
} }
// Accès concurrent stocke la page visitée // Accès concurrent stocke la page visitée
if ($this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')) { if ($this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
AND $this->getUser('id') ) {
$this->setData(['user',$this->getUser('id'),'accessUrl',$this->getUrl()]); $this->setData(['user',$this->getUser('id'),'accessUrl',$this->getUrl()]);
$this->setData(['user',$this->getUser('id'),'accessTimer',time()]); $this->setData(['user',$this->getUser('id'),'accessTimer',time()]);
} }
@ -2203,7 +2195,7 @@ class layout extends common {
window.dataLayer = window.dataLayer || []; window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);} function gtag(){dataLayer.push(arguments);}
gtag("js", new Date()); gtag("js", new Date());
gtag("config","'. $code .'"); gtag("config","'. $code .'",{ "anonymize_ip": true });
</script>'; </script>';
} }
} }
@ -2719,10 +2711,6 @@ class layout extends common {
foreach (common::$coreNotices as $item) $notification .= $item . ' | '; foreach (common::$coreNotices as $item) $notification .= $item . ' | ';
$notificationClass = 'notificationError'; $notificationClass = 'notificationError';
} }
if(common::$inputNotices) {
$notification = 'Impossible de soumettre le formulaire, car il contient des erreurs';
$notificationClass = 'notificationError';
}
elseif(empty($_SESSION['ZWII_NOTIFICATION_SUCCESS']) === false) { elseif(empty($_SESSION['ZWII_NOTIFICATION_SUCCESS']) === false) {
$notification = $_SESSION['ZWII_NOTIFICATION_SUCCESS']; $notification = $_SESSION['ZWII_NOTIFICATION_SUCCESS'];
$notificationClass = 'notificationSuccess'; $notificationClass = 'notificationSuccess';

View File

@ -172,14 +172,14 @@ class config extends common {
]; ];
// Sécurité de la connexion - tentative max avant blocage // Sécurité de la connexion - tentative max avant blocage
public static $connectAttempt = [ public static $connectAttempt = [
999 => 'Aucun', 999 => 'Sécurité désactivée',
3 => '3 tentatives', 3 => '3 tentatives',
5 => '5 tentatives', 5 => '5 tentatives',
10 => '10 tentatives' 10 => '10 tentatives'
]; ];
// Sécurité de la connexion - durée du blocage // Sécurité de la connexion - durée du blocage
public static $connectTimeout = [ public static $connectTimeout = [
0 => 'Aucun', 0 => 'Sécurité désactivée',
300 => '5 minutes', 300 => '5 minutes',
600 => '10 minutes', 600 => '10 minutes',
900 => '15 minutes' 900 => '15 minutes'

View File

@ -364,13 +364,13 @@
<div class="row"> <div class="row">
<div class="col3"> <div class="col3">
<?php echo template::select('configConnectAttempt', $module::$connectAttempt , [ <?php echo template::select('configConnectAttempt', $module::$connectAttempt , [
'label' => 'Connexions successives', 'label' => 'Tentatives avant blocage',
'selected' => $this->getData(['config', 'connect', 'attempt']) 'selected' => $this->getData(['config', 'connect', 'attempt'])
]); ?> ]); ?>
</div> </div>
<div class="col3"> <div class="col3">
<?php echo template::select('configConnectTimeout', $module::$connectTimeout , [ <?php echo template::select('configConnectTimeout', $module::$connectTimeout , [
'label' => 'Blocage après échecs', 'label' => 'Durée du blocage',
'selected' => $this->getData(['config', 'connect', 'timeout']) 'selected' => $this->getData(['config', 'connect', 'timeout'])
]); ?> ]); ?>
</div> </div>

View File

@ -334,15 +334,32 @@ class user extends common {
public function login() { public function login() {
// Soumission du formulaire // Soumission du formulaire
if($this->isPost()) { if($this->isPost()) {
// Check la captcha // Lire Id du compte
$userId = $this->getInput('userLoginId', helper::FILTER_ID, true);
// Check le captcha
if( if(
$this->getData(['config','connect','captcha']) $this->getData(['config','connect','captcha'])
AND password_verify($this->getInput('userLoginCaptcha', helper::FILTER_INT), $this->getInput('userLoginCaptchaResult') ) === false ) AND password_verify($this->getInput('userLoginCaptcha', helper::FILTER_INT), $this->getInput('userLoginCaptchaResult') ) === false )
{ {
self::$inputNotices['userLoginCaptcha'] = 'Incorrect'; //self::$inputNotices['userLoginCaptcha'] = 'Incorrect';
$notification = 'Captcha incorrect !';
// Cas 1 le nombre de connexions est inférieur aux tentatives autorisées : incrément compteur d'échec
if ($this->getData(['user',$userId,'connectFail']) < $this->getData(['config', 'connect', 'attempt'])) {
$this->setData(['user',$userId,'connectFail',$this->getdata(['user',$userId,'connectFail']) + 1 ]);
}
// Cas 2 la limite du nombre de connexion est atteinte : placer le timer
if ( $this->getdata(['user',$userId,'connectFail']) == $this->getData(['config', 'connect', 'attempt']) ) {
$this->setData(['user',$userId,'connectTimeout', time()]);
}
// Cas 3 le délai de bloquage court
if ($this->getData(['user',$userId,'connectTimeout']) + $this->getData(['config', 'connect', 'timeout']) > time() ) {
$notification = 'Accès bloqué ' . ($this->getData(['config', 'connect', 'timeout']) / 60) . ' minutes.';
}
// Valeurs en sortie
$this->addOutput([
'notification' => $notification
]);
} else { } else {
// Lire Id du compte
$userId = $this->getInput('userLoginId', helper::FILTER_ID, true);
/** /**
* Aucun compte existant * Aucun compte existant
*/ */
@ -363,14 +380,14 @@ class user extends common {
AND in_array($this->getData(['blacklist',$userId,'ip']),$ipBlackList) ) { AND in_array($this->getData(['blacklist',$userId,'ip']),$ipBlackList) ) {
// Valeurs en sortie // Valeurs en sortie
$this->addOutput([ $this->addOutput([
'notification' => 'Trop de tentatives, compte verrouillé', 'notification' => 'Compte verrouillé',
'redirect' => helper::baseUrl(), 'redirect' => helper::baseUrl(),
'state' => false 'state' => false
]); ]);
} else { } else {
// Valeurs en sortie // Valeurs en sortie
$this->addOutput([ $this->addOutput([
'notification' => 'Identifiant ou mot de passe incorrect' 'notification' => 'Identifiant ou mot de passe incorrects'
]); ]);
} }
/** /**
@ -391,6 +408,9 @@ class user extends common {
AND password_verify($this->getInput('userLoginPassword', helper::FILTER_STRING_SHORT, true), $this->getData(['user', $userId, 'password'])) AND password_verify($this->getInput('userLoginPassword', helper::FILTER_STRING_SHORT, true), $this->getData(['user', $userId, 'password']))
AND $this->getData(['user', $userId, 'group']) >= self::GROUP_MEMBER AND $this->getData(['user', $userId, 'group']) >= self::GROUP_MEMBER
) { ) {
// RAZ
$this->setData(['user',$userId,'connectFail',0 ]);
$this->setData(['user',$userId,'connectTimeout',0 ]);
// Expiration // Expiration
$expire = $this->getInput('userLoginLongTime') ? strtotime("+1 year") : 0; $expire = $this->getInput('userLoginLongTime') ? strtotime("+1 year") : 0;
$c = $this->getInput('userLoginLongTime', helper::FILTER_BOOLEAN) === true ? 'true' : 'false'; $c = $this->getInput('userLoginLongTime', helper::FILTER_BOOLEAN) === true ? 'true' : 'false';
@ -412,14 +432,14 @@ class user extends common {
} else { } else {
// Valeurs en sortie // Valeurs en sortie
$this->addOutput([ $this->addOutput([
'notification' => 'Connexion réussie', 'notification' => 'Bienvenue ' . $this->getData(['user',$userId,'firstname']) . ' ' . $this->getData(['user',$userId,'lastname']) ,
'redirect' => helper::baseUrl() . str_replace('_', '/', str_replace('__', '#', $this->getUrl(2))), 'redirect' => helper::baseUrl() . str_replace('_', '/', str_replace('__', '#', $this->getUrl(2))),
'state' => true 'state' => true
]); ]);
} }
// Sinon notification d'échec // Sinon notification d'échec
} else { } else {
$notification = 'Identifiant ou mot de passe incorrect'; $notification = 'Identifiant ou mot de passe incorrects';
// Cas 1 le nombre de connexions est inférieur aux tentatives autorisées : incrément compteur d'échec // Cas 1 le nombre de connexions est inférieur aux tentatives autorisées : incrément compteur d'échec
if ($this->getData(['user',$userId,'connectFail']) < $this->getData(['config', 'connect', 'attempt'])) { if ($this->getData(['user',$userId,'connectFail']) < $this->getData(['config', 'connect', 'attempt'])) {
$this->setData(['user',$userId,'connectFail',$this->getdata(['user',$userId,'connectFail']) + 1 ]); $this->setData(['user',$userId,'connectFail',$this->getdata(['user',$userId,'connectFail']) + 1 ]);
@ -430,7 +450,7 @@ class user extends common {
} }
// Cas 3 le délai de bloquage court // Cas 3 le délai de bloquage court
if ($this->getData(['user',$userId,'connectTimeout']) + $this->getData(['config', 'connect', 'timeout']) > time() ) { if ($this->getData(['user',$userId,'connectTimeout']) + $this->getData(['config', 'connect', 'timeout']) > time() ) {
$notification = 'Trop de tentatives, accès bloqué durant ' . ($this->getData(['config', 'connect', 'timeout']) / 60) . ' minutes.'; $notification = 'Accès bloqué ' . ($this->getData(['config', 'connect', 'timeout']) / 60) . ' minutes.';
} }
// Journalisation // Journalisation
$dataLog = mb_detect_encoding(strftime('%d/%m/%y',time()), 'UTF-8', true) $dataLog = mb_detect_encoding(strftime('%d/%m/%y',time()), 'UTF-8', true)

View File

@ -1,297 +1,303 @@
/** /**
* Initialisation de TinyMCE * Initialisation de TinyMCE
*/ */
tinymce.init({ tinymce.init({
// Classe où appliquer l'éditeur // Classe où appliquer l'éditeur
selector: ".editorWysiwyg", selector: ".editorWysiwyg",
// Aperçu dans le pied de page // Aperçu dans le pied de page
setup:function(ed) { setup:function(ed) {
ed.on('change', function(e) { ed.on('change', function(e) {
if (ed.id === 'themeFooterText') { if (ed.id === 'themeFooterText') {
$("#footerText").html(tinyMCE.get('themeFooterText').getContent()); $("#footerText").html(tinyMCE.get('themeFooterText').getContent());
} }
}); });
}, },
// Langue // Langue
language: "fr_FR", language: "fr_FR",
// Plugins // Plugins
plugins: "advlist anchor autolink autoresize autosave codemirror colorpicker contextmenu fullscreen hr image imagetools link lists media paste searchreplace stickytoolbar tabfocus table template textcolor emoticons nonbreaking", plugins: "advlist anchor autolink autoresize autosave codemirror colorpicker contextmenu fullscreen hr image imagetools link lists media paste searchreplace stickytoolbar tabfocus table template textcolor emoticons nonbreaking",
// Contenu de la barre d'outils // Contenu de la barre d'outils
toolbar: "restoredraft | undo redo | formatselect bold italic underline forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist emoticons | table template | image media link | code fullscreen", toolbar: "restoredraft | undo redo | formatselect bold italic underline forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist emoticons | table template | image media link | code fullscreen",
// Emoticons // Emoticons
emoticons_append: { emoticons_append: {
custom_mind_explode: { custom_mind_explode: {
keywords: ["brain", "mind", "explode", "blown"], keywords: ["brain", "mind", "explode", "blown"],
char: "🤯" char: "🤯"
} }
}, },
// CodeMirror // Autoriser tous les éléments
codemirror: { valid_elements : '*[*]',
indentOnInit: true, // Whether or not to indent code on init. // Autorise l'ajout de script
path: 'codemirror', // Path to CodeMirror distribution extended_valid_elements: "script[language|type|src]",
saveCursorPosition: false, // Insert caret marker // Conserver les styles
config: { // CodeMirror config object keep_styles: false,
/*theme: 'ambiance',*/ // CodeMirror
fullscreen: true, codemirror: {
/*mode: 'application/x-httpd-php',*/ indentOnInit: true, // Whether or not to indent code on init.
indentUnit: 4, path: 'codemirror', // Path to CodeMirror distribution
lineNumbers: true, saveCursorPosition: false, // Insert caret marker
mode: "htmlmixed", config: { // CodeMirror config object
}, /*theme: 'ambiance',*/
jsFiles: [ fullscreen: true,
'mode/php/php.js', /*mode: 'application/x-httpd-php',*/
'mode/css/css.js', indentUnit: 4,
'mode/htmlmixed/htmlmixed.js', lineNumbers: true,
'mode/htmlembedded/htmlembedded.js', mode: "htmlmixed",
'mode/javascript/javascript.js', },
'mode/xml/xml.js', jsFiles: [
'addon/search/searchcursor.js', 'mode/php/php.js',
'addon/search/search.js', 'mode/css/css.js',
], 'mode/htmlmixed/htmlmixed.js',
cssFiles: [ 'mode/htmlembedded/htmlembedded.js',
/*'theme/ambiance.css',*/ 'mode/javascript/javascript.js',
], 'mode/xml/xml.js',
width: 800, // Default value is 800 'addon/search/searchcursor.js',
height: 500 // Default value is 550 'addon/search/search.js',
}, ],
// Cibles de la target cssFiles: [
target_list: [ /*'theme/ambiance.css',*/
{title: 'None', value: ''}, ],
{title: 'Nouvel onglet', value: '_blank'} width: 800, // Default value is 800
], height: 500 // Default value is 550
// Target pour lightbox },
rel_list: [ // Cibles de la target
{title: 'None', value: ''}, target_list: [
{title: 'Une popup (Lity)', value: 'data-lity'}, {title: 'None', value: ''},
{title: 'Une galerie d\'images (SimpleLightbox)', value: 'gallery'} {title: 'Nouvel onglet', value: '_blank'}
], ],
// Titre des image // Target pour lightbox
image_title: true, rel_list: [
// Pages internes {title: 'None', value: ''},
link_list: baseUrl + "core/vendor/tinymce/links.php", {title: 'Une popup (Lity)', value: 'data-lity'},
// Contenu du menu contextuel {title: 'Une galerie d\'images (SimpleLightbox)', value: 'gallery'}
contextmenu: "selectall searchreplace | hr | media image link anchor nonbreaking | insertable cell row column deletetable", ],
// Fichiers CSS à intégrer à l'éditeur // Titre des image
content_css: [ image_title: true,
baseUrl + "core/layout/common.css", // Pages internes
baseUrl + "core/vendor/tinymce/content.css", link_list: baseUrl + "core/vendor/tinymce/links.php",
baseUrl + "site/data/theme.css", // Contenu du menu contextuel
baseUrl + "site/data/custom.css" contextmenu: "selectall searchreplace | hr | media image link anchor nonbreaking | insertable cell row column deletetable",
], // Fichiers CSS à intégrer à l'éditeur
// Classe à ajouter à la balise body dans l'iframe content_css: [
body_class: "editorWysiwyg", baseUrl + "core/layout/common.css",
// Cache les menus baseUrl + "core/vendor/tinymce/content.css",
menubar: true, baseUrl + "site/data/theme.css",
// URL menu contextuel baseUrl + "site/data/custom.css"
link_context_toolbar: true, ],
// Cache la barre de statut // Classe à ajouter à la balise body dans l'iframe
statusbar: false, body_class: "editorWysiwyg",
// Active le copié collé à partir du Web // Cache les menus
paste_data_images: true, menubar: true,
// Active le copié collé à partir du presse papier // URL menu contextuel
paste_filter_drop: false, link_context_toolbar: true,
/* Eviter BLOB à tester // Cache la barre de statut
images_dataimg_filter: function(img) { statusbar: false,
return img.hasAttribute('internal-blob'); // Active le copié collé à partir du Web
},*/ paste_data_images: true,
// Autorise l'ajout de script // Active le copié collé à partir du presse papier
// extended_valid_elements: "script[language|type|src]", paste_filter_drop: false,
// Bloque le dimensionnement des médias (car automatiquement en fullsize avec fitvids pour le responsive) /* Eviter BLOB à tester
media_dimensions: true, images_dataimg_filter: function(img) {
// Désactiver la dimension des images return img.hasAttribute('internal-blob');
image_dimensions: true, },*/
// Active l'onglet avancé lors de l'ajout d'une image // Autorise l'ajout de script
image_advtab: true, // extended_valid_elements: "script[language|type|src]",
// Urls absolues // Bloque le dimensionnement des médias (car automatiquement en fullsize avec fitvids pour le responsive)
relative_urls: false, media_dimensions: true,
// Url de base // Désactiver la dimension des images
document_base_url: baseUrl, image_dimensions: true,
// Gestionnaire de fichiers // Active l'onglet avancé lors de l'ajout d'une image
filemanager_access_key: privateKey, image_advtab: true,
external_filemanager_path: baseUrl + "core/vendor/filemanager/", // Urls absolues
external_plugins: { relative_urls: false,
"filemanager": baseUrl + "core/vendor/filemanager/plugin.min.js" // Url de base
}, document_base_url: baseUrl,
// Thème mobile // Gestionnaire de fichiers
// mobile: { filemanager_access_key: privateKey,
// theme: "mobile", external_filemanager_path: baseUrl + "core/vendor/filemanager/",
// plugins: [ 'autosave', 'lists', 'autolink' ], external_plugins: {
// toolbar: [ 'undo', 'bold', 'italic', 'styleselect' ] "filemanager": baseUrl + "core/vendor/filemanager/plugin.min.js"
//}, },
// Contenu du bouton insérer // Thème mobile
insert_button_items: "anchor hr table", // mobile: {
// Contenu du bouton formats // theme: "mobile",
style_formats: [ // plugins: [ 'autosave', 'lists', 'autolink' ],
{title: "Headers", items: [ // toolbar: [ 'undo', 'bold', 'italic', 'styleselect' ]
{title: "Header 1", format: "h1"}, //},
{title: "Header 2", format: "h2"}, // Contenu du bouton insérer
{title: "Header 3", format: "h3"}, insert_button_items: "anchor hr table",
{title: "Header 4", format: "h4"} // Contenu du bouton formats
]}, style_formats: [
{title: "Inline", items: [ {title: "Headers", items: [
{title: "Bold", icon: "bold", format: "bold"}, {title: "Header 1", format: "h1"},
{title: "Italic", icon: "italic", format: "italic"}, {title: "Header 2", format: "h2"},
{title: "Underline", icon: "underline", format: "underline"}, {title: "Header 3", format: "h3"},
{title: "Strikethrough", icon: "strikethrough", format: "strikethrough"}, {title: "Header 4", format: "h4"}
{title: "Superscript", icon: "superscript", format: "superscript"}, ]},
{title: "Subscript", icon: "subscript", format: "subscript"}, {title: "Inline", items: [
{title: "Code", icon: "code", format: "code"} {title: "Bold", icon: "bold", format: "bold"},
]}, {title: "Italic", icon: "italic", format: "italic"},
{title: "Blocks", items: [ {title: "Underline", icon: "underline", format: "underline"},
{title: "Paragraph", format: "p"}, {title: "Strikethrough", icon: "strikethrough", format: "strikethrough"},
{title: "Blockquote", format: "blockquote"}, {title: "Superscript", icon: "superscript", format: "superscript"},
{title: "Div", format: "div"}, {title: "Subscript", icon: "subscript", format: "subscript"},
{title: "Pre", format: "pre"} {title: "Code", icon: "code", format: "code"}
]}, ]},
{title: "Alignment", items: [ {title: "Blocks", items: [
{title: "Left", icon: "alignleft", format: "alignleft"}, {title: "Paragraph", format: "p"},
{title: "Center", icon: "aligncenter", format: "aligncenter"}, {title: "Blockquote", format: "blockquote"},
{title: "Right", icon: "alignright", format: "alignright"}, {title: "Div", format: "div"},
{title: "Justify", icon: "alignjustify", format: "alignjustify"} {title: "Pre", format: "pre"}
]} ]},
], {title: "Alignment", items: [
// Templates {title: "Left", icon: "alignleft", format: "alignleft"},
templates: [ {title: "Center", icon: "aligncenter", format: "aligncenter"},
{ {title: "Right", icon: "alignright", format: "alignright"},
title: "Bloc de texte", {title: "Justify", icon: "alignjustify", format: "alignjustify"}
url: baseUrl + "core/vendor/tinymce/templates/block.html", ]}
description: "Bloc de texte avec un titre." ],
}, // Templates
{ templates: [
title: "Effet accordéon", {
url: baseUrl + "core/vendor/tinymce/templates/accordion.html", title: "Bloc de texte",
description: "Bloc de texte avec effet accordéon." url: baseUrl + "core/vendor/tinymce/templates/block.html",
}, description: "Bloc de texte avec un titre."
{ },
title: "Grille symétrique : 6 - 6", {
url: baseUrl + "core/vendor/tinymce/templates/col6.html", title: "Effet accordéon",
description: "Grille adaptative sur 12 colonnes, sur mobile elles passent les unes en dessous des autres." url: baseUrl + "core/vendor/tinymce/templates/accordion.html",
}, description: "Bloc de texte avec effet accordéon."
{ },
title: "Grille symétrique : 4 - 4 - 4", {
url: baseUrl + "core/vendor/tinymce/templates/col4.html", title: "Grille symétrique : 6 - 6",
description: "Grille adaptative sur 12 colonnes, sur mobile elles passent les unes en dessous des autres." url: baseUrl + "core/vendor/tinymce/templates/col6.html",
}, description: "Grille adaptative sur 12 colonnes, sur mobile elles passent les unes en dessous des autres."
{ },
title: "Grille symétrique : 3 - 3 - 3 - 3", {
url: baseUrl + "core/vendor/tinymce/templates/col3.html", title: "Grille symétrique : 4 - 4 - 4",
description: "Grille adaptative sur 12 colonnes, sur mobile elles passent les unes en dessous des autres." url: baseUrl + "core/vendor/tinymce/templates/col4.html",
}, description: "Grille adaptative sur 12 colonnes, sur mobile elles passent les unes en dessous des autres."
{ },
title: "Grille asymétrique : 4 - 8", {
url: baseUrl + "core/vendor/tinymce/templates/col4-8.html", title: "Grille symétrique : 3 - 3 - 3 - 3",
description: "Grille adaptative sur 12 colonnes, sur mobile elles passent les unes en dessous des autres." url: baseUrl + "core/vendor/tinymce/templates/col3.html",
}, description: "Grille adaptative sur 12 colonnes, sur mobile elles passent les unes en dessous des autres."
{ },
title: "Grille asymétrique : 8 - 4", {
url: baseUrl + "core/vendor/tinymce/templates/col8-4.html", title: "Grille asymétrique : 4 - 8",
description: "Grille adaptative sur 12 colonnes, sur mobile elles passent les unes en dessous des autres." url: baseUrl + "core/vendor/tinymce/templates/col4-8.html",
}, description: "Grille adaptative sur 12 colonnes, sur mobile elles passent les unes en dessous des autres."
{ },
title: "Grille asymétrique : 2 - 10", {
url: baseUrl + "core/vendor/tinymce/templates/col2-10.html", title: "Grille asymétrique : 8 - 4",
description: "Grille adaptative sur 12 colonnes, sur mobile elles passent les unes en dessous des autres." url: baseUrl + "core/vendor/tinymce/templates/col8-4.html",
}, description: "Grille adaptative sur 12 colonnes, sur mobile elles passent les unes en dessous des autres."
{ },
title: "Grille asymétrique : 10 - 2", {
url: baseUrl + "core/vendor/tinymce/templates/col10-2.html", title: "Grille asymétrique : 2 - 10",
description: "Grille adaptative sur 12 colonnes, sur mobile elles passent les unes en dessous des autres." url: baseUrl + "core/vendor/tinymce/templates/col2-10.html",
} description: "Grille adaptative sur 12 colonnes, sur mobile elles passent les unes en dessous des autres."
] },
}); {
title: "Grille asymétrique : 10 - 2",
tinymce.PluginManager.add('stickytoolbar', function(editor, url) { url: baseUrl + "core/vendor/tinymce/templates/col10-2.html",
editor.on('init', function() { description: "Grille adaptative sur 12 colonnes, sur mobile elles passent les unes en dessous des autres."
setSticky(); }
}); ]
});
$(window).on('scroll', function() {
setSticky(); tinymce.PluginManager.add('stickytoolbar', function(editor, url) {
}); editor.on('init', function() {
setSticky();
function setSticky() { });
var container = editor.editorContainer;
var toolbars = $(container).find('.mce-toolbar-grp'); $(window).on('scroll', function() {
var statusbar = $(container).find('.mce-statusbar'); setSticky();
var menubar = $(container).find('.mce-menubar'); });
if (isSticky()) { function setSticky() {
$(container).css({ var container = editor.editorContainer;
paddingTop: menubar.outerHeight() var toolbars = $(container).find('.mce-toolbar-grp');
}); var statusbar = $(container).find('.mce-statusbar');
var menubar = $(container).find('.mce-menubar');
if (isAtBottom()) {
toolbars.css({ if (isSticky()) {
top: 'auto', $(container).css({
bottom: statusbar.outerHeight(), paddingTop: menubar.outerHeight()
position: 'absolute', });
width: '100%',
borderBottom: 'none' if (isAtBottom()) {
}); toolbars.css({
} else { top: 'auto',
menubar.css({ bottom: statusbar.outerHeight(),
top: 45, position: 'absolute',
bottom: 'auto', width: '100%',
position: 'fixed', borderBottom: 'none'
width: $(container).width(), });
borderBottom: '1px solid rgba(0,0,0,0.2)', } else {
background: '#fff' menubar.css({
}); top: 45,
toolbars.css({ bottom: 'auto',
top: 78, position: 'fixed',
bottom: 'auto', width: $(container).width(),
position: 'fixed', borderBottom: '1px solid rgba(0,0,0,0.2)',
width: $(container).width(), background: '#fff'
borderBottom: '1px solid rgba(0,0,0,0.2)' });
}); toolbars.css({
} top: 78,
} else { bottom: 'auto',
$(container).css({ position: 'fixed',
paddingTop: 0 width: $(container).width(),
}); borderBottom: '1px solid rgba(0,0,0,0.2)'
});
toolbars.css({ }
top:0, } else {
position: 'relative', $(container).css({
width: 'auto', paddingTop: 0
borderBottom: 'none' });
});
menubar.css({ toolbars.css({
top:0, top:0,
position: 'relative', position: 'relative',
width: 'auto', width: 'auto',
borderBottom: 'none' borderBottom: 'none'
}); });
} menubar.css({
} top:0,
position: 'relative',
function isSticky() { width: 'auto',
var container = editor.editorContainer, borderBottom: 'none'
editorTop = container.getBoundingClientRect().top; });
}
if (editorTop < 0) { }
return true;
} function isSticky() {
var container = editor.editorContainer,
return false; editorTop = container.getBoundingClientRect().top;
}
if (editorTop < 0) {
function isAtBottom() { return true;
var container = editor.editorContainer, }
editorTop = container.getBoundingClientRect().top;
return false;
var toolbarHeight = $(container).find('.mce-toolbar-grp').outerHeight(); }
var footerHeight = $(container).find('.mce-statusbar').outerHeight();
function isAtBottom() {
var hiddenHeight = -($(container).outerHeight() - toolbarHeight - footerHeight); var container = editor.editorContainer,
editorTop = container.getBoundingClientRect().top;
if (editorTop < hiddenHeight) {
return true; var toolbarHeight = $(container).find('.mce-toolbar-grp').outerHeight();
} var footerHeight = $(container).find('.mce-statusbar').outerHeight();
return false; var hiddenHeight = -($(container).outerHeight() - toolbarHeight - footerHeight);
}
}); if (editorTop < hiddenHeight) {
return true;
}
return false;
}
});

View File

@ -52,7 +52,7 @@ class blog extends common {
public static $users = []; public static $users = [];
const BLOG_VERSION = '2.11'; const BLOG_VERSION = '2.13';
/** /**
* Flux RSS * Flux RSS
@ -438,9 +438,9 @@ class blog extends common {
if ($this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'mailNotification']) === true) { if ($this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'mailNotification']) === true) {
$sent = $this->sendMail( $sent = $this->sendMail(
$to, $to,
'Nouveau commentaire', 'Nouveau commentaire déposé',
'Bonjour' . ' <strong>' . $user['firstname'] . ' ' . $user['lastname'] . '</strong>,<br><br>' . 'Bonjour' . ' <strong>' . $user['firstname'] . ' ' . $user['lastname'] . '</strong>,<br><br>' .
'Nouveau commentaire déposé sur la page "' . $this->getData(['page', $this->getUrl(0), 'posts', 'title']) . '" :<br><br>', 'L\'article <a href="' . helper::baseUrl() . $this->getUrl(0) . '/ ' . $this->getUrl(1) . '">' . $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'title']) . '</a> a reçu un nouveau commentaire.<br><br>',
'' ''
); );
// Valeurs en sortie // Valeurs en sortie
@ -448,7 +448,7 @@ class blog extends common {
'redirect' => helper::baseUrl() . $this->getUrl() . '#comment', 'redirect' => helper::baseUrl() . $this->getUrl() . '#comment',
//'notification' => 'Commentaire ajouté', //'notification' => 'Commentaire ajouté',
//'state' => true //'state' => true
'notification' => ($sent === true ? 'Commentaire ajouté et une notification envoyée' : 'Commentaire ajouté, erreur de notification : <br/>' . $sent), 'notification' => ($sent === true ? 'Commentaire ajouté, les administrateurs ont été notifiés.' : 'Commentaire ajouté. <br/>' . $sent),
'state' => ($sent === true ? true : null) 'state' => ($sent === true ? true : null)
]); ]);

View File

@ -3,6 +3,10 @@
Order deny,allow Order deny,allow
Deny from all Deny from all
</Files> </Files>
<Files *.json.back>
Order deny,allow
Deny from all
</Files>
# Bloque l'accès htaccess # Bloque l'accès htaccess
<Files .htaccess> <Files .htaccess>
Order deny,allow Order deny,allow