Compare commits

...

42 Commits

Author SHA1 Message Date
547dea7350 1.20.02 save dataversion 2025-01-30 13:07:37 +01:00
9d6e229e0a 1.20.02
Installe la variable de masquage des pages dans les thèmes où elle n'a pas été définie.
Corrige le décalage du contenu de la page lorsque les pages sont masquées du menu.
Mise à jour du jeu de données 1.20.02
2025-01-30 13:03:40 +01:00
22d69ccafb 1.20.01 bug template table 2025-01-29 20:39:49 +01:00
5e59984211 1.20.01 bug de theme à installation 2025-01-29 20:17:31 +01:00
0301e1f31d 1.20.00 commente 2025-01-29 16:51:02 +01:00
1f0d490688 Bug page Editor 2025-01-28 18:44:40 +01:00
288393778a Bug édition de page 2025-01-28 18:22:28 +01:00
7773bc916f Revert "Revert Optimisation Page Edit"
This reverts commit 5ebbcfcb9d025e53029ed9f252ac5bac686506c4.
2025-01-28 18:10:29 +01:00
5ebbcfcb9d Revert Optimisation Page Edit 2025-01-28 17:29:43 +01:00
f237d0bca1 1.20.00 2025-01-28 12:33:02 +01:00
70fa6efee7 Message d'erreur d'envoi de l'email de réinitialisaiton 2025-01-27 18:52:41 +01:00
30ed4349d9 Formatage du message d'erreur d'erreur de démarrage 2025-01-27 18:43:55 +01:00
ad998c17d1 Liste des users, corrige la colonne date de dernière connexion. 2025-01-26 20:50:19 +01:00
4d6b6f8d4f Bug template table, id des colonnes mal placés 2025-01-26 20:49:37 +01:00
dd3582b9db Supprime des commentaires 2025-01-25 22:25:12 +01:00
0e26e71a50 1.19.05 Script page edit optimisé 2025-01-25 22:22:45 +01:00
9a56fc9572 Revert "1.19.05 Page edit script optimisé"
This reverts commit ac8689b40aeadb0b43a5c90824cd74fc05f2ad75.
2025-01-25 21:58:16 +01:00
ac8689b40a 1.19.05 Page edit script optimisé 2025-01-25 21:50:29 +01:00
0f9a79411d 1.9.05 Tri par date et bug dernière page vue 2025-01-25 19:03:49 +01:00
b944a3bac7 Renforce jsonDB 2025-01-25 14:21:31 +01:00
4bab81c541 1.19.04 Fix bug du formulaire édition de page 2025-01-24 19:40:41 +01:00
2f3dd5926b 1.19..04 Supprime jquery de datatable 2025-01-24 18:47:07 +01:00
959139b239 Miniature des images vectorielles 2025-01-24 18:01:36 +01:00
e958287b9e 1.19.04 largeur de page de l'espace lorsque les pages sont désactivées dans le menu 2025-01-24 17:40:31 +01:00
e110e7b4f9 Bug setup 2025-01-24 17:28:55 +01:00
61ec8c04be Bug descripteur php 8 2025-01-24 17:17:50 +01:00
f0ccf8eb2f 1.19.04 bordure autour du form update et install 2025-01-23 21:22:43 +01:00
b831275901 1.19.04 cadre autour du form update 2025-01-23 21:17:11 +01:00
5217763afb Supprime l'événement beforeunload 2025-01-23 21:06:51 +01:00
6d19f6ebad 1.19.03
- jsonDB upgrade
- Supprime le change de $siteContent dans la gestion d'un espace
2025-01-23 20:39:22 +01:00
ed2b2c2826 jsondb WIP 2025-01-23 16:57:27 +01:00
91e18a9408 1.19.02 hauteur minimale de la barre de menu vide 2025-01-23 13:22:04 +01:00
9ae150f3aa Users par Nom puis Prénom 2025-01-22 15:35:26 +01:00
88acbae810 Message d'erreur d'envoi du mail de réinitialisation du mot de passe 2025-01-22 14:56:10 +01:00
162bb9a78d Message erreur d'envoi du mail de réinitialisation 2025-01-22 14:52:48 +01:00
81a996c714 Form 4.6 2025-01-22 12:42:37 +01:00
2e9cfaa991 Form 4.6 2025-01-22 12:39:18 +01:00
f2df3743c6 Fix bug reset admin theme 2025-01-22 11:36:43 +01:00
83943c6b52 Méthode number 2025-01-21 19:45:31 +01:00
0c975d8f42 1.18.00 mise à jour jsondb depuis ZwiiCMS
Rotation des fichiers de journaux
2025-01-18 22:54:51 +01:00
1b91289320 1.18.00 check type object $db 2025-01-18 22:18:56 +01:00
d9c57d2090 1.18.00 attribut required 2025-01-18 22:11:26 +01:00
32 changed files with 952 additions and 940 deletions

View File

@ -1,4 +1,4 @@
# ZwiiCampus 1.17.05 # ZwiiCampus 1.20.02
ZwiiCampus (Learning Management System) est logiciel auteur destiné à mettre en ligne des tutoriels. Il dispose de plusieurs modalités d'ouverture et d'accès des contenus. Basé sur la version 13 du CMS Zwii, la structure logicielle est solide, le framework de Zwii est éprouvé. ZwiiCampus (Learning Management System) est logiciel auteur destiné à mettre en ligne des tutoriels. Il dispose de plusieurs modalités d'ouverture et d'accès des contenus. Basé sur la version 13 du CMS Zwii, la structure logicielle est solide, le framework de Zwii est éprouvé.

View File

@ -1,4 +1,5 @@
<?php <?php
/** /**
* Created by PhpStorm. * Created by PhpStorm.
* User: Andrey Mistulov * User: Andrey Mistulov
@ -18,6 +19,10 @@ class JsonDb extends \Prowebcraft\Dot
protected $db = ''; protected $db = '';
protected $data = null; protected $data = null;
protected $config = []; protected $config = [];
// Tentative d'écriture après échec
const MAX_FILE_WRITE_ATTEMPTS = 5;
// Délais entre deux tentaives
const RETRY_DELAY_SECONDS = 1;
public function __construct($config = []) public function __construct($config = [])
{ {
@ -108,72 +113,97 @@ class JsonDb extends \Prowebcraft\Dot
/** /**
* Local database upload * Charge les données depuis un fichier JSON.
* @param bool $reload Reboot data? *
* @return array|mixed|null * @param bool $reload Force le rechargement des données si true
*
* @return array|null Les données chargées ou null si le fichier n'existe pas
*
* @throws \RuntimeException En cas d'erreur lors de la création de la sauvegarde
* @throws \InvalidArgumentException Si le fichier contient des données JSON invalides
*/ */
protected function loadData($reload = false) protected function loadData($reload = false): ?array
{ {
if ($this->data === null || $reload) { if ($this->data === null || $reload) {
$this->db = $this->config['dir'] . $this->config['name']; $this->db = $this->config['dir'] . $this->config['name'];
if (!file_exists($this->db)) { if (!file_exists($this->db)) {
return null; // Rebuild database manage by CMS return null; // Rebuild database managed by CMS
} else { }
if ($this->config['backup']) {
try { if ($this->config['backup']) {
copy($this->config['dir'] . DIRECTORY_SEPARATOR . $this->config['name'], $this->config['dir'] . DIRECTORY_SEPARATOR . $this->config['name'] . '.backup'); $backup_path = $this->config['dir'] . DIRECTORY_SEPARATOR . $this->config['name'] . '.backup';
} catch (\Exception $e) {
error_log('Erreur de chargement : ' . $e); try {
exit('Erreur de chargement : ' . $e); if (!copy($this->db, $backup_path)) {
throw new \RuntimeException('Échec de la création de la sauvegarde');
} }
} catch (\Exception $e) {
throw new \RuntimeException('Erreur de sauvegarde : ' . $e->getMessage());
} }
} }
$this->data = json_decode(file_get_contents($this->db), true);
if (!$this->data === null && json_last_error() !== JSON_ERROR_NONE) { $file_contents = file_get_contents($this->db);
throw new \InvalidArgumentException('Le fichier ' . $this->db
. ' contient des données invalides.'); $this->data = json_decode($file_contents, true);
if ($this->data === null) {
throw new \InvalidArgumentException('Le fichier ' . $this->db . ' contient des données invalides.');
} }
} }
return $this->data; return $this->data;
} }
/** /**
* Save database * Charge les données depuis un fichier JSON.
*
* @param bool $reload Force le rechargement des données si true
*
* @return array|null Les données chargées ou null si le fichier n'existe pas
*
* @throws \RuntimeException En cas d'erreur lors de la création de la sauvegarde
* @throws \InvalidArgumentException Si le fichier contient des données JSON invalides
*/ */
public function save() public function save(): void
{ {
// Encode les données au format JSON avec les options spécifiées if ($this->data === null) {
$encoded_data = json_encode($this->data, JSON_UNESCAPED_UNICODE | JSON_FORCE_OBJECT); throw new \RuntimeException('Tentative de sauvegarde de données nulles');
}
try {
$encoded_data = json_encode($this->data, JSON_UNESCAPED_UNICODE | JSON_FORCE_OBJECT | JSON_THROW_ON_ERROR);
} catch (\JsonException $e) {
throw new \RuntimeException('Erreur d\'encodage JSON : ' . $e->getMessage());
}
// Vérifie la longueur de la chaîne JSON encodée
$encoded_length = strlen($encoded_data); $encoded_length = strlen($encoded_data);
$max_attempts = 5;
// Initialise le compteur de tentatives for ($attempt = 0; $attempt < $max_attempts; $attempt++) {
$attempt = 0; $temp_file = $this->db . '.tmp' . uniqid();
// Tente d'encoder les données en JSON et de les sauvegarder jusqu'à 5 fois en cas d'échec try {
while ($attempt < 5) { $write_result = file_put_contents($temp_file, $encoded_data, LOCK_EX);
// Essaye d'écrire les données encodées dans le fichier de base de données
$write_result = file_put_contents($this->db, $encoded_data, LOCK_EX); // Les utilisateurs multiples obtiennent un verrou
// $now = \DateTime::createFromFormat('U.u', microtime(true)); if ($write_result === $encoded_length) {
// file_put_contents("tmplog.txt", '[JsonDb][' . $now->format('H:i:s.u') . ']--' . $this->db . "\r\n", FILE_APPEND); if (rename($temp_file, $this->db)) {
return;
// Vérifie si l'écriture a réussi }
if ($write_result === $encoded_length) { }
// Sort de la boucle si l'écriture a réussi
break; error_log("Échec sauvegarde : longueur incorrecte ou renommage échoué (tentative " . ($attempt + 1) . ")");
} catch (\Exception $e) {
error_log('Erreur de sauvegarde : ' . $e->getMessage());
if (file_exists($temp_file)) {
unlink($temp_file);
}
} }
// Incrémente le compteur de tentatives
$attempt++;
}
// Vérifie si l'écriture a échoué même après plusieurs tentatives
if ($write_result !== $encoded_length) {
// Enregistre un message d'erreur dans le journal des erreurs
error_log('Erreur d\'écriture, les données n\'ont pas été sauvegardées.');
// Affiche un message d'erreur et termine le script
exit('Erreur d\'écriture, les données n\'ont pas été sauvegardées.');
}
}
} usleep(pow(2, $attempt) * 250000);
}
throw new \RuntimeException('Échec de sauvegarde après ' . $max_attempts . ' tentatives');
}
}

View File

@ -613,8 +613,7 @@ class layout extends common
} }
// Retourne les items du menu // Retourne les items du menu
echo '<ul class="navMain" id="menuLeft">' . $itemsLeft . '</ul><ul class="navMain" id="menuRight">' . $itemsRight; echo '<ul class="navMain" id="menuLeft">' . $itemsLeft . '</ul><ul class="navMain" id="menuRight">' . $itemsRight . '</ul>';
echo '</ul>';
} }
/** /**

View File

@ -140,7 +140,7 @@ class core extends common
$css .= 'span.mce-text{background-color: unset !important;}'; $css .= 'span.mce-text{background-color: unset !important;}';
$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],input[type=date],input[type=time],input[type=week],input[type=month],input[type=datetime-local],.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],input[type=date],input[type=time],input[type=week],input[type=month],input[type=datetime-local],input[type=number],.inputFile,select,textarea{color:' . $this->getData(['theme', 'text', 'textColor']) . ';background-color:' . $this->getData(['theme', 'site', 'backgroundColor']) . ';}';
// spécifiques au module de blog // spécifiques au module de blog
$css .= '.blogDate {color:' . $this->getData(['theme', 'text', 'textColor']) . ';}.blogPicture img{border:1px solid ' . $this->getData(['theme', 'text', 'textColor']) . '; box-shadow: 1px 1px 5px ' . $this->getData(['theme', 'text', 'textColor']) . ';}'; $css .= '.blogDate {color:' . $this->getData(['theme', 'text', 'textColor']) . ';}.blogPicture img{border:1px solid ' . $this->getData(['theme', 'text', 'textColor']) . '; box-shadow: 1px 1px 5px ' . $this->getData(['theme', 'text', 'textColor']) . ';}';
// Couleur fixée dans admin.css // Couleur fixée dans admin.css
@ -168,7 +168,7 @@ class core extends common
$colors = helper::colorVariants($this->getData(['theme', 'button', 'backgroundColor'])); $colors = helper::colorVariants($this->getData(['theme', 'button', 'backgroundColor']));
$css .= '.speechBubble,.button,.button:hover,button[type=submit],.pagination a,.pagination a:hover,input[type=checkbox]:checked + label:before,input[type=radio]:checked + label:before,.helpContent{background-color:' . $colors['normal'] . ';color:' . $colors['text'] . '}'; $css .= '.speechBubble,.button,.button:hover,button[type=submit],.pagination a,.pagination a:hover,input[type=checkbox]:checked + label:before,input[type=radio]:checked + label:before,.helpContent{background-color:' . $colors['normal'] . ';color:' . $colors['text'] . '}';
$css .= '.helpButton span{color:' . $colors['normal'] . '}'; $css .= '.helpButton span{color:' . $colors['normal'] . '}';
$css .= 'input[type=text]:hover,input[type=date]:hover,input[type=time]:hover,input[type=week]:hover,input[type=month]:hover,input[type=datetime-local]:hover,input[type=password]:hover,.inputFile:hover,select:hover,textarea:hover{border-color:' . $colors['normal'] . '}'; $css .= 'input[type=text]:hover,input[type=date]:hover,input[type=time]:hover,input[type=week]:hover,input[type=month]:hover,input[type=datetime-local]:hover,input[type=number]:hover,input[type=password]:hover,.inputFile:hover,select:hover,textarea:hover{border-color:' . $colors['normal'] . '}';
$css .= '.speechBubble:before{border-color:' . $colors['normal'] . ' transparent transparent transparent}'; $css .= '.speechBubble:before{border-color:' . $colors['normal'] . ' transparent transparent transparent}';
$css .= '.button:hover,button[type=submit]:hover,.pagination a:hover,input[type=checkbox]:not(:active):checked:hover + label:before,input[type=checkbox]:active + label:before,input[type=radio]:checked:hover + label:before,input[type=radio]:not(:checked):active + label:before{background-color:' . $colors['darken'] . '}'; $css .= '.button:hover,button[type=submit]:hover,.pagination a:hover,input[type=checkbox]:not(:active):checked:hover + label:before,input[type=checkbox]:active + label:before,input[type=radio]:checked:hover + label:before,input[type=radio]:not(:checked):active + label:before{background-color:' . $colors['darken'] . '}';
$css .= '.helpButton span:hover{color:' . $colors['darken'] . '}'; $css .= '.helpButton span:hover{color:' . $colors['darken'] . '}';
@ -254,18 +254,18 @@ class core extends common
// Déterminer la hauteur max du menu pour éviter les débordements // Déterminer la hauteur max du menu pour éviter les débordements
$padding = $this->getData(['theme', 'menu', 'height']); // Par exemple, "10px 20px" // $padding = $this->getData(['theme', 'menu', 'height']); // Par exemple, "10px 20px"
$fontSize = (float) $this->getData(['theme', 'text', 'fontSize']); // Taille de référence en pixels // $fontSize = (float) $this->getData(['theme', 'text', 'fontSize']); // Taille de référence en pixels
$menuFontSize = (float) $this->getData(['theme', 'menu', 'fontSize']); // Taille du menu en em // $menuFontSize = (float) $this->getData(['theme', 'menu', 'fontSize']); // Taille du menu en em
// Extraire la première valeur du padding (par exemple "10px 20px" -> "10px") // Extraire la première valeur du padding (par exemple "10px 20px" -> "10px")
$firstPadding = (float) explode(" ", $padding)[0]; // Nous prenons la première valeur, supposée être en px // $firstPadding = (float) explode(" ", $padding)[0]; // Nous prenons la première valeur, supposée être en px
// Convertir menuFontSize (en em) en pixels // Convertir menuFontSize (en em) en pixels
$menuFontSizeInPx = $menuFontSize * $fontSize; // $menuFontSizeInPx = $menuFontSize * $fontSize;
// Calculer la hauteur totale // Calculer la hauteur totale
$totalHeight = $firstPadding + $fontSize + $menuFontSizeInPx; // $totalHeight = $firstPadding + $fontSize + $menuFontSizeInPx;
// Fixer la hauteur maximale de la barre de menu // Fixer la hauteur maximale de la barre de menu
// $css .= '#menuLeft, nav, a.active {max-height:' . $totalHeight . 'px}'; // $css .= '#menuLeft, nav, a.active {max-height:' . $totalHeight . 'px}';
@ -381,7 +381,7 @@ class core extends common
$css .= '.button.buttonGreen, button[type=submit] {background-color: ' . $colors['normal'] . ';color: ' . $colors['text'] . ';}.button.buttonGreen:hover, button[type=submit]:hover {background-color: ' . $colors['darken'] . ';color: ' . $colors['text'] . ';}.button.buttonGreen:active, button[type=submit]:active {background-color: ' . $colors['darken'] . ';color: ' . $colors['text'] . ';}'; $css .= '.button.buttonGreen, button[type=submit] {background-color: ' . $colors['normal'] . ';color: ' . $colors['text'] . ';}.button.buttonGreen:hover, button[type=submit]:hover {background-color: ' . $colors['darken'] . ';color: ' . $colors['text'] . ';}.button.buttonGreen:active, button[type=submit]:active {background-color: ' . $colors['darken'] . ';color: ' . $colors['text'] . ';}';
$colors = helper::colorVariants($this->getData(['admin', 'backgroundBlockColor'])); $colors = helper::colorVariants($this->getData(['admin', 'backgroundBlockColor']));
$css .= '.buttonTab, .block {border: 1px solid ' . $this->getData(['admin', 'borderBlockColor']) . ';}.buttonTab, .block h4 {background-color: ' . $colors['normal'] . ';color:' . $colors['text'] . ';}'; $css .= '.buttonTab, .block {border: 1px solid ' . $this->getData(['admin', 'borderBlockColor']) . ';}.buttonTab, .block h4 {background-color: ' . $colors['normal'] . ';color:' . $colors['text'] . ';}';
$css .= 'table tr,input[type=email],input[type=date],input[type=time],input[type=month],input[type=week],input[type=datetime-local],input[type=text],input[type=password],select:not(#barSelectCourse),select:not(#menuSelectCourse),select:not(#barSelectPage),textarea:not(.editorWysiwyg), textarea:not(.editorWysiwygComment),.inputFile{background-color: ' . $colors['normal'] . ';color:' . $colors['text'] . ';border: 1px solid ' . $this->getData(['admin', 'borderBlockColor']) . ';}'; $css .= 'table tr,input[type=email],input[type=date],input[type=time],input[type=month],input[type=week],input[type=datetime-local],input[type=text],input[type=number],input[type=password],select:not(#barSelectLanguage),select:not(#barSelectPage),textarea:not(.editorWysiwyg), textarea:not(.editorWysiwygComment),.inputFile{background-color: ' . $colors['normal'] . ';color:' . $colors['text'] . ';border: 1px solid ' . $this->getData(['admin', 'borderBlockColor']) . ';}';
// Bordure du contour TinyMCE // Bordure du contour TinyMCE
$css .= '.mce-tinymce{border: 1px solid ' . $this->getData(['admin', 'borderBlockColor']) . '!important;}'; $css .= '.mce-tinymce{border: 1px solid ' . $this->getData(['admin', 'borderBlockColor']) . '!important;}';
// Enregistre la personnalisation // Enregistre la personnalisation

View File

@ -245,6 +245,7 @@ class template
'readonly' => false, 'readonly' => false,
'value' => '', 'value' => '',
'type' => 'date', 'type' => 'date',
'required' => false,
], $attributes); ], $attributes);
// Traduction de l'aide et de l'étiquette // Traduction de l'aide et de l'étiquette
$attributes['label'] = helper::translate($attributes['label']); $attributes['label'] = helper::translate($attributes['label']);
@ -271,12 +272,21 @@ class template
} else { } else {
$attributes['value'] = ($attributes['value'] ? helper::filter($attributes['value'], $filter) : ''); $attributes['value'] = ($attributes['value'] ? helper::filter($attributes['value'], $filter) : '');
} }
// Gestion du champ obligatoire
if (isset($attributes['required']) && $attributes['required']) {
// Affiche l'astérisque dans le label
$required = ' required-field';
// Ajoute l'attribut required au champ input
$attributes['required'] = 'required';
}
// Début du wrapper // Début du wrapper
$html = '<div id="' . $attributes['id'] . 'Wrapper" class="inputWrapper ' . $attributes['classWrapper'] . '">'; $html = '<div id="' . $attributes['id'] . 'Wrapper" class="inputWrapper ' . $attributes['classWrapper'] . '">';
// Label // Label
if ($attributes['label']) { if ($attributes['label']) {
$html .= self::label($attributes['id'], $attributes['label'], [ $html .= self::label($attributes['id'], $attributes['label'], [
'help' => $attributes['help'] 'help' => $attributes['help'],
// Ajoute la classe required-field si le champ est obligatoire
'class' => isset($required) ? $required : ''
]); ]);
} }
// Notice // Notice
@ -326,7 +336,8 @@ class template
'type' => 2, 'type' => 2,
'value' => '', 'value' => '',
'folder' => '', 'folder' => '',
'language' => 'fr_FR' 'language' => 'fr_FR',
'required' => false,
], $attributes); ], $attributes);
// Traduction de l'aide et de l'étiquette // Traduction de l'aide et de l'étiquette
$attributes['value'] = helper::translate($attributes['value']); $attributes['value'] = helper::translate($attributes['value']);
@ -335,6 +346,13 @@ class template
if ($attributes['before'] and array_key_exists($attributes['id'], common::$inputBefore)) { if ($attributes['before'] and array_key_exists($attributes['id'], common::$inputBefore)) {
$attributes['value'] = common::$inputBefore[$attributes['id']]; $attributes['value'] = common::$inputBefore[$attributes['id']];
} }
// Gestion du champ obligatoire
if (isset($attributes['required']) && $attributes['required']) {
// Affiche l'astérisque dans le label
$required = ' required-field';
// Ajoute l'attribut required au champ input
$attributes['required'] = 'required';
}
// Début du wrapper // Début du wrapper
$html = '<div id="' . $attributes['id'] . 'Wrapper" class="inputWrapper ' . $attributes['classWrapper'] . '">'; $html = '<div id="' . $attributes['id'] . 'Wrapper" class="inputWrapper ' . $attributes['classWrapper'] . '">';
// Notice // Notice
@ -347,7 +365,9 @@ class template
// Label // Label
if ($attributes['label']) { if ($attributes['label']) {
$html .= self::label($attributes['id'], $attributes['label'], [ $html .= self::label($attributes['id'], $attributes['label'], [
'help' => $attributes['help'] 'help' => $attributes['help'],
// Ajoute la classe required-field si le champ est obligatoire
'class' => isset($required) ? $required : ''
]); ]);
} }
// Champ caché contenant l'url de la page // Champ caché contenant l'url de la page
@ -362,16 +382,16 @@ class template
$html .= sprintf( $html .= sprintf(
'<a '<a
href="' . href="' .
helper::baseUrl(false) . 'core/vendor/filemanager/dialog.php' . helper::baseUrl(false) . 'core/vendor/filemanager/dialog.php' .
'?relative_url=1' . '?relative_url=1' .
'&lang=' . $attributes['language'] . '&lang=' . $attributes['language'] .
'&field_id=' . $attributes['id'] . '&field_id=' . $attributes['id'] .
'&type=' . $attributes['type'] . '&type=' . $attributes['type'] .
'&akey=' . md5_file(core::DATA_DIR . 'core.json') . '&akey=' . md5_file(core::DATA_DIR . 'core.json') .
// Ajoute le nom du dossier si la variable est passée // Ajoute le nom du dossier si la variable est passée
(empty($attributes['folder']) ? '&fldr=/': '&fldr=' . $attributes['folder']) . (empty($attributes['folder']) ? '&fldr=/' : '&fldr=' . $attributes['folder']) .
($attributes['extensions'] ? '&extensions=' . $attributes['extensions'] : '') . ($attributes['extensions'] ? '&extensions=' . $attributes['extensions'] : '')
'" . '"
class="inputFile %s %s" class="inputFile %s %s"
%s %s
data-lity data-lity
@ -583,7 +603,8 @@ class template
'name' => $nameId, 'name' => $nameId,
'placeholder' => '', 'placeholder' => '',
'readonly' => false, 'readonly' => false,
'value' => '' 'value' => '',
'required' => false,
], $attributes); ], $attributes);
// Traduction de l'aide et de l'étiquette // Traduction de l'aide et de l'étiquette
$attributes['label'] = helper::translate($attributes['label']); $attributes['label'] = helper::translate($attributes['label']);
@ -593,12 +614,21 @@ class template
if ($attributes['before'] and array_key_exists($attributes['id'], common::$inputBefore)) { if ($attributes['before'] and array_key_exists($attributes['id'], common::$inputBefore)) {
$attributes['value'] = common::$inputBefore[$attributes['id']]; $attributes['value'] = common::$inputBefore[$attributes['id']];
} }
// Gestion du champ obligatoire
if (isset($attributes['required']) && $attributes['required']) {
// Affiche l'astérisque dans le label
$required = ' required-field';
// Ajoute l'attribut required au champ input
$attributes['required'] = 'required';
}
// Début du wrapper // Début du wrapper
$html = '<div id="' . $attributes['id'] . 'Wrapper" class="inputWrapper ' . $attributes['classWrapper'] . '">'; $html = '<div id="' . $attributes['id'] . 'Wrapper" class="inputWrapper ' . $attributes['classWrapper'] . '">';
// Label // Label
if ($attributes['label']) { if ($attributes['label']) {
$html .= self::label($attributes['id'], $attributes['label'], [ $html .= self::label($attributes['id'], $attributes['label'], [
'help' => $attributes['help'] 'help' => $attributes['help'],
// Ajoute la classe required-field si le champ est obligatoire
'class' => isset($required) ? $required : ''
]); ]);
} }
// Notice // Notice
@ -651,18 +681,28 @@ class template
//'maxlength' => '500', //'maxlength' => '500',
'name' => $nameId, 'name' => $nameId,
'placeholder' => '', 'placeholder' => '',
'readonly' => false 'readonly' => false,
'required' => false,
], $attributes); ], $attributes);
// Traduction de l'aide et de l'étiquette // Traduction de l'aide et de l'étiquette
$attributes['label'] = helper::translate($attributes['label']); $attributes['label'] = helper::translate($attributes['label']);
//$attributes['placeholder'] = helper::translate($attributes['placeholder']); //$attributes['placeholder'] = helper::translate($attributes['placeholder']);
$attributes['help'] = helper::translate($attributes['help']); $attributes['help'] = helper::translate($attributes['help']);
// Gestion du champ obligatoire
if (isset($attributes['required']) && $attributes['required']) {
// Affiche l'astérisque dans le label
$required = ' required-field';
// Ajoute l'attribut required au champ input
$attributes['required'] = 'required';
}
// Début du wrapper // Début du wrapper
$html = '<div id="' . $attributes['id'] . 'Wrapper" class="inputWrapper ' . $attributes['classWrapper'] . '">'; $html = '<div id="' . $attributes['id'] . 'Wrapper" class="inputWrapper ' . $attributes['classWrapper'] . '">';
// Label // Label
if ($attributes['label']) { if ($attributes['label']) {
$html .= self::label($attributes['id'], $attributes['label'], [ $html .= self::label($attributes['id'], $attributes['label'], [
'help' => $attributes['help'] 'help' => $attributes['help'],
// Ajoute la classe required-field si le champ est obligatoire
'class' => isset($required) ? $required : ''
]); ]);
} }
// Notice // Notice
@ -705,7 +745,8 @@ class template
'name' => $nameId, 'name' => $nameId,
'selected' => '', 'selected' => '',
'font' => [], 'font' => [],
'multiple' => '' 'multiple' => '',
'required' => false,
], $attributes); ], $attributes);
// Traduction de l'aide et de l'étiquette // Traduction de l'aide et de l'étiquette
$attributes['label'] = helper::translate($attributes['label']); $attributes['label'] = helper::translate($attributes['label']);
@ -719,12 +760,21 @@ class template
if ($attributes['before'] and array_key_exists($attributes['id'], common::$inputBefore)) { if ($attributes['before'] and array_key_exists($attributes['id'], common::$inputBefore)) {
$attributes['selected'] = common::$inputBefore[$attributes['id']]; $attributes['selected'] = common::$inputBefore[$attributes['id']];
} }
// Gestion du champ obligatoire
if (isset($attributes['required']) && $attributes['required']) {
// Affiche l'astérisque dans le label
$required = ' required-field';
// Ajoute l'attribut required au champ input
$attributes['required'] = 'required';
}
// Début du wrapper // Début du wrapper
$html = '<div id="' . $attributes['id'] . 'Wrapper" class="inputWrapper ' . $attributes['classWrapper'] . '">'; $html = '<div id="' . $attributes['id'] . 'Wrapper" class="inputWrapper ' . $attributes['classWrapper'] . '">';
// Label // Label
if ($attributes['label']) { if ($attributes['label']) {
$html .= self::label($attributes['id'], $attributes['label'], [ $html .= self::label($attributes['id'], $attributes['label'], [
'help' => $attributes['help'] 'help' => $attributes['help'],
// Ajoute la classe required-field si le champ est obligatoire
'class' => isset($required) ? $required : ''
]); ]);
} }
// Notice // Notice
@ -834,6 +884,10 @@ class template
$html = '<div id="' . $attributes['id'] . 'Wrapper" class="tableWrapper ' . $attributes['classWrapper'] . '">'; $html = '<div id="' . $attributes['id'] . 'Wrapper" class="tableWrapper ' . $attributes['classWrapper'] . '">';
// Début tableau // Début tableau
$html .= '<table id="' . $attributes['id'] . '" class="table ' . $attributes['class'] . '">'; $html .= '<table id="' . $attributes['id'] . '" class="table ' . $attributes['class'] . '">';
// Pas de tableau d'Id transmis, générer une numérotation
if (empty($rowsId)) {
$rowsId = range(0, count($cols));
}
// Entêtes // Entêtes
if ($head) { if ($head) {
// Début des entêtes // Début des entêtes
@ -841,21 +895,17 @@ class template
$html .= '<tr class="nodrag">'; $html .= '<tr class="nodrag">';
$i = 0; $i = 0;
foreach ($head as $th) { foreach ($head as $th) {
$html .= '<th class="col' . $cols[$i++] . '">' . $th . '</th>'; $html .= '<th id="' . $rowsId[$i] . '" class="col' . $cols[$i++] . '">' . $th . '</th>';
} }
// Fin des entêtes // Fin des entêtes
$html .= '</tr>'; $html .= '</tr>';
$html .= '</thead>'; $html .= '</thead>';
} }
// Pas de tableau d'Id transmis, générer une numérotation
if (empty($rowsId)) {
$rowsId = range(0, count($body));
}
// Début contenu // Début contenu
$j = 0; $j = 0;
foreach ($body as $tr) { foreach ($body as $tr) {
// Id de ligne pour les tableaux drag and drop // Id de ligne pour les tableaux drag and drop
$html .= '<tr id="' . $rowsId[$j++] . '">'; $html .= '<tr>';
$i = 0; $i = 0;
foreach ($tr as $td) { foreach ($tr as $td) {
$html .= '<td class="col' . $cols[$i++] . '">' . $td . '</td>'; $html .= '<td class="col' . $cols[$i++] . '">' . $td . '</td>';
@ -896,7 +946,8 @@ class template
'placeholder' => '', 'placeholder' => '',
'readonly' => false, 'readonly' => false,
'value' => '', 'value' => '',
'type' => 'text' 'type' => 'text',
'required' => false,
], $attributes); ], $attributes);
// Traduction de l'aide et de l'étiquette // Traduction de l'aide et de l'étiquette
$attributes['label'] = helper::translate($attributes['label']); $attributes['label'] = helper::translate($attributes['label']);
@ -906,12 +957,21 @@ class template
if ($attributes['before'] and array_key_exists($attributes['id'], common::$inputBefore)) { if ($attributes['before'] and array_key_exists($attributes['id'], common::$inputBefore)) {
$attributes['value'] = common::$inputBefore[$attributes['id']]; $attributes['value'] = common::$inputBefore[$attributes['id']];
} }
// Gestion du champ obligatoire
if (isset($attributes['required']) && $attributes['required']) {
// Affiche l'astérisque dans le label
$required = ' required-field';
// Ajoute l'attribut required au champ input
$attributes['required'] = 'required';
}
// Début du wrapper // Début du wrapper
$html = '<div id="' . $attributes['id'] . 'Wrapper" class="inputWrapper ' . $attributes['classWrapper'] . '">'; $html = '<div id="' . $attributes['id'] . 'Wrapper" class="inputWrapper ' . $attributes['classWrapper'] . '">';
// Label // Label
if ($attributes['label']) { if ($attributes['label']) {
$html .= self::label($attributes['id'], $attributes['label'], [ $html .= self::label($attributes['id'], $attributes['label'], [
'help' => $attributes['help'] 'help' => $attributes['help'],
// Ajoute la classe required-field si le champ est obligatoire
'class' => isset($required) ? $required : ''
]); ]);
} }
// Notice // Notice
@ -932,6 +992,117 @@ class template
return $html; return $html;
} }
/**
* Génère un champ de saisie de type number (input[type="number"])
*
* Cette méthode crée un champ numérique HTML complet avec son wrapper,
* son label et ses messages d'aide/erreur. Elle gère automatiquement
* la conversion des valeurs en nombres et les contraintes de validation.
*
* @param string $nameId Identifiant unique du champ, utilisé pour name et id
* @param array $attributes Tableau des attributs du champ avec les clés suivantes :
* @type boolean $before Active la récupération des données précédentes en cas d'erreur (défaut: true)
* @type string $class Classes CSS additionnelles pour l'input (défaut: '')
* @type string $classWrapper Classes CSS additionnelles pour le wrapper (défaut: '')
* @type boolean $noDirty Désactive le marquage dirty du champ (défaut: false)
* @type boolean $disabled Désactive le champ (défaut: false)
* @type string $help Texte d'aide affiché sous le label (défaut: '')
* @type string $label Texte du label (défaut: '')
* @type string $placeholder Texte de placeholder (défaut: '')
* @type boolean $readonly Rend le champ en lecture seule (défaut: false)
* @type mixed $value Valeur initiale du champ (défaut: '')
* @type number $min Valeur minimum autorisée (défaut: null)
* @type number $max Valeur maximum autorisée (défaut: null)
* @type number $step Pas d'incrémentation (ex: 1 pour entiers, 0.01 pour prix) (défaut: null)
* @type string $pattern Expression régulière de validation (défaut: null)
*
* @return string Code HTML du champ number complet
*/
public static function number($nameId, array $attributes = [])
{
// Attributs par défaut spécifiques aux champs numériques
$attributes = array_merge([
'type' => 'number',
'before' => true,
'class' => '',
'classWrapper' => '',
'noDirty' => false,
'disabled' => false,
'help' => '',
'id' => $nameId,
'label' => '',
'name' => $nameId,
'placeholder' => '',
'readonly' => false,
'required' => false,
'value' => '',
'min' => null,
'max' => null,
'step' => null,
'pattern' => null
], $attributes);
// Conversion de la valeur en nombre si elle n'est pas vide
if ($attributes['value'] !== '') {
$attributes['value'] = floatval($attributes['value']);
}
// Nettoyage des attributs null pour ne pas les afficher dans le HTML
foreach (['min', 'max', 'step', 'pattern'] as $attr) {
if ($attributes[$attr] === null) {
unset($attributes[$attr]);
}
}
// Traduction de l'aide et de l'étiquette
$attributes['label'] = helper::translate($attributes['label']);
$attributes['help'] = helper::translate($attributes['help']);
// Sauvegarde des données en cas d'erreur
if ($attributes['before'] && array_key_exists($attributes['id'], common::$inputBefore)) {
$attributes['value'] = common::$inputBefore[$attributes['id']];
}
// Gestion du champ obligatoire
if (isset($attributes['required']) && $attributes['required']) {
// Affiche l'astérisque dans le label
$required = ' required-field';
// Ajoute l'attribut required au champ input
$attributes['required'] = 'required';
}
// Début du wrapper
$html = '<div id="' . $attributes['id'] . 'Wrapper" class="inputWrapper ' . $attributes['classWrapper'] . '">';
// Label
if ($attributes['label']) {
$html .= self::label($attributes['id'], $attributes['label'], [
'help' => $attributes['help'],
// Ajoute la classe required-field si le champ est obligatoire
'class' => isset($required) ? $required : ''
]);
}
// Notice
$notice = '';
if (array_key_exists($attributes['id'], common::$inputNotices)) {
$notice = common::$inputNotices[$attributes['id']];
$attributes['class'] .= ' notice';
}
$html .= self::notice($attributes['id'], $notice);
// Input number
$html .= sprintf(
'<input type="number" %s>',
helper::sprintAttributes($attributes)
);
// Fin du wrapper
$html .= '</div>';
return $html;
}
/** /**
* Crée un champ texte long * Crée un champ texte long
* @param string $nameId Nom et id du champ * @param string $nameId Nom et id du champ
@ -953,7 +1124,8 @@ class template
//'maxlength' => '500', //'maxlength' => '500',
'name' => $nameId, 'name' => $nameId,
'readonly' => false, 'readonly' => false,
'value' => '' 'value' => '',
'required' => false,
], $attributes); ], $attributes);
// Traduction de l'aide et de l'étiquette // Traduction de l'aide et de l'étiquette
$attributes['label'] = helper::translate($attributes['label']); $attributes['label'] = helper::translate($attributes['label']);
@ -962,12 +1134,21 @@ class template
if ($attributes['before'] and array_key_exists($attributes['id'], common::$inputBefore)) { if ($attributes['before'] and array_key_exists($attributes['id'], common::$inputBefore)) {
$attributes['value'] = common::$inputBefore[$attributes['id']]; $attributes['value'] = common::$inputBefore[$attributes['id']];
} }
// Gestion du champ obligatoire
if (isset($attributes['required']) && $attributes['required']) {
// Affiche l'astérisque dans le label
$required = ' required-field';
// Ajoute l'attribut required au champ input
$attributes['required'] = 'required';
}
// Début du wrapper // Début du wrapper
$html = '<div id="' . $attributes['id'] . 'Wrapper" class="inputWrapper ' . $attributes['classWrapper'] . '">'; $html = '<div id="' . $attributes['id'] . 'Wrapper" class="inputWrapper ' . $attributes['classWrapper'] . '">';
// Label // Label
if ($attributes['label']) { if ($attributes['label']) {
$html .= self::label($attributes['id'], $attributes['label'], [ $html .= self::label($attributes['id'], $attributes['label'], [
'help' => $attributes['help'] 'help' => $attributes['help'],
// Ajoute la classe required-field si le champ est obligatoire
'class' => isset($required) ? $required : ''
]); ]);
} }
// Notice // Notice

View File

@ -116,30 +116,14 @@ core.confirm = function (text, yesCallback, noCallback) {
/** /**
* Scripts à exécuter en dernier * Scripts à exécuter en dernier
*/
core.end = function () { 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) {
message = "<?php echo helper::translate('Les modifications que vous avez apportées ne seront peut-être pas enregistrées.');?>";
return message;
}
});
formDOM.submit(function () {
$(window).off("beforeunload");
});
}; };
$(function () { $(function () {
core.end(); core.end();
}); });
*/
/** /**
* Ajoute une notice * Ajoute une notice
@ -390,12 +374,11 @@ core.start = function () {
var totalHeight = firstPadding + fontSize + menuFontSizeInPx; var totalHeight = firstPadding + fontSize + menuFontSizeInPx;
$("#menuLeft").css({ $("#menuLeft").css({
"visibility": "hidden", "visibility": "hidden",
"overflow": "hidden",
"max-width": "10px" "max-width": "10px"
}); });
// Par défaut pour tous les thèmes. // Par défaut pour tous les thèmes.
$("#menuLeft, nav").css("max-height", totalHeight + "px"); $("#menuLeft").css("max-height", totalHeight + "px").css("min-height", totalHeight + "px");
} }
}; };

View File

@ -51,7 +51,7 @@ class common
const ACCESS_TIMER = 1800; const ACCESS_TIMER = 1800;
// Numéro de version // Numéro de version
const ZWII_VERSION = '1.17.05'; const ZWII_VERSION = '1.20.02';
// URL autoupdate // URL autoupdate
const ZWII_UPDATE_URL = 'https://forge.chapril.org/ZwiiCMS-Team/campus-update/raw/branch/master/'; const ZWII_UPDATE_URL = 'https://forge.chapril.org/ZwiiCMS-Team/campus-update/raw/branch/master/';
@ -72,6 +72,9 @@ class common
const COURSE_ENROLMENT_SELF_KEY = 2; // Ouvert à tous les membres disposant de la clé const COURSE_ENROLMENT_SELF_KEY = 2; // Ouvert à tous les membres disposant de la clé
const COURSE_ENROLMENT_MANDATORY = 3; const COURSE_ENROLMENT_MANDATORY = 3;
// Taille et rotation des journaux
const LOG_MAXSIZE = 4 * 1024 * 1024;
const LOG_MAXARCHIVE = 5;
public static $actions = []; public static $actions = [];
public static $coreModuleIds = [ public static $coreModuleIds = [
@ -543,7 +546,7 @@ class common
public function deleteData($keys) public function deleteData($keys)
{ {
// Descripteur de la base // Descripteur de la base
$db = $this->dataFiles[$keys[0]]; $db = (object) $this->dataFiles[$keys[0]];
// Initialisation de la requête par le nom de la base // Initialisation de la requête par le nom de la base
$query = $keys[0]; $query = $keys[0];
// Construire la requête // Construire la requête
@ -581,7 +584,7 @@ class common
// Construire la requête dans la base inf à 1 retourner toute la base // Construire la requête dans la base inf à 1 retourner toute la base
if (count($keys) >= 1) { if (count($keys) >= 1) {
// Descripteur de la base // Descripteur de la base
$db = $this->dataFiles[$keys[0]]; $db = (object) $this->dataFiles[$keys[0]];
$query = $keys[0]; $query = $keys[0];
// Construire la requête // Construire la requête
// Ne pas tenir compte du dernier élément qui une une value donc < // Ne pas tenir compte du dernier élément qui une une value donc <
@ -605,7 +608,7 @@ class common
// Eviter une requete vide // Eviter une requete vide
if (count($keys) >= 1) { if (count($keys) >= 1) {
// descripteur de la base // descripteur de la base
$db = $this->dataFiles[$keys[0]]; $db = (object) $this->dataFiles[$keys[0]];
$query = $keys[0]; $query = $keys[0];
// Construire la requête // Construire la requête
for ($i = 1; $i < count($keys); $i++) { for ($i = 1; $i < count($keys); $i++) {
@ -722,7 +725,7 @@ class common
*/ */
public function saveDB($module): void public function saveDB($module): void
{ {
$db = $this->dataFiles[$module]; $db = (object) $this->dataFiles[$module];
$db->save(); $db->save();
} }
@ -1248,66 +1251,94 @@ class common
} }
/* /**
* Création d'une miniature * Crée une miniature à partir d'une image source.
* Fonction utilisée lors de la mise à jour d'une version 9 à une version 10 * Cette fonction prend en charge les formats raster (JPEG, PNG, GIF, WebP, AVIF) et vectoriels (SVG).
* @param string $src image source * Pour les images vectorielles (SVG), aucune redimension n'est effectuée : une copie est réalisée.
* @param string $dets image destination *
* @param integer $desired_width largeur demandée * @param string $src Chemin de l'image source.
*/ * @param string $dest Chemin de l'image destination (avec le nom du fichier et l'extension).
function makeThumb($src, $dest, $desired_width) * @param int $desired_width Largeur demandée pour la miniature (ignorée pour les SVG).
{ * @return bool True si l'opération a réussi, false sinon.
// Vérifier l'existence du dossier de destination. */
$fileInfo = pathinfo($dest); function makeThumb($src, $dest, $desired_width)
if (!is_dir($fileInfo['dirname'])) { {
mkdir($fileInfo['dirname'], 0755, true); // Vérifier l'existence du dossier de destination.
} $fileInfo = pathinfo($dest);
$source_image = ''; if (!is_dir($fileInfo['dirname'])) {
// Type d'image mkdir($fileInfo['dirname'], 0755, true);
switch ($fileInfo['extension']) { }
case 'jpeg':
case 'jpg': $extension = strtolower($fileInfo['extension']);
$source_image = imagecreatefromjpeg($src); $mime_type = mime_content_type($src);
break;
case 'png': // Gestion des fichiers SVG (copie simple sans redimensionnement)
$source_image = imagecreatefrompng($src); if ($extension === 'svg' || $mime_type === 'image/svg+xml') {
break; return copy($src, $dest);
case 'gif': }
$source_image = imagecreatefromgif($src);
break; // Chargement de l'image source selon le type
case 'webp': $source_image = '';
$source_image = imagecreatefromwebp($src); switch ($extension) {
break; case 'jpeg':
case 'avif': case 'jpg':
$source_image = imagecreatefromavif($src); $source_image = imagecreatefromjpeg($src);
} break;
// Image valide case 'png':
if ($source_image) { $source_image = imagecreatefrompng($src);
$width = imagesx($source_image); break;
$height = imagesy($source_image); case 'gif':
/* find the "desired height" of this thumbnail, relative to the desired width */ $source_image = imagecreatefromgif($src);
$desired_height = floor($height * ($desired_width / $width)); break;
/* create a new, "virtual" image */ case 'webp':
$virtual_image = imagecreatetruecolor($desired_width, $desired_height); $source_image = imagecreatefromwebp($src);
/* copy source image at a resized size */ break;
imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height); case 'avif':
switch (mime_content_type($src)) { if (function_exists('imagecreatefromavif')) {
case 'image/jpeg': $source_image = imagecreatefromavif($src);
case 'image/jpg': } else {
return (imagejpeg($virtual_image, $dest)); return false; // AVIF non supporté
case 'image/png': }
return (imagepng($virtual_image, $dest)); break;
case 'image/gif': default:
return (imagegif($virtual_image, $dest)); return false; // Format non pris en charge
case 'image/webp': }
return (imagewebp($virtual_image, $dest));
case 'image/avif': // Image valide (formats raster uniquement)
return (imageavif($virtual_image, $dest)); if (is_resource($source_image) || (is_object($source_image) && $source_image instanceof GdImage)) {
} $width = imagesx($source_image);
} else { $height = imagesy($source_image);
return (false);
} // Calcul de la hauteur proportionnelle à la largeur demandée
} $desired_height = floor($height * ($desired_width / $width));
// Création d'une nouvelle image virtuelle redimensionnée
$virtual_image = imagecreatetruecolor($desired_width, $desired_height);
// Copie de l'image source dans l'image virtuelle avec redimensionnement
imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
// Enregistrement de l'image redimensionnée au format approprié
switch ($mime_type) {
case 'image/jpeg':
case 'image/jpg':
return imagejpeg($virtual_image, $dest);
case 'image/png':
return imagepng($virtual_image, $dest);
case 'image/gif':
return imagegif($virtual_image, $dest);
case 'image/webp':
return imagewebp($virtual_image, $dest);
case 'image/avif':
if (function_exists('imageavif')) {
return imageavif($virtual_image, $dest);
}
}
}
return false; // En cas d'échec
}
/** /**
@ -1514,18 +1545,67 @@ class common
} }
/** /**
* Journalisation * Journalisation avec gestion de la taille maximale et compression
*/ */
public function saveLog($message = '') public function saveLog($message = '')
{ {
// Journalisation // Chemin du fichier journal
$dataLog = helper::dateUTF8('%Y%m%d', time(), self::$i18nUI) . ';' . helper::dateUTF8('%H:%M', time(), self::$i18nUI) . ';'; $logFile = self::DATA_DIR . 'journal.log';
// Vérifier la taille du fichier
if (file_exists($logFile) && filesize($logFile) > self::LOG_MAXSIZE) {
$this->rotateLogFile();
}
// Création de l'entrée de journal
$dataLog = helper::dateUTF8('%Y%m%d', time(), self::$i18nUI) . ';' .
helper::dateUTF8('%H:%M', time(), self::$i18nUI) . ';';
$dataLog .= helper::getIp($this->getData(['config', 'connect', 'anonymousIp'])) . ';'; $dataLog .= helper::getIp($this->getData(['config', 'connect', 'anonymousIp'])) . ';';
$dataLog .= empty($this->getUser('id')) ? 'visitor;' : $this->getUser('id') . ';'; $dataLog .= empty($this->getUser('id')) ? 'visitor;' : $this->getUser('id') . ';';
$dataLog .= $message ? $this->getUrl() . ';' . $message : $this->getUrl(); $dataLog .= $message ? $this->getUrl() . ';' . $message : $this->getUrl();
$dataLog .= PHP_EOL; $dataLog .= PHP_EOL;
// Écriture dans le fichier si la journalisation est activée
if ($this->getData(['config', 'connect', 'log'])) { if ($this->getData(['config', 'connect', 'log'])) {
file_put_contents(self::DATA_DIR . 'journal.log', $dataLog, FILE_APPEND); file_put_contents($logFile, $dataLog, FILE_APPEND);
}
}
/**
* Gère la rotation et la compression des fichiers journaux
*/
private function rotateLogFile()
{
$logFile = self::DATA_DIR . 'journal.log';
// Décaler tous les fichiers d'archive existants
for ($i = self::LOG_MAXARCHIVE - 1; $i > 0; $i--) {
$oldFile = self::DATA_DIR . 'journal-' . $i . '.log.gz';
$newFile = self::DATA_DIR . 'journal-' . ($i + 1) . '.log.gz';
if (file_exists($oldFile)) {
if ($i == self::LOG_MAXARCHIVE - 1) {
unlink($oldFile); // Supprimer le plus ancien
} else {
rename($oldFile, $newFile);
}
}
}
// Compresser le fichier journal actuel
if (file_exists($logFile)) {
$gz = gzopen(self::DATA_DIR . 'journal-1.log.gz', 'w9');
$handle = fopen($logFile, 'r');
while (!feof($handle)) {
gzwrite($gz, fread($handle, 8192));
}
fclose($handle);
gzclose($gz);
// Créer un nouveau fichier journal vide
file_put_contents($logFile, '');
} }
} }
@ -1535,9 +1615,9 @@ class common
* Retourne les contenus d'un utilisateur * Retourne les contenus d'un utilisateur
* @param string $userId identifiant * @param string $userId identifiant
* @param string $serStatus teacher ou student ou admin * @param string $serStatus teacher ou student ou admin
* * @return array
* CETTE FONCTION EST UTILISEE PAR LAYOUT * CETTE FONCTION EST UTILISEE PAR LAYOUT
* *
*/ */
public function getCoursesByProfil() public function getCoursesByProfil()
{ {

View File

@ -3,26 +3,23 @@
/** /**
* Vérification de la version de PHP * Vérification de la version de PHP
*/ */
if (version_compare(PHP_VERSION, '7.2.0', '<')) {
if(version_compare(PHP_VERSION, '7.2.0', '<') ) { displayErrorPage('PHP 7.2+ mini requis - PHP 7.2+ mini required');
exit('PHP 7.2+ mini requis - PHP 7.2+ mini required');
} }
if ( version_compare(PHP_VERSION, '8.3.999', '>') ) { if (version_compare(PHP_VERSION, '8.3.999', '>')) {
exit('PHP 8.3 pas encore supporté, installez PHP 7.n ou PHP 8.1.n - PHP 8.3 not yet supported, install PHP 7.n or PHP 8.1.n'); displayErrorPage('PHP 8.3 pas encore supporté, installez PHP 7.n ou PHP 8.1.n - PHP 8.3 not yet supported, install PHP 7.n or PHP 8.1.n');
} }
/** /**
* Check les modules installés * Check les modules installés
*/ */
$e = [ $e = [
'gd', 'gd',
'json', 'json',
'date', 'date',
'mbstring', 'mbstring',
'zip', 'zip',
'intl', 'intl',
'exif', 'exif',
'Phar', 'Phar',
@ -31,26 +28,57 @@ $e = [
]; ];
$m = get_loaded_extensions(); $m = get_loaded_extensions();
$b = false; $b = false;
$missingModules = [];
foreach ($e as $k => $v) { foreach ($e as $k => $v) {
if (array_search($v,$m) === false) { if (array_search($v, $m) === false) {
$b = true; $b = true;
echo '<pre><p>Module PHP : ' . $v . ' manquant - Module PHP ' . $v . ' missing.</p></pre>'; $missingModules[] = $v;
} }
} }
if ($b) if ($b) {
exit('<pre><p>ZwiiCMS ne peut pas démarrer ; activez les extensions requises dans PHP.ini- ZwiiCMS cannot start, enabled PHP missing extensions into PHP.ini</p></pre>'); $errorMessage = 'ZwiiCMS ne peut pas démarrer ; les modules PHP suivants sont manquants : ' . implode(', ', $missingModules) . '<br />';
/** $errorMessage .= 'ZwiiCMS cannot start, the following PHP modules are missing: ' . implode(', ', $missingModules);
* Contrôle les htacess displayErrorPage($errorMessage);
*/ }
/**
* Contrôle les htaccess
*/
$d = [ $d = [
'', '',
'site/data/', 'site/data/',
'site/backup/', 'site/backup/',
'site/tmp/', 'site/tmp/',
// 'site/i18n/', pas contrôler pour éviter les pbs de mise à jour // 'site/i18n/', pas contrôler pour éviter les pbs de mise à jour
]; ];
foreach ($d as $key) { foreach ($d as $key) {
if (file_exists($key . '.htaccess') === false) if (file_exists($key . '.htaccess') === false) {
exit('<pre>ZwiiCMS ne peut pas démarrer, le fichier ' .$key . '.htaccess est manquant.<br />ZwiiCMS cannot start, file ' . $key . '.htaccess is missing.</pre>' ); $errorMessage = 'ZwiiCMS ne peut pas démarrer, le fichier ' . $key . '.htaccess est manquant.<br />';
$errorMessage .= 'ZwiiCMS cannot start, file ' . $key . '.htaccess is missing.';
displayErrorPage($errorMessage);
}
}
/**
* Fonction pour afficher une page d'erreur stylisée
*/
function displayErrorPage($message)
{
echo '<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Erreur - ZwiiCMS</title>
<link rel="stylesheet" href="core\layout\error.css">
</head>
<body>
<div class="error-container">
<h1>Erreur</h1>
<p>' . $message . '</p>
</div>
</body>
</html>';
exit;
} }

View File

@ -22,7 +22,6 @@ if (
$this->setData(['core', 'dataVersion', 1700]); $this->setData(['core', 'dataVersion', 1700]);
} }
if ( if (
$this->getData(['core', 'dataVersion']) < 1800 $this->getData(['core', 'dataVersion']) < 1800
) { ) {
@ -45,4 +44,33 @@ if (
fclose($fp); fclose($fp);
} }
$this->setData(['core', 'dataVersion', 1800]); $this->setData(['core', 'dataVersion', 1800]);
} }
if (
$this->getData(['core', 'dataVersion']) < 12002
) {
/**
* Installe dans le thème du menu la variable hidePages
**/
// Tableau à insérer
$a = [
'theme' =>
['menu' => [
'hidePages' => false
]]];
// Parcourir la structure pour écrire dans les fichiers JSON
foreach ($this->getData(['course']) as $courseId => $courseValues) {
$d = json_decode(file_get_contents(self::DATA_DIR . $courseId . '/theme.json'), true);
// Insérer la variable hidePages si elle n'existe pas
if (isset($d['theme']['menu']['hidePages']) === false) {
$result = array_replace_recursive($d, $a);
file_put_contents(self::DATA_DIR . $courseId . '/theme.json', json_encode($result,JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
}
// Forcer la régénération du fichier theme.css
if (file_exists(self::DATA_DIR . $courseId . '/theme.css')) {
unlink(self::DATA_DIR . $courseId . '/theme.css');
}
}
$this->setData(['core', 'dataVersion', 12002]);
}

View File

@ -354,6 +354,14 @@ td>.col12 {
color: #E74C3C; color: #E74C3C;
} }
/* Asterisque des champs obligatoires */
.required-field::after {
content: '\00204E';
color: #E74C3C;
margin-left: 4px;
font-size: larger;
}
/* Mauvaise position dans les champs File /* Mauvaise position dans les champs File
.inputFile.notice { .inputFile.notice {
@ -649,6 +657,7 @@ nav a:hover {
#menuLeft { #menuLeft {
display: inline-flex; display: inline-flex;
float: left;
} }
#menuRight { #menuRight {
@ -1192,6 +1201,7 @@ input[type='datetime-local'],
input[type='time'], input[type='time'],
input[type='month'], input[type='month'],
input[type='week'], input[type='week'],
input[type='number'],
.inputFile, .inputFile,
select, select,
textarea { textarea {
@ -1217,6 +1227,7 @@ input[type='datetime-local']:hover,
input[type='time']:hover, input[type='time']:hover,
input[type='month']:hover, input[type='month']:hover,
input[type='week']:hover, input[type='week']:hover,
input[type='number']:hover,
.inputFile:hover, .inputFile:hover,
select:hover, select:hover,
textarea:hover { textarea:hover {
@ -1231,6 +1242,7 @@ input[type='datetime-local'].notice,
input[type='time'].notice, input[type='time'].notice,
input[type='month'].notice, input[type='month'].notice,
input[type='week'].notice, input[type='week'].notice,
input[type='number'].notice,
.inputFile.notice, .inputFile.notice,
select.notice, select.notice,
textarea.notice { textarea.notice {
@ -1246,6 +1258,7 @@ input[type='datetime-local'].notice:hover,
input[type='time'].notice:hover, input[type='time'].notice:hover,
input[type='month'].notice:hover, input[type='month'].notice:hover,
input[type='week'].notice:hover, input[type='week'].notice:hover,
input[type='number'].notice:hover,
.inputFile.notice:hover, .inputFile.notice:hover,
select.notice:hover, select.notice:hover,
textarea.notice:hover { textarea.notice:hover {

22
core/layout/error.css Normal file
View File

@ -0,0 +1,22 @@
body {
color: #000;
font: 75%/1.7em "Helvetica Neue", Helvetica, arial, sans-serif;
margin: 0;
padding: 80px;
background: url('../vendor/zwiico/png/error.png') 30px 30px no-repeat #fff;
}
h1 {
font-weight: bold;
color: #000;
font-size: 300%;
margin: 20px 0;
padding: 0;
}
p {
margin: 10px 0;
color: #777;
font-size: 16px;
line-height: 1.6;
}

View File

@ -20,7 +20,7 @@
]); ?> ]); ?>
</div> </div>
<div class="col2"> <div class="col2">
<?php echo template::text('configProxyPort', [ <?php echo template::number('configProxyPort', [
'label' => 'Port du proxy', 'label' => 'Port du proxy',
'placeholder' => '6060', 'placeholder' => '6060',
'value' => $this->getData(['config', 'proxyPort']) 'value' => $this->getData(['config', 'proxyPort'])

View File

@ -44,7 +44,7 @@
<?php echo template::checkbox('configRewrite', true, 'Apache URL intelligentes', [ <?php echo template::checkbox('configRewrite', true, 'Apache URL intelligentes', [
'checked' => helper::checkRewrite(), 'checked' => helper::checkRewrite(),
'help' => 'Supprime le point d\'interrogation dans les URL, l\'option est indisponible avec les autres serveurs Web', 'help' => 'Supprime le point d\'interrogation dans les URL, l\'option est indisponible avec les autres serveurs Web',
'disabled' => helper::checkServerSoftware() === false and config->isModRewriteEnabled() 'disabled' => helper::checkServerSoftware() === false and self::isModRewriteEnabled()
]); ?> ]); ?>
</div> </div>
</div> </div>

View File

@ -180,7 +180,7 @@ class course extends common
$this->initData('theme', $courseId); $this->initData('theme', $courseId);
// Pointer RFM sur le dossier de l'espace // Pointer RFM sur le dossier de l'espace
self::$siteContent = $courseId; // self::$siteContent = $courseId;
// Ordonne les pages par position // Ordonne les pages par position
$this->buildHierarchy(); $this->buildHierarchy();
@ -319,7 +319,7 @@ class course extends common
$this->initDB('page', $courseId); $this->initDB('page', $courseId);
// Pointer RFM sur le dossier de l'espace // Pointer RFM sur le dossier de l'espace
self::$siteContent = $courseId; // self::$siteContent = $courseId;
// Ordonne les pages par position // Ordonne les pages par position
$this->buildHierarchy(); $this->buildHierarchy();
@ -339,7 +339,7 @@ class course extends common
// Valeurs en sortie // Valeurs en sortie
$this->addOutput([ $this->addOutput([
'title' => sprintf('%s %s (%s)', helper::translate('Editer l\'espace'), $this->getData(['course', $courseId, 'title' ]), $this->getUrl(2)), 'title' => sprintf('%s %s (%s)', helper::translate('Editer l\'espace'), $this->getData(['course', $courseId, 'title']), $this->getUrl(2)),
'view' => 'edit' 'view' => 'edit'
]); ]);
} }
@ -378,7 +378,7 @@ class course extends common
$this->initDB('page', $courseId); $this->initDB('page', $courseId);
// Pointer RFM sur le dossier de l'espace // Pointer RFM sur le dossier de l'espace
self::$siteContent = $courseId; // self::$siteContent = $courseId;
// Ordonne les pages par position // Ordonne les pages par position
$this->buildHierarchy(); $this->buildHierarchy();
@ -398,7 +398,7 @@ class course extends common
// Valeurs en sortie // Valeurs en sortie
$this->addOutput([ $this->addOutput([
'title' => sprintf('%s %s (%s)', helper::translate('Gérer l\'espace'), $this->getData(['course', $courseId, 'title' ]), $this->getUrl(2)), 'title' => sprintf('%s %s (%s)', helper::translate('Gérer l\'espace'), $this->getData(['course', $courseId, 'title']), $this->getUrl(2)),
'view' => 'manage' 'view' => 'manage'
]); ]);
} }
@ -736,17 +736,15 @@ class course extends common
} }
self::$courseUsers[] = [ self::$courseUsers[] = [
//$userId, //$userId,
$this->getData(['user', $userId, 'firstname']) . ' ' . $this->getData(['user', $userId, 'lastname']), sprintf('%s %s', $this->getData(['user', $userId, 'lastname']), $this->getData(['user', $userId, 'firstname'])),
array_key_exists('lastPageView', $userValue) && isset($pages[$userValue['lastPageView']]['title']) array_key_exists('lastPageView', $userValue) && isset($pages['page'][$userValue['lastPageView']]['title'])
? $pages[$userValue['lastPageView']]['title'] ? $pages['page'][$userValue['lastPageView']]['title']
: '',
array_key_exists('lastPageView', $userValue)
? helper::dateUTF8('%d/%m/%Y', $userValue['datePageView'])
: '',
array_key_exists('datePageView', $userValue)
? helper::dateUTF8('%H:%M', $userValue['datePageView'])
: '', : '',
$this->getData(['user', $userId, 'tags']), $this->getData(['user', $userId, 'tags']),
array_key_exists('lastPageView', $userValue)
// ? helper::dateUTF8('%d/%m/%Y', $userValue['datePageView'])
? $userValue['datePageView']
: '',
$reportButton, $reportButton,
template::button('userDelete' . $userId, [ template::button('userDelete' . $userId, [
'class' => 'userDelete buttonRed', 'class' => 'userDelete buttonRed',
@ -1740,7 +1738,7 @@ class course extends common
$this->initDB('page', $courseId); $this->initDB('page', $courseId);
// Pointer RFM sur le dossier de l'espace // Pointer RFM sur le dossier de l'espace
self::$siteContent = $courseId; // self::$siteContent = $courseId;
// Ordonne les pages par position // Ordonne les pages par position
$this->buildHierarchy(); $this->buildHierarchy();
@ -1846,7 +1844,7 @@ class course extends common
// Valeurs en sortie // Valeurs en sortie
$this->addOutput([ $this->addOutput([
'title' => sprintf('%s %s (%s)', helper::translate('Export des pages de l\'espace'), $this->getData(['course', $courseId, 'title' ]), $this->getUrl(2)), 'title' => sprintf('%s %s (%s)', helper::translate('Export des pages de l\'espace'), $this->getData(['course', $courseId, 'title']), $this->getUrl(2)),
'view' => 'export' 'view' => 'export'
]); ]);
} }
@ -2223,4 +2221,4 @@ class course extends common
// Afficher le JSON; // Afficher le JSON;
return $data; return $data;
} }
} }

View File

@ -21,7 +21,6 @@ $(document).ready((function () {
$(location).attr("href", _this.attr("href")) $(location).attr("href", _this.attr("href"))
})) }))
})); }));
$.fn.dataTable.moment( 'DD/MM/YYYY' );
$('#dataTables').DataTable({ $('#dataTables').DataTable({
language: { language: {
url: "core/vendor/datatables/french.json" url: "core/vendor/datatables/french.json"
@ -29,14 +28,20 @@ $(document).ready((function () {
order: [[3, 'desc']], order: [[3, 'desc']],
locale: 'fr', locale: 'fr',
stateSave: true, stateSave: true,
"lengthMenu": [[10, 25, 50, 100, 299, -1], [10, 25, 50, 100, 200, "Tout"]], "lengthMenu": [[10, 25, 50, 100, 299, -1], [10, 25, 50, 100, 200, "Tout"]],
"columnDefs": [ "columnDefs": [
{ {
target: 6, targets: 3,
type: 'numeric',
render: function (data) {
return moment(data * 1000).format('DD/MM/YYYY HH:mm');
}
},
{
targets: 5,
orderable: false, orderable: false,
searchable: false searchable: false
} }]
]
}); });
})); }));

View File

@ -53,7 +53,7 @@
</div> </div>
<?php echo template::formClose(); ?> <?php echo template::formClose(); ?>
<?php if (course::$courseUsers): ?> <?php if (course::$courseUsers): ?>
<?php echo template::table([3, 4, 1, 1, 1, 1, 1], course::$courseUsers, ['Nom Prénom', 'Dernière page vue', 'Date' , 'Heure', 'Étiquettes', 'Progression', ''], ['id' => 'dataTables']); ?> <?php echo template::table([3, 3, 2, 2, 1, 1], course::$courseUsers, ['Nom Prénom', 'Dernière page vue', 'Date' , 'Étiquettes', 'Progression', ''], ['id' => 'dataTables']); ?>
<?php else: ?> <?php else: ?>
<?php echo template::speech('Aucun participant'); ?> <?php echo template::speech('Aucun participant'); ?>
<?php endif; ?> <?php endif; ?>

View File

@ -64,7 +64,7 @@ class init extends common
] ]
], ],
'core' => [ 'core' => [
'dataVersion' => 1700, 'dataVersion' => 12002,
'lastBackup' => 0, 'lastBackup' => 0,
'lastClearTmp' => 0, 'lastClearTmp' => 0,
'lastAutoUpdate' => 0, 'lastAutoUpdate' => 0,
@ -903,7 +903,8 @@ class init extends common
'selectSpace' => true, 'selectSpace' => true,
'burgerLogo' => '', 'burgerLogo' => '',
'burgerContent' => 'title', 'burgerContent' => 'title',
'width' => 'container' 'width' => 'container',
'hidePages' => false,
], ],
'site' => [ 'site' => [
'backgroundColor' => 'rgba(255, 255, 255, 1)', 'backgroundColor' => 'rgba(255, 255, 255, 1)',

View File

@ -15,4 +15,8 @@
/** NE PAS EFFACER /** NE PAS EFFACER
* admin.css * admin.css
*/ */
.container.light {
filter: drop-shadow(5px 5px 10px rgba(0, 0, 0, 0.2));
}

View File

@ -19,4 +19,8 @@
.title { .title {
font-weight: bold; font-weight: bold;
}
.container.light {
filter: drop-shadow(5px 5px 10px rgba(0, 0, 0, 0.2));
} }

View File

@ -24,7 +24,6 @@ class page extends common
'duplicate' => self::GROUP_EDITOR, 'duplicate' => self::GROUP_EDITOR,
'jsEditor' => self::GROUP_EDITOR, 'jsEditor' => self::GROUP_EDITOR,
'cssEditor' => self::GROUP_EDITOR, 'cssEditor' => self::GROUP_EDITOR,
'register' => self::GROUP_EDITOR,
]; ];
public static $pagesNoParentId = [ public static $pagesNoParentId = [
'' => 'Aucune' '' => 'Aucune'
@ -474,7 +473,7 @@ class page extends common
$this->setData(['config', 'page302', $pageId], false); $this->setData(['config', 'page302', $pageId], false);
} }
// Sauvegarde la base manuellement // Sauvegarde la base manuellement
$this->saveDB(module: 'config'); $this->saveDB('config');
// Si la page est une page enfant, actualise les positions des autres enfants du parent, sinon actualise les pages sans parents // Si la page est une page enfant, actualise les positions des autres enfants du parent, sinon actualise les pages sans parents
$lastPosition = 1; $lastPosition = 1;
$hierarchy = $this->getInput('pageEditParentPageId') ? $this->getHierarchy($this->getInput('pageEditParentPageId')) : array_keys($this->getHierarchy()); $hierarchy = $this->getInput('pageEditParentPageId') ? $this->getHierarchy($this->getInput('pageEditParentPageId')) : array_keys($this->getHierarchy());
@ -594,11 +593,24 @@ class page extends common
] ]
]); ]);
/**
* Sauvegarde l'onglet de l'utilisateur
*/
$this->setData([
'user',
$this->getUser('id'),
'view',
[
'page' => $this->getInput('containerSelected'),
'config' => $this->getData(['user', $this->getUser('id'), 'view', 'config']),
]
]);
// Creation du contenu de la page // Creation du contenu de la page
if (!is_dir(self::DATA_DIR . self::$siteContent . '/content')) { if (!is_dir(self::DATA_DIR . self::$siteContent . '/content')) {
mkdir(self::DATA_DIR . self::$siteContent . '/content', 0755); mkdir(self::DATA_DIR . self::$siteContent . '/content', 0755);
} }
$content = empty($this->getInput('pageEditContent', null)) ? '<p></p>' : str_replace('<p></p>', '<p>&nbsp;</p>', $this->getInput('pageEditContent', null)); $content = empty($this->getInput('pageEditWysiwyg', null)) ? '<p></p>' : str_replace('<p></p>', '<p>&nbsp;</p>', $this->getInput('pageEditWysiwyg', null));
$this->setPage($pageId, $content, self::$siteContent); $this->setPage($pageId, $content, self::$siteContent);
// Met à jour le sitemap // Met à jour le sitemap
@ -760,25 +772,4 @@ class page extends common
return json_encode($d); return json_encode($d);
} }
/**
* Stocke la variable dans les paramètres de l'utilisateur pour activer la tab à sa prochaine visite
* @return never
*/
public function register(): void
{
$this->setData([
'user',
$this->getUser('id'),
'view',
[
'page' => $this->getUrl(2),
'config' => $this->getData(['user', $this->getUser('id'), 'view', 'config']),
]
]);
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . 'page/edit/' . $this->getUrl(3) . '/' . self::$siteContent,
]);
}
} }

View File

@ -6,720 +6,306 @@
* *
* @author Rémi Jean <remi.jean@outlook.com> * @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean * @copyright Copyright (C) 2008-2018, Rémi Jean
* @authorFrédéric Tempez <frederic.tempez@outlook.com> * @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez * @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International * @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/ * @link http://zwiicms.fr/
*/ */
/** // Cache jQuery objects for better performance
* Confirmation de suppression const $pageEditDelete = $("#pageEditDelete");
*/ const $pageEditModuleId = $("#pageEditModuleId");
$("#pageEditDelete").on("click", function() { const $pageEditModuleIdOld = $("#pageEditModuleIdOld");
var _this = $(this); const $pageEditModuleIdOldText = $("#pageEditModuleIdOldText");
var message_delete = "<?php echo helper::translate('Confirmer la suppression de la page'); ?>"; const $pageEditGroup = $("#pageEditGroup");
return core.confirm(message_delete, function() { const $pageEditGroupProfil = $(".pageEditGroupProfil");
$(location).attr("href", _this.attr("href")); const $pageEditContentContainer = $("#pageEditContentContainer");
}); const $pageEditExtensionContainer = $("#pageEditExtensionContainer");
const $pageEditPositionContainer = $("#pageEditPositionContainer");
const $pageEditLayoutContainer = $("#pageEditLayoutContainer");
const $pageEditPermissionContainer = $("#pageEditPermissionContainer");
const $pageEditModuleConfig = $("#pageEditModuleConfig");
const $pageModulePositionWrapper = $("#pageModulePositionWrapper");
const $pageEditContentWrapper = $("#pageEditContentWrapper");
const $pageEditHideTitleWrapper = $("#pageEditHideTitleWrapper");
const $pageEditBlockLayout = $("#pageEditBlockLayout");
const $pageEditBlock = $("#pageEditBlock");
const $pageEditBarLeftWrapper = $("#pageEditBarLeftWrapper");
const $pageEditBarRightWrapper = $("#pageEditBarRightWrapper");
const $pageEditMenu = $("#pageEditMenu");
const $pageEditbreadCrumbWrapper = $("#pageEditbreadCrumbWrapper");
const $pageEditModuleIdWrapper = $("#pageEditModuleIdWrapper");
const $pageEditDisplayMenuWrapper = $("#pageEditDisplayMenuWrapper");
const $pageTypeMenuWrapper = $("#pageTypeMenuWrapper");
const $pageEditSeoWrapper = $("#pageEditSeoWrapper");
const $pageEditAdvancedWrapper = $("#pageEditAdvancedWrapper");
const $pageEditHideMenuSideWrapper = $("#pageEditHideMenuSideWrapper");
const $pageEditHideMenuChildrenWrapper = $("#pageEditHideMenuChildrenWrapper");
const $pageEditParentPageId = $("#pageEditParentPageId");
const $pageEditDisable = $("#pageEditDisable");
const $pageEditExtraPosition = $("#pageEditExtraPosition");
const $pageEditPosition = $("#pageEditPosition");
const $pageEditHideTitle = $("#pageEditHideTitle");
const $pageTypeMenu = $("#pageTypeMenu");
const $pageIconUrlWrapper = $("#pageIconUrlWrapper");
const $pageEditTitle = $("#pageEditTitle");
const $pageEditShortTitle = $("#pageEditShortTitle");
// Confirmation de suppression
$pageEditDelete.on("click", function() {
return core.confirm($("#pageEditDataContainer").data("translate-delete"), () => {
$(location).attr("href", $(this).attr("href"));
});
}); });
$("#pageEditModuleId").on("change", function() { // Gestion du changement de module
protectModule(); $pageEditModuleId.on("change", protectModule);
});
function protectModule() { function protectModule() {
var oldModule = $("#pageEditModuleIdOld").val(); const oldModule = $pageEditModuleIdOld.val();
var oldModuleText = $("#pageEditModuleIdOldText").val(); const newModule = $pageEditModuleId.val();
var newModule = $("#pageEditModuleId").val(); if (oldModule && oldModule !== newModule) {
if ( oldModule !== "" && core.confirm($("#pageEditDataContainer").data("translate-module-delete") + " " + $pageEditModuleIdOldText.val(), () => {
oldModule !== newModule) { $(location).attr("href", $(this).attr("href"));
var _this = $(this); }, () => {
var message_delete = "<?php echo helper::translate('Confirmer la suppression des données du module'); ?>"; $pageEditModuleId.val(oldModule);
core.confirm(message_delete + " " + oldModuleText, });
function() { }
$(location).attr("href", _this.attr("href"));
return true;
},
function() {
$("#pageEditModuleId").val(oldModule);
return false;
}
);
}
} }
// Paramètres par défaut au chargement
$(document).ready(function() {
// Changement de profil
$pageEditGroupProfil.hide();
$(`#pageEditGroupProfil${$pageEditGroup.val()}`).show();
$pageEditGroup.on("change", function() {
$pageEditGroupProfil.hide();
$(`#pageEditGroupProfil${$(this).val()}`).show();
});
/** // Sélection des onglets
* Paramètres par défaut au chargement const pageLayout = $("#pageEditDataContainer").data("page-layout") || "content";
*/ $pageEditContentContainer.hide();
$( document ).ready(function() { $pageEditExtensionContainer.hide();
$pageEditPositionContainer.hide();
$pageEditLayoutContainer.hide();
$pageEditPermissionContainer.hide();
$(`#pageEdit${capitalizeFirstLetter(pageLayout)}Container`).show();
$(`#pageEdit${capitalizeFirstLetter(pageLayout)}Button`).addClass("activeButton");
// Changement de profil // Enleve le menu fixe en édition de page
$(".pageEditGroupProfil").hide(); $("nav").removeAttr('id');
$("#pageEditGroupProfil" + $("#pageEditGroup").val()).show();
$("#pageEditGroup").on("change", function () {
$(".pageEditGroupProfil").hide();
$("#pageEditGroupProfil" + $(this).val()).show();
});
/** // Gestion des modules
* Sélection des onglets if ($pageEditModuleId.val() === "") {
*/ $pageEditModuleConfig.addClass("disabled");
var pageLayout = "<?php echo $this->getData(['user', $this->getUser('id'), 'view', 'page']);?>"; } else {
// Non défini, valeur par défaut $pageEditModuleConfig.removeClass("disabled");
if (pageLayout == "") { $pageEditBlock.find("option[value='bar']").remove();
pageLayout = "content"; }
}
// Tout cacher
$("#pageEditContentContainer").hide();
$("#pageEditExtensionContainer").hide();
$("#pageEditPositionContainer").hide();
$("#pageEditLayoutContainer").hide();
$("#pageEditPermissionContainer").hide();
// Afficher la bonne tab
$("#pageEdit" + capitalizeFirstLetter(pageLayout) + "Container").show();
$("#pageEdit" + capitalizeFirstLetter(pageLayout) + "Button").addClass("activeButton");
// Masquer et afficher les éléments en fonction du module sélectionné
toggleModuleElements($pageEditModuleId.val());
/* // Masquer et afficher les éléments en fonction du bloc sélectionné
* Enleve le menu fixe en édition de page toggleBlockElements($pageEditBlock.val());
*/
$("nav").removeAttr('id');
/** // Masquer ou afficher le chemin de fer
* Bloque/Débloque le bouton de configuration au changement de module toggleBreadCrumb($pageEditHideTitle.is(':checked'), $pageEditParentPageId.val());
* Affiche ou masque la position du module selon le call_user_func
*/
if($("#pageEditModuleId").val() === "") {
$("#pageEditModuleConfig").addClass("disabled");
/*$("#pageEditContentContainer").hide();*/
} // Masquer ou afficher la sélection de l'icône
else { toggleIconUrl($pageTypeMenu.val());
$("#pageEditModuleConfig").removeClass("disabled");
/*$("#pageEditContentContainer").hide();*/
$("#pageEditBlock option[value='bar']").remove();
}
/**
* Masquer et affiche la sélection de position du module
*/
if( $("#pageEditModuleId").val() === "redirection" ||
$("#pageEditModuleId").val() === "" ) {
$("#pageModulePositionWrapper").removeClass("disabled");
$("#pageModulePositionWrapper").slideUp();
}
else {
$("#pageModulePositionWrapper").addClass("disabled");
$("#pageModulePositionWrapper").slideDown();
}
/**
* Masquer et démasquer le contenu pour les modules code et redirection
*/
if( $("#pageEditModuleId").val() === "redirection") {
$("#pageEditContentWrapper").removeClass("disabled");
$("#pageEditContentWrapper").slideUp();
} else {
$("#pageEditContentWrapper").addClass("disabled");
$("#pageEditContentWrapper").slideDown();
}
/**
* Masquer et démasquer le masquage du titre pour le module redirection
*/
if( $("#pageEditModuleId").val() === "redirection" ) {
$("#pageEditHideTitleWrapper").removeClass("disabled");
$("#pageEditHideTitleWrapper").hide();
$("#pageEditBlockLayout").removeClass("disabled");
$("#pageEditBlockLayout").hide();
} else {
$("#pageEditHideTitleWrapper").addClass("disabled");
$("#pageEditHideTitleWrapper").show();
$("#pageEditBlockLayout").addClass("disabled");
$("#pageEditBlockLayout").show();
}
/**
* Masquer et démasquer la sélection des barres
*/
switch ($("#pageEditBlock").val()) {
case "bar":
case "12":
$("#pageEditBarLeftWrapper").removeClass("disabled");
$("#pageEditBarLeftWrapper").slideUp();
$("#pageEditBarRightWrapper").removeClass("disabled");
$("#pageEditBarRightWrapper").slideUp();
break;
case "3-9":
case "4-8":
$("#pageEditBarLeftWrapper").addClass("disabled");
$("#pageEditBarLeftWrapper").slideDown();
$("#pageEditBarRightWrapper").removeClass("disabled");
$("#pageEditBarRightWrapper").slideUp();
break;
case "9-3":
case "8-4":
$("#pageEditBarLeftWrapper").removeClass("disabled");
$("#pageEditBarLeftWrapper").slideUp();
$("#pageEditBarRightWrapper").addClass("disabled");
$("#pageEditBarRightWrapper").slideDown();
break;
case "3-6-3":
case "2-7-3":
case "3-7-2":
$("#pageEditBarLeftWrapper").addClass("disabled");
$("#pageEditBarLeftWrapper").slideDown();
$("#pageEditBarRightWrapper").addClass("disabled");
$("#pageEditBarRightWrapper").slideDown();
break;
};
if ($("#pageEditBlock").val() === "bar") {
$("#pageEditMenu").removeClass("disabled");
$("#pageEditMenu").hide();
$("#pageEditHideTitleWrapper").removeClass("disabled");
$("#pageEditHideTitleWrapper").slideUp();
$("#pageEditbreadCrumbWrapper").removeClass("disabled");
$("#pageEditbreadCrumbWrapper").slideUp();
$("#pageEditModuleIdWrapper").removeClass("disabled");
$("#pageEditModuleIdWrapper").slideUp();
$("#pageEditModuleConfig").removeClass("disabled");
$("#pageEditModuleConfig").slideUp();
$("#pageEditDisplayMenuWrapper").addClass("disabled");
$("#pageEditDisplayMenuWrapper").slideDown();
$("#pageTypeMenuWrapper").removeClass("disabled");
$("#pageTypeMenuWrapper").slideUp();
$("#pageEditSeoWrapper").removeClass("disabled");
$("#pageEditSeoWrapper").slideUp();
$("#pageEditAdvancedWrapper").removeClass("disabled");
$("#pageEditAdvancedWrapper").slideUp();
$(".navSelect").slideUp();
/*
$("#pageEditBlockLayout").removeClass("col6");
$("#pageEditBlockLayout").addClass("col12");
*/
} else {
$("#pageEditDisplayMenuWrapper").removeClass("disabled");
$("#pageEditDisplayMenuWrapper").slideUp();
}
/**
* Masquer ou afficher le chemin de fer
* Quand le titre est masqué
*/
if ($("input[name=pageEditHideTitle]").is(':checked') ||
$("#pageEditParentPageId").val() === "" ) {
$("#pageEditbreadCrumbWrapper").removeClass("disabled");
$("#pageEditbreadCrumbWrapper").slideUp();
} else {
if ($("#pageEditParentPageId").val() !== "") {
$("#pageEditbreadCrumbWrapper").addClass("disabled");
$("#pageEditbreadCrumbWrapper").slideDown();
}
}
/**
* Masquer ou afficher la sélection de l'icône
*/
if ($("#pageTypeMenu").val() !== "text") {
$("#pageIconUrlWrapper").addClass("disabled");
$("#pageIconUrlWrapper").slideDown();
} else {
$("#pageIconUrlWrapper").removeClass("disabled");
$("#pageIconUrlWrapper").slideUp();
}
/**
* Cache les options de masquage dans les menus quand la page n'est pas affichée.
*/
if ($("#pageEditPosition").val() === "0" ) {
$("#pageEditHideMenuSideWrapper").removeClass("disabled");
$("#pageEditHideMenuSideWrapper").slideUp();
} else {
$("#pageEditHideMenuSideWrapper").addClass("disabled");
$("#pageEditHideMenuSideWrapper").slideDown();
}
/**
* Cache l'option de masquage des pages enfants
*/
if ($("#pageEditParentPageId").val() !== "") {
$("#pageEditHideMenuChildrenWrapper").removeClass("disabled");
$("#pageEditHideMenuChildrenWrapper").slideUp();
} else {
$("#pageEditHideMenuChildrenWrapper").addClass("disabled");
$("#pageEditHideMenuChildrenWrapper").slideDown();
}
/**
* Cache le l'option "ne pas afficher les pages enfants dans le menu horizontal" lorsque la page est désactivée
*/
if ($("#pageEditDisable").is(':checked') ) {
$("#pageEditHideMenuChildrenWrapper").removeClass("disabled");
$("#pageEditHideMenuChildrenWrapper").slideUp();
} else {
$("#pageEditHideMenuChildrenWrapper").addClass("disabled");
$("#pageEditHideMenuChildrenWrapper").slideDown();
}
/**
* Liste des pages pour le menu accessoire
*/
if ($("#pageEditExtraPosition").val() == 1 ) {
var positionDOM = $("#pageEditPosition");
var positionInitial = <?php echo $this->getData(['page',$this->getUrl(2),"position"]); ?>;
buildPagesList(true);
$("#pageEditPosition").val(positionInitial);
}
// Masquer ou afficher les options de masquage dans les menus
toggleMenuOptions($pageEditPosition.val(), $pageEditParentPageId.val(), $pageEditDisable.is(':checked'));
// Liste des pages pour le menu accessoire
if ($pageEditExtraPosition.val() == 1) {
buildPagesList(true);
$pageEditPosition.val($("#pageEditDataContainer").data("position-initial"));
}
}); });
// Gestion des événements
// Gestion des évènements $('#pageEditForm').on('submit', function() {
//-------------------------------------------------------------------------------------- $('#containerSelected').val(pageLayout);
/**
* Sélection de la page de configuration à afficher
*/
$("#pageEditContentButton").on("click", function () {
$("#pageEditContentContainer").show();
$("#pageEditExtensionContainer").hide();
$("#pageEditPositionContainer").hide();
$("#pageEditLayoutContainer").hide();
$("#pageEditPermissionContainer").hide();
$("#pageEditContentButton").addClass("activeButton");
$("#pageEditExtensionButton").removeClass("activeButton");
$("#PageEditPositionButton").removeClass("activeButton");
$("#pageEditLayoutButton").removeClass("activeButton");
$("#pageEditPermissionButton").removeClass("activeButton");
});
$("#pageEditExtensionButton").on("click", function () {
$("#pageEditContentContainer").hide();
$("#pageEditExtensionContainer").show();
$("#pageEditPositionContainer").hide();
$("#pageEditLayoutContainer").hide();
$("#pageEditPermissionContainer").hide();
$("#pageEditContentButton").removeClass("activeButton");
$("#pageEditExtensionButton").addClass("activeButton");
$("#PageEditPositionButton").removeClass("activeButton");
$("#pageEditLayoutButton").removeClass("activeButton");
$("#pageEditPermissionButton").removeClass("activeButton");
});
$("#PageEditPositionButton").on("click", function () {
$("#pageEditContentContainer").hide();
$("#pageEditExtensionContainer").hide();
$("#pageEditPositionContainer").show();
$("#pageEditLayoutContainer").hide();
$("#pageEditPermissionContainer").hide();
$("#pageEditContentButton").removeClass("activeButton");
$("#pageEditExtensionButton").removeClass("activeButton");
$("#PageEditPositionButton").addClass("activeButton");
$("#pageEditLayoutButton").removeClass("activeButton");
$("#pageEditPermissionButton").removeClass("activeButton");
});
$("#pageEditLayoutButton").on("click", function () {
$("#pageEditContentContainer").hide();
$("#pageEditExtensionContainer").hide();
$("#pageEditPositionContainer").hide();
$("#pageEditLayoutContainer").show();
$("#pageEditPermissionContainer").hide();
$("#pageEditContentButton").removeClass("activeButton");
$("#pageEditExtensionButton").removeClass("activeButton");
$("#PageEditPositionButton").removeClass("activeButton");
$("#pageEditLayoutButton").addClass("activeButton");
$("#pageEditPermissionButton").removeClass("activeButton");
});
$("#pageEditPermissionButton").on("click", function () {
$("#pageEditContentContainer").hide();
$("#pageEditExtensionContainer").hide();
$("#pageEditPositionContainer").hide();
$("#pageEditLayoutContainer").hide();
$("#pageEditPermissionContainer").show();
$("#pageEditContentButton").removeClass("activeButton");
$("#pageEditExtensionButton").removeClass("activeButton");
$("#pageEditPositionButton").removeClass("activeButton");
$("#pageEditLayoutButton").removeClass("activeButton");
$("#pageEditPermissionButton").addClass("activeButton");
});
/**
* Cache le l'option "ne pas afficher les pages enfants dans le menu horizontal" lorsque la page est désactivée
*/
var pageEditDisableDOM = $("#pageEditDisable");
pageEditDisableDOM.on("change", function() {
if ($(this).is(':checked') ) {
$("#pageEditHideMenuChildrenWrapper").removeClass("disabled");
$("#pageEditHideMenuChildrenWrapper").slideUp();
$("#pageEditHideMenuChildren").prop("checked", false);
} else {
$("#pageEditHideMenuChildrenWrapper").addClass("disabled");
$("#pageEditHideMenuChildrenWrapper").slideDown();
}
}); });
$("#pageEditContentButton, #pageEditPositionButton, #pageEditExtensionButton, #pageEditLayoutButton, #pageEditPermissionButton").on("click", function() {
/** const tab = $(this).attr("id").replace("pageEdit", "").replace("Button", "").toLowerCase();
* Cache les options de masquage dans les menus quand la page n'est pas affichée. $pageEditContentContainer.hide();
*/ $pageEditExtensionContainer.hide();
var pageEditPositionDOM = $("#pageEditPosition"); $pageEditPositionContainer.hide();
pageEditPositionDOM.on("change", function() { $pageEditLayoutContainer.hide();
if ($(this).val() === "0" ) { $pageEditPermissionContainer.hide();
$("#pageEditHideMenuSideWrapper").removeClass("disabled"); $(`#pageEdit${capitalizeFirstLetter(tab)}Container`).show();
$("#pageEditHideMenuSideWrapper").slideUp(); $(this).addClass("activeButton").siblings().removeClass("activeButton");
} else {
$("#pageEditHideMenuSideWrapper").addClass("disabled");
$("#pageEditHideMenuSideWrapper").slideDown();
}
}); });
/** $pageEditDisable.on("change", function() {
* Bloque/Débloque le bouton de configuration au changement de module toggleMenuOptions($pageEditPosition.val(), $pageEditParentPageId.val(), $(this).is(':checked'));
* Affiche ou masque la position du module selon le call_user_func
*/
var pageEditModuleIdDOM = $("#pageEditModuleId");
pageEditModuleIdDOM.on("change", function() {
if($(this).val() === "") {
$("#pageEditModuleConfig").addClass("disabled");
$("#pageEditBlock").append('<option value="bar">Barre latérale</option>');
}
else {
$("#pageEditModuleConfig").removeClass("disabled");
$("#pageEditBlock option[value='bar']").remove();
}
}); });
$pageEditPosition.on("change", function() {
toggleMenuOptions($(this).val(), $pageEditParentPageId.val(), $pageEditDisable.is(':checked'));
/**
* Masquer et affiche la sélection de position du module
*
* */
var pageEditModuleIdDOM = $("#pageEditModuleId");
pageEditModuleIdDOM.on("change", function() {
if( $(this).val() === "redirection" ||
$(this).val() === "") {
$("#pageModulePositionWrapper").removeClass("disabled");
$("#pageModulePositionWrapper").slideUp();
}
else {
$("#pageModulePositionWrapper").addClass("disabled");
$("#pageModulePositionWrapper").slideDown();
}
}); });
$pageEditModuleId.on("change", function() {
toggleModuleElements($(this).val());
/**
* Masquer et démasquer le contenu pour les modules code et redirection
*/
var pageEditModuleIdDOM = $("#pageEditModuleId");
pageEditModuleIdDOM.on("change", function() {
if( $(this).val() === "redirection") {
$("#pageEditContentWrapper").removeClass("disabled");
$("#pageEditContentWrapper").slideUp();
}
else {
$("#pageEditContentWrapper").addClass("disabled");
$("#pageEditContentWrapper").slideDown();
}
}); });
$pageEditBlock.on("change", function() {
toggleBlockElements($(this).val());
/**
* Masquer et démasquer le masquage du titre pour le module redirection
*/
var pageEditModuleIdDOM = $("#pageEditModuleId");
pageEditModuleIdDOM.on("change", function() {
if( $(this).val() === "redirection") {
$("#pageEditHideTitleWrapper").removeClass("disabled");
$("#pageEditHideTitleWrapper").slideUp();
$("#pageEditBlockLayout").removeClass("disabled");
$("#pageEditBlockLayout").slideUp();
}
else {
$("#pageEditHideTitleWrapper").addClass("disabled");
$("#pageEditHideTitleWrapper").slideDown();
$("#pageEditBlockLayout").addClass("disabled");
$("#pageEditBlockLayout").slideDown();
}
}); });
$pageEditHideTitle.on("change", function() {
/** toggleBreadCrumb($(this).is(':checked'), $pageEditParentPageId.val());
* Masquer et démasquer la sélection des barres
*/
var pageEditBlockDOM = $("#pageEditBlock");
pageEditBlockDOM.on("change", function() {
switch ($(this).val()) {
case "bar":
case "12":
$("#pageEditBarLeftWrapper").removeClass("disabled");
$("#pageEditBarLeftWrapper").slideUp();
$("#pageEditBarRightWrapper").removeClass("disabled");
$("#pageEditBarRightWrapper").slideUp();
break;
case "3-9":
case "4-8":
$("#pageEditBarLeftWrapper").addClass("disabled");
$("#pageEditBarLeftWrapper").slideDown();
$("#pageEditBarRightWrapper").removeClass("disabled");
$("#pageEditBarRightWrapper").slideUp();
break;
case "9-3":
case "8-4":
$("#pageEditBarLeftWrapper").removeClass("disabled");
$("#pageEditBarLeftWrapper").slideUp();
$("#pageEditBarRightWrapper").addClass("disabled");
$("#pageEditBarRightWrapper").slideDown();
break;
case "3-6-3":
case "2-7-3":
case "3-7-2":
$("#pageEditBarLeftWrapper").addClass("disabled");
$("#pageEditBarLeftWrapper").slideDown();
$("#pageEditBarRightWrapper").addClass("disabled");
$("#pageEditBarRightWrapper").slideDown();
break;
}
if ($(this).val() === "bar") {
$("#pageEditMenu").removeClass("disabled");
$("#pageEditMenu").hide();
$("#pageEditHideTitleWrapper").removeClass("disabled");
$("#pageEditHideTitleWrapper").slideUp();
$("#pageTypeMenuWrapper").removeClass("disabled");
$("#pageTypeMenuWrapper").slideUp();
$("#pageEditSeoWrapper").removeClass("disabled");
$("#pageEditSeoWrapper").slideUp();
$("#pageEditAdvancedWrapper").removeClass("disabled");
$("#pageEditAdvancedWrapper").slideUp();
$("#pageEditbreadCrumbWrapper").removeClass("disabled");
$("#pageEditbreadCrumbWrapper").slideUp();
$("#pageEditModuleIdWrapper").removeClass("disabled");
$("#pageEditModuleIdWrapper").slideUp();
$("#pageEditModuleConfig").removeClass("disabled");
$("#pageEditModuleConfig").slideUp();
$("#pageEditDisplayMenuWrapper").addClass("disabled");
$("#pageEditDisplayMenuWrapper").slideDown();
$(".navSelect").slideUp();
/*
$("#pageEditBlockLayout").removeClass("col6");
$("#pageEditBlockLayout").addClass("col12");
*/
} else {
$("#pageEditMenu").addClass("disabled");
$("#pageEditMenu").show();
$("#pageEditHideTitleWrapper").addClass("disabled");
$("#pageEditHideTitleWrapper").slideDown();
$("#pageTypeMenuWrapper").addClass("disabled");
$("#pageTypeMenuWrapper").slideDown();
$("#pageEditSeoWrapper").addClass("disabled");
$("#pageEditSeoWrapper").slideDown();
$("#pageEditAdvancedWrapper").addClass("disabled");
$("#pageEditAdvancedWrapper").slideDown();
$("#pageEditModuleIdWrapper").addClass("disabled");
$("#pageEditModuleIdWrapper").slideDown();
$("#pageEditModuleConfig").slideDown();
$("#pageEditDisplayMenuWrapper").removeClass("disabled");
$("#pageEditDisplayMenuWrapper").slideUp();
$(".navSelect").slideDown();
if ($("#pageEditParentPageId").val() !== "") {
$("#pageEditbreadCrumbWrapper").addClass("disabled");
$("#pageEditbreadCrumbWrapper").slideDown();
$("#pageEditExtraPositionWrapper").slideDown();
} else {
}
if ($("#pageEditModuleId").val() === "") {
$("#pageEditModuleConfig").addClass("disabled");
} else {
$("#pageEditModuleConfig").removeClass("disabled");
}
/*
$("#pageEditBlockLayout").removeClass("col12");
$("#pageEditBlockLayout").addClass("col6");
*/
}
}); });
$pageTypeMenu.on("change", function() {
toggleIconUrl($(this).val());
/**
* Masquer ou afficher le chemin de fer
* Quand le titre est masqué
*/
var pageEditHideTitleDOM = $("#pageEditHideTitle");
pageEditHideTitleDOM.on("change", function() {
if ($("input[name=pageEditHideTitle]").is(':checked')) {
$("#pageEditbreadCrumbWrapper").removeClass("disabled");
$("#pageEditbreadCrumbWrapper").slideUp();
} else {
if ($("#pageEditParentPageId").val() !== "") {
$("#pageEditbreadCrumbWrapper").addClass("disabled");
$("#pageEditbreadCrumbWrapper").slideDown();
}
}
}); });
$pageEditTitle.on("input", function() {
/** $pageEditShortTitle.val($(this).val());
* Masquer ou afficher le chemin de fer
* Quand la page n'est pas mère et que le menu n'est pas masqué
*/
var pageEditParentPageIdDOM = $("#pageEditParentPageId");
pageEditParentPageIdDOM.on("change", function() {
if ($(this).val() === "" &&
!$('input[name=pageEditHideTitle]').is(':checked') ) {
$("#pageEditbreadCrumbWrapper").removeClass("disabled");
$("#pageEditbreadCrumbWrapper").slideUp();
$("#pageEditExtraPositionWrapper").slideUp();
} else {
$("#pageEditbreadCrumbWrapper").addClass("disabled");
$("#pageEditbreadCrumbWrapper").slideDown();
$("#pageEditExtraPositionWrapper").slideDown();
}
if ($(this).val() !== "") {
$("#pageEditHideMenuChildrenWrapper").removeClass("disabled");
$("#pageEditHideMenuChildrenWrapper").slideUp();
$("#pageEditExtraPositionWrapper").slideUp();
} else {
$("#pageEditHideMenuChildrenWrapper").addClass("disabled");
$("#pageEditHideMenuChildrenWrapper").slideDown();
$("#pageEditExtraPositionWrapper").slideDown();
}
}); });
$pageEditExtraPosition.on("change", function() {
buildPagesList($(this).val() == 1);
/**
* Masquer ou afficher la sélection de l'icône
*/
var pageTypeMenuDOM = $("#pageTypeMenu");
pageTypeMenuDOM.on("change", function() {
if ($(this).val() !== "text") {
$("#pageIconUrlWrapper").addClass("disabled");
$("#pageIconUrlWrapper").slideDown();
} else {
$("#pageIconUrlWrapper").removeClass("disabled");
$("#pageIconUrlWrapper").slideUp();
}
}); });
/** $pageEditModuleConfig.on("click", function() {
* Duplication du champ Title dans Short title $("#pageEditModuleRedirect").val(1);
*/ $("#pageEditForm").trigger("submit");
$("#pageEditTitle").on("input", function() {
$("#pageEditShortTitle").val($(this).val());
}); });
/** $pageEditParentPageId.on("change", function() {
* Actualise la liste de pages lorsque le menu accessoire est sélectionné buildPagesList(false);
*/
// Initialise à Début si le menu accessoire est sélectionné
$("#pageEditExtraPosition").on("change", function() {
if ($("#pageEditExtraPosition").val() == 1 ) {
buildPagesList(true);
} else {
buildPagesList(false);
//$("#pageEditParentPageId").trigger("change");
}
});
/**
* Soumission du formulaire pour éditer le module
*/
$("#pageEditModuleConfig").on("click", function() {
$("#pageEditModuleRedirect").val(1);
$("#pageEditForm").trigger("submit");
});
/**
* Affiche les pages en fonction de la page parent dans le choix de la position
*/
$("#pageEditParentPageId").on("change", function() {
buildPagesList(false);
}).trigger("change"); }).trigger("change");
/**
* Construit un select contenant la liste des pages du site.
*/
function buildPagesList(extraPosition) { function buildPagesList(extraPosition) {
var hierarchy = <?php echo json_encode($this->getHierarchy()); ?>; // Récupération des données depuis les attributs data-*
var pages = <?php echo $module->getPageInfo(); ?>; const hierarchy = $("#pageEditDataContainer").data("hierarchy");
var positionInitial = <?php echo $this->getData(['page',$this->getUrl(2),"position"]); ?>; const pages = $("#pageEditDataContainer").data("pages");
var extraPosition = $("#pageEditExtraPosition").val(); const positionInitial = $("#pageEditDataContainer").data("position-initial");
var positionDOM = $("#pageEditPosition"); const currentPage = $("#pageEditDataContainer").data("current-page");
var message_none = "<?php echo helper::translate('Ne pas afficher'); ?>";
var message_begin = "<?php echo helper::translate('Au début'); ?>"; const positionDOM = $pageEditPosition;
var message_after = "<?php echo helper::translate('Après'); ?>"; const message_none = $("#pageEditDataContainer").data("translate-none");
positionDOM.empty().append( const message_begin = $("#pageEditDataContainer").data("translate-begin");
$("<option>").val(0).text(message_none), const message_after = $("#pageEditDataContainer").data("translate-after");
$("<option>").val(1).text(message_begin)
);
var parentSelected = $("#pageEditParentPageId").val();
var positionSelected = 0;
var positionPrevious = 1;
// Aucune page parent sélectionnée positionDOM.empty().append(
if(parentSelected === "") { $("<option>").val(0).text(message_none),
// Liste des pages sans parents $("<option>").val(1).text(message_begin)
for(var key in hierarchy) { );
if(hierarchy.hasOwnProperty(key) ) {
// Sélectionne la page avant s'il s'agit de la page courante
if(key === "<?php echo $this->getUrl(2); ?>") {
positionSelected = positionPrevious;
}
// Sinon ajoute la page à la liste
else {
// Enregistre la position de cette page afin de la sélectionner si la prochaine page de la liste est la page courante
if (extraPosition == pages[key].extraPosition ) {
positionPrevious++;
// Ajout à la liste
positionDOM.append(
$("<option>").val(positionPrevious).html(message_after + " \"" + (pages[key].title) + "\"")
);
}
} const parentSelected = $pageEditParentPageId.val();
} let positionSelected = 0;
} let positionPrevious = 1;
if (positionInitial === 0) {
positionSelected = 0; // --- Début de la logique originale ---
} if (parentSelected === "") {
} for (const key in hierarchy) {
// Une page parent est sélectionnée if (hierarchy.hasOwnProperty(key)) {
else { if (key === currentPage) {
// Liste des pages enfants de la page parent positionSelected = positionPrevious;
for(var i = 0; i < hierarchy[parentSelected].length; i++) { } else {
// Pour page courante sélectionne la page précédente (pas de - 1 à positionSelected à cause des options par défaut) if (extraPosition == pages[key].extraPosition) {
if(hierarchy[parentSelected][i] === "<?php echo $this->getUrl(2); ?>") { positionPrevious++;
positionSelected = positionPrevious; positionDOM.append(
} $("<option>").val(positionPrevious).html(`${message_after} "${pages[key].title}"`)
// Sinon ajoute la page à la liste );
else { }
// Enregistre la position de cette page afin de la sélectionner si la prochaine page de la liste est la page courante }
positionPrevious++; }
// Ajout à la liste }
positionDOM.append( if (positionInitial === 0) {
$("<option>").val(positionPrevious).html(message_after + " \"" + (pages[hierarchy[parentSelected][i]].title) + "\"") positionSelected = 0;
); }
} } else {
} for (let i = 0; i < hierarchy[parentSelected].length; i++) {
} if (hierarchy[parentSelected][i] === currentPage) {
// Sélectionne la bonne position positionSelected = positionPrevious;
positionDOM.val(positionSelected); } else {
}; positionPrevious++;
positionDOM.append(
$("<option>").val(positionPrevious).html(`${message_after} "${pages[hierarchy[parentSelected][i]].title}"`)
);
}
}
}
// --- Fin de la logique originale ---
positionDOM.val(positionSelected);
}
// Define function to capitalize the first letter of a string
function capitalizeFirstLetter(string) { function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1); return string.charAt(0).toUpperCase() + string.slice(1);
}
function toggleModuleElements(moduleId) {
const isRedirection = moduleId === "redirection";
const isEmpty = moduleId === "";
$pageModulePositionWrapper.toggleClass("disabled", !isEmpty && !isRedirection).slideToggle(isEmpty || isRedirection);
$pageEditContentWrapper.toggleClass("disabled", !isRedirection).slideToggle(isRedirection);
$pageEditHideTitleWrapper.toggleClass("disabled", !isRedirection).slideToggle(isRedirection);
$pageEditBlockLayout.toggleClass("disabled", !isRedirection).slideToggle(isRedirection);
}
function toggleBlockElements(blockValue) {
const isBar = blockValue === "bar";
const isFullWidth = blockValue === "12";
const isLeftBar = blockValue === "3-9" || blockValue === "4-8";
const isRightBar = blockValue === "9-3" || blockValue === "8-4";
const isDoubleBar = blockValue === "3-6-3" || blockValue === "2-7-3" || blockValue === "3-7-2";
$pageEditBarLeftWrapper.toggleClass("disabled", !isBar && !isFullWidth && !isRightBar).slideToggle(isBar || isFullWidth || isRightBar);
$pageEditBarRightWrapper.toggleClass("disabled", !isBar && !isFullWidth && !isLeftBar).slideToggle(isBar || isFullWidth || isLeftBar);
if (isBar) {
$pageEditMenu.removeClass("disabled").hide();
$pageEditHideTitleWrapper.removeClass("disabled").slideUp();
$pageTypeMenuWrapper.removeClass("disabled").slideUp();
$pageEditSeoWrapper.removeClass("disabled").slideUp();
$pageEditAdvancedWrapper.removeClass("disabled").slideUp();
$pageEditbreadCrumbWrapper.removeClass("disabled").slideUp();
$pageEditModuleIdWrapper.removeClass("disabled").slideUp();
$pageEditModuleConfig.removeClass("disabled").slideUp();
$pageEditDisplayMenuWrapper.addClass("disabled").slideDown();
$(".navSelect").slideUp();
} else {
$pageEditMenu.addClass("disabled").show();
$pageEditHideTitleWrapper.addClass("disabled").slideDown();
$pageTypeMenuWrapper.addClass("disabled").slideDown();
$pageEditSeoWrapper.addClass("disabled").slideDown();
$pageEditAdvancedWrapper.addClass("disabled").slideDown();
$pageEditModuleIdWrapper.addClass("disabled").slideDown();
$pageEditModuleConfig.slideDown();
$pageEditDisplayMenuWrapper.removeClass("disabled").slideUp();
$(".navSelect").slideDown();
if ($pageEditParentPageId.val() !== "") {
$pageEditbreadCrumbWrapper.addClass("disabled").slideDown();
$pageEditExtraPositionWrapper.slideDown();
}
if ($pageEditModuleId.val() === "") {
$pageEditModuleConfig.addClass("disabled");
} else {
$pageEditModuleConfig.removeClass("disabled");
}
}
}
function toggleBreadCrumb(isHideTitleChecked, parentPageId) {
$pageEditbreadCrumbWrapper.toggleClass("disabled", !isHideTitleChecked && parentPageId !== "").slideToggle(isHideTitleChecked || parentPageId === "");
}
function toggleIconUrl(menuType) {
$pageIconUrlWrapper.toggleClass("disabled", menuType !== "text").slideToggle(menuType !== "text");
}
function toggleMenuOptions(position, parentPageId, isDisableChecked) {
$pageEditHideMenuSideWrapper.toggleClass("disabled", position !== "0").slideToggle(position === "0");
$pageEditHideMenuChildrenWrapper.toggleClass("disabled", parentPageId === "" && !isDisableChecked).slideToggle(parentPageId !== "" || isDisableChecked);
} }

View File

@ -35,30 +35,53 @@
<?php echo template::button('pageEditContentButton', [ <?php echo template::button('pageEditContentButton', [
'value' => 'Contenu', 'value' => 'Contenu',
'class' => 'buttonTab', 'class' => 'buttonTab',
'href' => helper::baseUrl() . 'page/register/content/' . $this->geturl(2)
]); ?> ]); ?>
<?php echo template::button('pageEditPositionButton', [ <?php echo template::button('pageEditPositionButton', [
'value' => 'Menu', 'value' => 'Menu',
'class' => 'buttonTab', 'class' => 'buttonTab',
'href' => helper::baseUrl() . 'page/register/position/' . $this->geturl(2)
]); ?> ]); ?>
<?php echo template::button('pageEditExtensionButton', [ <?php echo template::button('pageEditExtensionButton', [
'value' => 'Extension', 'value' => 'Extension',
'class' => 'buttonTab', 'class' => 'buttonTab',
'href' => helper::baseUrl() . 'page/register/extension/' . $this->geturl(2)
]); ?> ]); ?>
<?php echo template::button('pageEditLayoutButton', [ <?php echo template::button('pageEditLayoutButton', [
'value' => 'Mise en page', 'value' => 'Mise en page',
'class' => 'buttonTab', 'class' => 'buttonTab',
'href' => helper::baseUrl() . 'page/register/layout/' . $this->geturl(2)
]); ?> ]); ?>
<?php echo template::button('pageEditPermissionButton', [ <?php echo template::button('pageEditPermissionButton', [
'value' => 'Permission', 'value' => 'Permission',
'class' => 'buttonTab', 'class' => 'buttonTab',
'href' => helper::baseUrl() . 'page/register/permission/' . $this->geturl(2)
]); ?> ]); ?>
</div> </div>
<?php
// Données dynamiques à insérer dans le JavaScript
$data = [
'translate-delete' => helper::translate('Confirmer la suppression de la page'),
'translate-module-delete' => helper::translate('Confirmer la suppression des données du module'),
'translate-none' => helper::translate('Ne pas afficher'),
'translate-begin' => helper::translate('Au début'),
'translate-after' => helper::translate('Après'),
'hierarchy' => json_encode($this->getHierarchy()),
'pages' => $module->getPageInfo(),
'position-initial' => $this->getData(['page', $this->getUrl(2), 'position']),
'current-page' => $this->getUrl(2),
'page-layout' => $this->getData(['user', $this->getUser('id'), 'view', 'page'])
];
// Génération du contenu JavaScript
echo '<div id="pageEditDataContainer"';
foreach ($data as $key => $value) {
// Convertit explicitement les valeurs null en chaîne vide
$sanitizedValue = $value ?? '';
echo ' data-' . htmlspecialchars($key) . '="' . htmlspecialchars((string)$sanitizedValue) . '"';
}
echo '></div>';
?>
<!-- Champ caché pour transmettre l'onglet-->
<?php echo template::hidden('containerSelected'); ?>
<div id="pageEditContentContainer" class="tabContent"> <div id="pageEditContentContainer" class="tabContent">
<div class="row"> <div class="row">
<div class="col12"> <div class="col12">
@ -99,7 +122,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col12"> <div class="col12">
<?php echo template::textarea('pageEditContent', [ <?php echo template::textarea('pageEditWysiwyg', [
'class' => 'editorWysiwyg', 'class' => 'editorWysiwyg',
'value' => $this->getPage($this->getUrl(2), self::$siteContent) 'value' => $this->getPage($this->getUrl(2), self::$siteContent)
]); ?> ]); ?>

View File

@ -893,11 +893,11 @@ class theme extends common
$redirect = ''; $redirect = '';
switch ($this->getUrl(2)) { switch ($this->getUrl(2)) {
case 'admin': case 'admin':
$this->initData('admin', self::$i18nUI); unlink(self::DATA_DIR . 'admin.css');
$redirect = helper::baseUrl() . 'theme/admin'; $redirect = helper::baseUrl() . 'theme/admin';
break; break;
case 'manage': case 'manage':
$this->initData('theme', self::$i18nUI); $this->initData('theme', self::$siteContent);
$redirect = helper::baseUrl() . 'theme/manage'; $redirect = helper::baseUrl() . 'theme/manage';
break; break;
case 'custom': case 'custom':

View File

@ -594,11 +594,12 @@ class user extends common
); );
} }
// L'utilisateur n'existe pas, on ne le précise pas
// Valeurs en sortie // Valeurs en sortie
$this->addOutput([ $this->addOutput([
'notification' => helper::translate('Un mail a été envoyé pour confirmer la réinitialisation'), 'notification' => $sent === true ? helper::translate('Un mail a été envoyé pour confirmer la réinitialisation') : helper::translate('Le mail de réinitialisation ne peut pas être envoyé, contactez l\'administrateur'),
'state' => ($sent === true ? true : null) 'state' => ($sent === true ? true : false),
'redirect' => helper::baseUrl()
]); ]);
} }
// Valeurs en sortie // Valeurs en sortie
@ -679,18 +680,19 @@ class user extends common
continue; continue;
} }
// Formatage de la liste // Formatage de la liste
self::$users[] = [ self::$users[] = [
//$userId, //$userId,
$this->getData(['user', $userId, 'firstname']) . ' ' . $userLastNames, sprintf('%s %s',$userLastNames, $this->getData(['user', $userId, 'firstname'])),
helper::translate(self::$groups[(int) $this->getData(['user', $userId, 'group'])]), helper::translate(self::$groups[(int) $this->getData(['user', $userId, 'group'])]),
empty($this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name'])) empty($this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name']))
? helper::translate(self::$groups[(int) $this->getData(['user', $userId, 'group'])]) ? helper::translate(self::$groups[(int) $this->getData(['user', $userId, 'group'])])
: $this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name']), : $this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name']),
$this->getData(['user', $userId, 'tags']), $this->getData(['user', $userId, 'tags']),
helper::dateUTF8('%d/%m/%Y', $this->getData(['user', $userId, 'accessTimer']), self::$i18nUI), is_null($this->getData(['user', $userId, 'accessTimer']))
? 'Jamais'
: $this->getData(['user', $userId, 'accessTimer']),
//helper::dateUTF8('%d/%m/%Y', $this->getData(['user', $userId, 'accessTimer']), self::$i18nUI),
//helper::dateUTF8('%H:%M', $this->getData(['user', $userId, 'accessTimer']), self::$i18nUI), //helper::dateUTF8('%H:%M', $this->getData(['user', $userId, 'accessTimer']), self::$i18nUI),
template::button('userEdit' . $userId, [ template::button('userEdit' . $userId, [
'href' => helper::baseUrl() . 'user/edit/' . $userId, 'href' => helper::baseUrl() . 'user/edit/' . $userId,

View File

@ -15,4 +15,14 @@
/** NE PAS EFFACER /** NE PAS EFFACER
* admin.css * admin.css
*/ */
/** Hide the timestamp column in the user list
*/
tbody td:nth-child(5) {
color: transparent; /* Masquer le texte par défaut */
}
tbody td.visible-text {
color: inherit; /* Rétablir la couleur du texte */
}

View File

@ -31,6 +31,20 @@ $(document).ready((function () {
stateSave: true, stateSave: true,
"lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "Tout"]], "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "Tout"]],
"columnDefs": [ "columnDefs": [
{
target: 4,
type: 'num', // Utilisez 'num' pour le tri
render: function (data) {
// Si data est un nombre, formatez-le en date
if (typeof data === 'number' || !isNaN(data)) {
return moment(Number(data) * 1000).format('DD/MM/YYYY HH:mm');
} else {
return data; // Sinon, affichez le texte tel quel
}
},
orderable: false,
searchable: false
},
{ {
target: 5, target: 5,
orderable: false, orderable: false,
@ -43,4 +57,15 @@ $(document).ready((function () {
} }
] ]
}); });
// Injecter la règle CSS pour la colonne cible
$('<style>')
.prop('type', 'text/css')
.html(`
table.dataTable tbody td:nth-child(5) {
color: inherit !important; /* Rétablir la couleur du texte */
}
`)
.appendTo('head');
})); }));

View File

@ -68,4 +68,4 @@
</div> </div>
</div> </div>
<?php echo template::formClose(); ?> <?php echo template::formClose(); ?>
<?php echo template::table([3, 2, 2, 2, 2, 1, 1], user::$users, ['Nom', 'Groupe', 'Profil', 'Étiquettes', 'Date dernière vue', '', ''], ['id' => 'dataTables']); ?> <?php echo template::table([3, 2, 2, 1, 3, 1, 1], user::$users, ['Nom', 'Groupe', 'Profil', 'Étiquettes', 'Dernière connexion', '', ''], ['id' => 'dataTables'], ['name','group','profile','tag','data-timestamp','edit','delete']); ?>

File diff suppressed because one or more lines are too long

BIN
core/vendor/zwiico/png/error.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

@ -1,3 +1,5 @@
# Version 4.6
- Correction de syntaxe.
# Version 4.5 # Version 4.5
- Remplacement du nom générique de classe dans les vues. - Remplacement du nom générique de classe dans les vues.
# Version 4.4 # Version 4.4

View File

@ -1 +1 @@
{"name":"form","realName":"Formulaire","version":"4.1","update":"0.0","delete":true,"dataDirectory":""} {"name":"form","realName":"Formulaire","version":"4.6","update":"0.0","delete":true,"dataDirectory":""}

View File

@ -17,7 +17,7 @@
class form extends common class form extends common
{ {
const VERSION = '4.5'; const VERSION = '4.6';
const REALNAME = 'Formulaire'; const REALNAME = 'Formulaire';
const DATADIRECTORY = ''; // Contenu localisé inclus par défaut (page.json et module.json) const DATADIRECTORY = ''; // Contenu localisé inclus par défaut (page.json et module.json)
@ -479,7 +479,7 @@ class form extends common
if (!empty($singlemail)) { if (!empty($singlemail)) {
$to[] = $singlemail; $to[] = $singlemail;
} }
if ($to) { if (empty($to)=== false) {
// Sujet du mail // Sujet du mail
$subject = $this->getData(['module', $this->getUrl(0), 'config', 'subject']); $subject = $this->getData(['module', $this->getUrl(0), 'config', 'subject']);
if ($subject === '') { if ($subject === '') {
@ -495,6 +495,7 @@ class form extends common
$this->getData(['config', 'smtp', 'from']) $this->getData(['config', 'smtp', 'from'])
); );
} }
} }
// Redirection // Redirection
$redirect = $this->getData(['module', $this->getUrl(0), 'config', 'pageId']); $redirect = $this->getData(['module', $this->getUrl(0), 'config', 'pageId']);