Compare commits

...

228 Commits

Author SHA1 Message Date
9df1fe8be2 1.14.06 En-tête export manqueait mail 2024-10-25 10:20:31 +02:00
0f71d820c5 1.14.06 Ajout du champ mail dans l'export de la progression des participants 2024-10-25 10:04:01 +02:00
a8891b310f 1.14.05 2024-10-25 09:25:31 +02:00
59de6bc32f 1.14.05 Stocke la progression d'un participant dans la bdd enrolment pour éviter un recalcul à l'avenir. 2024-10-25 09:25:16 +02:00
3a25c33e3a Merge branch 'master' into user_progress 2024-10-24 09:47:45 +02:00
eff57bf0ab Stokce la progression dans la base des inscriptions 2024-10-24 09:37:46 +02:00
20c09d8b0a 1.14.04 Fix methode name usersReportExport 2024-10-21 21:20:09 +02:00
a634445dde initialisation reste l'affichage 2024-10-20 17:59:08 +02:00
4c88d7a71d Revert "Fix bug parametre de langue inutile dans RFM"
This reverts commit 889e2754fc.
2024-10-20 16:13:29 +02:00
bb9b8b086f 1.14.03 et non 1.14.04 2024-10-20 16:10:04 +02:00
61752a9a51 Optimisation récupérationstatistiques individuelle, la méthode getReports peut retourner les stats d'un utilisateur 2024-10-20 16:07:58 +02:00
889e2754fc Fix bug parametre de langue inutile dans RFM 2024-10-20 16:06:56 +02:00
77b241c69f 1.14.04 TEST Empêche qu'un espace réservé aux membres ne reste ouvert après une déconnexion de la session 2024-10-17 10:40:50 +02:00
442b84a99d Commente un test de validation de la consultation d'un espace 2024-10-16 12:27:32 +02:00
09f9f6bdd6 Optimisation setData 2024-10-12 11:16:44 +02:00
5eace20e26 Fix position d'un bouton 2024-10-12 11:10:58 +02:00
18df3848f8 Update Blog and News 2024-10-12 11:02:08 +02:00
7b9d145533 supprime console.log 2024-10-12 08:15:54 +02:00
75ad5853cf 1.14.03 fix subword 2024-10-10 19:59:53 +02:00
7862815f1f 1.14.02 Module blog bug de présentation, version 7.12 2024-10-08 15:23:51 +02:00
a87d439282 fix update store 2024-10-03 12:01:59 +02:00
509c1d6365 Bug de changement de mot de passe 2024-10-02 19:00:05 +02:00
e641ec1c03 Fix module checkup 2024-10-02 12:40:51 +02:00
3dec8a8ce5 Points virgules !! 2024-10-02 12:18:36 +02:00
54f0b0c6e3 redondance de déclaration de charset 2024-10-02 12:12:52 +02:00
931e59db2e Merge branch 'master' of https://forge.chapril.org/ZwiiCMS-Team/ZwiiCampus 2024-10-02 11:54:16 +02:00
cae0e6c8fb fix reset password key check 2024-10-02 11:54:11 +02:00
afee77b1e7 fix reset password key check 2024-10-02 11:50:19 +02:00
cf8963248e Merge branch 'reste_user_message' of https://forge.chapril.org/ZwiiCMS-Team/ZwiiCampus into reste_user_message 2024-10-02 07:56:09 +02:00
22af8ac751 message rest password 2024-10-02 07:56:04 +02:00
2d4385f038 :q 2024-10-02 07:52:40 +02:00
c4bb124bea Init 2024-09-30 19:22:08 +02:00
a474a41402 init 2024-09-30 18:50:31 +02:00
e9f65a1e22 Bug de connexion quand le site est à la racine 2024-09-30 15:38:15 +02:00
8183b0bb0b Install optimisation config 2024-09-29 18:55:30 +02:00
585c888007 supprime un log 2024-09-29 16:00:58 +02:00
d0997ccbf9 Version 1.13.00 2024-09-29 14:17:19 +02:00
1a6bfab54b Supprimer les traceurs de process de stockage 2024-09-29 14:12:32 +02:00
23f38a606f Forçage inutile 2024-09-28 18:34:10 +02:00
f1e1989c97 Corrections Rémi, optimisations 2024-09-28 17:54:05 +02:00
9bb5793720 save = false and saveDB 2024-09-28 15:34:00 +02:00
64443322a0 Merge branch 'master' into write_process 2024-09-28 13:32:46 +02:00
76258c835f 1.12.00.16 restaure l'onglet étiquette dans la configuration 2024-09-28 13:30:48 +02:00
dc81614114 init WIP 2024-09-28 13:24:55 +02:00
8dc1adc28a 1.12.00.15 Position d'un bouton de la vlidation dans la configuration 2024-09-27 10:25:20 +02:00
65eac4ac4a 1.12.00.14 user vide écrit au logout 2024-09-26 19:58:00 +02:00
a8f70dc73a suscribe 2.3 2024-09-26 19:42:24 +02:00
02aeb6ec22 readme 2024-09-26 14:25:19 +02:00
5bdb38dfec Message modification tag(s) 2024-09-26 10:54:13 +02:00
5700200bc3 erreur de commentaire 2024-09-26 10:51:19 +02:00
4ffca0e34a 1.12.00.13 bug initialisation et content dans le script RFM 2024-09-26 10:42:30 +02:00
f4553110e1 1.12.00.12 inverse la position des icones users et config 2024-09-19 10:52:07 +02:00
bf3b1f9cb0 suscribe 2.2 enum 2024-09-19 10:47:20 +02:00
05d8ae9e0b 1.12.00.11 empêche la saisie des caractères interdits lors de l'auto inscription dans le champ id du login 2024-09-19 10:43:24 +02:00
6128d0778d 2.2 register 2024-09-19 10:41:32 +02:00
c38bd7d12f 1.12.00.10 Erreur d'initialisation 2024-09-19 09:59:59 +02:00
cd509dfcb1 Suppression du cookie configLayout 2024-09-18 19:33:59 +02:00
5ab8efe72e 1.12.00.09 supprime le cookie pageLayout 2024-09-18 19:01:44 +02:00
6cf6a8d6b2 numéro de version 2024-09-18 10:19:05 +02:00
daaf309b4d Numero de version 2024-09-18 10:17:46 +02:00
2d5c395aec numérotation et changes 2024-09-17 19:14:03 +02:00
5d5b1e3140 Merge branch 'master' of https://forge.chapril.org/ZwiiCMS-Team/ZwiiCampus 2024-09-17 19:05:27 +02:00
8aa24e50ab 1.12.08 empêche la déconnexion après la modification du compte connecté 2024-09-17 19:05:21 +02:00
8574970bb0 1.12.08 corrige l'affichage des flèches de navigation dans les vues des modules 2024-09-17 18:23:32 +02:00
89e6e12748 1.12.08 supprime le cookie relatif au contenu de site sans passer par main.php 2024-09-17 17:52:57 +02:00
9234b76062 1.12.08 change 2024-09-16 23:42:58 +02:00
08c1619162 1.12.08 suppression du cookie ZWII_SITE_CONTENT 2024-09-16 23:40:59 +02:00
576f349b4a changes 2024-09-16 23:01:40 +02:00
be6eaa748e 1.12.07 la clé n'a pas été déclarée 2024-09-16 22:55:58 +02:00
1dc7b08d2e 1.12.06 Index correct 2024-09-16 21:24:17 +02:00
35521766cd 1.12.06 Chemin du cookie de session 2024-09-16 21:20:35 +02:00
c8ee063963 1.12.05 modification du cookie de consentement 2024-09-15 19:00:40 +02:00
f0aba44ddb 1.12.05 icone plugin rouge si module mis à jour 2024-09-15 17:54:25 +02:00
e6236e5de2 readme 2024-09-09 20:55:20 +02:00
e5629de660 1.12.04 Complète l'ide de site avec le chemin 2024-09-08 15:00:06 +02:00
92d3da27a3 1.12.03 2024-09-07 18:17:24 +02:00
1fd0c5492f 1.12.03 bouton de génération du sitemap 2024-09-07 14:25:47 +02:00
bab06033ed 1.11.03 bloque les bots sauf les moteurs 2024-09-07 14:12:22 +02:00
f7cf0f94fc Search 3.3 une seule lecture de module 2024-09-07 09:53:54 +02:00
c244651b62 readme ! 2024-09-07 08:53:38 +02:00
beb58da3bd rfm thumb config 2024-09-07 08:18:40 +02:00
8756162428 1.12.01 default RFM thumb size 2024-09-07 08:11:24 +02:00
511a6ff0bb csrf key sur 64 octets 2024-09-06 17:18:17 +02:00
4175ff828d 1.12.00 Méthode d'authentification 2024-09-06 17:16:04 +02:00
087b5d9999 1.11.01 isole la session dans l'onglet 2024-09-06 09:35:12 +02:00
fea1c40a4b Bug datatables ajout dans user 2024-09-06 09:08:21 +02:00
2dce24f9f2 Bug datatables 2024-09-06 09:06:52 +02:00
0e0fd23913 version 1.11.00 2024-09-06 08:39:12 +02:00
0eab6c9049 1.11.02 datables.net filtrage des éléments, nombre d'éléments et position sur l'écran 2024-09-06 08:35:25 +02:00
ca7ce4e857 1.11.01 bouton décalé 2024-09-05 10:51:06 +02:00
510afbe8f1 1.11.01 le template file affiche le dossier du fichier ou de l'image sélectionnée 2024-09-05 10:31:35 +02:00
c89a64ca7f readme 2024-09-05 08:28:51 +02:00
9280d59aac readme 2024-09-05 08:24:33 +02:00
3accdc135b readme 2024-09-05 08:23:50 +02:00
3d31ce5e55 Libellés 2024-09-05 08:05:57 +02:00
8c60eba918 Icônes tag + design et zones d' aide 2024-09-04 20:28:43 +02:00
b182c2ec90 Titre de la page d'export 2024-09-04 20:21:42 +02:00
523f30027a Position des boutons selectall et selectnone dans le filtre 2024-09-04 20:06:36 +02:00
f9faaa651e Design gestion des tags 2024-09-04 19:26:29 +02:00
1aca92b1c7 Edition des tags 2024-09-04 18:03:50 +02:00
4444c58902 Amélioration esthétique 2024-09-04 09:34:10 +02:00
75dbae7cc6 commentaire et renomme resource 2024-09-04 09:27:27 +02:00
79e0ac2a38 Droits du profil associés à l'export 2024-09-04 09:19:17 +02:00
1cb4b191bf Formatage du code 2024-09-04 08:48:36 +02:00
485392e772 Optimisation de l'export 2024-09-04 08:48:10 +02:00
9c5f6448b1 Bug export 2024-09-04 08:30:38 +02:00
b78e52c5d4 course export Boutons tout et rien sélectionner
Portée sélection excluant les barres
2024-09-04 08:25:10 +02:00
e687f26d2d Tri des pages selon la position 2024-09-03 21:38:12 +02:00
769808ca4e Version 1.11.00 2024-09-03 19:05:00 +02:00
5ba20aec09 Pointer RFM sur le dossier de l'espace 2024-09-03 19:04:37 +02:00
5c0efda0ca Page manage, RFM pointe sur le dossier de l'espace 2024-09-03 19:02:02 +02:00
b6d3eb8ec2 Export du coentnu html ok 2024-09-03 18:54:41 +02:00
30df948d81 1.10.16 suppression de compte en masse, protection du compte connecté 2024-09-02 21:44:39 +02:00
15430af1d6 1.10.15 warning à la création d'une nouvelle page 2024-08-29 06:45:43 +02:00
2aae298d7d Revert plugin 2024-08-28 21:17:10 +02:00
adeaeb624b 1.10.14 catalogue parfois vide, erreur d'url, slash en fin d'adresse 2024-08-28 21:01:31 +02:00
d98e235dfc 1.10.13 Login, connexion persistante 2024-08-24 17:29:13 +02:00
ef5de018b0 1.10.12 fix bug mot clé [MODULE] absent 2024-08-24 07:20:17 +02:00
c024340af7 suprime un commentaire 2024-08-23 21:19:30 +02:00
6a964938bc 1.10.11 slider 7.1 2024-08-22 05:33:37 +02:00
f024a960e4 slider 7.1 2024-08-22 05:33:06 +02:00
c3ff13856b Version 2024-08-19 16:45:45 +02:00
806e1cc14f Slider 7.0 2024-08-19 16:45:07 +02:00
a48cdf6690 1.10.09 Étiquette et non Etiquette 2024-08-19 15:46:38 +02:00
df646488d6 1.10.08 2024-08-15 18:04:55 +02:00
1a36e233be Corrige le filtre FILTER_FLOAT: pour la géolocalisation 2024-08-15 17:54:22 +02:00
b8c5d613bc Filtres CMS 13.3.05 2024-08-14 09:23:38 +02:00
60455ae036 1.10.07 Mise à jour du module BLOG 2024-08-01 19:16:04 +02:00
893db4034d 1.10.06 mise à jour module News 2024-07-30 21:17:55 +02:00
3d1d4be997 Corrige la génération des miniatures au format avif et webp 2024-07-28 18:04:14 +02:00
5f3bd590a8 Module auto inscription en version 2 2024-07-20 17:33:14 +02:00
cbffc3f44b update suscribe 2024-07-17 20:13:53 +02:00
8131162dae Vérification de la réécriture 2024-06-09 02:51:55 +02:00
72655aec3d Page de configuration des modules inactives 2024-06-07 20:47:25 +02:00
08ed4075c7 Chargement lassif des images 2024-06-07 09:57:23 +02:00
6d5ddf1f8d Corrige le bug de FF 2024-06-07 09:47:20 +02:00
8b354ff40e Corrige la détection de serveur 2024-06-07 09:42:57 +02:00
5be3d0b1a7 Layout d'édition : supprime tous les boutons de gestion de page comme pour les pages avec module 2024-06-07 09:32:52 +02:00
f0c4ea6bd0 Lorsque des éléments inutiles sont ajoutés à l'adresse d'une page, une erreur 403 est levée. 2024-06-04 22:21:10 +02:00
cbe06426bb ** Corrections :**
- Dans la configuration, l'option Apache URL intelligentes ne s'active que si le serveur est Apache et que le module Rewriter est actif. Ce qui exclue les autres serveurs non compatibles comme Nginx, Caddy etc.
2024-06-02 15:07:55 +02:00
adac7395cc Lignes vides en trop dans htaccess 2024-06-02 14:40:38 +02:00
2c459a03d4 nouvel htaccess 2024-06-02 14:33:59 +02:00
751bc450b5 1.10.04
** Corrections :**
- L'ajout d'un slash en fin d'adresse avec la réécriture active provoquait une mauvaise détermination des adresses des images dans TinyMCE. Résolution : une directive htaccess supprime tous les slash en fin d'adresse.
- Lorsque la page est ouverte en édition, un clic sur le bouton édition dans la barre d'administration affiche une erreur, le lien étant incorrect. Afin d'éviter cette erreur et une redondance, le bouton d'édition est masqué lorsque la page est éditée.
2024-06-01 21:20:41 +02:00
6c8d9097f6 Readme version 2024-05-20 14:15:03 +02:00
b0814d50dc 1.10.03 Workshop Tri des espaces par titre 2024-05-18 18:47:06 +02:00
a7a2424d93 1.10.02 2024-05-16 09:26:23 +02:00
43b15987d6 Suivi Zwii 13.3.02 2024-05-16 09:25:40 +02:00
e100a580ad Mise à jour des modules 1.10.01 2024-05-08 18:47:43 +02:00
397bdee655 Merge branch '1902-isole-les-commandes-sur-les-espaces' 2024-04-23 18:13:06 +02:00
78b2d87ec1 supprime sync.db ! 2024-04-23 18:09:55 +02:00
8dedb39a33 Déplace les boutons de gestion des utilisateurs dans la liste des espaces. 2024-04-23 18:09:28 +02:00
7cee16389f commentaire 2024-04-23 17:25:50 +02:00
04b2877c48 1.10.00 2024-04-23 14:58:01 +02:00
f6c7e5f954 Dans router self devient common
Nouveau routage en cas de changement d'espace en évitant une 404
2024-04-23 14:57:10 +02:00
b5ea22021b redirige vers la page ouverte 2024-04-23 11:54:50 +02:00
3ee1398e2f supprime un commentaire 2024-04-23 11:54:32 +02:00
cfb07b81d5 Id non fourni 2024-04-22 21:46:41 +02:00
0da23149db 2.0.0 corrige le warning blog vide + transmet tn paramètre sécurisant l'édition des espaces dans plusieurs onglets 2024-04-22 15:40:35 +02:00
1322cb4eb0 Revert "section par main"
This reverts commit 271ee347a8.
2024-04-22 14:19:47 +02:00
167ad06dde Revert "section par main"
This reverts commit 271ee347a8.
2024-04-22 14:16:12 +02:00
271ee347a8 section par main 2024-04-22 10:56:49 +02:00
04a6d86999 1.9.01 supprime la balise section 2024-04-21 15:54:15 +02:00
05e6ff8f9b init 2024-04-18 14:52:13 +02:00
6e53ca8766 readme 2024-04-18 10:37:56 +02:00
dda93cf3d3 Merge branch 'master' of https://forge.chapril.org/fredtempez/ZwiiLMS 2024-04-18 10:37:19 +02:00
075e4afa1c changes 2024-04-18 10:37:11 +02:00
fd144d55f0 changes 2024-04-18 10:32:27 +02:00
b595120227 1.9 Désincription en masse de la plateforme 2024-04-18 10:27:33 +02:00
b0b3b34bde 1.8.01 alignement de boutons 2024-04-17 16:42:03 +02:00
2f592e2818 1.8.01
Corrige :
- Mauvais initialisation d'un profil
Modifie :
- Ajoute des effets de sélection dans la gestion des profils des espaces (add and edit)
2024-04-17 10:53:02 +02:00
8c9bc40a02 1.8.01
Corrige :
 - tableau de stats vides affichant une erreur
 - correction relative au rapport d'un participant
Nouvelle fonctionnalité :
- Bouton de réinitialisation d'un espace, efface les historiques et les inscriptions, les désinscriptions des participants et par autorité ne suppriment plus les historiques.
2024-04-17 10:33:00 +02:00
37f77b3d50 readme 2024-04-13 10:13:48 +02:00
a5130e1d9f readme 2024-04-13 10:13:13 +02:00
256da35d53 1.8.00 user report in csv 2024-04-13 10:07:47 +02:00
bb93b1b313 User report ok 2024-04-13 10:05:32 +02:00
195ed682f6 Merge branch 'master' into 1.8.00-report-csv 2024-04-12 22:10:38 +02:00
ae71b9649d Clic dans le TR active la case à cocher. 2024-04-12 22:09:37 +02:00
71418548e7 getReport utilisé dans la liste des users d'un courseId 2024-04-12 22:04:24 +02:00
03c7e0a7fa update.inc.php mise à jour ok 2024-04-12 21:22:57 +02:00
6bc8636692 Update json to csv ok 2024-04-12 09:52:39 +02:00
c0d14596a7 fonction update à tester 2024-04-11 15:46:42 +02:00
6a72676c7c update to test 2024-04-11 07:58:26 +02:00
44c4997436 upodaite to 1.8 WIP 2024-04-11 07:55:48 +02:00
cce73886a2 UsersHistory devient UsersReport 2024-04-11 07:46:27 +02:00
b9733d7391 User History devient user report 2024-04-11 07:44:43 +02:00
56ff0580a1 création du CS sans courseID 2024-04-10 18:59:41 +02:00
5b9f86fb38 Code à mettre dans une fonction 2024-04-10 18:58:04 +02:00
d43a78d307 1.7.09 icones de tout sélectionner ou tout déselectionner 2024-04-10 18:41:39 +02:00
deedb002e8 1.8.00 report csv 2024-04-10 14:31:29 +02:00
6d7c2f725d secure_file_put_contents stocke les données brutes 2024-04-10 11:56:31 +02:00
5ede9b71c2 1709 supprime la variable sessionId 2024-04-10 08:33:53 +02:00
04f4c7be6d 1.7.09 bug double slash initDB 2024-04-10 08:32:08 +02:00
0c94b2f546 erreur secure_file_put_contents 2024-04-09 18:58:22 +02:00
8b72ae01dc Revert "DOuble slashs"
This reverts commit 0934bf847d.
2024-04-09 18:51:11 +02:00
1ca77b89ce Merge branch 'master' of https://forge.chapril.org/fredtempez/ZwiiLMS 2024-04-09 18:42:06 +02:00
0934bf847d DOuble slashs 2024-04-09 18:40:52 +02:00
2a152a9a1d version 2024-04-09 18:38:50 +02:00
bc8fbc17b8 numéro de version² 2024-04-09 18:32:54 +02:00
a8635f0c8a supprime target new dans course manage 2024-04-09 18:32:05 +02:00
ca875f9f05 Supprime un echo de test. 2024-04-09 18:29:05 +02:00
b4258fe496 Supprimer le target blank 2024-04-09 18:20:54 +02:00
509c95a680 double fonction 2024-04-09 18:17:13 +02:00
2027c5918f Merge commit '0f55df8d31a7093f83c2ee4510a1d27bb9a087ac' 2024-04-09 18:14:16 +02:00
3593e853d8 DOuble slashs 2024-04-09 18:07:41 +02:00
cb13d2b0c9 session_id WIP 2024-04-09 15:09:02 +02:00
0f55df8d31 En test 2024-04-09 14:33:01 +02:00
939cb8d53f 1.7.09 réintalle la dernière version correcte 2024-04-09 13:32:25 +02:00
730e4ac9c4 1.7.09 reformatage 2024-04-07 18:14:43 +02:00
d3d96f795a formatage 2024-04-06 09:14:37 +02:00
e68f58fad1 Merge branch 'master' of https://forge.chapril.org/fredtempez/ZwiiLMS 2024-04-06 09:13:07 +02:00
6c0f34ae71 1.7.09 flock sur le fichier principal (PB sous win) 2024-04-06 09:10:52 +02:00
9373880239 Détruit le fichier de verrouillage créé par secureFilePutContent 2024-04-05 18:21:09 +02:00
a35036e0fa Double slashs 2024-04-05 17:54:10 +02:00
19246a59c9 déplace les constantes 2024-04-05 17:16:25 +02:00
15c4d1edd3 annule secureFilePutContents dans les modules. Non justifié dans la configuration du module 2024-04-05 16:51:03 +02:00
24f679d531 1.7.09 fonction secureFilePutContents 2024-04-05 16:33:46 +02:00
56f8f03581 readme 2024-04-05 09:22:38 +02:00
ae1f15e8b8 1.7.09 Formatage 2024-04-05 09:07:38 +02:00
7544bb9862 1.7.09 ajoute un temps d'attente 2024-04-05 09:06:42 +02:00
ebd078848a 1.7.09 durcit le contrôle d'enregistrement un verrou 2024-04-05 09:06:26 +02:00
e8e4f98be0 1.7.09 json db 2024-04-03 12:52:49 +02:00
c8c74d9be3 1.7.09 supprimer un test dans jsondb 2024-04-03 12:50:42 +02:00
63541cf83a 1.7.08 worhsop liend e désinscription dans les options 2024-04-02 18:25:39 +02:00
1cb54d46b1 Revert "1.7.08 Le lien de désinscription par l'utilisateur ne supprime plus l'historique."
This reverts commit 84df59c52b.
2024-04-02 18:12:24 +02:00
cf3936b7c0 Merge branch 'master' of https://forge.chapril.org/fredtempez/ZwiiLMS 2024-04-02 12:54:52 +02:00
84df59c52b 1.7.08 Le lien de désinscription par l'utilisateur ne supprime plus l'historique. 2024-04-02 12:54:47 +02:00
c75b71f217 1.7.07 Le lien de désinscription par l'utilisateur ne supprime plus l'historique. 2024-04-02 12:54:19 +02:00
269abc5699 Version 1.7.07 2024-03-30 09:17:06 +01:00
203 changed files with 4844 additions and 2563 deletions

View File

@ -32,5 +32,13 @@ Options -Indexes
Options -MultiViews
</IfModule>
# Enlever le slash final des URL
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} ^(.+)/$
RewriteRule ^ %1 [R=301,L]
# ne pas supprimer la ligne URL rewriting !
# URL rewriting

View File

@ -1,7 +1,17 @@
# ZwiiCampus 1.7.06
# ZwiiCampus 1.14.06
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é.
Ce logiciel s'installe en ligne ou localement depuis un serveur Web.
## Principales fonctionnalités :
- Gestion des espaces d'enseignement : Créez des espaces de formation avec accès restreint.
- Participation et suivi des apprenants : Inscription libre, imposée ou avec clé, et suivi statistique détaillé des participants.
- Gestion des accès : Contrôlez la disponibilité des espaces (ouvert, limité dans le temps, fermé).
- Outils statistiques : Visualisation et exportation CSV des progrès des apprenants.
- Facilité d'installation : Pas besoin de base de données, tout fonctionne avec des fichiers JSON.
- Sauvegarde et restauration : Outils de gestion pour réinitialiser les espaces et les participations.
## Configuration recommandée
@ -68,6 +78,7 @@ A l'occasion de l'installation d'une version majeure, il est recommandé de réa
[F] module.json Données des modules de pages
[F] theme.css Thème de ce contenu
[F] theme.json Thème de ce contenu
[F] report.csv Rapport de participation
[R] content Dossier des contenus de page
[F] accueil.html Exemple contenu de la page d'accueil
[R] fonts Dossier contenant les fontes installées
@ -84,7 +95,7 @@ A l'occasion de l'installation d'une version majeure, il est recommandé de réa
[F] core.json Configuration du noyau
[F] course.json Données de contenus
[F] custom.css Feuille de style de la personnalisation avancée
[F] enrolment.json Données des inscriptions et des statistiques par contenu
[F] enrolment.json Inscriptions dans les espaces, dernière page vue et timetamp
[F] font.json Descripteur des fontes personnalisées
[F] journal.log Journalisation des activités
[F] language.json Langues de l'interface

View File

@ -8,7 +8,7 @@ class helper
/** Filtres personnalisés */
const FILTER_BOOLEAN = 1;
const FILTER_DATETIME = 2;
const FILTER_DATETIME = 2; // filtre pour le champ de formulaire A conserver pour la compatibilité
const FILTER_FLOAT = 3;
const FILTER_ID = 4;
const FILTER_INT = 5;
@ -16,8 +16,14 @@ class helper
const FILTER_PASSWORD = 7;
const FILTER_STRING_LONG = 8;
const FILTER_STRING_SHORT = 9;
const FILTER_TIMESTAMP = 10;
const FILTER_TIMESTAMP = 10; // Saisie d'une date en locatime
const FILTER_URL = 11;
const FILTER_DATE = 12; // filtre pour le champ de formulaire
const FILTER_TIME = 13; // filtre pour le champ de formulair
const FILTER_MONTH = 14; // filtre pour le champ de formulair
const FILTER_YEAR = 16; // filtre pour le champ de formulair
/**
@ -77,7 +83,7 @@ class helper
// Créer la variable
$data = array_merge($data, [$text => '']);
}
file_put_contents('site/i18n/' . $to . '.json', json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT), LOCK_EX);
file_put_contents('site/i18n/' . $to . '.json', json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
}
}
@ -193,7 +199,7 @@ class helper
{
// Creation du ZIP
$baseName = str_replace('/', '', helper::baseUrl(false, false));
$baseName = empty($baseName) ? 'Campus' : $baseName;
$baseName = empty($baseName) ? 'ZwiiCMS' : $baseName;
$fileName = $baseName . '-backup-' . date('Y-m-d-H-i-s', time()) . '.zip';
$zip = new ZipArchive();
$zip->open($folder . $fileName, ZipArchive::CREATE | ZipArchive::OVERWRITE);
@ -338,13 +344,12 @@ class helper
{
// N'interroge que le serveur Apache
if (strpos($_SERVER["SERVER_SOFTWARE"], 'Apache') > 0) {
self::$rewriteStatus === false;
} elseif (self::$rewriteStatus === null) {
self::$rewriteStatus = false;
} else {
// Ouvre et scinde le fichier .htaccess
$htaccess = explode('# URL rewriting', file_get_contents('.htaccess'));
// Retourne un boolean en fonction du contenu de la partie réservée à l'URL rewriting
//self::$rewriteStatus = (empty($htaccess[1]) === false);
self::$rewriteStatus = (strpos($htaccess[1], 'RewriteEngine on') > 0) ? true : false;
self::$rewriteStatus = (strpos($htaccess[1], 'RewriteEngine on') !== false);
}
return self::$rewriteStatus;
}
@ -374,7 +379,6 @@ class helper
}
/**
* Génère des variations d'une couleur
* @param string $rgba Code rgba de la couleur
@ -404,8 +408,8 @@ class helper
*/
public static function deleteCookie($cookieKey)
{
unset($_COOKIE[$cookieKey]);
setcookie($cookieKey, '', time() - 3600, helper::baseUrl(false, false), '', false, true);
unset($_COOKIE[$cookieKey]);
}
/**
@ -428,7 +432,8 @@ class helper
$text = (int) $date->format('U');
break;
case self::FILTER_FLOAT:
$text = filter_var($text, FILTER_SANITIZE_NUMBER_FLOAT);
$text = str_replace(',', '.', $text); // Remplacer les virgules par des points
$text = filter_var($text, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
$text = (float) $text;
break;
case self::FILTER_ID:
@ -475,6 +480,11 @@ class helper
case self::FILTER_URL:
$text = filter_var($text, FILTER_SANITIZE_URL);
break;
case self::FILTER_DATE:
$text = date('Y-m-d', $text);
break;
case self::FILTER_TIME:
$text = date('H:i', $text);
}
return $text;
}
@ -663,10 +673,30 @@ class helper
public static function subword($text, $start, $length)
{
$text = trim($text);
if (strlen($text) > $length) {
// Vérifier si la longueur du texte sans les balises dépasse la longueur souhaitée
if (mb_strlen(strip_tags($text)) > $length) {
// Utiliser mb_substr pour couper le texte
$text = mb_substr($text, $start, $length);
$text = mb_substr($text, 0, min(mb_strlen($text), mb_strrpos($text, ' ')));
// S'assurer que le texte ne se termine pas au milieu d'un mot
$lastSpace = mb_strrpos($text, ' ');
if ($lastSpace !== false) {
$text = mb_substr($text, 0, $lastSpace);
}
// Fermer les balises HTML ouvertes
$dom = new DOMDocument();
@$dom->loadHTML('<div>' . $text . '</div>', LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$text = $dom->saveHTML();
// Retirer la balise de conteneur ajoutée
$text = preg_replace('~^<div>(.*)</div>$~s', '$1', $text);
// Ajouter des points de suspension si le texte a été coupé
$text .= '...';
}
return $text;
}

View File

@ -142,25 +142,38 @@ class JsonDb extends \Prowebcraft\Dot
*/
public function save()
{
$v = json_encode($this->data, JSON_UNESCAPED_UNICODE | JSON_FORCE_OBJECT | JSON_PRETTY_PRINT);
// $v = json_encode($this->data, JSON_UNESCAPED_UNICODE | JSON_FORCE_OBJECT);
$l = strlen($v);
$t = 0;
if ($v === false) {
error_log('Erreur d\'encodage JSON : ' . json_last_error_msg());
exit ('Erreur d\'encodage JSON : ' . json_last_error_msg());
}
while ($t < 5) {
$w = file_put_contents($this->db, $v); // Multi user get a locker
if ($w == $l) {
// Encode les données au format JSON avec les options spécifiées
$encoded_data = json_encode($this->data, JSON_UNESCAPED_UNICODE | JSON_FORCE_OBJECT | JSON_PRETTY_PRINT);
// Vérifie la longueur de la chaîne JSON encodée
$encoded_length = strlen($encoded_data);
// Initialise le compteur de tentatives
$attempt = 0;
// Tente d'encoder les données en JSON et de les sauvegarder jusqu'à 5 fois en cas d'échec
while ($attempt < 5) {
// 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));
// file_put_contents("tmplog.txt", '[JsonDb][' . $now->format('H:i:s.u') . ']--' . $this->db . "\r\n", FILE_APPEND);
// Vérifie si l'écriture a réussi
if ($write_result === $encoded_length) {
// Sort de la boucle si l'écriture a réussi
break;
}
$t++;
// Incrémente le compteur de tentatives
$attempt++;
}
if ($w !== $l) {
// 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.');
}
}
}
}

View File

@ -82,17 +82,25 @@ class layout extends common
$content = 'col' . $blocks[1];
$blockright = 'col' . $blocks[2];
}
// Page pleine pour la configuration des modules et l'édition des pages sauf l'affichage d'un article de blog
$pattern = ['config', 'edit', 'add', 'comment', 'data'];
// Toujours en pleine page pour la configuration des modules et l'édition des pages sauf l'affichage d'un article de blog
$pattern = ['config', 'edit', 'add', 'comment', 'data', 'option', 'theme', 'comment', 'article', 'data', 'gallery', 'update', 'users', 'validate'];
if (
(sizeof($blocks) === 1 ||
in_array($this->getUrl(1), $pattern))
) { // Pleine page en mode configuration
if ($this->getData(['page', $this->getUrl(0), 'navLeft']) === 'top' || $this->getData(['page', $this->getUrl(0), 'navRight']) === 'top') {
if (
($this->getData(['page', $this->getUrl(0), 'navLeft']) === 'top'
|| $this->getData(['page', $this->getUrl(0), 'navRight']) === 'top')
&& in_array($this->getUrl(1), $pattern) === false
) {
$this->showNavButtons('top');
}
$this->showContent();
if ($this->getData(['page', $this->getUrl(0), 'navLeft']) === 'bottom' || $this->getData(['page', $this->getUrl(0), 'navRight']) === 'bottom') {
if (
($this->getData(['page', $this->getUrl(0), 'navLeft']) === 'bottom'
|| $this->getData(['page', $this->getUrl(0), 'navRight']) === 'bottom')
&& in_array($this->getUrl(1), $pattern) === false
) {
$this->showNavButtons('bottom');
}
} else {
@ -151,7 +159,7 @@ class layout extends common
}
echo '</div>';
}
echo '</main></section>';
echo '</section></main>';
}
/**
@ -330,7 +338,7 @@ class layout extends common
// Affichage du lien de connexion
if (
($this->getData(['theme', 'footer', 'loginLink'])
and $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')
and $this->isConnected() === false
)
or $this->getUrl(0) === 'theme'
) {
@ -520,8 +528,10 @@ class layout extends common
) {
// Affiche l'icône RFM
if ($this->getUser('permission', 'filemanager') === true
&& $this->getUser('permission', 'folder', (self::$siteContent === 'home' ? 'homePath' : 'coursePath')) !== 'none') {
if (
$this->getUser('permission', 'filemanager') === true
&& $this->getUser('permission', 'folder', (self::$siteContent === 'home' ? 'homePath' : 'coursePath')) !== 'none'
) {
$itemsRight .= '<li>' . template::ico('folder', [
'href' => helper::baseUrl(false) . 'core/vendor/filemanager/dialog.php?type=0&akey=' . md5_file(self::DATA_DIR . 'core.json') . '&lang=' . $this->getData(['user', $this->getUser('id'), 'language']) . '&fldr=/' . self::$siteContent,
'attr' => 'data-lity',
@ -547,7 +557,7 @@ class layout extends common
// Lien de connexion
if (
($this->getData(['theme', 'menu', 'loginLink'])
and $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')
and $this->isConnected() === false
)
or $this->getUrl(0) === 'theme'
) {
@ -592,9 +602,9 @@ class layout extends common
if (
($this->getData(['page', $parentPageId, 'disable']) === true
and $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')
and $this->isConnected() === false
) or ($this->getData(['page', $parentPageId, 'disable']) === true
and $this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
and $this->isConnected() === true
and $this->getUser('group') < self::GROUP_EDITOR
)
) {
@ -658,9 +668,9 @@ class layout extends common
$items .= '<li id=' . $childKey . '>';
if (
($this->getData(['page', $childKey, 'disable']) === true
and $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')
and $this->isConnected() === false
) or ($this->getData(['page', $childKey, 'disable']) === true
and $this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
and $this->isConnected() === true
and $this->getUser('group') < self::GROUP_EDITOR
)
) {
@ -754,7 +764,7 @@ class layout extends common
$items .= '<li class="menuSideChild">';
if (
$this->getData(['page', $parentPageId, 'disable']) === true
and $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')
and $this->isConnected() === false
) {
$items .= '<a href="' . $this->getUrl(1) . '">';
} else {
@ -778,7 +788,7 @@ class layout extends common
if (
$this->getData(['page', $childKey, 'disable']) === true
and $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')
and $this->isConnected() === false
) {
$itemsChildren .= '<a href="' . $this->getUrl(1) . '">';
} else {
@ -914,7 +924,7 @@ class layout extends common
*/
public function showBar()
{
if ($this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')) {
if ($this->isConnected() === true) {
// Items de gauche
$leftItems = '';
// Sélecteur de contenu
@ -1001,7 +1011,7 @@ class layout extends common
// Bouton Ajouter une page
if ($this->getUser('permission', 'page', 'add')) {
$leftItems .= '<li>' . template::ico('plus', [
'href' => helper::baseUrl() . 'page/add',
'href' => helper::baseUrl() . 'page/add/' . self::$siteContent,
'help' => 'Nouvelle page ou barre latérale'
]) . '</li>';
}
@ -1018,16 +1028,20 @@ class layout extends common
or $this->getUrl(0) === ''
) {
// Bouton Editer une page
if ($this->getUser('permission', 'page', 'edit')) {
if (
$this->getUser('permission', 'page', 'edit')
and $this->geturl(1) !== 'edit'
) {
$leftItems .= '<li>' . template::ico('pencil', [
'href' => helper::baseUrl() . 'page/edit/' . $this->getUrl(0),
'href' => helper::baseUrl() . 'page/edit/' . $this->getUrl(0) . '/' . self::$siteContent,
'help' => 'Éditer la page'
]) . '</li>';
}
// Bouton Editer le module d'une page
if (
$this->getUser('permission', 'page', 'module')
&& $this->getData(['page', $this->getUrl(0), 'moduleId'])
and $this->geturl(1) !== 'edit'
and $this->getData(['page', $this->getUrl(0), 'moduleId'])
) {
$leftItems .= '<li>' . template::ico('gear', [
'href' => helper::baseUrl() . $this->getUrl(0) . '/config',
@ -1037,9 +1051,10 @@ class layout extends common
// Bouton dupliquer une page
if (
$this->getUser('permission', 'page', 'duplicate')
and $this->geturl(1) !== 'edit'
) {
$leftItems .= '<li>' . template::ico('clone', [
'href' => helper::baseUrl() . 'page/duplicate/' . $this->getUrl(0),
'href' => helper::baseUrl() . 'page/duplicate/' . $this->getUrl(0) . '/' . self::$siteContent,
'help' => 'Dupliquer la page'
])
. '</li>';
@ -1047,9 +1062,11 @@ class layout extends common
// Bouton Effacer une page
if (
$this->getUser('permission', 'page', 'delete')
and $this->geturl(1) !== 'edit'
) {
$leftItems .= '<li>' . template::ico('trash', [
'href' => helper::baseUrl() . 'page/delete/' . $this->getUrl(0),
'href' => helper::baseUrl() . 'page/delete/' . $this->getUrl(0) . '/' . self::$siteContent,
'help' => 'Supprimer la page',
'id' => 'pageDelete'
])
@ -1077,22 +1094,18 @@ class layout extends common
self::$siteContent === 'home'
&& $this->getUser('group') >= self::GROUP_ADMIN
) {
$rightItems .= '<li>' . template::ico('puzzle', [
'help' => 'Modules',
'href' => helper::baseUrl() . 'plugin'
]) . '</li>';
$rightItems .= '<li>' . template::ico('flag', [
'help' => 'Langues',
'href' => helper::baseUrl() . 'language'
]) . '</li>';
$rightItems .= '<li>' . template::ico('cog-alt', [
'help' => 'Configuration',
'href' => helper::baseUrl() . 'config'
]) . '</li>';
$rightItems .= '<li>' . template::ico('users', [
'help' => 'Utilisateurs',
'href' => helper::baseUrl() . 'user'
]) . '</li>';
$rightItems .= '<li>' . template::ico('cog-alt', [
'help' => 'Configuration',
'href' => helper::baseUrl() . 'config'
]) . '</li>';
// Mise à jour automatique
$today = mktime(0, 0, 0);
$checkUpdate = $this->getData(['core', 'lastAutoUpdate']);
@ -1104,21 +1117,54 @@ class layout extends common
$today > $checkUpdate + $this->getData(['config', 'autoUpdateDelay', 86400])
) {
// Dernier auto controle
$this->setData(['core', 'lastAutoUpdate', $today]);
$this->setData(['core', 'lastAutoUpdate', $today], false);
if (
helper::checkNewVersion(common::ZWII_UPDATE_CHANNEL)
) {
$this->setData(['core', 'updateAvailable', true]);
$this->setData(['core', 'updateAvailable', true], false);
}
// Modules installés
$infoModules = helper::getModules();
// Recherche de mise à jour des modules
$store = plugin::getStore();
if (is_array($store)) {
// Parcourir les données des modules du store
foreach ($store as $key => $value) {
if (empty($key)) {
continue;
}
// Mise à jour d'un module
// Le module est installé et une mise à jour est en ligne
if (
isset($infoModules[$key])
&&
version_compare($infoModules[$key]['version'], $value['version'], '<')
) {
$this->setData(['core', 'updateModuleAvailable', true], false);
}
}
}
// Sauvegarde la base manuellement
$this->saveDB('core');
}
}
// Afficher le bouton : Mise à jour détectée + activée
if ($this->getData(['core', 'updateAvailable'])) {
$rightItems .= '<li><a href="' . helper::baseUrl() . 'install/update" data-tippy-content="Mettre à jour Zwii ' . common::ZWII_VERSION . ' vers ' . helper::getOnlineVersion(common::ZWII_UPDATE_CHANNEL) . '">' . template::ico('update colorRed') . '</a></li>';
}
}
if ($this->getData(['core', 'updateModuleAvailable'])) {
$rightItems .= '<li>' . template::ico('puzzle colorRed', [
'help' => 'Modules',
'href' => helper::baseUrl() . 'plugin'
]) . '</li>';
} else {
$rightItems .= '<li>' . template::ico('puzzle', [
'help' => 'Modules',
'href' => helper::baseUrl() . 'plugin'
]) . '</li>';
}
// Boutons depuis le groupe éditeur
if (
$this->getUser('group') >= self::GROUP_EDITOR
&& $this->getUser('permission', 'user', 'edit')
@ -1206,7 +1252,7 @@ class layout extends common
$vars = 'var baseUrl = ' . json_encode(helper::baseUrl(false)) . ';';
$vars .= 'var baseUrlQs = ' . json_encode(helper::baseUrl()) . ';';
if (
$this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
$this->isConnected() === true
and $this->getUser('group') >= self::GROUP_EDITOR
) {
$vars .= 'var privateKey = ' . json_encode(md5_file(self::DATA_DIR . 'core.json')) . ';';

View File

@ -11,16 +11,16 @@ class core extends common
parent::__construct();
// Token CSRF
if (empty($_SESSION['csrf'])) {
$_SESSION['csrf'] = bin2hex(openssl_random_pseudo_bytes(128));
$_SESSION['csrf'] = bin2hex(openssl_random_pseudo_bytes(64));
}
// Fuseau horaire
self::$timezone = $this->getData(['config', 'timezone']); // Utile pour transmettre le timezone à la classe helper
date_default_timezone_set(self::$timezone);
common::$timezone = $this->getData(['config', 'timezone']); // Utile pour transmettre le timezone à la classe helper
date_default_timezone_set(common::$timezone);
// Supprime les fichiers temporaires
$lastClearTmp = mktime(0, 0, 0);
if ($lastClearTmp > $this->getData(['core', 'lastClearTmp']) + 86400) {
$iterator = new DirectoryIterator(self::TEMP_DIR);
$iterator = new DirectoryIterator(common::TEMP_DIR);
foreach ($iterator as $fileInfos) {
if (
$fileInfos->isFile() &&
@ -32,8 +32,6 @@ class core extends common
}
// Date de la dernière suppression
$this->setData(['core', 'lastClearTmp', $lastClearTmp]);
// Enregistre les données
//$this->SaveData();
}
// Backup automatique des données
$lastBackup = mktime(0, 0, 0);
@ -43,11 +41,11 @@ class core extends common
and $this->getData(['user']) // Pas de backup pendant l'installation
) {
// Copie des fichier de données
helper::autoBackup(self::BACKUP_DIR, ['backup', 'tmp', 'file']);
helper::autoBackup(common::BACKUP_DIR, ['backup', 'tmp', 'file']);
// Date du dernier backup
$this->setData(['core', 'lastBackup', $lastBackup]);
// Supprime les backups de plus de 30 jours
$iterator = new DirectoryIterator(self::BACKUP_DIR);
$iterator = new DirectoryIterator(common::BACKUP_DIR);
foreach ($iterator as $fileInfos) {
if (
$fileInfos->isFile()
@ -60,23 +58,23 @@ class core extends common
}
// Crée le fichier de personnalisation avancée
if (file_exists(self::DATA_DIR . 'custom.css') === false) {
file_put_contents(self::DATA_DIR . 'custom.css', ('core/module/theme/resource/custom.css'));
chmod(self::DATA_DIR . 'custom.css', 0755);
if (file_exists(common::DATA_DIR . 'custom.css') === false) {
file_put_contents(common::DATA_DIR . 'custom.css', ('core/module/theme/resource/custom.css'));
chmod(common::DATA_DIR . 'custom.css', 0755);
}
// Crée le fichier de personnalisation
if (file_exists(self::DATA_DIR . self::$siteContent . '/theme.css') === false) {
file_put_contents(self::DATA_DIR . self::$siteContent . '/theme.css', '');
chmod(self::DATA_DIR . self::$siteContent . '/theme.css', 0755);
if (file_exists(common::DATA_DIR . common::$siteContent . '/theme.css') === false) {
file_put_contents(common::DATA_DIR . common::$siteContent . '/theme.css', '');
chmod(common::DATA_DIR . common::$siteContent . '/theme.css', 0755);
}
// Crée le fichier de personnalisation de l'administration
if (file_exists(self::DATA_DIR . 'admin.css') === false) {
file_put_contents(self::DATA_DIR . 'admin.css', '');
chmod(self::DATA_DIR . 'admin.css', 0755);
if (file_exists(common::DATA_DIR . 'admin.css') === false) {
file_put_contents(common::DATA_DIR . 'admin.css', '');
chmod(common::DATA_DIR . 'admin.css', 0755);
}
// Check la version rafraichissement du theme
$cssVersion = preg_split('/\*+/', file_get_contents(self::DATA_DIR . self::$siteContent . '/theme.css'));
$cssVersion = preg_split('/\*+/', file_get_contents(common::DATA_DIR . common::$siteContent . '/theme.css'));
if (empty($cssVersion[1]) or $cssVersion[1] !== md5(json_encode($this->getData(['theme'])))) {
// Version
$css = '/*' . md5(json_encode($this->getData(['theme']))) . '*/';
@ -92,7 +90,7 @@ class core extends common
// Fonts disponibles
$fontsAvailable['files'] = $this->getData(['font', 'files']);
$fontsAvailable['imported'] = $this->getData(['font', 'imported']);
$fontsAvailable['websafe'] = self::$fontsWebSafe;
$fontsAvailable['websafe'] = common::$fontsWebSafe;
// Fontes installées
$fonts = [
@ -273,7 +271,7 @@ class core extends common
$css .= '#footerCopyright{text-align:' . $this->getData(['theme', 'footer', 'copyrightAlign']) . '}';
// Enregistre la personnalisation
file_put_contents(self::DATA_DIR . self::$siteContent . '/theme.css', $css);
file_put_contents(common::DATA_DIR . common::$siteContent . '/theme.css', $css);
// Effacer le cache pour tenir compte de la couleur de fond TinyMCE
header("Expires: Tue, 01 Jan 2000 00:00:00 GMT");
@ -284,7 +282,7 @@ class core extends common
}
// Check la version rafraichissement du theme admin
$cssVersion = preg_split('/\*+/', file_get_contents(self::DATA_DIR . 'admin.css'));
$cssVersion = preg_split('/\*+/', file_get_contents(common::DATA_DIR . 'admin.css'));
if (empty($cssVersion[1]) or $cssVersion[1] !== md5(json_encode($this->getData(['admin'])))) {
// Version
@ -293,7 +291,7 @@ class core extends common
// Fonts disponibles
$fontsAvailable['files'] = $this->getData(['font', 'files']);
$fontsAvailable['imported'] = $this->getData(['font', 'imported']);
$fontsAvailable['websafe'] = self::$fontsWebSafe;
$fontsAvailable['websafe'] = common::$fontsWebSafe;
/**
* Import des polices de caractères
@ -368,7 +366,7 @@ class core extends common
// Bordure du contour TinyMCE
$css .= '.mce-tinymce{border: 1px solid ' . $this->getData(['admin', 'borderBlockColor']) . '!important;}';
// Enregistre la personnalisation
file_put_contents(self::DATA_DIR . 'admin.css', $css);
file_put_contents(common::DATA_DIR . 'admin.css', $css);
}
}
/**
@ -384,8 +382,8 @@ class core extends common
require 'core/module/' . $classPath;
}
// Module
elseif (is_readable(self::MODULE_DIR . $classPath)) {
require self::MODULE_DIR . $classPath;
elseif (is_readable(common::MODULE_DIR . $classPath)) {
require common::MODULE_DIR . $classPath;
}
// Librairie
elseif (is_readable('core/vendor/' . $classPath)) {
@ -414,22 +412,23 @@ class core extends common
// Sauvegarde la dernière page visitée par l'utilisateur connecté et enregistre l'historique des consultations
if (
$this->getUser('id')
&& self::$siteContent !== 'home'
&& common::$siteContent !== 'home'
&& in_array($this->getUrl(0), array_keys($this->getData(['page'])))
// Le userId n'est pas celui d'un admis ni le prof du contenu
&& (
$this->getUser('group') < self::GROUP_ADMIN
|| $this->getUser('id') !== $this->getData(['course', self::$siteContent, 'author'])
$this->getUser('group') < common::GROUP_ADMIN
|| $this->getUser('id') !== $this->getData(['course', common::$siteContent, 'author'])
)
) {
// Stocke l'historique des pages vues
$data = is_array($this->getData(['enrolment', self::$siteContent, $this->getUser('id'), 'history', $this->getUrl(0)]))
? array_merge([time()], $this->getData(['enrolment', self::$siteContent, $this->getUser('id'), 'history', $this->getUrl(0)]))
: [time()];
$this->setData(['enrolment', self::$siteContent, $this->getUser('id'), 'history', $this->getUrl(0), $data]);
// Stocke la dernière page vue et sa date de consultation
$this->setData(['enrolment', self::$siteContent, $this->getUser('id'), 'lastPageView', $this->getUrl(0)]);
$this->setData(['enrolment', self::$siteContent, $this->getUser('id'), 'datePageView', time()]);
$this->setData(['enrolment', common::$siteContent, $this->getUser('id'), 'lastPageView', $this->getUrl(0)], false);
$this->setData(['enrolment', common::$siteContent, $this->getUser('id'), 'datePageView', time()]);
// Stocke le rapport en CSV
$file = fopen(common::DATA_DIR . common::$siteContent . '/report.csv', 'a+');
fputcsv($file, [$this->getUser('id'), $this->getUrl(0), time()], ';');
fclose($file);
}
// Journalisation
@ -437,8 +436,8 @@ class core extends common
// Force la déconnexion des membres bannis ou d'une seconde session
if (
$this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
and ($this->getUser('group') === self::GROUP_BANNED
$this->isConnected() === true
and ($this->getUser('group') === common::GROUP_BANNED
or ($_SESSION['csrf'] !== $this->getData(['user', $this->getUser('id'), 'accessCsrf'])
and $this->getData(['config', 'connect', 'autoDisconnect']) === true)
)
@ -451,9 +450,9 @@ class core extends common
$this->getData(['config', 'maintenance'])
and in_array($this->getUrl(0), ['maintenance', 'user']) === false
and $this->getUrl(1) !== 'login'
and ($this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')
or ($this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
and $this->getUser('group') < self::GROUP_ADMIN
and ($this->isConnected() === false
or ($this->isConnected() === true
and $this->getUser('group') < common::GROUP_ADMIN
)
)
) {
@ -466,32 +465,12 @@ class core extends common
exit();
}
// Pour éviter une 404 sur une langue étrangère, bascule dans la langue correcte.
if (is_null($this->getData(['page', $this->getUrl(0)]))) {
foreach ($this->getData(['course']) as $key => $value) {;
if (
is_dir(self::DATA_DIR . $key) &&
file_exists(self::DATA_DIR . $key . '/page.json')
) {
$pagesId = json_decode(file_get_contents(self::DATA_DIR . $key . '/page.json'), true);
if (
is_array($pagesId['page']) &&
array_key_exists($this->getUrl(0), $pagesId['page'])
) {
//$_SESSION['ZWII_SITE_CONTENT'] = $key;
header('Refresh:0; url=' . helper::baseUrl() . 'course/swap/' . $key . '/' . $this->getUrl(0));
exit();
}
}
}
}
// Check l'accès à la page
$access = null;
if ($this->getData(['page', $this->getUrl(0)]) !== null) {
if (
$this->getData(['page', $this->getUrl(0), 'group']) === self::GROUP_VISITOR
or ($this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
$this->getData(['page', $this->getUrl(0), 'group']) === common::GROUP_VISITOR
or ($this->isConnected() === true
// and $this->getUser('group') >= $this->getData(['page', $this->getUrl(0), 'group'])
// Modification qui tient compte du profil de la page
and ($this->getUser('group') * 10 + $this->getUser('profil')) >= ($this->getData(['page', $this->getUrl(0), 'group']) * 10 + $this->getData(['page', $this->getUrl(0), 'profil']))
@ -508,14 +487,54 @@ class core extends common
// Empêcher l'accès aux pages désactivées par URL directe
if (
($this->getData(['page', $this->getUrl(0), 'disable']) === true
and $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')
and $this->isConnected() === false
) or ($this->getData(['page', $this->getUrl(0), 'disable']) === true
and $this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
and $this->getUser('group') < self::GROUP_EDITOR
and $this->isConnected() === true
and $this->getUser('group') < common::GROUP_EDITOR
)
) {
$access = false;
}
// Lève une erreur si l'url est celle d'une page avec des éléments surnuméraires https://www.site.fr/page/truc
if (
array_key_exists($this->getUrl(0), $this->getData(['page']))
and $this->getUrl(1)
and $this->getData(['page', $this->getUrl(0), 'moduleId']) === ''
) {
$access = false;
}
/** Empêche la consultation d'un espace laissé ouvert après la déconnexion et redirige vers home
* L'utilisateur n'est pas connecté
* ET l'accueil n'est pas affiché
* ET l'espace affiché nécessite un compte d'accès, enrolment vaut 1,2 ou 3
* */
if (
$this->isConnected() === false
and self::$siteContent !== 'home'
and $this->getData(['course', self::$siteContent, 'enrolment']) > 0
) {
$_SESSION['ZWII_SITE_CONTENT'] = 'home';
header(header: 'Location:' . helper::baseUrl(true) . 'swap/' . self::$siteContent);
exit();
}
/**
* Récupère les statistiques de l'utilisateur non admin
* en dehors de home
* et si la connextion est nécessaire et que le membre est connecté
* stocke la progression dans la base des inscriptions
*
*/
if (
$this->isConnected() === true
and self::$siteContent !== 'home'
and $this->getData(['course', self::$siteContent, 'enrolment']) > 0
) {
$course = new course();
self::$userProgress = $course->userProgress(self::$siteContent, $this->getUser('id'));
$this->setData(['enrolment', self::$siteContent, $this->getUser('id'), 'progress', self::$userProgress ]);
}
}
/**
@ -537,9 +556,9 @@ class core extends common
$this->getUser('id') &&
$userId !== $this->getUser('id') &&
$this->getData(['user', $userId, 'accessUrl']) === $this->getUrl() &&
array_intersect($t, self::$concurrentAccess) &&
//array_intersect($t, self::$accessExclude) !== false &&
time() < $this->getData(['user', $userId, 'accessTimer']) + self::ACCESS_TIMER
array_intersect($t, common::$concurrentAccess) &&
//array_intersect($t, common::$accessExclude) !== false &&
time() < $this->getData(['user', $userId, 'accessTimer']) + common::ACCESS_TIMER
) {
$access = false;
$accessInfo['userName'] = $this->getData(['user', $userId, 'lastname']) . ' ' . $this->getData(['user', $userId, 'firstname']);
@ -549,10 +568,11 @@ class core extends common
}
// Accès concurrent stocke la page visitée
if (
$this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
$this->isConnected() === true
&& $this->getUser('id')
&& !$this->isPost()
) {
$this->setData(['user', $this->getUser('id'), 'accessUrl', $this->getUrl()]);
$this->setData(['user', $this->getUser('id'), 'accessUrl', $this->getUrl()], false);
$this->setData(['user', $this->getUser('id'), 'accessTimer', time()]);
}
// Breadcrumb
@ -576,10 +596,10 @@ class core extends common
$inlineScript[] = $this->getData(['page', $this->getUrl(0), 'js']) === null ? '' : $this->getData(['page', $this->getUrl(0), 'js']);
// Importe le contenu, le CSS et le script des barres
$contentRight = $this->getData(['page', $this->getUrl(0), 'barRight']) ? $this->getPage($this->getData(['page', $this->getUrl(0), 'barRight']), self::$siteContent) : '';
$contentRight = $this->getData(['page', $this->getUrl(0), 'barRight']) ? $this->getPage($this->getData(['page', $this->getUrl(0), 'barRight']), common::$siteContent) : '';
$inlineStyle[] = $this->getData(['page', $this->getData(['page', $this->getUrl(0), 'barRight']), 'css']) === null ? '' : $this->getData(['page', $this->getData(['page', $this->getUrl(0), 'barRight']), 'css']);
$inlineScript[] = $this->getData(['page', $this->getData(['page', $this->getUrl(0), 'barRight']), 'js']) === null ? '' : $this->getData(['page', $this->getData(['page', $this->getUrl(0), 'barRight']), 'js']);
$contentLeft = $this->getData(['page', $this->getUrl(0), 'barLeft']) ? $this->getPage($this->getData(['page', $this->getUrl(0), 'barLeft']), self::$siteContent) : '';
$contentLeft = $this->getData(['page', $this->getUrl(0), 'barLeft']) ? $this->getPage($this->getData(['page', $this->getUrl(0), 'barLeft']), common::$siteContent) : '';
$inlineStyle[] = $this->getData(['page', $this->getData(['page', $this->getUrl(0), 'barLeft']), 'css']) === null ? '' : $this->getData(['page', $this->getData(['page', $this->getUrl(0), 'barLeft']), 'css']);
$inlineScript[] = $this->getData(['page', $this->getData(['page', $this->getUrl(0), 'barLeft']), 'js']) === null ? '' : $this->getData(['page', $this->getData(['page', $this->getUrl(0), 'barLeft']), 'js']);
@ -597,7 +617,7 @@ class core extends common
$this->addOutput([
'title' => $title,
'content' => $this->getPage($this->getUrl(0), self::$siteContent),
'content' => $this->getPage($this->getUrl(0), common::$siteContent),
'metaDescription' => $this->getData(['page', $this->getUrl(0), 'metaDescription']),
'metaTitle' => $this->getData(['page', $this->getUrl(0), 'metaTitle']),
'typeMenu' => $this->getData(['page', $this->getUrl(0), 'typeMenu']),
@ -623,7 +643,7 @@ class core extends common
: $this->getData(['page', $this->getUrl(0), 'metaDescription']);
// Importe le CSS de la page principale
$pageContent = $this->getPage($this->getUrl(0), self::$siteContent);
$pageContent = $this->getPage($this->getUrl(0), common::$siteContent);
$this->addOutput([
'title' => $title,
@ -668,8 +688,8 @@ class core extends common
$output = $module->output;
// Check le groupe de l'utilisateur
if (
($module::$actions[$action] === self::GROUP_VISITOR
or ($this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
($module::$actions[$action] === common::GROUP_VISITOR
or ($this->isConnected() === true
and $this->getUser('group') >= $module::$actions[$action]
and $this->getUser('permission', $moduleId, $action)
)
@ -681,10 +701,10 @@ class core extends common
foreach ($_POST as $postId => $postValue) {
if (is_array($postValue)) {
foreach ($postValue as $subPostId => $subPostValue) {
self::$inputBefore[$postId . '_' . $subPostId] = $subPostValue;
common::$inputBefore[$postId . '_' . $subPostId] = $subPostValue;
}
} else {
self::$inputBefore[$postId] = $postValue;
common::$inputBefore[$postId] = $postValue;
}
}
}
@ -724,9 +744,9 @@ class core extends common
// Contenu par vue
elseif ($output['view']) {
// Chemin en fonction d'un module du coeur ou d'un module
$modulePath = in_array($moduleId, self::$coreModuleIds) ? 'core/' : '';
$modulePath = in_array($moduleId, common::$coreModuleIds) ? 'core/' : '';
// CSS
$stylePath = $modulePath . self::MODULE_DIR . $moduleId . '/view/' . $output['view'] . '/' . $output['view'] . '.css';
$stylePath = $modulePath . common::MODULE_DIR . $moduleId . '/view/' . $output['view'] . '/' . $output['view'] . '.css';
if (file_exists($stylePath)) {
$this->addOutput([
'style' => file_get_contents($stylePath)
@ -739,7 +759,7 @@ class core extends common
}
// JS
$scriptPath = $modulePath . self::MODULE_DIR . $moduleId . '/view/' . $output['view'] . '/' . $output['view'] . '.js.php';
$scriptPath = $modulePath . common::MODULE_DIR . $moduleId . '/view/' . $output['view'] . '/' . $output['view'] . '.js.php';
if (file_exists($scriptPath)) {
ob_start();
include $scriptPath;
@ -748,7 +768,7 @@ class core extends common
]);
}
// Vue
$viewPath = $modulePath . self::MODULE_DIR . $moduleId . '/view/' . $output['view'] . '/' . $output['view'] . '.php';
$viewPath = $modulePath . common::MODULE_DIR . $moduleId . '/view/' . $output['view'] . '/' . $output['view'] . '.php';
if (file_exists($viewPath)) {
ob_start();
include $viewPath;
@ -757,7 +777,7 @@ class core extends common
$this->addOutput([
'content' => ob_get_clean() . ($output['showPageContent'] ? $pageContent : '')
]);
} else if ($modpos === 'free') {
} elseif ($modpos === 'free' && strstr($pageContent, '[MODULE]')) {
if (strstr($pageContent, '[MODULE]', true) === false) {
$begin = strstr($pageContent, '[]', true);
} else {
@ -834,14 +854,41 @@ class core extends common
}
}
} elseif ($this->output['content'] === '') {
// Pour éviter une 404, bascule dans l'espace correct si la page existe dans cet espace.
// Parcourir les espaces y compris l'accueil
foreach (array_merge(['home' => []], $this->getData(['course'])) as $courseId => $value) {
;
if (
// l'espace existe
is_dir(common::DATA_DIR . $courseId) &&
file_exists(common::DATA_DIR . $courseId . '/page.json')
) {
// Lire les données des pages
$pagesId = json_decode(file_get_contents(common::DATA_DIR . $courseId . '/page.json'), true);
if (
// La page existe
is_array($pagesId['page']) &&
array_key_exists($this->getUrl(0), $pagesId['page'])
) {
// Basculer
$_SESSION['ZWII_SITE_CONTENT'] = $courseId;
header('Refresh:0; url=' . helper::baseUrl() . $this->getUrl());
exit();
}
}
}
// La page n'existe pas dans les esapces, on génére une erreur 404
http_response_code(404);
if (
// une page est définie dans la configuration mais dans home
$this->getData(['config', 'page404']) !== 'none'
//and $this->getData(['page', $this->getData(['config', 'page404'])])
) {
// Bascule sur l'acccueil et rediriger
$_SESSION['ZWII_SITE_CONTENT'] = 'home';
header('Location:' . helper::baseUrl() . $this->getData(['config', 'page404']));
} else {
// Page par défaut
$this->addOutput([
'title' => 'Page indisponible',
'content' => template::speech('<p>' . helper::translate('La page demandée n\'existe pas ou est introuvable (erreur 404)') . '</p><p><a style="color:inherit" href="javascript:history.back()">' . helper::translate('Retour') . '</a></p>')
@ -867,25 +914,25 @@ class core extends common
}
switch ($this->output['display']) {
// Layout brut
case self::DISPLAY_RAW:
case common::DISPLAY_RAW:
echo $this->output['content'];
break;
// Layout vide
case self::DISPLAY_LAYOUT_BLANK:
case common::DISPLAY_LAYOUT_BLANK:
require 'core/layout/blank.php';
break;
// Affichage en JSON
case self::DISPLAY_JSON:
case common::DISPLAY_JSON:
header('Content-Type: application/json');
echo json_encode($this->output['content']);
break;
// RSS feed
case self::DISPLAY_RSS:
case common::DISPLAY_RSS:
header('Content-type: application/rss+xml; charset=UTF-8');
echo $this->output['content'];
break;
// Layout allégé
case self::DISPLAY_LAYOUT_LIGHT:
case common::DISPLAY_LAYOUT_LIGHT:
ob_start();
require 'core/layout/light.php';
$content = ob_get_clean();
@ -896,7 +943,7 @@ class core extends common
echo $content;
break;
// Layout principal
case self::DISPLAY_LAYOUT_MAIN:
case common::DISPLAY_LAYOUT_MAIN:
ob_start();
require 'core/layout/main.php';
$content = ob_get_clean();

View File

@ -128,7 +128,17 @@ class SitemapGenerator
*/
private $sampleRobotsLines = [
"User-agent: *",
"Disallow: /",
"User-agent: Googlebot",
"Allow: /",
"User-agent: bingbot",
"Allow: /",
"User-agent: Slurp",
"Allow: /",
"User-agent: DuckDuckBot",
"Allow: /",
"User-agent: Baiduspider",
"Allow: /"
];
/**
* @var array list of valid changefreq values according to the spec

View File

@ -224,7 +224,7 @@ class template
* Crée un champ date
* @param string $nameId Nom et id du champ
* @param array $attributes Attributs ($key => $value)
* @param string type date time datetime-local month week
* @param string type date seule ; time heure seule ; datetime-local (jour et heure)
* @return string
*/
public static function date($nameId, array $attributes = [])
@ -250,11 +250,26 @@ class template
$attributes['label'] = helper::translate($attributes['label']);
$attributes['help'] = helper::translate($attributes['help']);
//$attributes['placeholder'] = helper::translate($attributes['placeholder']);
// Filtre selon le type
switch ($attributes['type']) {
case 'datetime-local':
$filter = helper::FILTER_TIMESTAMP;
break;
case 'date':
$filter = helper::FILTER_DATE; // Pour générer une valeur uniquement sur la date
break;
case 'time':
$filter = helper::FILTER_TIME; // Pour générer une valeur uniquement sur l'heure
break;
default:
$filter = null; // pas de filtre pour month and year
break;
}
// Sauvegarde des données en cas d'erreur
if ($attributes['before'] and array_key_exists($attributes['id'], common::$inputBefore)) {
$attributes['value'] = common::$inputBefore[$attributes['id']];
} else {
$attributes['value'] = ($attributes['value'] ? helper::filter($attributes['value'], helper::FILTER_TIMESTAMP) : '');
$attributes['value'] = ($attributes['value'] ? helper::filter($attributes['value'], $filter) : '');
}
// Début du wrapper
$html = '<div id="' . $attributes['id'] . 'Wrapper" class="inputWrapper ' . $attributes['classWrapper'] . '">';
@ -310,6 +325,7 @@ class template
'name' => $nameId,
'type' => 2,
'value' => '',
'folder' => '',
'language' => 'fr_FR'
], $attributes);
// Traduction de l'aide et de l'étiquette
@ -352,6 +368,8 @@ class template
'&field_id=' . $attributes['id'] .
'&type=' . $attributes['type'] .
'&akey=' . md5_file(core::DATA_DIR . 'core.json') .
// Ajoute le nom du dossier si la variable est passée
(!empty($attributes['folder']) ? '&fldr=' . $attributes['folder'] : '') .
($attributes['extensions'] ? '&extensions=' . $attributes['extensions'] : '')
. '"
class="inputFile %s %s"
@ -486,7 +504,7 @@ class template
*/
public static function flag($langId, $size = 'auto')
{
$lang = 'home';
$lang = 'fr_FR';
switch ($langId) {
case '':
break;
@ -496,6 +514,8 @@ class template
case 'selected':
if (isset($_SESSION['ZWII_SITE_CONTENT'])) {
$lang = $_SESSION['ZWII_SITE_CONTENT'];
} else {
$lang = 'fr_FR';
}
}
return '<img class="flag" src="' . helper::baseUrl(false) . 'core/vendor/i18n/png/' . $lang . '.png"
@ -748,6 +768,7 @@ class template
return $html;
}
/**
* Crée une bulle de dialogue
* @param string $text Texte de la bulle

View File

@ -216,15 +216,16 @@ core.start = function () {
// Variables des cookies
var getUrl = window.location;
var domain = "domain=" + getUrl.hostname + ";";
var basePath = getUrl.pathname.substring(0, getUrl.pathname.lastIndexOf('/') + 1);
var path = "path=" + basePath + ";";
var e = new Date();
e.setFullYear(e.getFullYear() + 1);
var expires = "expires=" + e.toUTCString();
var expires = "expires=" + e.toUTCString() + ";";
// Stocke le cookie d'acceptation
document.cookie = "ZWII_COOKIE_CONSENT=true;samesite=strict;" + domain + expires;
document.cookie = "ZWII_COOKIE_CONSENT=true; samesite=lax; " + domain + path + expires;
});
/**
* Fermeture de la popup des cookies
*/
@ -462,7 +463,7 @@ $(document).ready(function () {
/**
* Chargement paresseux des images et des iframes
*/
$("img,picture,iframe").attr("loading", "lazy");
$("img").attr("loading", "lazy");
/**
* Effet accordéon

View File

@ -51,7 +51,7 @@ class common
const ACCESS_TIMER = 1800;
// Numéro de version
const ZWII_VERSION = '1.7.06';
const ZWII_VERSION = '1.14.06';
// URL autoupdate
const ZWII_UPDATE_URL = 'https://forge.chapril.org/ZwiiCMS-Team/campus-update/raw/branch/master/';
@ -174,8 +174,12 @@ class common
public static $dialog;
// Langue de l'interface sélectionnée
public static $i18nUI = 'fr_FR';
// Langues de contenu
// Espace, contenu sélectionné
public static $siteContent = 'home';
// Progression d'un participant
public static $userProgress = '';
public static $languages = [
'de' => 'Deutsch',
'en_EN' => 'English',
@ -319,28 +323,57 @@ class common
public function __construct()
{
// Construct cache
if (isset($GLOBALS['common_construct'])) {
$this->input['_POST'] = $GLOBALS['common_construct']['input']['_POST'];
$this->input['_COOKIE'] = $GLOBALS['common_construct']['input']['_COOKIE'];
self::$siteContent = $GLOBALS['common_construct']['siteContent'];
$this->dataFiles = $GLOBALS['common_construct']['dataFiles'];
$this->configFiles = $GLOBALS['common_construct']['configFiles'];
$this->user = $GLOBALS['common_construct']['user'];
self::$i18nUI = $GLOBALS['common_construct']['i18nUI'];
$this->hierarchy = $GLOBALS['common_construct']['hierarchy'];
$this->url = $GLOBALS['common_construct']['url'];
self::$dialog = $GLOBALS['common_construct']['dialog'];
return;
}
// Extraction des données http
if (isset($_POST)) {
$this->input['_POST'] = $_POST;
// Cache
$GLOBALS['common_construct']['input']['_POST'] = $this->input['_POST'];
}
if (isset($_COOKIE)) {
$this->input['_COOKIE'] = $_COOKIE;
// Cache
$GLOBALS['common_construct']['input']['_COOKIE'] = $this->input['_COOKIE'];
}
// Déterminer le contenu du site
if (isset($_SESSION['ZWII_SITE_CONTENT'])) {
// Déterminé par la session présente
self::$siteContent = $_SESSION['ZWII_SITE_CONTENT'];
} else {
$_SESSION['ZWII_SITE_CONTENT'] = 'home';
self::$siteContent = 'home';
}
// Cache
$GLOBALS['common_construct']['siteContent'] = self::$siteContent;
// Instanciation de la classe des entrées / sorties
// Les fichiers de configuration
foreach ($this->configFiles as $module => $value) {
$this->initDB($module);
}
// Cache
$GLOBALS['common_construct']['configFiles'] = $this->configFiles;
// Les fichiers des contenus
foreach ($this->contentFiles as $module => $value) {
$this->initDB($module, self::$siteContent);
}
// Cache
$GLOBALS['common_construct']['dataFiles'] = $this->dataFiles;
// Installation fraîche, initialisation de la configuration inexistante
@ -367,6 +400,8 @@ class common
if ($this->user === []) {
$this->user = $this->getData(['user', $this->getInput('ZWII_USER_ID')]);
}
// Cache
$GLOBALS['common_construct']['user'] = $this->user;
// Langue de l'administration si le user est connecté
if ($this->getData(['user', $this->getUser('id'), 'language'])) {
@ -389,13 +424,17 @@ class common
// Stocker le cookie de langue pour l'éditeur de texte ainsi que l'url du contenu pour le theme
setcookie('ZWII_UI', self::$i18nUI, time() + 3600, '', '', false, false);
// Stocker l'courseId pour le thème de TinyMCE
setcookie('ZWII_SITE_CONTENT', self::$siteContent, time() + 3600, '', '', false, false);
//setcookie('ZWII_SITE_CONTENT', self::$siteContent, time() + 3600, '', '', false, false);
setlocale(LC_ALL, self::$i18nUI);
// Cache
$GLOBALS['common_construct']['i18nUI'] = self::$i18nUI;
// Construit la liste des pages parents/enfants
if ($this->hierarchy['all'] === []) {
$this->buildHierarchy();
}
// Cache
$GLOBALS['common_construct']['hierarchy'] = $this->hierarchy;
// Construit l'url
if ($this->url === '') {
@ -405,6 +444,8 @@ class common
$this->url = $this->homePageId();
}
}
// Cache
$GLOBALS['common_construct']['url'] = $this->url;
// Chargement des dialogues
if (!file_exists(self::I18N_DIR . self::$i18nUI . '.json')) {
@ -424,6 +465,8 @@ class common
self::$dialog = array_merge(self::$dialog, $d);
}
}
// Cache
$GLOBALS['common_construct']['dialog'] = self::$dialog;
// Données de proxy
$proxy = $this->getData(['config', 'proxyType']) . $this->getData(['config', 'proxyUrl']) . ':' . $this->getData(['config', 'proxyPort']);
@ -451,8 +494,6 @@ class common
}
/**
* Ajoute les valeurs en sortie
* @param array $output Valeurs en sortie
@ -512,8 +553,10 @@ class common
/**
* Sauvegarde des données
* @param array $keys Clé(s) des données
* @param bool $save Indique si le fichier doit être sauvegardé après modification (par défaut true)
* @return bool Succès de l'opération
*/
public function setData($keys = [])
public function setData($keys = [], $save = true)
{
// Pas d'enregistrement lorsqu'une notice est présente ou tableau transmis vide
if (
@ -541,11 +584,12 @@ class common
$query .= '.' . $keys[$i];
}
// Appliquer la modification, le dernier élément étant la donnée à sauvegarder
$success = is_object($db->set($query, $keys[count($keys) - 1], true));
$success = is_object($db->set($query, $keys[count($keys) - 1], $save));
}
return $success;
}
/**
* Accède aux données
* @param array $keys Clé(s) des données
@ -589,13 +633,13 @@ class common
/**
* Ecrire les données de la page
* @param string pageId
* @param string contenu de la page
* @param array contenu de la page
* @return int nombre d'octets écrits ou erreur
*/
public function setPage($page, $value, $path)
{
return file_put_contents(self::DATA_DIR . $path . '/content/' . $page . '.html', $value);
return $this->secure_file_put_contents(self::DATA_DIR . $path . '/content/' . $page . '.html', $value);
}
@ -611,18 +655,74 @@ class common
}
/**
* Écrit les données dans un fichier avec plusieurs tentatives d'écriture et verrouillage
*
* @param string $filename Le nom du fichier
* @param string $data Les données à écrire dans le fichier
* @param int $flags Les drapeaux optionnels à passer à la fonction $this->secure_file_put_contents
* @return bool True si l'écriture a réussi, sinon false
*/
function secure_file_put_contents($filename, $data, $flags = 0)
{
// Initialise le compteur de tentatives
$attempts = 0;
// Vérifie la longueur des données
$data_length = strlen($data);
// Effectue jusqu'à 5 tentatives d'écriture
while ($attempts < 5) {
// Essaye d'écrire les données dans le fichier avec verrouillage exclusif
$write_result = file_put_contents($filename, $data, LOCK_EX | $flags);
// $now = \DateTime::createFromFormat('U.u', microtime(true));
// file_put_contents("tmplog.txt", '[SecurePut][' . $now->format('H:i:s.u') . ']' . "\r\n", FILE_APPEND);
// Vérifie si l'écriture a réussi
if ($write_result !== false && $write_result === $data_length) {
// Sort de la boucle si l'écriture a réussi
break;
}
// Incrémente le compteur de tentatives
$attempts++;
sleep(1);
}
// Etat de l'écriture
return ($attempts < 5);
}
public function initDB($module, $path = '')
{
// Instanciation de la classe des entrées / sorties
// Constructeur JsonDB;
$this->dataFiles[$module] = new \Prowebcraft\JsonDb([
// Chemin complet vers le fichier JSON
$dir = empty($path) ? self::DATA_DIR : self::DATA_DIR . $path . '/';
$config = [
'name' => $module . '.json',
'dir' => self::DATA_DIR . $path . '/',
'backup' => file_exists('site/data/.backup')
]);
'dir' => $dir,
'backup' => file_exists('site/data/.backup'),
'update' => false,
];
// Instanciation de l'objet et stockage dans dataFiles
$this->dataFiles[$module] = new \Prowebcraft\JsonDb($config);
}
/**
* Cette fonction est liée à saveData
* @param mixed $module
* @return void
*/
public function saveDB($module): void
{
$db = $this->dataFiles[$module];
$db->save();
}
/**
* Initialisation des données sur un contenu ou la page d'accueil
* @param string $course : id du module à générer
@ -705,7 +805,7 @@ class common
* Fonction pour construire le tableau des pages
*/
private function buildHierarchy()
public function buildHierarchy()
{
$pages = helper::arrayColumn($this->getData(['page']), 'position', 'SORT_ASC');
@ -716,7 +816,7 @@ class common
$this->getData(['page', $pageId, 'parentPageId']) === ""
// Ignore les pages dont l'utilisateur n'a pas accès
and ($this->getData(['page', $pageId, 'group']) === self::GROUP_VISITOR
or ($this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
or ($this->getUser('authKey') === $this->getInput('ZWII_AUTH_KEY')
//and $this->getUser('group') >= $this->getData(['page', $pageId, 'group'])
// Modification qui tient compte du profil de la page
and ($this->getUser('group') * self::MAX_PROFILS + $this->getUser('profil')) >= ($this->getData(['page', $pageId, 'group']) * self::MAX_PROFILS + $this->getData(['page', $pageId, 'profil']))
@ -747,7 +847,7 @@ class common
$this->getData(['page', $parentId, 'group']) === self::GROUP_VISITOR
)
or (
$this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
$this->getUser('authKey') === $this->getInput('ZWII_AUTH_KEY')
and
$this->getUser('group') * self::MAX_PROFILS + $this->getUser('profil')) >= ($this->getData(['page', $pageId, 'group']) * self::MAX_PROFILS + $this->getData(['page', $pageId, 'profil'])
@ -973,6 +1073,17 @@ class common
}
}
/**
* @return bool l'utilisateur est connecté true sinon false
*/
public function isConnected()
{
return (
!empty($this->getUser('authKey'))
&&
$this->getUser('authKey') === $this->getInput('ZWII_AUTH_KEY'));
}
/**
* Check qu'une valeur est transmise par la méthode _POST
* @return bool
@ -1063,8 +1174,9 @@ class common
}
// Articles du blog
if (
$this->getData(['page', $parentPageId, 'moduleId']) === 'blog' &&
!empty($this->getData(['module', $parentPageId]))
$this->getData(['page', $parentPageId, 'moduleId']) === 'blog'
&& !empty($this->getData(['module', $parentPageId]))
&& $this->getData(['module', $parentPageId, 'posts'])
) {
foreach ($this->getData(['module', $parentPageId, 'posts']) as $articleId => $article) {
if ($this->getData(['module', $parentPageId, 'posts', $articleId, 'state']) === true) {
@ -1161,8 +1273,7 @@ class common
$source_image = imagecreatefromwebp($src);
break;
case 'avif':
$source_image = function_exists('imagecreatefromavif') ? imagecreatefromavif($src) : null;
break;
$source_image = imagecreatefromavif($src);
}
// Image valide
if ($source_image) {
@ -1182,9 +1293,9 @@ class common
return (imagepng($virtual_image, $dest));
case 'image/gif':
return (imagegif($virtual_image, $dest));
case 'webp':
case 'image/webp':
return (imagewebp($virtual_image, $dest));
case 'avif':
case 'image/avif':
return (imageavif($virtual_image, $dest));
}
} else {

View File

@ -21,3 +21,28 @@ if (
}
$this->setData(['core', 'dataVersion', 1700]);
}
if (
$this->getData(['core', 'dataVersion']) < 1800
) {
// Parcourir la structure pour écrire dans les fichiers CSV
foreach ($this->getData(['enrolment']) as $courseId => $users) {
$filename = self::DATA_DIR . $courseId . '/report.csv';
$fp = fopen($filename, 'w');
foreach ($users as $userId => $userData) {
$history = array_key_exists('history', $userData) ? $userData['history'] : null;
if (is_array($history)) {
foreach ($history as $pageId => $timestamps) {
foreach ($timestamps as $timestamp) {
fputcsv($fp, [$userId, $pageId, $timestamp], ';');
}
}
}
$this->deleteData(['enrolment', $courseId, $userId, 'history']);
}
fclose($fp);
}
$this->setData(['core', 'dataVersion', 1800]);
}

View File

@ -2,7 +2,7 @@
<html prefix="og: http://ogp.me/ns#" lang="fr_FR">
<head>
<meta charset="UTF-8">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta http-equiv="content-type" content="text/html;">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php $layout->showMetaTitle(); ?>
<?php $layout->showMetaDescription(); ?>

View File

@ -2,7 +2,7 @@
<html prefix="og: http://ogp.me/ns#" lang="fr_FR">
<head>
<meta charset="UTF-8">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta http-equiv="content-type" content="text/html;">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php $layout->showMetaTitle(); ?>
<?php $layout->showMetaDescription(); ?>

View File

@ -2,7 +2,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr_FR">
<head>
<meta charset="UTF-8">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta http-equiv="content-type" content="text/html;">
<meta name="viewport" content="width=device-width">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="x-apple-disable-message-reformatting">

View File

@ -2,7 +2,7 @@
<html prefix="og: http://ogp.me/ns#" lang="fr_FR">
<head>
<meta charset="UTF-8">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta http-equiv="content-type" content="text/html;">
<meta meta="description=" content="ZwiiCMS le CMS multilingue sans base de données">
<meta name="generator" content="ZiiCMS https://forge.chapril.org/ZwiiCMS-Team/ZwiiCMS">
<meta name="viewport" content="width=device-width, initial-scale=1">
@ -47,7 +47,7 @@
if (
$this->getData(['theme', 'menu', 'position']) === 'top'
and $this->getData(['theme', 'menu', 'fixed']) === true
and $this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')
and $this->isConnected() === true
and $this->getUser('group') > self::GROUP_MEMBER
) {
echo '<nav id="navfixedconnected" >';

View File

@ -22,7 +22,7 @@ class config extends common
'copyBackups' => self::GROUP_ADMIN,
'delBackups' => self::GROUP_ADMIN,
'configMetaImage' => self::GROUP_ADMIN,
'siteMap' => self::GROUP_ADMIN,
'sitemap' => self::GROUP_ADMIN,
'index' => self::GROUP_ADMIN,
'restore' => self::GROUP_ADMIN,
'updateBaseUrl' => self::GROUP_ADMIN,
@ -30,7 +30,8 @@ class config extends common
'logReset' => self::GROUP_ADMIN,
'logDownload' => self::GROUP_ADMIN,
'blacklistReset' => self::GROUP_ADMIN,
'blacklistDownload' => self::GROUP_ADMIN
'blacklistDownload' => self::GROUP_ADMIN,
'register' => self::GROUP_ADMIN,
];
public static $timezones = [
@ -213,7 +214,7 @@ class config extends common
* Sitemap compressé et non compressé
* Robots.txt
*/
public function siteMap()
public function sitemap()
{
// La page n'existe pas
if (
@ -556,7 +557,7 @@ class config extends common
) {
// Ajout des lignes dans le .htaccess
$fileContent = file_get_contents('.htaccess');
$rewriteData = PHP_EOL .
$rewriteData =
'# URL rewriting' . PHP_EOL .
'<IfModule mod_rewrite.c>' . PHP_EOL .
"\tRewriteEngine on" . PHP_EOL .
@ -565,7 +566,7 @@ class config extends common
"\tRewriteCond %{REQUEST_FILENAME} !-d" . PHP_EOL .
"\tRewriteRule ^(.*)$ index.php?$1 [L]" . PHP_EOL .
'</IfModule>' . PHP_EOL .
'# URL rewriting' . PHP_EOL;
'# URL rewriting';
$fileContent = str_replace('# URL rewriting', $rewriteData, $fileContent);
file_put_contents(
'.htaccess',
@ -592,7 +593,7 @@ class config extends common
}
}
// Générer robots.txt et sitemap
$this->siteMap();
$this->sitemap();
// Valeurs en sortie
$this->addOutput([
'title' => helper::translate('Configuration'),
@ -957,4 +958,40 @@ class config extends common
]);
}
}
/**
* Fonction pour vérifier la présence du module de réécriture
* @return bool
*/
public function isModRewriteEnabled() {
// Check if Apache and mod_rewrite is loaded
if (function_exists('apache_get_modules')) {
$modules = apache_get_modules();
return in_array('mod_rewrite', $modules);
} else {
// Fallback if not using Apache or unable to detect modules
return getenv('HTTP_MOD_REWRITE') == 'On' || getenv('REDIRECT_STATUS') == '200';
}
}
/**
* 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',
[
'config' => $this->getUrl(2),
'page' => $this->getData(['user', $this->getUser('id'), 'view', 'page']),
]
]);
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . 'config/' . $this->getUrl(2),
]);
}
}

View File

@ -66,11 +66,12 @@ $(document).ready(function () {
$("#connectCaptchaStrong").prop("checked", false);
}
var configLayout = getCookie("configLayout");
if (configLayout == null) {
configLayout = "locale";
setCookie("configLayout", "locale");
var configLayout = "<?php echo $this->getData(['user', $this->getUser('id'), 'view', 'config']);?>";
// Non défini, valeur par défaut
if (configLayout == "") {
configLayout = "setup";
}
$("#localeContainer").hide();
$("#socialContainer").hide();
$("#connectContainer").hide();
@ -168,7 +169,6 @@ $(document).ready(function () {
$("#configSocialButton").removeClass("activeButton");
$("#configConnectButton").removeClass("activeButton");
$("#configNetworkButton").removeClass("activeButton");
setCookie("configLayout", "locale");
});
$("#configSetupButton").on("click", function () {
$("#localeContainer").hide();
@ -181,7 +181,6 @@ $(document).ready(function () {
$("#configSocialButton").removeClass("activeButton");
$("#configConnectButton").removeClass("activeButton");
$("#configNetworkButton").removeClass("activeButton");
setCookie("configLayout", "setup");
});
$("#configSocialButton").on("click", function () {
@ -195,7 +194,6 @@ $(document).ready(function () {
$("#configSocialButton").addClass("activeButton");
$("#configConnectButton").removeClass("activeButton");
$("#configNetworkButton").removeClass("activeButton");
setCookie("configLayout", "social");
});
$("#configConnectButton").on("click", function () {
$("#setupContainer").hide();
@ -208,7 +206,6 @@ $(document).ready(function () {
$("#configSocialButton").removeClass("activeButton");
$("#configConnectButton").addClass("activeButton");
$("#configNetworkButton").removeClass("activeButton");
setCookie("configLayout", "connect");
});
$("#configNetworkButton").on("click", function () {
$("#setupContainer").hide();
@ -221,7 +218,6 @@ $(document).ready(function () {
$("#configSocialButton").removeClass("activeButton");
$("#configConnectButton").removeClass("activeButton");
$("#configNetworkButton").addClass("activeButton");
setCookie("configLayout", "network");
});
@ -308,27 +304,6 @@ $(document).ready(function () {
});
function setCookie(name, value, days) {
var expires = "";
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + (value || "") + expires + "; path=/; samesite=lax";
}
function getCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
// Define function to capitalize the first letter of a string
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);

View File

@ -7,44 +7,35 @@
'value' => template::ico('home')
]); ?>
</div>
<div class="col1">
<?php /**echo template::button('configHelp', [
'class' => 'buttonHelp',
'href' => 'https://doc.zwiicms.fr/configuration-du-site',
'target' => '_blank',
'value' => template::ico('help'),
'help' => 'Consulter l\'aide en ligne'
]); */?>
</div>
<div class="col2 offset8">
<div class="col2 offset9">
<?php echo template::submit('Submit'); ?>
</div>
</div>
<div class="tab">
<?php echo template::button('configLocaleButton', [
'value' => 'Identité - Etiquettes',
'value' => 'Identité - Étiquettes',
'class' => 'buttonTab'
]); ?>
<?php echo template::button('configSetupButton', [
'value' => 'Configuration - Outils',
'class' => 'buttonTab'
'value' => 'Configuration',
'class' => 'buttonTab',
//'href' => helper::baseUrl() . 'config/register/setup'
]); ?>
<?php echo template::button('configSocialButton', [
'value' => 'Réseaux sociaux',
'class' => 'buttonTab'
'value' => 'Référencement',
'class' => 'buttonTab',
//'href' => helper::baseUrl() . 'config/register/social'
]); ?>
<?php echo template::button('configConnectButton', [
'value' => 'Sécurité',
'class' => 'buttonTab'
'value' => 'Connexion',
'class' => 'buttonTab',
//'href' => helper::baseUrl() . 'config/register/connect'
]); ?>
<?php echo template::button('configNetworkButton', [
'value' => 'Réseau',
'class' => 'buttonTab'
'class' => 'buttonTab',
//'href' => helper::baseUrl() . 'config/register/network'
]); ?>
</div>
<?php include('core/module/config/view/locale/locale.php') ?>
<?php include('core/module/config/view/setup/setup.php') ?>

View File

@ -98,7 +98,7 @@
<h4>
<?php echo helper::translate('Étiquettes des pages spéciales'); ?>
<!--<span id="labelHelpButton" class="helpDisplayButton" title="Cliquer pour consulter l'aide en ligne">
<a href="https://doc.zwiicms.fr/etiquettes-des-pages-speciales" target="_blank">
<a href="https://doc.zwiicms.fr/Étiquettes-des-pages-speciales" target="_blank">
<?php //echo template::ico('help', ['margin' => 'left']); ?>
</a>
</span>-->

View File

@ -7,7 +7,7 @@
'value' => template::ico('left')
]); ?>
</div>
<div class="col2 offset8">
<div class="col2 offset9">
<?php echo template::submit('configManageSubmit', [
'value' => 'Valider',
'ico' => 'check'

View File

@ -3,12 +3,6 @@
<div class="col12">
<div class="block">
<h4><?php echo helper::translate('Paramètres'); ?>
<!--<span id="setupHelpButton" class="helpDisplayButton">
<a href="https://doc.zwiicms.fr/parametres" target="_blank" title="Cliquer pour consulter l'aide en ligne">
<?php //echo template::ico('help', ['margin' => 'left']);
?>
</a>-->
</span>
</h4>
<div class="row">
<div class="col4">
@ -17,7 +11,8 @@
'language' => $this->getData(['user', $this->getUser('id'), 'language']),
'help' => 'Pensez à supprimer le cache de votre navigateur si la favicon ne change pas.',
'label' => 'Favicon',
'value' => $this->getData(['config', 'favicon'])
'value' => $this->getData(['config', 'favicon']),
'folder' => $this->getData(['config', 'favicon']) ? dirname($this->getData(['config', 'favicon'])) : ''
]); ?>
</div>
<div class="col4">
@ -26,7 +21,8 @@
'language' => $this->getData(['user', $this->getUser('id'), 'language']),
'help' => 'Sélectionnez une icône adaptée à un thème sombre.<br>Pensez à supprimer le cache de votre navigateur si la favicon ne change pas.',
'label' => 'Favicon thème sombre',
'value' => $this->getData(['config', 'faviconDark'])
'value' => $this->getData(['config', 'faviconDark']),
'folder' => $this->getData(['config', 'faviconDark']) ? dirname($this->getData(['config', 'faviconDark'])) : ''
]); ?>
</div>
<div class="col4">
@ -48,7 +44,7 @@
<?php echo template::checkbox('configRewrite', true, 'Apache URL intelligentes', [
'checked' => helper::checkRewrite(),
'help' => 'Supprime le point d\'interrogation dans les URL, l\'option est indisponible avec les autres serveurs Web',
'disabled' => stripos($_SERVER["SERVER_SOFTWARE"], 'nginx')
'disabled' => stripos($_SERVER["SERVER_SOFTWARE"], 'Apache') === false and $module->isModRewriteEnabled()
]); ?>
</div>
</div>
@ -59,12 +55,6 @@
<div class="col12">
<div class="block">
<h4><?php echo helper::translate('Mise à jour automatisée'); ?>
<!--<span id="updateHelpButton" class="helpDisplayButton">
<a href="https://doc.zwiicms.fr/mise-a-jour" target="_blank" title="Cliquer pour consulter l'aide en ligne">
<?php //echo template::ico('help', ['margin' => 'left']);
?>
</a>
</span>-->
</h4>
<div class="row">
<div class="col6">
@ -109,12 +99,6 @@
<div class="col12">
<div class="block">
<h4><?php echo helper::translate('Maintenance'); ?>
<!--<span id="maintenanceHelpButton" class="helpDisplayButton">
<a href="https://doc.zwiicms.fr/mode-maintenance" target="_blank" title="Cliquer pour consulter l'aide en ligne">
<?php //echo template::ico('help', ['margin' => 'left']);
?>
</a>
</span>-->
</h4>
<div class="row">
<div class="col6">
@ -169,12 +153,6 @@
<div class="col12">
<div class="block">
<h4><?php echo helper::translate('Scripts externes'); ?>
<!--<span id="specialeHelpButton" class="helpDisplayButton">
<a href="https://doc.zwiicms.fr/scripts-externes" target="_blank" title="Cliquer pour consulter l'aide en ligne">
<?php //echo template::ico('help', ['margin' => 'left']);
?>
</a>
</span>-->
</h4>
<div class="row">
<div class="col4 offset1 verticalAlignBottom">
@ -198,13 +176,21 @@
<div class="row">
<div class="col12">
<div class="block">
<h4>ZwiiCMS <a href="https://zwiicms.fr" target="_blank">Site Web</a> - <a href="https://forum.zwiicms.fr" target="_blank">Forum</a>
<h4>ZwiiCMS <a href="https://zwiicms.fr" target="_blank">Site Web</a> - <a
href="https://forum.zwiicms.fr" target="_blank">Forum</a>
</h4>
<div class="row textAlignCenter">
<div class="col12">
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a>
<p>Cette œuvre est mise à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Licence Creative Commons Attribution - Pas d&#39;Utilisation Commerciale - Pas de Modification 4.0 International.</a></p>
<p>Pour voir une copie de cette licence, visitez http://creativecommons.org/licenses/by-nc-nd/4.0/ ou écrivez à Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.</p>
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img
alt="Licence Creative Commons" style="border-width:0"
src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a>
<p>Cette œuvre est mise à disposition selon les termes de la <a rel="license"
href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Licence Creative Commons
Attribution - Pas d&#39;Utilisation Commerciale - Pas de Modification 4.0
International.</a></p>
<p>Pour voir une copie de cette licence, visitez
http://creativecommons.org/licenses/by-nc-nd/4.0/ ou écrivez à Creative Commons, PO Box
1866, Mountain View, CA 94042, USA.</p>
</div>
</div>
</div>

View File

@ -4,11 +4,6 @@
<div class="block">
<h4>
<?php echo helper::translate('Capture d\'écran Open Graph'); ?>
<!--<span id="specialeHelpButton" class="helpDisplayButton">
<a href="https://doc.zwiicms.fr/referencement" target="_blank" title="Cliquer pour consulter l'aide en ligne">
<?php //echo template::ico('help', ['margin' => 'left']); ?>
</a>
</span>-->
</h4>
<div class="row">
<div class="col6">
@ -18,6 +13,7 @@
'language' => $this->getData(['user', $this->getUser('id'), 'language']),
'label' => 'Image Open Graph',
'value' => $this->getData(['config', 'seo', 'openGraphImage']),
'folder' => $this->getData(['config', 'seo', 'openGraphImage']) ? dirname($this->getData(['config', 'seo', 'openGraphImage'])) : '',
'type' => 1,
'help' => sprintf('%s : JPG - PNG<br />', helper::translate('Format')) .
sprintf('%s : 1200 x 630 pixels<br />', helper::translate('Dimensions minimales')) .
@ -84,12 +80,6 @@
<div class="col12">
<div class="block">
<h4>
<?php echo helper::translate('Réseaux sociaux'); ?>
<!--<span id="specialeHelpButton" class="helpDisplayButton">
<a href="https://doc.zwiicms.fr/reseaux-sociaux" target="_blank" title="Cliquer pour consulter l'aide en ligne">
<?php //echo template::ico('help', ['margin' => 'left']); ?>
</a>
</span>-->
</h4>
<div class="row">
<div class="col3">

View File

@ -26,12 +26,13 @@ class course extends common
'users' => self::GROUP_EDITOR, // fait
'usersAdd' => self::GROUP_EDITOR, //Fait
'usersDelete' => self::GROUP_EDITOR, //Fait
'usersHistoryExport' => self::GROUP_EDITOR, //fait
'usersReportExport' => self::GROUP_EDITOR, //fait
'userDelete' => self::GROUP_EDITOR, //Fait
'userHistory' => self::GROUP_EDITOR, //Fait
'userHistoryExport' => self::GROUP_EDITOR, //Fait
'userReport' => self::GROUP_EDITOR, //Fait
'userReportExport' => self::GROUP_EDITOR, //Fait
'backup' => self::GROUP_EDITOR, // Fait
'restore' => self::GROUP_EDITOR, //Fait
'reset' => self::GROUP_EDITOR,
'clone' => self::GROUP_ADMIN,
'add' => self::GROUP_ADMIN,
'delete' => self::GROUP_ADMIN,
@ -39,6 +40,7 @@ class course extends common
'categoryAdd' => self::GROUP_ADMIN,
'categoryEdit' => self::GROUP_ADMIN,
'categoryDelete' => self::GROUP_ADMIN,
'export' => self::GROUP_ADMIN,
];
public static $courseAccess = [
@ -74,7 +76,7 @@ class course extends common
public static $pagesList = ['accueil' => 'Accueil'];
public static $userHistory = [];
public static $userReport = [];
public static $userGraph = [];
@ -85,6 +87,9 @@ class course extends common
// Tableau à transmettre à la fvue
self::$courses = array();
// Pointer RFM sur le dossier de l'espace
self::$siteContent = 'home';
if (
$this->getUser('id')
&& $this->getUser('group')
@ -104,19 +109,26 @@ class course extends common
? sprintf('%s %s', $this->getData(['user', $this->getData(['course', $courseId, 'author']), 'firstname']), $this->getData(['user', $this->getData(['course', $courseId, 'author']), 'lastname']))
: '';
$categorieUrl = helper::baseUrl() . 'course/swap/' . $courseId;
$info = sprintf(' <a href="%s" target="_blank">%s</a><br />Auteur : %s<br />Id : %s<br />', $categorieUrl, $this->getData(['course', $courseId, 'title']), $author, $courseId, );
$info = sprintf(' <a href="%s">%s</a><br />Auteur : %s<br />Id : %s<br />', $categorieUrl, $this->getData(['course', $courseId, 'title']), $author, $courseId, );
$enrolment = sprintf(
'Accès : %s<br />Inscription : %s<br />',
self::$courseAccess[$this->getData(['course', $courseId, 'access'])],
self::$courseEnrolment[$this->getData(['course', $courseId, 'enrolment'])]
);
if ($this->getUser('permission', 'course', 'users') === true) {
$users = template::button('categoryUser' . $this->getUrl(2), [
'href' => helper::baseUrl() . 'course/users/' . $courseId,
'value' => template::ico('users'),
]);
}
self::$courses[] = [
$info,
$this->getData(['course', $courseId, 'description']),
$enrolment,
$users,
template::button('categoryUser' . $courseId, [
'href' => helper::baseUrl() . 'course/manage/' . $courseId,
'value' => template::ico('eye'),
'value' => template::ico('sliders'),
'help' => 'Gérer'
])
];
@ -165,6 +177,11 @@ class course extends common
$this->initData('module', $courseId);
$this->initData('theme', $courseId);
// Pointer RFM sur le dossier de l'espace
self::$siteContent = $courseId;
// Ordonne les pages par position
$this->buildHierarchy();
// BDD des inscrits
$this->setData([
'enrolment',
@ -295,14 +312,24 @@ class course extends common
self::$courseCategories = $this->getData(['category']);
// Liste des pages disponibles
$this->initDB('page', $this->getUrl(2));
$this->initDB('page', $courseId);
// Pointer RFM sur le dossier de l'espace
self::$siteContent = $courseId;
// Ordonne les pages par position
$this->buildHierarchy();
// Données pour le formulaire
self::$pagesList = $this->getData(['page']);
foreach (self::$pagesList as $page => $pageId) {
// Exclure les barres et les pages désactivées
foreach (self::$pagesList as $pageId => $page) {
if (
$this->getData(['page', $page, 'block']) === 'bar' ||
$this->getData(['page', $page, 'disable']) === true
$page['block'] === 'bar' ||
$page['disable'] === true
) {
unset(self::$pagesList[$page]);
unset(self::$pagesList[$pageId]);
}
}
@ -344,14 +371,24 @@ class course extends common
self::$courseCategories = $this->getData(['category']);
// Liste des pages disponibles
$this->initDB('page', $this->getUrl(2));
$this->initDB('page', $courseId);
// Pointer RFM sur le dossier de l'espace
self::$siteContent = $courseId;
// Ordonne les pages par position
$this->buildHierarchy();
// Données pour le formulaire
self::$pagesList = $this->getData(['page']);
foreach (self::$pagesList as $page => $pageId) {
// Exclure les barres et les pages désactivées
foreach (self::$pagesList as $pageId => $page) {
if (
$this->getData(['page', $page, 'block']) === 'bar' ||
$this->getData(['page', $page, 'disable']) === true
$page['block'] === 'bar' ||
$page['disable'] === true
) {
unset(self::$pagesList[$page]);
unset(self::$pagesList[$pageId]);
}
}
@ -632,36 +669,19 @@ class course extends common
// Liste des pages contenues dans cet espace et exclure les barres et les pages masquées
$sumPages = 0;
$pages = json_decode(file_get_contents(self::DATA_DIR . $courseId . '/page.json'), true);
$pages = $pages['page'];
foreach ($pages as $pageId => $pageData) {
if ($pageData['position'] > 0) {
$sumPages++;
}
}
$sumPages = $this->countPages($pages['page']);
// Liste des inscrits dans le contenu sélectionné.
$users = $this->getData(['enrolment', $courseId]);
// Obtient les statistiques de l'ensemble de la cohorte
$reports = $this->getReport($courseId);
if (is_array($users)) {
// Tri du tableau par défaut par $userId
ksort($users);
foreach ($users as $userId => $userValue) {
// Date et heure de la dernière page vue
// Compatibilité anciennes versions
if (
$this->getData(['enrolment', $courseId, $userId, 'lastPageView']) === null
or $this->getData(['enrolment', $courseId, $userId, 'datePageView']) === null
) {
if (!empty ($userValue['history'])) {
$maxTime = max($userValue['history']);
$lastPageId = array_search($maxTime, $userValue['history']);
$this->setData(['enrolment', $courseId, $userId, 'lastPageView', $lastPageId]);
$this->setData(['enrolment', $courseId, $userId, 'datePageView', $maxTime]);
}
}
// Compte les rôles valides
if (isset($profils[$this->getData(['user', $userId, 'group']) . $this->getData(['user', $userId, 'profil'])])) {
$profils[$this->getData(['user', $userId, 'group']) . $this->getData(['user', $userId, 'profil'])]++;
@ -694,28 +714,36 @@ class course extends common
}
// Progression
$viewPages = $this->getData(['enrolment', $courseId, $userId, 'history']) !== null ?
count(array_keys($this->getData(['enrolment', $courseId, $userId, 'history']))) :
$viewPages = array_key_exists($userId, $reports) ?
count($reports[$userId]) :
0;
// Construction du tableau
self::$courseUsers[] = [
//$userId,
$this->getData(['user', $userId, 'firstname']) . ' ' . $this->getData(['user', $userId, 'lastname']),
isset ($pages[$this->getData(['enrolment', $courseId, $userId, 'lastPageView'])]['title'])
? $pages[$this->getData(['enrolment', $courseId, $userId, 'lastPageView'])]['title']
array_key_exists('lastPageView', $userValue) && isset($pages[$userValue['lastPageView']]['title'])
? $pages[$userValue['lastPageView']]['title']
: '',
$this->getData(['enrolment', $courseId, $userId, 'datePageView'])
? helper::dateUTF8('%d/%m/%Y', $this->getData(['enrolment', $courseId, $userId, 'datePageView']))
array_key_exists('lastPageView', $userValue)
? helper::dateUTF8('%d/%m/%Y', $userValue['datePageView'])
: '',
$this->getData(['enrolment', $courseId, $userId, 'datePageView'])
? helper::dateUTF8('%H:%M', $this->getData(['enrolment', $courseId, $userId, 'datePageView']))
array_key_exists('datePageView', $userValue)
? helper::dateUTF8('%H:%M', $userValue['datePageView'])
: '',
$this->getData(['user', $userId, 'tags']),
template::button('userHistory' . $userId, [
'href' => helper::baseUrl() . 'course/userHistory/' . $courseId . '/' . $userId,
'value' => !empty ($userValue['history']) ? min(round(($viewPages * 100) / $sumPages, 1), 100) . ' %' : '0%',
'disable' => empty ($userValue['history'])
template::button('userReport' . $userId, [
'href' => helper::baseUrl() . 'course/userReport/' . $courseId . '/' . $userId,
/** La lecture de la progression s'effectue selon la nouvelle méthode (progression dans la base des enrolements)
* Soit avec l'ancienne méthode qui consiste à recalculer la progression.
* TRANSITOIRE A SUPPRIMER EN FIN D'ANNEE
**/
'value' => array_key_exists('progress', $userValue)
? $userValue['progress']
: ($viewPages ? min(round(($viewPages * 100) / $sumPages, 1), 100) . ' %' : '0%'),
'disable' => empty($userValue['datePageView']),
//'value' => $viewPages ? min(round(($viewPages * 100) / $sumPages, 1), 100) . ' %' : '0%',
//'disable' => empty($viewPages)
]),
template::button('userDelete' . $userId, [
'class' => 'userDelete buttonRed',
@ -773,9 +801,11 @@ class course extends common
$this->getData(['user', $keyPost]) !== null
&& $this->getData(['enrolment', $courseId, $keyPost]) === null
) {
$this->setData(['enrolment', $courseId, $keyPost, 'history', array()]);
$this->setData(['enrolment', $courseId, $keyPost, 'history', array()], false);
}
}
// Sauvegarde la base manuellement
$this->saveDB('enrolment');
}
// Liste des groupes et des profils
@ -915,6 +945,7 @@ class course extends common
/**
* Désinscription de tous les utilisateurs
* Les désinscriptions ne suppriment pas les historiques
*/
public function usersDelete()
{
@ -1050,6 +1081,47 @@ class course extends common
]);
}
/**
* Désincription de tous les utilisateurs hors les éditeurs
* Effacement des historiques
*/
public function reset()
{
// Contenu sélectionné
$courseId = $this->getUrl(2);
// Accès limité aux admins, à l'auteur ou éditeurs inscrits
if (
$this->permissionControl(__FUNCTION__, $courseId) === false
) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
}
// Active l'accueil
$_SESSION['ZWII_SITE_CONTENT'] = 'home';
// Efface les inscriptions
$success = $this->setData(['enrolment', $courseId, []]);
// Efface les rapports
if (file_exists(self::DATA_DIR . $courseId . '/report.csv')) {
unlink(self::DATA_DIR . $courseId . '/report.csv');
}
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . 'course',
'notification' => helper::translate('Espace réinitialisé'),
'state' => true
]);
}
/*
* Traitement du changement de langue
*/
@ -1087,11 +1159,6 @@ class course extends common
? helper::baseUrl() . $this->getData(['enrolment', $courseId, $userId, 'lastPageView'])
: helper::baseUrl();
/*
$essage = $this->getData(['enrolment', $courseId, $userId, 'datePageView'])
? $this->getData(['enrolment', $courseId, $userId, 'datePageView'])
: '';
*/
if ($this->getData(['course', $courseId, 'access']) === self::COURSE_ACCESS_DATE) {
$to = helper::dateUTF8('%d %B %Y', $this->getData(['course', $courseId, 'closingDate']), self::$i18nUI) . helper::translate(' à ') . helper::dateUTF8('%H:%M', $this->getData(['course', $courseId, 'closingDate']), self::$i18nUI);
$message .= sprintf(helper::translate('Ce contenu ferme le %s'), $to);
@ -1164,7 +1231,7 @@ class course extends common
/**
* Liste les pages consultées par un utilisateur
*/
public function userHistory()
public function userReport()
{
// Espace sélectionné
@ -1181,17 +1248,18 @@ class course extends common
}
$userId = $this->getUrl(3);
$h = $this->getData(['enrolment', $courseId, $userId, 'history']);
$h = $this->getReport($courseId, $userId);
$h = $h[$userId];
// Inversion des clés et des valeurs
$history = array();
$report = array();
foreach ($h as $key => $values) {
foreach ($values as $value) {
$history[$value] = $key;
$report[$value] = $key;
}
}
ksort($history);
ksort($report);
// Liste des pages contenues dans cet espace et exclure les barres et les pages masquées
$p = json_decode(file_get_contents(self::DATA_DIR . $courseId . '/page.json'), true);
@ -1207,11 +1275,11 @@ class course extends common
$topTime = 0;
$lastView = 0;
foreach ($history as $time => $pageId) {
foreach ($report as $time => $pageId) {
if (isset($pages[$pageId]['title'])) {
$lastView = ($lastView === 0) ? $time : $lastView;
$diff = $time - $lastView;
self::$userHistory[] = [
self::$userReport[] = [
html_entity_decode($pages[$pageId]['title']),
$time,
($diff < 1800) ? sprintf("%d' %d''", floor($diff / 60), $diff % 60) : "Non significatif",
@ -1230,16 +1298,16 @@ class course extends common
}
// Décale les temps de consultation
for ($i = 0; $i < count(self::$userHistory) - 1; $i++) {
self::$userHistory[$i][2] = self::$userHistory[$i + 1][2];
for ($i = 0; $i < count(self::$userReport) - 1; $i++) {
self::$userReport[$i][2] = self::$userReport[$i + 1][2];
}
// Décale les temps de consultation
for ($i = 0; $i < count(self::$userGraph) - 1; $i++) {
self::$userHistory[$i][1] = self::$userHistory[$i + 1][1];
self::$userReport[$i][1] = self::$userReport[$i + 1][1];
}
// Formate le timestamp
array_walk(self::$userHistory, function (&$item) {
array_walk(self::$userReport, function (&$item) {
$item[1] = helper::dateUTF8('%d/%m/%Y %H:%M:%S', $item[1]);
});
@ -1257,7 +1325,7 @@ class course extends common
// Valeurs en sortie
$this->addOutput([
'title' => helper::translate('Historique ') . $this->getData(['user', $userId, 'firstname']) . ' ' . $this->getData(['user', $userId, 'lastname']),
'view' => 'userHistory',
'view' => 'userReport',
'vendor' => [
"plotly"
]
@ -1265,7 +1333,7 @@ class course extends common
}
public function usersHistoryExport()
public function usersReportExport()
{
$courseId = $this->getUrl(2);
@ -1281,7 +1349,7 @@ class course extends common
}
self::$courseUsers = [
0 => ['UserId', 'Prénom', 'Nom', 'Page Titre', 'Consultation Date', 'Consultation Heure', 'Progression']
0 => ['UserId', 'Prénom', 'Nom', 'Mail', 'Page Titre', 'Consultation Date', 'Consultation Heure', 'Progression']
];
// Statistiques du contenu sélectionné calcul du nombre de pages
@ -1298,6 +1366,9 @@ class course extends common
// Liste des inscrits dans le contenu sélectionné.
$users = $this->getData(['enrolment', $courseId]);
// Obtient les statistiques de l'ensemble de la cohorte
$reports = $this->getReport($courseId);
if (is_array($users)) {
// Tri du tableau par défaut par $userId
ksort($users);
@ -1322,14 +1393,14 @@ class course extends common
if (!empty($userValue['history'])) {
$maxTime = max($userValue['history']);
$lastPageId = array_search($maxTime, $userValue['history']);
$this->setData(['enrolment', $courseId, $userId, 'lastPageView', $lastPageId]);
$this->setData(['enrolment', $courseId, $userId, 'lastPageView', $lastPageId], false);
$this->setData(['enrolment', $courseId, $userId, 'datePageView', $maxTime]);
}
}
// Progression
$viewPages = $this->getData(['enrolment', $courseId, $userId, 'history']) !== null ?
count(array_keys($this->getData(['enrolment', $courseId, $userId, 'history']))) :
$viewPages = array_key_exists($userId, $reports) ?
count($reports[$userId]) :
0;
// Construction du tableau
@ -1337,12 +1408,20 @@ class course extends common
$userId,
$this->getData(['user', $userId, 'firstname']),
$this->getData(['user', $userId, 'lastname']),
$this->getData(['user', $userId, 'mail']),
isset($pages[$this->getData(['enrolment', $courseId, $userId, 'lastPageView'])])
? $pages[$this->getData(['enrolment', $courseId, $userId, 'lastPageView'])]
: $this->getData(['enrolment', $courseId, $userId, 'lastPageView']) . ' (supprimée)',
helper::dateUTF8('%d/%d/%Y', $this->getData(['enrolment', $courseId, $userId, 'datePageView'])),
helper::dateUTF8('%H:%M', $this->getData(['enrolment', $courseId, $userId, 'datePageView'])),
number_format(min(round(($viewPages * 100) / $sumPages, 1) / 100, 1), 2, ',')
/** La lecture de la progression s'effectue selon la nouvelle méthode (progression dans la base des enrolements)
* Soit avec l'ancienne méthode qui consiste à recalculer la progression.
* TRANSITOIRE A SUPPRIMER EN FIN D'ANNEE
**/
array_key_exists('progress', $userValue)
? $userValue['progress']
: ($viewPages ? min(round(($viewPages * 100) / $sumPages, 1), 100) . ' %' : '0%'),
//number_format(min(round(($viewPages * 100) / $sumPages, 1) / 100, 1), 2, ','),
];
// Synthèse des historiques
@ -1371,7 +1450,7 @@ class course extends common
}
}
public function userHistoryExport()
public function userReportExport()
{
$courseId = $this->getUrl(2);
@ -1388,17 +1467,18 @@ class course extends common
}
// Traitement de l'historique
$h = $this->getData(['enrolment', $courseId, $userId, 'history']);
$h = $this->getReport($courseId);
$h = $h[$userId];
// Inversion des clés et des valeurs
$history = array();
$report = array();
foreach ($h as $key => $values) {
foreach ($values as $value) {
$history[$value] = $key;
$report[$value] = $key;
}
}
ksort($history);
ksort($report);
// Liste des pages contenues dans cet espace et exclure les barres et les pages masquées
$p = json_decode(file_get_contents(self::DATA_DIR . $courseId . '/page.json'), true);
@ -1412,11 +1492,11 @@ class course extends common
$lastView = 0;
foreach ($history as $time => $pageId) {
foreach ($report as $time => $pageId) {
if (isset($pages[$pageId]['title'])) {
$lastView = ($lastView === 0) ? $time : $lastView;
$diff = $time - $lastView;
self::$userHistory[] = [
self::$userReport[] = [
$pageId,
html_entity_decode($pages[$pageId]['title']),
$time,
@ -1429,16 +1509,16 @@ class course extends common
}
// Décale les temps de consultation
for ($i = 0; $i < count(self::$userHistory) - 1; $i++) {
self::$userHistory[$i][3] = self::$userHistory[$i + 1][3];
for ($i = 0; $i < count(self::$userReport) - 1; $i++) {
self::$userReport[$i][3] = self::$userReport[$i + 1][3];
}
// Formate le timestamp
array_walk(self::$userHistory, function (&$item) {
array_walk(self::$userReport, function (&$item) {
$item[2] = helper::dateUTF8('%d/%m/%Y %H:%M:%S', $item[2]);
});
// Ajoute les entêtes
self::$userHistory = array_merge([0 => ['PageId', 'Page Titre', 'Consultation Date', 'Temps Consultation']], self::$userHistory);
self::$userReport = array_merge([0 => ['PageId', 'Page Titre', 'Consultation Date', 'Temps Consultation']], self::$userReport);
// Dossier d'export
if (is_dir(self::FILE_DIR . 'source/' . $courseId) === false) {
@ -1451,7 +1531,7 @@ class course extends common
$file = fopen($filename, 'w');
foreach (self::$userHistory as $keys => $values) {
foreach (self::$userReport as $keys => $values) {
$data = $values;
// Écrire la ligne dans le fichier CSV
fputcsv($file, $data, ';');
@ -1461,7 +1541,7 @@ class course extends common
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . 'course/userHistory/' . $courseId . '/' . $userId,
'redirect' => helper::baseUrl() . 'course/userReport/' . $courseId . '/' . $userId,
'notification' => 'Création ' . basename($filename) . ' dans le dossier "Export"',
'state' => true,
]);
@ -1551,6 +1631,7 @@ class course extends common
/**
* Désinscription d'un participant
* La désinscription ne supprime pas les historiques,
*/
public function unsuscribe()
{
@ -1597,17 +1678,17 @@ class course extends common
// Participants avec historiques
$enrolment = $this->getData(['enrolment', $courseId]);
// Générer un fichier dans le dossier de l'espace
file_put_contents(self::DATA_DIR . $courseId . '/enrolment.json', json_encode([$courseId => $enrolment], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
$this->secure_file_put_contents(self::DATA_DIR . $courseId . '/enrolment.json', json_encode([$courseId => $enrolment], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
// Idem pour les données du cours
$course = $this->getData(['course', $courseId]);
// Générer un fichier dans le dossier de l'espace
file_put_contents(self::DATA_DIR . $courseId . '/course.json', json_encode([$courseId => $course], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
$this->secure_file_put_contents(self::DATA_DIR . $courseId . '/course.json', json_encode([$courseId => $course], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
// Idem pour la catégorie
$category = $this->getData(['category', $this->getData(['course', $courseId, 'category'])]);
// Générer un fichier dans le dossier de l'espace
file_put_contents(self::DATA_DIR . $courseId . '/category.json', json_encode([$this->getData(['course', $courseId, 'category']) => $category], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
$this->secure_file_put_contents(self::DATA_DIR . $courseId . '/category.json', json_encode([$this->getData(['course', $courseId, 'category']) => $category], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
// Génère une archive ZIP
@ -1639,8 +1720,158 @@ class course extends common
}
// Générer un fichier externe contenant le contenu des pages
public function export()
{
// Espace sélectionné
$courseId = $this->getUrl(2);
// Accès limité aux admins, à l'auteur ou éditeurs inscrits
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) === false
) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
}
// Liste des pages disponibles
$this->initDB('page', $courseId);
// Pointer RFM sur le dossier de l'espace
self::$siteContent = $courseId;
// Ordonne les pages par position
$this->buildHierarchy();
// Tableau de retour
self::$pagesList = [];
// Exclure les barres et les pages désactivées
foreach ($this->getData(['page']) as $pageId => $page) {
if (
$this->getData(['page', $pageId, 'block']) !== 'bar' &&
$this->getData(['page', $pageId, 'disable']) !== true
) {
self::$pagesList[] = template::checkbox('courseManageExport' . $pageId, true, $page['title'], [
'class' => 'courseManageCheckbox'
]);
}
}
// Soumission du formulaire
if ($this->isPost()) {
$datas = '<h1>' . $this->getData(['course', $courseId, 'title']) . '</h1>';
$resources = [];
foreach ($this->getData(['page']) as $pageId => $page) {
if ($this->getInput('courseManageExport' . $pageId, helper::FILTER_BOOLEAN) === true) {
$pageContent = $this->getPage($pageId, $courseId);
// Extraction des URLs des ressources (images, vidéos, fichiers, etc.)
preg_match_all('/<img[^>]+src=["\'](.*?)["\']/i', $pageContent, $imgMatches); // Images
preg_match_all('/<a[^>]+href=["\'](.*?)["\']/i', $pageContent, $linkMatches); // Liens
preg_match_all('/<video[^>]+src=["\'](.*?)["\']/i', $pageContent, $videoMatches); // Vidéos directes
preg_match_all('/<source[^>]+src=["\'](.*?)["\']/i', $pageContent, $sourceMatches); // Vidéos dans balises <source>
// Traitement des images
$this->processResources($pageContent, '/<img[^>]+src=["\'](.*?)["\']/i', $resources);
// Traitement des vidéos directes via <video>
$this->processResources($pageContent, '/<video[^>]+src=["\'](.*?)["\']/i', $resources);
// Traitement des sources dans les balises <source> (utilisées dans <video> ou <audio>)
$this->processResources($pageContent, '/<source[^>]+src=["\'](.*?)["\']/i', $resources);
// Traitement des liens <a>
$this->processResources($pageContent, '/<a[^>]+href=["\'](.*?)["\']/i', $resources);
// Traitement des liens
if (!empty($linkMatches[1])) {
$resources = array_merge($resources, $linkMatches[1]);
// Remplacement des chemins pour les liens dans $pageContent
foreach ($linkMatches[1] as $resourceUrl) {
$resourcePath = parse_url($resourceUrl, PHP_URL_PATH);
$resourceFile = basename($resourcePath);
$pageContent = str_replace($resourceUrl, $resourceFile, $pageContent);
}
}
$datas .= $pageContent;
}
}
// Créer le dossier d'export
$path = self::FILE_DIR . 'source/' . $courseId . '/';
if (is_dir($path . 'export') === false) {
mkdir($path . 'export');
}
// Copier les ressources dans le dossier d'export
foreach ($resources as $resourceUrl) {
$resourcePath = parse_url($resourceUrl, PHP_URL_PATH);
$resourceFile = basename($resourcePath);
if (file_exists($resourcePath)) { // Utilisation du chemin correct
copy($resourcePath, $path . 'export/' . $resourceFile);
}
}
// Ajouter les balises HTML manquantes
$datas = '<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>' . $this->getData(['course', $courseId, 'title']) . '</title>
<link rel="stylesheet" href="style.css">
</head>
<body>' . $datas . '</body></html>';
// Sauvegarder le fichier HTML
file_put_contents($path . '/export/export_' . $this->getData(['course', $courseId, 'title']) . '.html', $datas, LOCK_EX);
// Copie une feuille de style
copy('core/module/course/resource/style.css', $path . 'export/style.css');
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . 'course/manage/' . $courseId,
'notification' => helper::translate('Pages exportées dans le dossier de cet espace'),
'state' => true,
]);
}
// Valeurs en sortie
$this->addOutput([
'title' => sprintf('%s id : %s', helper::translate('Export des pages de l\'espace'), $this->getUrl(2)),
'view' => 'export'
]);
}
// Fonction utilisé par l'export en html pour corriger les URL des ressources
private function processResources(&$pageContent, $regex, &$resources)
{
preg_match_all($regex, $pageContent, $matches);
if (!empty($matches[1])) {
$resources = array_merge($resources, $matches[1]);
// Remplacement des chemins dans $pageContent
foreach ($matches[1] as $resourceUrl) {
$resourcePath = parse_url($resourceUrl, PHP_URL_PATH);
$resourceFile = basename($resourcePath);
$pageContent = str_replace($resourceUrl, $resourceFile, $pageContent);
}
}
}
/**
* Sauvegarde d'un cours sans option
* Récupération d'un espace sans option
*/
public function restore()
@ -1807,7 +2038,7 @@ class course extends common
/**
* Autorise l'accès à un contenu
* @param @return bool le user a le droit d'entrée dans le contenu
* @return bool le user a le droit d'entrée dans le contenu
* @param string $courseId identifiant du contenu sollicité
*/
public function courseIsAvailable($courseId)
@ -1818,7 +2049,7 @@ class course extends common
}
// Si un utilisateur connecté est admin ou auteur, c'est autorisé
if (
$this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD') &&
$this->isConnected() === true &&
($this->getUser('group') === self::GROUP_ADMIN ||
$this->getUser('id') === $this->getData(['course', $courseId, 'author']))
) {
@ -1835,17 +2066,50 @@ class course extends common
time() <= $this->getData(['course', $courseId, 'closingDate'])
);
case self::COURSE_ACCESS_CLOSE:
default:
return false;
}
}
/**
* Méthode externe pour afficher la progression dans les espaces.
*
* @param mixed $courseId
* @param mixed $userId
* @return string Ratio de pages vues
*/
public function userProgress($courseId, $userId): string
{
// Obtient les statistiques de l'ensemble de la cohorte
$reports = $this->getReport($courseId, $userId);
// Nombre de pages dans l'espace
$viewPages = array_key_exists($userId, $reports) ?
count($reports[$userId]) :
0;
// Nombre de pages vues
$sumPages = $this->countPages($this->getData(['page']));
// Calcule le ratio
$ratio = number_format(min(round(($viewPages * 100) / $sumPages, 1) / 100, 1), 2, ',');
return $ratio;
}
/**
*
* Compte les pages d'un espace
* @param mixed $array Tableau des pages de l'espace
* @return int Nombre de pages
*/
private function countPages($array)
{
$count = 0;
foreach ($array as $key => $value) {
$count++; // Incrémente le compteur pour chaque clé associative trouvée
if (is_array($value)) {
$count += $this->countPages($value); // Appelle récursivement la fonction si la valeur est un tableau
foreach ($array as $pageId => $pageData) {
if ($pageData['position'] > 0) {
$count++;
}
}
return $count;
@ -1858,14 +2122,16 @@ class course extends common
$courseId,
$userId,
[
'history' => [],
'lastPageView' => '',
'datePageView' => '',
'progress' => '',
]
]);
}
/**
* Autorise l'accès à un contenu
* @param @return bool le user a le droit d'entrée dans le contenu
* @return bool le user a le droit d'entrée dans le contenu
* @param string $userId identifiant de l'utilisateur
* @param string $courseId identifiant du contenu sollicité
*/
@ -1895,4 +2161,52 @@ class course extends common
return $r;
}
/**
* Lit le contenu des fichiers de traces au format CS et renvoie un tableau associatif
*/
private function getReport($courseId, $userId = null)
{
$data = [];
if (file_exists(self::DATA_DIR . $courseId . '/report.csv')) {
// Remplacez 'chemin/vers/votre/fichier.csv' par le chemin réel de votre fichier CSV
$file = fopen(self::DATA_DIR . $courseId . '/report.csv', "r");
$data = array();
// Lire ligne par ligne
while (($line = fgetcsv($file, 1000, ";")) !== false) {
$name = $line[0];
$pageId = $line[1];
$timestamp = $line[2];
// Filtre userId
if (
is_null($userId) === false
&& $name !== $userId
) {
continue;
}
// Initialiser le tableau si nécessaire
if (!isset($data[$name][$pageId])) {
$data[$name][$pageId] = array();
}
// Ajouter le timestamp
$data[$name][$pageId][] = $timestamp;
}
// Fermer le fichier
fclose($file);
// Trier les timestamps
foreach ($data as &$userData) {
foreach ($userData as &$pageData) {
sort($pageData);
}
}
}
// Afficher le JSON;
return $data;
}
}

View File

@ -0,0 +1,98 @@
/* Réinitialisation de base pour supprimer les marges et les espacements par défaut */
body, h1, h2, h3, h4, h5, h6, p, ul, ol, li {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
line-height: 1.6;
}
body {
margin: 20px;
padding: 0;
background-color: #f5f5f5;
color: #333;
}
h1, h2, h3, h4, h5, h6 {
margin-bottom: 10px;
color: #2c3e50;
}
p {
margin-bottom: 10px;
}
ul, ol {
margin-bottom: 10px;
padding-left: 20px;
}
a {
color: #3498db;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
img {
max-width: 100%;
height: auto;
display: block;
margin: 0 auto 10px;
}
.container {
max-width: 800px;
margin: 0 auto;
background-color: #fff;
padding: 20px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.header, .footer {
background-color: #34495e;
color: #fff;
text-align: center;
padding: 10px 0;
}
.header h1, .footer p {
margin: 0;
}
.content {
padding: 20px;
}
code {
background-color: #f4f4f4;
border: 1px solid #e1e1e1;
padding: 2px 4px;
font-family: "Courier New", Courier, monospace;
color: #c7254e;
display: block;
margin-bottom: 10px;
white-space: pre-wrap;
}
pre {
background-color: #f4f4f4;
border: 1px solid #e1e1e1;
padding: 10px;
font-family: "Courier New", Courier, monospace;
overflow-x: auto;
color: #333;
}
/* Media Queries pour rendre la page responsive */
@media (max-width: 600px) {
body {
margin: 10px;
}
.container {
padding: 10px;
}
}

View File

@ -33,7 +33,7 @@
</div>
<div class="row">
<div class="col6">
<?php echo template::select('courseEditHomePageId', helper::arrayColumn($module::$pagesList, 'title', 'SORT_ASC'), [
<?php echo template::select('courseEditHomePageId', helper::arrayColumn($module::$pagesList, 'title'), [
'label' => 'Page d\'accueil',
'selected' => $this->getdata(['course', $this->getUrl(2), 'homePageId']),
]); ?>

View File

@ -0,0 +1,18 @@
/**
* This file is part of Zwii.
*
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2024, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/** NE PAS EFFACER
* admin.css
*/

View File

@ -0,0 +1,27 @@
/**
* This file is part of Zwii.
*
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2024, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
$(document).ready(function() {
// Quand le bouton "Cocher toutes" est cliqué
$('#courseExportSelectAll').on('click', function() {
// Cocher toutes les checkboxes avec la classe 'courseManageCheckbox'
$('.courseManageCheckbox').prop('checked', true);
});
// Quand le bouton "Décocher toutes" est cliqué
$('#courseExportSelectNone').on('click', function() {
// Décocher toutes les checkboxes avec la classe 'courseManageCheckbox'
$('.courseManageCheckbox').prop('checked', false);
});
});

View File

@ -0,0 +1,42 @@
<?php echo template::formOpen('courseExportForm'); ?>
<div class="row">
<div class="col1">
<?php echo template::button('courseExportBack', [
'class' => 'buttonGrey',
'href' => helper::baseUrl() . 'course/manage/' . $this->getUrl(2),
'value' => template::ico('left')
]); ?>
</div>
<div class="col1 offset7">
<?php echo template::button('courseExportSelectAll', [
'value' => template::ico('square-check'),
'help' => 'Tout sélectionner'
]); ?>
</div>
<div class="col1">
<?php echo template::button('courseExportSelectNone', [
'value' => template::ico('square-check-empty'),
'help' => 'Tout désélectionner'
]); ?>
</div>
<div class="col2">
<?php echo template::submit('courseExportSubmit', [
'value' => 'Valider'
]); ?>
</div>
</div>
<div class="row">
<div class="col12">
<div class="block">
<h4><?php echo helper::translate('Sélection des pages de l\'espace') ?></h4>
<div class='row'>
<div class='col10 offset2'>
<?php foreach ($module::$pagesList as $key => $value) {
echo $value;
}
?>
</div>
</div>
</div>
</div>
</div>

View File

@ -18,11 +18,22 @@ $(document).ready(function () {
},
locale: 'fr',
stateSave: true,
"lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "Tout"]],
"columnDefs": [
{
target: 2,
orderable: false,
searchable: false
},
{
target: 3,
orderable: false,
searchable: false
},
{
target: 4,
orderable: false,
searchable: false
}
]
});

View File

@ -37,7 +37,7 @@
</div>
</div>
<?php if ($module::$courses): ?>
<?php echo template::table([4, 4, 3, 1], $module::$courses, ['Titre court', 'Description', 'Inscription', '',], ['id' => 'dataTables']); ?>
<?php echo template::table([4, 3, 3, 1, 1], $module::$courses, ['Titre court', 'Description', 'Inscription', '', '',], ['id' => 'dataTables']); ?>
<?php else: ?>
<?php echo template::speech('Aucun espace'); ?>
<?php endif; ?>

View File

@ -21,3 +21,14 @@ $(".courseDelete").on("click", function () {
$(location).attr("href", _this.attr("href"));
});
});
/**
* Confirmation de suppression
*/
$(".courseReset").on("click", function () {
var _this = $(this);
var message = "<?php echo helper::translate('Réinitialiser cet espace ?'); ?>";
return core.confirm(message, function () {
$(location).attr("href", _this.attr("href"));
});
});

View File

@ -14,7 +14,19 @@
'class' => 'courseDelete buttonRed',
'href' => helper::baseUrl() . 'course/delete/' . $this->getUrl(2),
'value' => 'Supprimer',
'ico' => 'trash'
'ico' => 'trash',
'help' => 'Supprime l\'espace et les historiques des participants',
]); ?>
</div>
<?php endif; ?>
<?php if ($this->getUser('permission', 'course', 'reset') === true): ?>
<div class="col2 ">
<?php echo template::button('courseManageReset' . $this->getUrl(2), [
'class' => 'courseReset buttonRed',
'href' => helper::baseUrl() . 'course/reset/' . $this->getUrl(2),
'value' => 'Réinitaliser',
'ico' => 'cancel',
'help' => 'Désinscrit les participants et supprime les historiques',
]); ?>
</div>
<?php endif; ?>
@ -23,7 +35,8 @@
<?php echo template::button('courseManageDownload' . $this->getUrl(2), [
'href' => helper::baseUrl() . 'course/backup/' . $this->getUrl(2),
'value' => 'Sauvegarder',
'ico' => 'download-cloud'
'ico' => 'download-cloud',
'help' => 'Génère une copie de sauvegarde de l\'espace',
]); ?>
</div>
<?php endif; ?>
@ -32,32 +45,33 @@
<?php echo template::button('courseManageDuplicate' . $this->getUrl(2), [
'href' => helper::baseUrl() . 'course/clone/' . $this->getUrl(2),
'value' => 'Cloner',
'ico' => 'clone'
'ico' => 'clone',
'help' => 'Copie l\'espace et son contenu sans les participants et leurs historiques',
]); ?>
</div>
<?php endif; ?>
<?php if ($this->getUser('permission', 'course', 'edit') === true): ?>
<div class="col2">
<?php echo template::button('courseManageEdit' . $this->getUrl(2), [
'href' => helper::baseUrl() . 'course/edit/' . $this->getUrl(2),
'value' => 'Éditer',
'ico' => 'pencil'
'ico' => 'pencil',
'help' => 'Modifie les paramètres de l\'espace',
]); ?>
</div>
<?php endif; ?>
<?php if ($this->getUser('permission', 'course', 'users') === true): ?>
<?php if ($this->getUser('permission', 'course', 'export') === true): ?>
<div class="col2">
<?php echo template::button('categoryUser' . $this->getUrl(2), [
'href' => helper::baseUrl() . 'course/users/' . $this->getUrl(2),
'value' => 'Participants',
'ico' => 'users'
<?php echo template::button('courseManageExport' . $this->getUrl(2), [
'href' => helper::baseUrl() . 'course/export/' . $this->getUrl(2),
'value' => 'Exporter HTML',
'ico' => 'upload',
'help' => 'Le contenu de l\'espace est exporté dans une page web autonome',
]); ?>
</div>
<?php endif; ?>
</div>
<div class="row">
<div class="col12">
<div class="block">

View File

@ -8,7 +8,7 @@
</div>
<div class="col1 offset10">
<?php echo template::button('userDeleteAll', [
'href' => helper::baseUrl() . 'course/userHistoryExport/' . $this->getUrl(2) . '/' . $this->getUrl(3),
'href' => helper::baseUrl() . 'course/userReportExport/' . $this->getUrl(2) . '/' . $this->getUrl(3),
'value' => template::ico('download'),
'help' => 'Exporter',
]) ?>
@ -20,7 +20,7 @@
</div>
</div>
</div>
<?php if ($module::$userHistory): ?>
<?php if ($module::$userReport): ?>
<div class="row">
<div class="col4 offset2">
<?php if ($this->getData(['course', $this->getUrl(2), 'access']) === self::COURSE_ACCESS_DATE): ?>
@ -46,7 +46,7 @@
</div>
<div class="row textAlignCenter">
<div class="col8">
<?php echo template::table([6, 3, 3], $module::$userHistory, ['Page', 'Début de Consultation', 'Temps consultation']); ?>
<?php echo template::table([6, 3, 3], $module::$userReport, ['Page', 'Début de Consultation', 'Temps consultation']); ?>
</div>
</div>
<?php else: ?>

View File

@ -29,6 +29,7 @@ $(document).ready((function () {
order: [[3, 'desc']],
locale: 'fr',
stateSave: true,
"lengthMenu": [[10, 25, 50, 100, 299, -1], [10, 25, 50, 100, 200, "Tout"]],
"columnDefs": [
{
target: 6,

View File

@ -2,13 +2,13 @@
<div class="col1">
<?php echo template::button('courseUserBack', [
'class' => 'buttonGrey',
'href' => helper::baseUrl() . 'course/manage/' . $this->getUrl(2),
'href' => helper::baseUrl() . 'course/' . $this->getUrl(2),
'value' => template::ico('left')
]); ?>
</div>
<div class="col1 offset8">
<?php echo template::button('userDeleteAll', [
'href' => helper::baseUrl() . 'course/usersHistoryExport/' . $this->getUrl(2),
'href' => helper::baseUrl() . 'course/usersReportExport/' . $this->getUrl(2),
'value' => template::ico('download'),
'help' => 'Exporter',
]) ?>

View File

@ -17,10 +17,6 @@
* admin.css
*/
#courseUserAddSelectAll {
color: lightgreen;
}
#courseUserAddSelectNone {
color: lightcoral;
tr {
cursor: pointer;
}

View File

@ -13,6 +13,13 @@
$(document).ready((function () {
$('tr').click(function(){
// Cochez ou décochez la case à cocher dans cette ligne
$(this).find('input[type="checkbox"]').prop('checked', function(i, val){
return !val; // Inverse l'état actuel de la case à cocher
});
});
$('#courseUserAddSelectAll').on('click', function() {
$('.checkboxSelect').prop('checked', true);
saveCheckboxState();
@ -33,6 +40,7 @@ $(document).ready((function () {
},
locale: 'fr',
stateSave: true,
"lengthMenu": [[10, 25, 50, 100, 299, -1], [10, 25, 50, 100, 200, "Tout"]],
"columnDefs": [
{
target: 0,

View File

@ -7,21 +7,9 @@
'value' => template::ico('left')
]); ?>
</div>
<div class="col1 offset8">
<?php echo template::button('courseUserAddSelectAll', [
'value' => template::ico('check'),
'help' => 'Tout sélectionner'
]); ?>
</div>
<div class="col1">
<?php echo template::button('courseUserAddSelectNone', [
'value' => template::ico('cancel'),
'help' => 'Tout désélectionner'
]); ?>
</div>
<div class="col1">
<div class="col2 offset9">
<?php echo template::submit('courseUsersAddSubmit', [
'value' => '',
'value' => 'Inscrire',
'ico' => 'plus',
]); ?>
</div>
@ -45,6 +33,18 @@
'selected' => isset($_POST['courseFilterLastName']) ? $_POST['courseFilterLastName'] : 'all',
]); ?>
</div>
<div class="col1 offset1 verticalAlignBottom">
<?php echo template::button('courseUserAddSelectAll', [
'value' => template::ico('square-check'),
'help' => 'Tout sélectionner'
]); ?>
</div>
<div class="col1 verticalAlignBottom">
<?php echo template::button('courseUserAddSelectNone', [
'value' => template::ico('square-check-empty'),
'help' => 'Tout désélectionner'
]); ?>
</div>
</div>
<?php if ($module::$courseUsers): ?>
<?php echo template::table([1, 2, 3, 3, 3], $module::$courseUsers, ['', 'Id', 'Prénom', 'Nom', 'Étiquettes'], ['id' => 'dataTables']); ?>

View File

@ -21,10 +21,6 @@
background-color: rgba(217, 95, 78, 1);
}
#courseUserDeleteSelectAll {
color: lightgreen;
}
#courseUserDeleteSelectNone {
color: lightcoral;
tr {
cursor: pointer;
}

View File

@ -13,6 +13,13 @@
$(document).ready((function () {
$('tr').click(function () {
// Cochez ou décochez la case à cocher dans cette ligne
$(this).find('input[type="checkbox"]').prop('checked', function (i, val) {
return !val; // Inverse l'état actuel de la case à cocher
});
});
$('#courseUserDeleteSelectAll').on('click', function () {
$('.checkboxSelect').prop('checked', true);
saveCheckboxState();
@ -32,6 +39,7 @@ $(document).ready((function () {
url: "core/vendor/datatables/french.json"
},
locale: 'fr',
"lengthMenu": [[10, 25, 50, 100, 299, -1], [10, 25, 50, 100, 200, "Tout"]],
"columnDefs": [
{
target: 0,

View File

@ -7,23 +7,11 @@
'value' => template::ico('left')
]); ?>
</div>
<div class="col1 offset8">
<?php echo template::button('courseUserDeleteSelectAll', [
'value' => template::ico('check'),
'help' => 'Tout sélectionner'
]); ?>
</div>
<div class="col1">
<?php echo template::button('courseUserDeleteSelectNone', [
'value' => template::ico('cancel'),
'help' => 'Tout désélectionner'
]); ?>
</div>
<div class="col1">
<div class="col2 offset9">
<?php echo template::submit('courseUsersDeleteSubmit', [
'class' => 'buttonRed',
'ico' => '',
'value' => template::ico('minus'),
'ico' => 'minus',
'value' => 'Désinscrire',
]); ?>
</div>
</div>
@ -46,6 +34,18 @@
'selected' => isset($_POST['courseFilterLastName']) ? $_POST['courseFilterLastName'] : 'all',
]); ?>
</div>
<div class="col1 offset1 verticalAlignBottom">
<?php echo template::button('courseUserDeleteSelectAll', [
'value' => template::ico('square-check'),
'help' => 'Tout sélectionner'
]); ?>
</div>
<div class="col1 verticalAlignBottom">
<?php echo template::button('courseUserDeleteSelectNone', [
'value' => template::ico('square-check-empty'),
'help' => 'Tout désélectionner'
]); ?>
</div>
</div>
<?php if ($module::$courseUsers): ?>
<?php echo template::table([1, 2, 3, 3, 3], $module::$courseUsers, ['', 'Id', 'Prénom', 'Nom', 'Étiquettes'], ['id' => 'dataTables']); ?>

View File

@ -158,9 +158,9 @@ class install extends common
);
// Sauvegarder la configuration du Proxy
$this->setData(['config', 'proxyType', $this->getInput('installProxyType')]);
$this->setData(['config', 'proxyUrl', $this->getInput('installProxyUrl')]);
$this->setData(['config', 'proxyPort', $this->getInput('installProxyPort', helper::FILTER_INT)]);
$this->setData(['config', 'proxyType', $this->getInput('installProxyType')], false);
$this->setData(['config', 'proxyUrl', $this->getInput('installProxyUrl')], false);
$this->setData(['config', 'proxyPort', $this->getInput('installProxyPort', helper::FILTER_INT)], false);
// Images exemples livrées dans tous les cas
try {
@ -203,7 +203,7 @@ class install extends common
$this->copyDir('core/module/install/ressource/i18n', self::I18N_DIR);
// Fixe l'adresse from pour les envois d'email
$this->setData(['config', 'smtp', 'from', 'no-reply@' . str_replace('www.', '', $_SERVER['HTTP_HOST'])]);
$this->setData(['config', 'smtp', 'from', 'no-reply@' . str_replace('www.', '', $_SERVER['HTTP_HOST'])], false);
// Valeurs en sortie
$this->addOutput([
@ -212,9 +212,10 @@ class install extends common
'state' => true
]);
}
// Force la sauvegarde
$this->saveDB('config');
// Affichage du formulaire
// Valeurs en sortie
$this->addOutput([
'display' => self::DISPLAY_LAYOUT_LIGHT,
@ -387,7 +388,7 @@ class install extends common
'</IfModule>' . PHP_EOL .
'# URL rewriting' . PHP_EOL;
$fileContent = str_replace('# URL rewriting', $rewriteData, $fileContent);
$success = file_put_contents(
$success = $this->secure_file_put_contents(
'.htaccess',
$fileContent
);

View File

@ -246,13 +246,14 @@ class init extends common
'manage' => false,
'users' => false,
'userHistory' => false,
'userHistoryExport' => false,
'userReportExport' => false,
'usersAdd' => false,
'userDelete' => false,
'usersDelete' => false,
'edit' => false,
'backup' => false,
'restore' => false
'restore' => false,
'reset' => false,
],
'folder' => [
'create' => false,
@ -342,13 +343,14 @@ class init extends common
'manage' => false,
'users' => false,
'userHistory' => false,
'userHistoryExport' => false,
'userReportExport' => false,
'usersAdd' => false,
'userDelete' => false,
'usersDelete' => false,
'edit' => false,
'backup' => false,
'restore' => false
'restore' => false,
'reset' => false,
],
'folder' => [
'create' => false,
@ -443,13 +445,14 @@ class init extends common
'manage' => true,
'users' => true,
'userHistory' => true,
'userHistoryExport' => true,
'userReportExport' => true,
'usersAdd' => true,
'userDelete' => false,
'usersDelete' => false,
'edit' => false,
'backup' => false,
'restore' => false
'restore' => false,
'reset' => false,
],
'folder' => [
'create' => false,
@ -540,13 +543,14 @@ class init extends common
'manage' => true,
'users' => true,
'userHistory' => true,
'userHistoryExport' => true,
'userReportExport' => true,
'usersAdd' => true,
'userDelete' => true,
'usersDelete' => true,
'edit' => true,
'backup' => true,
'restore' => true
'restore' => true,
'reset' => true,
],
'folder' => [
'create' => true,

View File

@ -99,7 +99,7 @@ class language extends common
is_array($descripteur['language'][$lang])
) {
if ($this->setData(['language', $lang, $descripteur['language'][$lang]])) {
$success = file_put_contents(self::I18N_DIR . $lang . '.json', json_encode($languageData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
$success = $this->secure_file_put_contents(self::I18N_DIR . $lang . '.json', json_encode($languageData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
$success = is_int($success) ? true : false;
}
}

View File

@ -23,7 +23,8 @@ class page extends common
'edit' => self::GROUP_EDITOR,
'duplicate' => self::GROUP_EDITOR,
'jsEditor' => self::GROUP_EDITOR,
'cssEditor' => self::GROUP_EDITOR
'cssEditor' => self::GROUP_EDITOR,
'register' => self::GROUP_EDITOR,
];
public static $pagesNoParentId = [
'' => 'Aucune'
@ -85,12 +86,23 @@ class page extends common
*/
public function duplicate()
{
// La session ne correspond pas au site ouvert dans cet onglet
if (
// Contrôle la présence de l'id d'espace uniquement si l'id est fourni afin de ne pas bloquer les modules non mis à jour
$this->getUrl(3) && $this->getUrl(3) != self::$siteContent
) {
$_SESSION['ZWII_SITE_CONTENT'] = $this->getUrl(3);
header('Refresh:0; url=' . helper::baseUrl() . $this->getUrl());
exit();
}
// Adresse sans le token
$page = $this->getUrl(2);
// La page n'existe pas
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true ||
$this->getData(['page', $page]) === null
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true
|| $this->getData(['page', $page]) === null
) {
// Valeurs en sortie
$this->addOutput([
@ -107,18 +119,20 @@ class page extends common
$page
]);
// Ecriture
$this->setData(['page', $pageId, $data]);
$this->setData(['page', $pageId, $data], false);
$notification = helper::translate('Page dupliquée');
// Duplication du module présent
if ($this->getData(['page', $page, 'moduleId'])) {
$data = $this->getData(['module', $page]);
$this->setData(['module', $pageId, $data]);
$this->setData(['module', $pageId, $data], false);
$notification = helper::translate('Page et module dupliqués');
}
// Force la sauvegarde
$this->saveDB('page');
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . 'page/edit/' . $pageId,
'redirect' => helper::baseUrl() . 'page/edit/' . $pageId . '/' . self::$siteContent,
'notification' => $notification,
'state' => true
]);
@ -131,7 +145,19 @@ class page extends common
*/
public function add()
{
if ($this->getUser('permission', __CLASS__, __FUNCTION__) !== true) {
// La session ne correspond pas au site ouvert dans cet onglet
if (
// Contrôle la présence de l'id d'espace uniquement si l'id est fourni afin de ne pas bloquer les modules non mis à jour
$this->getUrl(3) && $this->getUrl(3) != self::$siteContent
) {
$_SESSION['ZWII_SITE_CONTENT'] = $this->getUrl(3);
header('Refresh:0; url=' . helper::baseUrl() . $this->getUrl());
exit();
}
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true
) {
// Valeurs en sortie
$this->addOutput([
'access' => false
@ -180,8 +206,8 @@ class page extends common
//file_put_contents(self::DATA_DIR . self::$siteContent . '/content/' . $pageId . '.html', '<p>Contenu de votre nouvelle page.</p>');
$this->setPage($pageId, '<p>Contenu de votre nouvelle page.</p>', self::$siteContent);
// Met à jour le sitemap
$this->updateSitemap();
// Ne met à jour le sitemap pour éviter un warning, de toute manière la nouvelle page doit être éditée.
// $this->updateSitemap();
// Valeurs en sortie
$this->addOutput([
@ -198,12 +224,24 @@ class page extends common
*/
public function delete()
{
// La session ne correspond pas au site ouvert dans cet onglet
if (
// Contrôle la présence de l'id d'espace uniquement si l'id est fourni afin de ne pas bloquer les modules non mis à jour
$this->getUrl(3) && $this->getUrl(3) != self::$siteContent
) {
$_SESSION['ZWII_SITE_CONTENT'] = $this->getUrl(3);
header('Refresh:0; url=' . helper::baseUrl() . $this->getUrl());
exit();
}
// $url prend l'adresse sans le token
$page = $this->getUrl(2);
// La page n'existe pas
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true ||
$this->getData(['page', $page]) === null
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true
|| $this->getData(['page', $page]) === null
) {
// Valeurs en sortie
$this->addOutput([
@ -264,7 +302,7 @@ class page extends common
elseif ($this->getHierarchy($page, null)) {
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . 'page/edit/' . $page,
'redirect' => helper::baseUrl() . 'page/edit/' . $page . '/' . self::$siteContent,
'notification' => helper::translate('Impossible de supprimer une page contenant des pages enfants')
]);
}
@ -304,10 +342,24 @@ class page extends common
*/
public function edit()
{
// La session ne correspond pas au site ouvert dans cet onglet
if (
// Contrôle la présence de l'id d'espace uniquement si l'id est fourni afin de ne pas bloquer les modules non mis à jour
$this->getUrl(3) && $this->getUrl(3) != self::$siteContent
) {
$_SESSION['ZWII_SITE_CONTENT'] = $this->getUrl(3);
header('Refresh:0; url=' . helper::baseUrl() . $this->getUrl());
exit();
}
// La page n'existe pas
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true ||
$this->getData(['page', $this->getUrl(2)]) === null
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true
|| $this->getData(['page', $this->getUrl(2)]) === null
// Contrôle la présence de l'id d'espace uniquement si l'id est fourni afin de ne pas bloquer les modules non mis à jour
|| (
$this->getUrl(3)
&& $this->getUrl(3) != self::$siteContent
)
) {
// Valeurs en sortie
$this->addOutput([
@ -341,11 +393,11 @@ class page extends common
$pageId = helper::increment($pageId, self::$moduleIds);
// Met à jour les enfants
foreach ($this->getHierarchy($this->getUrl(2), null) as $childrenPageId) {
$this->setData(['page', $childrenPageId, 'parentPageId', $pageId]);
$this->setData(['page', $childrenPageId, 'parentPageId', $pageId], false);
}
// Change l'id de page dans les données des modules
if ($this->getData(['module', $this->getUrl(2)]) !== null) {
$this->setData(['module', $pageId, $this->getData(['module', $this->getUrl(2)])]);
$this->setData(['module', $pageId, $this->getData(['module', $this->getUrl(2)])], false);
$this->deleteData(['module', $this->getUrl(2)]);
// Renommer le dossier du module
$moduleId = $this->getData(['page', $this->getUrl(2), 'moduleId']);
@ -356,8 +408,10 @@ class page extends common
copy($modulesData[$moduleId]['dataDirectory'] . $this->getUrl(2), $modulesData[$moduleId]['dataDirectory'] . $pageId);
$this->deleteDir($modulesData[$moduleId]['dataDirectory'] . $this->getUrl(2));
// Mettre à jour le nom de la feuille de style
$this->setData(['module', $pageId, 'theme', 'style', $modulesData[$moduleId]['dataDirectory'] . $pageId]);
$this->setData(['module', $pageId, 'theme', 'style', $modulesData[$moduleId]['dataDirectory'] . $pageId], false);
}
// Sauvegarde la base manuellement
$this->saveDB('module');
}
// Met à jour les historiques des utilisateurs
foreach ($this->getData(['enrolment', self::$siteContent]) as $userId => $userData) {
@ -368,14 +422,17 @@ class page extends common
) {
// Remplacer l'ancienne ID par la nouvelle
$datas = $this->getData(['enrolment', self::$siteContent, $userId, 'history', $this->getUrl(2)]);
$this->setData(['enrolment', self::$siteContent, $userId, 'history', $pageId, $datas]);
$this->setData(['enrolment', self::$siteContent, $userId, 'history', $pageId, $datas], false);
$this->deleteData(['enrolment', self::$siteContent, $userId, 'history', $this->getUrl(2)]);
}
// Mettre à jour la dernière page vue si nécessaire
if ($this->getData(['enrolment', self::$siteContent, $userId, 'lastPageView']) === $this->getUrl(2)) {
$this->setData(['enrolment', self::$siteContent, $userId, 'lastPageView', $pageId]);
$this->setData(['enrolment', self::$siteContent, $userId, 'lastPageView', $pageId], false);
}
}
// Sauvegarde la base manuellement
$this->saveDB('enrolment');
// Met à jour la homePage si nécessaire
if ($this->getUrl(2) === $this->getData(['course', self::$siteContent, 'homePageId'])) {
$this->setData(['course', self::$siteContent, 'homePageId', $pageId]);
@ -383,7 +440,7 @@ class page extends common
// Si la page correspond à la page d'accueil, change l'id dans la configuration du site
if ($this->getData(['config', 'homePageId']) === $this->getUrl(2)) {
$this->setData(['config', 'homePageId', $pageId]);
$this->setData(['config', 'homePageId', $pageId], false);
}
}
// Supprime les données du module en cas de changement de module
@ -399,20 +456,22 @@ class page extends common
}
// Traitement des pages spéciales affectées dans la config :
if ($this->getUrl(2) === $this->getData(['config', 'legalPageId'])) {
$this->setData(['config', 'legalPageId', $pageId]);
$this->setData(['config', 'legalPageId', $pageId], false);
}
if ($this->getUrl(2) === $this->getData(['config', 'searchPageId'])) {
$this->setData(['config', 'searchPageId', $pageId]);
$this->setData(['config', 'searchPageId', $pageId], false);
}
if ($this->getUrl(2) === $this->getData(['config', 'page404'])) {
$this->setData(['config', 'page404', $pageId]);
$this->setData(['config', 'page404', $pageId], false);
}
if ($this->getUrl(2) === $this->getData(['config', 'page403'])) {
$this->setData(['config', 'page403', $pageId]);
$this->setData(['config', 'page403', $pageId], false);
}
if ($this->getUrl(2) === $this->getData(['config', 'page302'])) {
$this->setData(['config', 'page302', $pageId]);
$this->setData(['config', 'page302', $pageId], false);
}
// Sauvegarde la base manuellement
$this->saveDB(module: 'config');
// Si la page est une page enfant, actualise les positions des autres enfants du parent, sinon actualise les pages sans parents
$lastPosition = 1;
$hierarchy = $this->getInput('pageEditParentPageId') ? $this->getHierarchy($this->getInput('pageEditParentPageId')) : array_keys($this->getHierarchy());
@ -431,11 +490,12 @@ class page extends common
$lastPosition++;
}
// Change la position
$this->setData(['page', $hierarchyPageId, 'position', $lastPosition]);
$this->setData(['page', $hierarchyPageId, 'position', $lastPosition], false);
// Incrémente pour la prochaine position
$lastPosition++;
}
}
if ($this->getinput('pageEditBlock') !== 'bar') {
$barLeft = $this->getinput('pageEditBarLeft');
$barRight = $this->getinput('pageEditBarRight');
@ -456,7 +516,7 @@ class page extends common
) {
foreach ($this->getHierarchy($pageId) as $parentId => $childId) {
if ($this->getData(['page', $childId, 'parentPageId']) === $pageId) {
$this->setData(['page', $childId, 'position', 0]);
$this->setData(['page', $childId, 'position', 0], false);
}
}
}
@ -465,17 +525,17 @@ class page extends common
if ($this->getinput('pageEditBlock') === 'bar') {
foreach ($this->getHierarchy() as $eachPageId => $parentId) {
if ($this->getData(['page', $eachPageId, 'barRight']) === $this->getUrl(2)) {
$this->setData(['page', $eachPageId, 'barRight', $pageId]);
$this->setData(['page', $eachPageId, 'barRight', $pageId], false);
}
if ($this->getData(['page', $eachPageId, 'barLeft']) === $this->getUrl(2)) {
$this->setData(['page', $eachPageId, 'barLeft', $pageId]);
$this->setData(['page', $eachPageId, 'barLeft', $pageId], false);
}
foreach ($parentId as $childId) {
if ($this->getData(['page', $childId, 'barRight']) === $this->getUrl(2)) {
$this->setData(['page', $childId, 'barRight', $pageId]);
$this->setData(['page', $childId, 'barRight', $pageId], false);
}
if ($this->getData(['page', $childId, 'barLeft']) === $this->getUrl(2)) {
$this->setData(['page', $childId, 'barLeft', $pageId]);
$this->setData(['page', $childId, 'barLeft', $pageId], false);
}
}
}
@ -634,7 +694,7 @@ class page extends common
// Valeurs en sortie
$this->addOutput([
'notification' => helper::translate('Modifications enregistrées'),
'redirect' => helper::baseUrl() . 'page/edit/' . $this->getUrl(2),
'redirect' => helper::baseUrl() . 'page/edit/' . $this->getUrl(2) . '/' . self::$siteContent,
'state' => true
]);
}
@ -669,7 +729,7 @@ class page extends common
// Valeurs en sortie
$this->addOutput([
'notification' => helper::translate('Modifications enregistrées'),
'redirect' => helper::baseUrl() . 'page/edit/' . $this->getUrl(2),
'redirect' => helper::baseUrl() . 'page/edit/' . $this->getUrl(2) . '/' . self::$siteContent,
'state' => true
]);
}
@ -685,7 +745,7 @@ class page extends common
/**
* Retourne les informations sur les pages en omettant les clés CSS et JS qui occasionnent des bugs d'affichage dans l'éditeur de page
* @return array tableau associatif des pages dans le menu
* @return string tableau associatif des pages dans le menu
*/
public function getPageInfo()
{
@ -695,6 +755,27 @@ class page extends common
return $d;
}, $p);
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

@ -3,7 +3,7 @@
<div class="col1">
<?php echo template::button('pageCssEditorBack', [
'class' => 'buttonGrey',
'href' => helper::baseUrl() . 'page/edit/' . $this->getUrl(2),
'href' => helper::baseUrl() . 'page/edit/' . $this->getUrl(2) . '/' . self::$siteContent,
'value' => template::ico('left')
]); ?>
</div>

View File

@ -67,16 +67,18 @@ $( document ).ready(function() {
/**
* Sélection des onglets
*/
var pageLayout = getCookie("pageLayout");
if (pageLayout == null) {
var pageLayout = "<?php echo $this->getData(['user', $this->getUser('id'), 'view', 'page']);?>";
// Non défini, valeur par défaut
if (pageLayout == "") {
pageLayout = "content";
setCookie("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");
@ -295,7 +297,6 @@ $( document ).ready(function() {
$("#PageEditPositionButton").removeClass("activeButton");
$("#pageEditLayoutButton").removeClass("activeButton");
$("#pageEditPermissionButton").removeClass("activeButton");
setCookie("pageLayout", "content");
});
$("#pageEditExtensionButton").on("click", function () {
$("#pageEditContentContainer").hide();
@ -308,7 +309,6 @@ $( document ).ready(function() {
$("#PageEditPositionButton").removeClass("activeButton");
$("#pageEditLayoutButton").removeClass("activeButton");
$("#pageEditPermissionButton").removeClass("activeButton");
setCookie("pageLayout", "extension");
});
$("#PageEditPositionButton").on("click", function () {
$("#pageEditContentContainer").hide();
@ -321,7 +321,6 @@ $( document ).ready(function() {
$("#PageEditPositionButton").addClass("activeButton");
$("#pageEditLayoutButton").removeClass("activeButton");
$("#pageEditPermissionButton").removeClass("activeButton");
setCookie("pageLayout", "position");
});
$("#pageEditLayoutButton").on("click", function () {
$("#pageEditContentContainer").hide();
@ -334,7 +333,6 @@ $( document ).ready(function() {
$("#PageEditPositionButton").removeClass("activeButton");
$("#pageEditLayoutButton").addClass("activeButton");
$("#pageEditPermissionButton").removeClass("activeButton");
setCookie("pageLayout", "layout");
});
$("#pageEditPermissionButton").on("click", function () {
$("#pageEditContentContainer").hide();
@ -347,7 +345,6 @@ $( document ).ready(function() {
$("#pageEditPositionButton").removeClass("activeButton");
$("#pageEditLayoutButton").removeClass("activeButton");
$("#pageEditPermissionButton").addClass("activeButton");
setCookie("pageLayout", "permission");
});
/**
@ -722,30 +719,6 @@ function buildPagesList(extraPosition) {
positionDOM.val(positionSelected);
};
/**
* Cookies
*/
function setCookie(name, value, days) {
var expires = "";
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + (value || "") + expires + "; path=/; samesite=lax";
}
function getCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
// Define function to capitalize the first letter of a string
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);

View File

@ -1,4 +1,6 @@
<?php echo template::formOpen('pageEditForm'); ?>
<!-- Variable transmise à TinyMCE -->
<div id="zwii_site_content" data-variable="<?php echo htmlspecialchars(isset($_SESSION['ZWII_SITE_CONTENT']) ? $_SESSION['ZWII_SITE_CONTENT'] : 'home'); ?>"></div>
<div class="row">
<div class="col1">
<?php echo template::button('configModulesBack', [
@ -7,26 +9,17 @@
'value' => template::ico('left')
]); ?>
</div>
<div class="col1">
<?php /**echo template::button('pageEditHelp', [
'href' => 'https://doc.zwiicms.fr/edition-des-pages',
'target' => '_blank',
'value' => template::ico('help'),
'class' => 'buttonHelp',
'help' => 'Consulter l\'aide en ligne'
]); */?>
</div>
<div class="col1 offset6">
<div class="col1 offset7">
<?php echo template::button('pageEditDelete', [
'class' => 'buttonRed',
'href' => helper::baseUrl() . 'page/delete/' . $this->getUrl(2),
'href' => helper::baseUrl() . 'page/delete/' . $this->getUrl(2) . '/' . self::$siteContent,
'value' => template::ico('trash'),
'help' => 'Effacer la page'
]); ?>
</div>
<div class="col1">
<?php echo template::button('pageEditDuplicate', [
'href' => helper::baseUrl() . 'page/duplicate/' . $this->getUrl(2),
'href' => helper::baseUrl() . 'page/duplicate/' . $this->getUrl(2) . '/' . self::$siteContent,
'value' => template::ico('clone'),
'help' => 'Dupliquer la page'
]); ?>
@ -41,23 +34,28 @@
<div class="tab">
<?php echo template::button('pageEditContentButton', [
'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',
'class' => 'buttonTab'
'class' => 'buttonTab',
'href' => helper::baseUrl() . 'page/register/position/' . $this->geturl(2)
]); ?>
<?php echo template::button('pageEditExtensionButton', [
'value' => 'Extension',
'class' => 'buttonTab'
'class' => 'buttonTab',
'href' => helper::baseUrl() . 'page/register/extension/' . $this->geturl(2)
]); ?>
<?php echo template::button('pageEditLayoutButton', [
'value' => 'Mise en page',
'class' => 'buttonTab'
'class' => 'buttonTab',
'href' => helper::baseUrl() . 'page/register/layout/' . $this->geturl(2)
]); ?>
<?php echo template::button('pageEditPermissionButton', [
'value' => 'Permission',
'class' => 'buttonTab'
'class' => 'buttonTab',
'href' => helper::baseUrl() . 'page/register/permission/' . $this->geturl(2)
]); ?>
</div>
@ -67,11 +65,6 @@
<div class="block">
<h4>
<?php echo helper::translate('Titres'); ?>
<!--<span id="infoHelpButton" class="helpDisplayButton">
<a href="https://doc.zwiicms.fr/informations-generales" target="_blank" title="Cliquer pour consulter l'aide en ligne">
<?php //echo template::ico('help', ['margin' => 'left']); ?>
</a>
</span>-->
</h4>
<div class="row">
<div class="col8">
@ -120,11 +113,6 @@
<div class="block">
<h4>
<?php echo helper::translate('Emplacement dans le menu'); ?>
<!--<span id="positionHelpButton" class="helpDisplayButton">
<a href="https://doc.zwiicms.fr/emplacement-dans-le-menu" target="_blank" title="Cliquer pour consulter l'aide en ligne">
<?php //echo template::ico('help', ['margin' => 'left']); ?>
</a>
</span>-->
</h4>
<div class="blockContainer">
<div class="row">
@ -176,11 +164,6 @@
<div class="block">
<h4>
<?php echo helper::translate('Options avancées'); ?>
<!--<span id="advancedHelpButton" class="helpDisplayButton">
<a href="https://doc.zwiicms.fr/options-d-emplacement-avancee" target="_blank" title="Cliquer pour consulter l'aide en ligne">
<?php //echo template::ico('help', ['margin' => 'left']); ?>
</a>
</span>-->
</h4>
<div class="blockContainer">
<div class="row">
@ -195,7 +178,8 @@
'help' => 'Sélectionnez une image ou une icône de petite dimension',
'language' => $this->getData(['user', $this->getUser('id'), 'language']),
'label' => 'Icône',
'value' => $this->getData(['page', $this->getUrl(2), 'iconUrl'])
'value' => $this->getData(['page', $this->getUrl(2), 'iconUrl']),
'folder' => $this->getData(['page', $this->getUrl(2), 'iconUrl']) ? dirname($this->getData(['page', $this->getUrl(2), 'iconUrl'])) : '',
]); ?>
</div>
</div>
@ -292,11 +276,6 @@
<div class="block">
<h4>
<?php echo helper::translate('Mise en page'); ?>
<!--<span id="layoutHelpButton" class="helpDisplayButton">
<a href="https://doc.zwiicms.fr/mise-en-page-2" target="_blank" title="Cliquer pour consulter l'aide en ligne">
<?php //echo template::ico('help', ['margin' => 'left']); ?>
</a>
</span>-->
</h4>
<div class="blockContainer">
<div class="row">
@ -372,11 +351,6 @@
<div class="block">
<h4>
<?php echo helper::translate('Permission et référencement'); ?>
<!--<span id="seoHelpButton" class="helpDisplayButton">
<a href="https://doc.zwiicms.fr/permission-et-referencement" target="_blank" title="Cliquer pour consulter l'aide en ligne">
<?php //echo template::ico('help', ['margin' => 'left']); ?>
</a>
</span>-->
</h4>
<div class="blockContainer">
<div class="row">
@ -424,5 +398,4 @@
</div>
</div>
</div>
<?php echo template::formClose(); ?>

View File

@ -3,7 +3,7 @@
<div class="col1">
<?php echo template::button('pageJsEditorBack', [
'class' => 'buttonGrey',
'href' => helper::baseUrl() . 'page/edit/' . $this->getUrl(2),
'href' => helper::baseUrl() . 'page/edit/' . $this->getUrl(2) . '/' . self::$siteContent,
'value' => template::ico('left')
]); ?>
</div>

View File

@ -25,7 +25,7 @@ class plugin extends common
'delete' => self::GROUP_ADMIN,
'save' => self::GROUP_ADMIN,
'store' => self::GROUP_ADMIN,
'item' => self::GROUP_ADMIN,
//'item' => self::GROUP_ADMIN,
// détail d'un objet
'upload' => self::GROUP_ADMIN,
// Téléverser catalogue
@ -391,7 +391,6 @@ class plugin extends common
];
}
}
// Valeurs en sortie
$this->addOutput([
'title' => helper::translate('Catalogue de modules'),
@ -414,6 +413,16 @@ class plugin extends common
]);
}
/**
* Retourne le contenu du store en ligne
* @return mixed
*/
public static function getStore()
{
$store = json_decode(helper::getUrlContents(self::BASEURL_STORE . self::MODULE_STORE . 'list'), true);
return $store;
}
/**
* Gestion des modules
*/
@ -561,6 +570,9 @@ class plugin extends common
}
}
// Désactive l'icône rouge
$this->setData(['core', 'updateModuleAvailable', false]);
// Valeurs en sortie
$this->addOutput([
'title' => helper::translate('Gestion des modules'),

View File

@ -507,21 +507,23 @@ class theme extends common
'featureContent' => $featureContent,
'featureFiles' => $files
]
]);
], false);
// Modification de la position du menu selon la position de la bannière
if ($this->getData(['theme', 'header', 'position']) == 'site') {
$this->setData(['theme', 'menu', 'position', str_replace('body-', 'site-', $this->getData(['theme', 'menu', 'position']))]);
$this->setData(['theme', 'menu', 'position', str_replace('body-', 'site-', $this->getData(['theme', 'menu', 'position']))], false);
}
if ($this->getData(['theme', 'header', 'position']) == 'body') {
$this->setData(['theme', 'menu', 'position', str_replace('site-', 'body-', $this->getData(['theme', 'menu', 'position']))]);
$this->setData(['theme', 'menu', 'position', str_replace('site-', 'body-', $this->getData(['theme', 'menu', 'position']))], false);
}
// Menu accroché à la bannière qui devient cachée
if (
$this->getData(['theme', 'header', 'position']) == 'hide' &&
in_array($this->getData(['theme', 'menu', 'position']), ['body-first', 'site-first', 'body-first', 'site-second'])
) {
$this->setData(['theme', 'menu', 'position', 'site']);
$this->setData(['theme', 'menu', 'position', 'site'], false);
}
// Sauvegarde la base manuellement
$this->saveDB(module: 'theme');
// Valeurs en sortie
$this->addOutput([
'notification' => helper::translate('Modifications enregistrées'),
@ -929,7 +931,7 @@ class theme extends common
'fontWeight' => $this->getInput('themeTitleFontWeight'),
'textTransform' => $this->getInput('themeTitleTextTransform')
]
]);
], false);
$this->setData([
'theme',
'text',
@ -939,7 +941,7 @@ class theme extends common
'textColor' => $this->getInput('themeTextTextColor'),
'linkColor' => $this->getInput('themeTextLinkColor')
]
]);
], false);
$this->setData([
'theme',
'site',
@ -950,14 +952,14 @@ class theme extends common
'width' => $this->getInput('themeSiteWidth'),
'margin' => $this->getInput('themeSiteMargin', helper::FILTER_BOOLEAN)
]
]);
], false);
$this->setData([
'theme',
'button',
[
'backgroundColor' => $this->getInput('themeButtonBackgroundColor')
]
]);
], false);
$this->setData([
'theme',
'block',

View File

@ -7,15 +7,7 @@
'value' => template::ico('left')
]); ?>
</div>
<div class="col1">
<?php /* echo template::button('themeBodyHelp', [
'href' => 'https://doc.zwiicms.fr/arriere-plan',
'target' => '_blank',
'value' => template::ico('help'),
'class' => 'buttonHelp'
]); */ ?>
</div>
<div class="col2 offset8">
<div class="col2 offset9">
<?php echo template::submit('themeBodySubmit'); ?>
</div>
</div>
@ -68,7 +60,8 @@
'language' => $this->getData(['user', $this->getUser('id'), 'language']),
'label' => 'Arrière plan',
'type' => 1,
'value' => $imageFile
'value' => $imageFile,
'folder' => $imageFile ? dirname($imageFile) : ''
]); ?>
</div>
</div>

View File

@ -20,6 +20,7 @@ $('#dataTables').DataTable({
},
locale: 'fr',
stateSave: true,
"lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "Tout"]],
"columnDefs": [{
target: 5,
orderable: false,

View File

@ -7,15 +7,7 @@
'value' => template::ico('left')
]); ?>
</div>
<div class="col1">
<?php /* echo template::button('themeHeaderHelp', [
'href' => 'https://doc.zwiicms.fr/banniere',
'target' => '_blank',
'value' => template::ico('help'),
'class' => 'buttonHelp'
]); */?>
</div>
<div class="col2 offset8">
<div class="col2 offset9">
<?php echo template::submit('themeHeaderSubmit'); ?>
</div>
</div>
@ -158,13 +150,17 @@
'label' => 'Image',
'language' => $this->getData(['user', $this->getUser('id'), 'language']),
'type' => 1,
'value' => $imageFile
'value' => $imageFile,
'folder' => $imageFile ? dirname($imageFile) : ''
]);
?>
<span class="themeHeaderImageOptions displayNone" id="themeHeaderImageInfo">
<?php echo helper::translate('Largeur de l\'image'); ?> <span id="themeHeaderImageWidth"></span> ; <?php echo helper::translate('Largeur du site :'); ?> <?php echo $this->getData(['theme', 'site', 'width']); ?>
<?php echo helper::translate('Largeur de l\'image'); ?> <span id="themeHeaderImageWidth"></span>
; <?php echo helper::translate('Largeur du site :'); ?>
<?php echo $this->getData(['theme', 'site', 'width']); ?>
|
<?php echo helper::translate('Hauteur de l\'image'); ?> <span id="themeHeaderImageHeight"></span>
<?php echo helper::translate('Hauteur de l\'image'); ?> <span
id="themeHeaderImageHeight"></span>
|
<?php echo helper::translate('Ratio'); ?> <span id="themeHeaderImageRatio"></span>
</span>

View File

@ -7,15 +7,7 @@
'value' => template::ico('left')
]); ?>
</div>
<div class="col1">
<?php /* echo template::button('themeMenuHelp', [
'href' => 'https://doc.zwiicms.fr/menu',
'target' => '_blank',
'value' => template::ico('help'),
'class' => 'buttonHelp'
]); */?>
</div>
<div class="col2 offset8">
<div class="col2 offset9">
<?php echo template::submit('themeMenuSubmit'); ?>
</div>
</div>
@ -112,8 +104,7 @@
'selected' => $this->getData(['theme', 'menu', 'burgerContent']),
]); ?>
</div>
<div class="col6" id="themeMenuBurgerLogoId"
class="<?php if ($this->getData(['theme', 'menu', 'burgerContent']) !== 'logo')
<div class="col6" id="themeMenuBurgerLogoId" class="<?php if ($this->getData(['theme', 'menu', 'burgerContent']) !== 'logo')
echo 'displayNone'; ?>">
<?php $imageFile = file_exists(self::FILE_DIR . 'source/' . $this->getData(['theme', 'menu', 'burgerLogo'])) ? $this->getData(['theme', 'menu', 'burgerLogo']) : ""; ?>
<?php echo template::file('themeMenuBurgerLogo', [
@ -121,7 +112,8 @@
'language' => $this->getData(['user', $this->getUser('id'), 'language']),
'label' => 'Logo du menu burger',
'type' => 1,
'value' => $imageFile
'value' => $imageFile,
'folder' => $imageFile ? dirname($imageFile) : ''
]);
?>
</div>

View File

@ -19,6 +19,7 @@ class user extends common
public static $actions = [
'add' => self::GROUP_ADMIN,
'delete' => self::GROUP_ADMIN,
'usersDelete' => self::GROUP_ADMIN,
'import' => self::GROUP_ADMIN,
'index' => self::GROUP_ADMIN,
'template' => self::GROUP_ADMIN,
@ -31,6 +32,7 @@ class user extends common
'profilEdit' => self::GROUP_ADMIN,
'profilAdd' => self::GROUP_ADMIN,
'profilDelete' => self::GROUP_ADMIN,
'tag' => self::GROUP_ADMIN,
];
public static $users = [];
@ -229,6 +231,161 @@ class user extends common
}
}
/**
* Désinscription de tous les utilisateurs
* Les désinscriptions ne suppriment pas les historiques
*/
public function usersDelete()
{
// Contenu sélectionné
$courseId = $this->getUrl(2);
// Accès limité aux admins, à l'auteur ou éditeurs inscrits
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true
) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
}
// Inscription des utilisateurs cochés
if (
isset($_POST['usersDeleteSubmit'])
) {
$notification = helper::translate('Suppression de %s compte');
$success = true;
$count = 0;
foreach ($_POST as $keyPost => $valuePost) {
// Exclure les variables post qui ne sont pas des userId et ne traiter que les non inscrits
if (
$this->getData(['user', $keyPost]) !== null
) {
if ($keyPost === $this->getUser('id')) {
$notification = helper::translate('Votre compte n\'a pas été supprimé !') . '<br />' . $notification;
$success = 1;
} else {
$this->deleteData(['user', $keyPost]);
$count += 1;
}
}
}
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . 'user/usersDelete',
'notification' => sprintf($count > 1 ? $notification . 's' : $notification, $count),
'state' => $success
]);
}
// Liste des groupes et des profils
$usersGroups = $this->getData(['profil']);
foreach ($usersGroups as $groupId => $groupValue) {
switch ($groupId) {
case "-1":
case "0":
break;
case "3":
self::$usersGroups['30'] = 'Administrateur';
$profils['30'] = 0;
break;
case "1":
case "2":
foreach ($groupValue as $profilId => $profilValue) {
if ($profilId) {
self::$usersGroups[$groupId . $profilId] = sprintf(helper::translate('Groupe %s - Profil %s'), self::$groupPublics[$groupId], $profilValue['name']);
$profils[$groupId . $profilId] = 0;
}
}
}
}
// Liste alphabétique
self::$alphabet = range('A', 'Z');
$alphabet = range('A', 'Z');
self::$alphabet = array_combine($alphabet, self::$alphabet);
self::$alphabet = array_merge(['all' => 'Tout'], self::$alphabet);
// Liste des inscrits dans le contenu sélectionné.
$users = $this->getData(['user']);
if (is_array($users)) {
// Tri du tableau par défaut par $userId
ksort($users);
foreach ($users as $userId => $userValue) {
// Compte les rôles
if (isset($profils[$this->getData(['user', $userId, 'group']) . $this->getData(['user', $userId, 'profil'])])) {
$profils[$this->getData(['user', $userId, 'group']) . $this->getData(['user', $userId, 'profil'])]++;
}
// Filtres
if (
isset($_POST['usersFilterGroup'])
|| isset($_POST['usersFilterFirstName'])
|| isset($_POST['usersFilterLastName'])
) {
// Groupe et profils
$group = (string) $this->getData(['user', $userId, 'group']);
$profil = (string) $this->getData(['user', $userId, 'profil']);
$firstName = $this->getData(['user', $userId, 'firstname']);
$lastName = $this->getData(['user', $userId, 'lastname']);
if (
$this->getInput('usersFilterGroup', helper::FILTER_INT) > 0
&& $this->getInput('usersFilterGroup', helper::FILTER_STRING_SHORT) !== $group . $profil
)
continue;
// Première lettre du prénom
if (
$this->getInput('usersFilterFirstName', helper::FILTER_STRING_SHORT) !== 'all'
&& $this->getInput('usersFilterFirstName', helper::FILTER_STRING_SHORT) !== strtoupper(substr($firstName, 0, 1))
)
continue;
// Première lettre du nom
if (
$this->getInput('usersFilterLastName', helper::FILTER_STRING_SHORT) !== 'all'
&& $this->getInput('usersFilterLastName', helper::FILTER_STRING_SHORT) !== strtoupper(substr($lastName, 0, 1))
)
continue;
}
// Construction du tableau
self::$users[] = [
template::checkbox($userId, true, '', ['class' => 'checkboxSelect']),
$userId,
$this->getData(['user', $userId, 'firstname']),
$this->getData(['user', $userId, 'lastname']),
$this->getData(['user', $userId, 'tags']),
];
}
}
// Ajoute les effectifs aux profils du sélecteur
foreach (self::$usersGroups as $groupId => $groupValue) {
if ($groupId === 'all') {
self::$usersGroups['all'] = self::$usersGroups['all'] . ' (' . array_sum($profils) . ')';
} else {
self::$usersGroups[$groupId] = self::$usersGroups[$groupId] . ' (' . $profils[$groupId] . ')';
}
}
// Valeurs en sortie
$this->addOutput([
'title' => helper::translate('Désincription en masse'),
'view' => 'usersDelete',
'vendor' => [
'datatables'
]
]);
}
/**
* Édition
*/
@ -271,7 +428,9 @@ class user extends common
if ($this->getUser('group') < self::GROUP_ADMIN) {
if ($this->getInput('userEditNewPassword')) {
// L'ancien mot de passe est correct
if (password_verify(html_entity_decode($this->getInput('userEditOldPassword')), $this->getData(['user', $this->getUrl(2), 'password']))) {
if (
password_verify(html_entity_decode($this->getInput('userEditOldPassword')), $this->getData(['user', $this->getUrl(2), 'password']))
) {
// La confirmation correspond au mot de passe
if ($this->getInput('userEditNewPassword') === $this->getInput('userEditConfirmPassword')) {
$newPassword = $this->getInput('userEditNewPassword', helper::FILTER_PASSWORD, true);
@ -345,6 +504,7 @@ class user extends common
'files' => $this->getInput('userEditFiles', helper::FILTER_BOOLEAN),
'language' => $this->getInput('userEditLanguage', helper::FILTER_STRING_SHORT),
'tags' => $this->getInput('userEditTags', helper::FILTER_STRING_SHORT),
'authKey' => $this->getData(['user', $this->getUrl(2), 'authKey']),
]
]);
// Redirection spécifique si l'utilisateur change son mot de passe
@ -415,7 +575,7 @@ class user extends common
// Enregistre la date de la demande dans le compte utilisateur
$this->setData(['user', $userId, 'forgot', time()]);
// Crée un id unique pour la réinitialisation
$uniqId = md5(json_encode($this->getData(['user', $userId])));
$uniqId = md5(json_encode($this->getData(['user', $userId, 'forgot'])));
// Envoi le mail
$sent = $this->sendMail(
$this->getData(['user', $userId, 'mail']),
@ -711,12 +871,35 @@ class user extends common
// Droit d'intervenir sur tous les espaces
'tutor' => $this->getInput('profilEditCourseTutor', helper::FILTER_BOOLEAN),
// Droit d'accéder à la fenêtre de gestion pour tous les éditeurs et plus
'index' => $this->getUser('group') >= self::GROUP_EDITOR,
'manage' => $this->getUser('group') >= self::GROUP_EDITOR,
'index' => $this->getInput('profilEditCourseUsers', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseUserHistory', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseUserExport', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCoursExport', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseUserAdd', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseUsersAdd', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseUserDelete', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseUsersDelete', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseEdit', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseBackup', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseRestore', helper::FILTER_BOOLEAN),
'manage' => $this->getInput('profilEditCourseUsers', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseUserHistory', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseUserExport', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCoursExport', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseUserAdd', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseUsersAdd', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseUserDelete', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseUsersDelete', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseEdit', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseBackup', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseRestore', helper::FILTER_BOOLEAN)
|| $this->getInput('profilEditCourseReset', helper::FILTER_BOOLEAN),
// Droits spécifiques
'users' => $this->getInput('profilEditCourseUsers', helper::FILTER_BOOLEAN),
'userHistory' => $this->getInput('profilEditCourseUserHistory', helper::FILTER_BOOLEAN),
'userHistoryExport' => $this->getInput('profilEditCourseUserHistoryExport', helper::FILTER_BOOLEAN),
'userReportExport' => $this->getInput('profilEditCourseuserReportExport', helper::FILTER_BOOLEAN),
'export' => $this->getInput('profilEditCourseExport', helper::FILTER_BOOLEAN),
'userAdd' => $this->getInput('profilEditCourseUserAdd', helper::FILTER_BOOLEAN),
'usersAdd' => $this->getInput('profilEditCourseUsersAdd', helper::FILTER_BOOLEAN),
'userDelete' => $this->getInput('profilEditCourseUserDelete', helper::FILTER_BOOLEAN),
@ -724,6 +907,7 @@ class user extends common
'edit' => $this->getInput('profilEditCourseEdit', helper::FILTER_BOOLEAN),
'backup' => $this->getInput('profilEditCourseBackup', helper::FILTER_BOOLEAN),
'restore' => $this->getInput('profilEditCourseRestore', helper::FILTER_BOOLEAN),
'reset' => $this->getInput('profilEditCourseReset', helper::FILTER_BOOLEAN),
]
];
@ -888,6 +1072,7 @@ class user extends common
'index' => $this->getInput('profilAddCourseUsers', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCourseUserHistory', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCourseUserExport', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCoursExport', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCourseUserAdd', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCourseUsersAdd', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCourseUserDelete', helper::FILTER_BOOLEAN)
@ -895,21 +1080,23 @@ class user extends common
|| $this->getInput('profilAddCourseEdit', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCourseBackup', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCourseRestore', helper::FILTER_BOOLEAN),
'manage' => $this->getInput('profilAddCourseUsers', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCourseUserHistory', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCourseUserExport', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCoursExport', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCourseUserAdd', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCourseUsersAdd', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCourseUserDelete', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCourseUsersDelete', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCourseEdit', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCourseBackup', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCourseRestore', helper::FILTER_BOOLEAN),
|| $this->getInput('profilAddCourseRestore', helper::FILTER_BOOLEAN)
|| $this->getInput('profilAddCourseReset', helper::FILTER_BOOLEAN),
// La suite
'users' => $this->getInput('profilAddCourseUsers', helper::FILTER_BOOLEAN),
'userHistory' => $this->getInput('profilAddCourseUserHistory', helper::FILTER_BOOLEAN),
'userHistoryExport' => $this->getInput('profilAddCourseUserHistoryExport', helper::FILTER_BOOLEAN),
'userReportExport' => $this->getInput('profilAddCourseuserReportExport', helper::FILTER_BOOLEAN),
'export' => $this->getInput('profilAddCourseExport', helper::FILTER_BOOLEAN),
'userAdd' => $this->getInput('profilAddCourseUserAdd', helper::FILTER_BOOLEAN),
'usersAdd' => $this->getInput('profilAddCourseUsersAdd', helper::FILTER_BOOLEAN),
'userDelete' => $this->getInput('profilAddCourseUserDelete', helper::FILTER_BOOLEAN),
@ -917,6 +1104,7 @@ class user extends common
'edit' => $this->getInput('profilAddCourseEdit', helper::FILTER_BOOLEAN),
'backup' => $this->getInput('profilAddCourseBackup', helper::FILTER_BOOLEAN),
'restore' => $this->getInput('profilAddCourseRestore', helper::FILTER_BOOLEAN),
'reset' => $this->getInput('profilAddCourseReset', helper::FILTER_BOOLEAN),
]
];
@ -1074,7 +1262,7 @@ class user extends common
'lastFail' => time(),
'ip' => helper::getIp()
]
]);
], false);
// Verrouillage des IP
$ipBlackList = helper::arrayColumn($this->getData(['blacklist']), 'ip');
if (
@ -1103,8 +1291,8 @@ class user extends common
$this->getData(['user', $userId, 'connectTimeout']) + $this->getData(['config', 'connect', 'timeout']) < time()
and $this->getData(['user', $userId, 'connectFail']) === $this->getData(['config', 'connect', 'attempt'])
) {
$this->setData(['user', $userId, 'connectFail', 0]);
$this->setData(['user', $userId, 'connectTimeout', 0]);
$this->setData(['user', $userId, 'connectFail', 0], false);
$this->setData(['user', $userId, 'connectTimeout', 0], false);
}
// Check la présence des variables et contrôle du blocage du compte si valeurs dépassées
// Vérification du mot de passe et du groupe
@ -1116,14 +1304,36 @@ class user extends common
and $captcha === true
) {
// RAZ
$this->setData(['user', $userId, 'connectFail', 0]);
$this->setData(['user', $userId, 'connectTimeout', 0]);
// Expiration
$this->setData(['user', $userId, 'connectFail', 0], false);
$this->setData(['user', $userId, 'connectTimeout', 0], false);
// Clé d'authenfication
$authKey = uniqid('', true) . bin2hex(random_bytes(8));
$this->setData(['user', $userId, 'authKey', $authKey], false);
// Validité du cookie
$expire = $this->getInput('userLoginLongTime', helper::FILTER_BOOLEAN) === true ? strtotime("+1 year") : 0;
switch ($this->getInput('userLoginLongTime', helper::FILTER_BOOLEAN)) {
case false:
// Cookie de session
setcookie('ZWII_USER_ID', $userId, $expire, helper::baseUrl(false, false), '', helper::isHttps(), true);
setcookie('ZWII_USER_PASSWORD', $this->getData(['user', $userId, 'password']), $expire, helper::baseUrl(false, false), '', helper::isHttps(), true);
//setcookie('ZWII_USER_PASSWORD', $this->getData(['user', $userId, 'password']), $expire, helper::baseUrl(false, false), '', helper::isHttps(), true);
// Connexion par clé
setcookie('ZWII_AUTH_KEY', $authKey, $expire, helper::baseUrl(false, false), '', helper::isHttps(), true);
break;
default:
// Cookie persistant
setcookie('ZWII_USER_ID', $userId, $expire, helper::baseUrl(false, false));
//setcookie('ZWII_USER_PASSWORD', $this->getData(['user', $userId, 'password']), $expire, helper::baseUrl(false, false));
// Connexion par clé
setcookie('ZWII_AUTH_KEY', $authKey, $expire, helper::baseUrl(false, false));
break;
}
// Accès multiples avec le même compte
$this->setData(['user', $userId, 'accessCsrf', $_SESSION['csrf']]);
$this->setData(['user', $userId, 'accessCsrf', $_SESSION['csrf']], false);
// Valeurs en sortie lorsque le site est en maintenance et que l'utilisateur n'est pas administrateur
if (
$this->getData(['config', 'maintenance'])
@ -1157,11 +1367,11 @@ class user extends common
$logStatus = $captcha === true ? 'Erreur de mot de passe' : 'Erreur de captcha';
// Cas 1 le nombre de connexions est inférieur aux tentatives autorisées : incrément compteur d'échec
if ($this->getData(['user', $userId, 'connectFail']) < $this->getData(['config', 'connect', 'attempt'])) {
$this->setData(['user', $userId, 'connectFail', $this->getdata(['user', $userId, 'connectFail']) + 1]);
$this->setData(['user', $userId, 'connectFail', $this->getdata(['user', $userId, 'connectFail']) + 1], false);
}
// Cas 2 la limite du nombre de connexion est atteinte : placer le timer
if ($this->getdata(['user', $userId, 'connectFail']) == $this->getData(['config', 'connect', 'attempt'])) {
$this->setData(['user', $userId, 'connectTimeout', time()]);
$this->setData(['user', $userId, 'connectTimeout', time()], false);
}
// Cas 3 le délai de bloquage court
if ($this->getData(['user', $userId, 'connectTimeout']) + $this->getData(['config', 'connect', 'timeout']) > time()) {
@ -1174,7 +1384,10 @@ class user extends common
]);
}
}
// Sauvegarde la base manuellement
$this->saveDB(module: 'user');
}
// Journalisation
$this->saveLog($logStatus);
@ -1197,7 +1410,8 @@ class user extends common
public function logout()
{
helper::deleteCookie('ZWII_USER_ID');
helper::deleteCookie('ZWII_USER_PASSWORD');
//helper::deleteCookie('ZWII_USER_PASSWORD');
helper::deleteCookie('ZWII_AUTH_KEY');
// Détruit la session
session_destroy();
@ -1222,13 +1436,29 @@ class user extends common
// Lien de réinitialisation trop vieux
or $this->getData(['user', $this->getUrl(2), 'forgot']) + 86400 < time()
// Id unique incorrecte
or $this->getUrl(3) !== md5(json_encode($this->getData(['user', $this->getUrl(2)])))
or $this->getUrl(3) !== md5(json_encode($this->getData(['user', $this->getUrl(2), 'forgot'])))
) {
$this->saveLog(
' Erreur de réinitialisation de mot de passe ' . $this->getUrl(2) .
' Compte : ' . $this->getData(['user', $this->getUrl(2)]) .
' Temps : ' . $this->getData(['user', $this->getUrl(2), 'forgot']) + 86400 < time() .
' Clé : ' . $this->getUrl(3) !== md5(json_encode($this->getData(['user', $this->getUrl(2), 'forgot'])))
);
// Message d'erreur en cas de problème de réinitialisation de mot de passe
$message = $this->getData(['user', $this->getUrl(2)]) === null
? ' Utilisateur inconnu '
: '';
$message = $this->getData(['user', $this->getUrl(2), 'forgot']) + 86400 < time()
? ' Temps dépassé '
: $message;
$message = $this->getUrl(3) !== md5(json_encode($this->getData(['user', $this->getUrl(2)])))
? ' Clé invalide '
: $message;
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseurl(),
'notification' => helper::translate('Impossible de réinitialiser le mot de passe de ce compte !'),
'notification' => helper::translate('Impossible de réinitialiser le mot de passe de ce compte !') . $message,
'state' => false
//'access' => false
]);
@ -1251,12 +1481,14 @@ class user extends common
$newPassword = $this->getInput('userResetNewPassword', helper::FILTER_PASSWORD, true);
}
// Modifie le mot de passe
$this->setData(['user', $this->getUrl(2), 'password', $newPassword]);
$this->setData(['user', $this->getUrl(2), 'password', $newPassword], false);
// Réinitialise la date de la demande
$this->setData(['user', $this->getUrl(2), 'forgot', 0]);
$this->setData(['user', $this->getUrl(2), 'forgot', 0], false);
// Réinitialise le blocage
$this->setData(['user', $this->getUrl(2), 'connectFail', 0]);
$this->setData(['user', $this->getUrl(2), 'connectTimeout', 0]);
$this->setData(['user', $this->getUrl(2), 'connectFail', 0], false);
$this->setData(['user', $this->getUrl(2), 'connectTimeout', 0], false);
// Sauvegarde la base manuellement
$this->saveDB('user');
// Valeurs en sortie
$this->addOutput([
'notification' => helper::translate('Nouveau mot de passe enregistré'),
@ -1370,7 +1602,7 @@ class user extends common
"accessCsrf" => null,
'tags' => $item['tags']
]
]);
], false);
// Icône de notification
$item['notification'] = $create ? template::ico('check') : template::ico('cancel');
// Envoi du mail
@ -1411,6 +1643,8 @@ class user extends common
}
}
// Sauvegarde la base manuellement
$this->saveDB(module: 'user');
if (empty(self::$users)) {
$notification = helper::translate('Rien à importer, erreur de format ou fichier incorrect');
$success = false;
@ -1452,6 +1686,154 @@ class user extends common
}
public function tag()
{
// Contenu sélectionné
$courseId = $this->getUrl(2);
// Accès limité aux admins, à l'auteur ou éditeurs inscrits
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true
) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
}
// Inscription des utilisateurs cochés
if (
isset($_POST['usersTagSubmit'])
) {
$notification = helper::translate('Modification de %s étiquette(s)');
$success = true;
$count = 0;
$newTags = $this->getInput('usersTagLabel', null, true);
foreach ($_POST as $keyPost => $valuePost) {
// Exclure les variables post qui ne sont pas des userId et ne traiter que les non inscrits
if (
$this->getData(['user', $keyPost]) !== null
) {
$this->setData(['user', $keyPost, 'tags', $newTags], false);
$count += 1;
}
}
// Sauvegarde la base manuellement
$this->saveDB(module: 'user');
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . 'user/tag',
'notification' => sprintf($count > 1 ? $notification . 's' : $notification, $count),
'state' => $success
]);
}
// Liste des groupes et des profils
$usersGroups = $this->getData(['profil']);
foreach ($usersGroups as $groupId => $groupValue) {
switch ($groupId) {
case "-1":
case "0":
break;
case "3":
self::$usersGroups['30'] = 'Administrateur';
$profils['30'] = 0;
break;
case "1":
case "2":
foreach ($groupValue as $profilId => $profilValue) {
if ($profilId) {
self::$usersGroups[$groupId . $profilId] = sprintf(helper::translate('Groupe %s - Profil %s'), self::$groupPublics[$groupId], $profilValue['name']);
$profils[$groupId . $profilId] = 0;
}
}
}
}
// Liste alphabétique
self::$alphabet = range('A', 'Z');
$alphabet = range('A', 'Z');
self::$alphabet = array_combine($alphabet, self::$alphabet);
self::$alphabet = array_merge(['all' => 'Tout'], self::$alphabet);
// Liste des inscrits dans le contenu sélectionné.
$users = $this->getData(['user']);
if (is_array($users)) {
// Tri du tableau par défaut par $userId
ksort($users);
foreach ($users as $userId => $userValue) {
// Compte les rôles
if (isset($profils[$this->getData(['user', $userId, 'group']) . $this->getData(['user', $userId, 'profil'])])) {
$profils[$this->getData(['user', $userId, 'group']) . $this->getData(['user', $userId, 'profil'])]++;
}
// Filtres
if (
isset($_POST['usersFilterGroup'])
|| isset($_POST['usersFilterFirstName'])
|| isset($_POST['usersFilterLastName'])
) {
// Groupe et profils
$group = (string) $this->getData(['user', $userId, 'group']);
$profil = (string) $this->getData(['user', $userId, 'profil']);
$firstName = $this->getData(['user', $userId, 'firstname']);
$lastName = $this->getData(['user', $userId, 'lastname']);
if (
$this->getInput('usersFilterGroup', helper::FILTER_INT) > 0
&& $this->getInput('usersFilterGroup', helper::FILTER_STRING_SHORT) !== $group . $profil
)
continue;
// Première lettre du prénom
if (
$this->getInput('usersFilterFirstName', helper::FILTER_STRING_SHORT) !== 'all'
&& $this->getInput('usersFilterFirstName', helper::FILTER_STRING_SHORT) !== strtoupper(substr($firstName, 0, 1))
)
continue;
// Première lettre du nom
if (
$this->getInput('usersFilterLastName', helper::FILTER_STRING_SHORT) !== 'all'
&& $this->getInput('usersFilterLastName', helper::FILTER_STRING_SHORT) !== strtoupper(substr($lastName, 0, 1))
)
continue;
}
// Construction du tableau
self::$users[] = [
template::checkbox($userId, true, '', ['class' => 'checkboxSelect']),
$userId,
$this->getData(['user', $userId, 'firstname']),
$this->getData(['user', $userId, 'lastname']),
$this->getData(['user', $userId, 'tags']),
];
}
}
// Ajoute les effectifs aux profils du sélecteur
foreach (self::$usersGroups as $groupId => $groupValue) {
if ($groupId === 'all') {
self::$usersGroups['all'] = self::$usersGroups['all'] . ' (' . array_sum($profils) . ')';
} else {
self::$usersGroups[$groupId] = self::$usersGroups[$groupId] . ' (' . $profils[$groupId] . ')';
}
}
// Valeurs en sortie
$this->addOutput([
'view' => 'tag',
'title' => 'Étiquettes',
'vendor' => [
'datatables'
]
]);
}
/**
* Liste les dossier contenus dans RFM
*/

View File

@ -29,6 +29,7 @@ $(document).ready((function () {
},
locale: 'fr',
stateSave: true,
"lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "Tout"]],
"columnDefs": [
{
target: 5,

View File

@ -6,27 +6,35 @@
'value' => template::ico('home')
]); ?>
</div>
<div class="col1">
<?php /**echo template::button('userHelp', [
'href' => 'https://doc.zwiicms.fr/gestion-des-utilisateurs',
'target' => '_blank',
'value' => template::ico('help'),
'class' => 'buttonHelp',
'help' => 'Consulter l\'aide en ligne'
]);*/?>
</div>
<div class="col1 offset7">
<div class="col2 offset2">
<?php echo template::button('userImport', [
'href' => helper::baseUrl() . 'user/import',
'value' => template::ico('upload'),
'help' => 'Importer des utilisateurs en masse'
'ico' => 'users',
'value' => 'Importer en masse'
]); ?>
</div>
<div class="col1">
<div class="col2">
<?php echo template::button('userDeleteAll', [
'class' => 'userDeleteAll buttonRed',
'href' => helper::baseUrl() . 'user/usersDelete/' . $this->getUrl(2),
'ico' => 'users',
'value' => 'Désinscrire en masse',
]) ?>
</div>
<div class="col2">
<?php echo template::button('userTag', [
'href' => helper::baseUrl() . 'user/tag',
'ico' => 'tags',
'value' => 'Étiquettes',
'help' => 'Filtrer les utilisateurs avec des tags'
]); ?>
</div>
<div class="col2">
<?php echo template::button('userGroup', [
'href' => helper::baseUrl() . 'user/profil',
'value' => template::ico('lock'),
'help' => 'Profils'
'ico' => 'lock',
'value' => 'Profils',
'help' => 'Permissions par profils'
]); ?>
</div>
<div class="col1">

View File

@ -8,4 +8,32 @@
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
$(document).ready((function(){$(".zwiico-eye").mouseenter((function(){$("#userLoginPassword").attr("type","text")})),$(".zwiico-eye").mouseleave((function(){$("#userLoginPassword").attr("type","password")}))}));
$(document).ready((function() {
$("#userLoginId").on("change keydown keyup", function (event) {
var userId = $(this).val();
if (
event.keyCode !== 8 // BACKSPACE
&& event.keyCode !== 37 // LEFT
&& event.keyCode !== 39 // RIGHT
&& event.keyCode !== 46 // DELETE
&& window.getSelection().toString() !== userId // Texte sélectionné
) {
var searchReplace = {
"á": "a", "à": "a", "â": "a", "ä": "a", "ã": "a", "å": "a", "ç": "c", "é": "e", "è": "e", "ê": "e", "ë": "e", "í": "i", "ì": "i", "î": "i", "ï": "i", "ñ": "n", "ó": "o", "ò": "o", "ô": "o", "ö": "o", "õ": "o", "ú": "u", "ù": "u", "û": "u", "ü": "u", "ý": "y", "ÿ": "y",
"Á": "A", "À": "A", "Â": "A", "Ä": "A", "Ã": "A", "Å": "A", "Ç": "C", "É": "E", "È": "E", "Ê": "E", "Ë": "E", "Í": "I", "Ì": "I", "Î": "I", "Ï": "I", "Ñ": "N", "Ó": "O", "Ò": "O", "Ô": "O", "Ö": "O", "Õ": "O", "Ú": "U", "Ù": "U", "Û": "U", "Ü": "U", "Ý": "Y", "Ÿ": "Y",
"'": "-", "\"": "-", " ": "-"
};
userId = userId.replace(/[áàâäãåçéèêëíìîïñóòôöõúùûüýÿ'" ]/ig, function (match) {
return searchReplace[match];
});
userId = userId.replace(/[^a-z0-9-]/ig, "");
$(this).val(userId);
}
});
$(".zwiico-eye").mouseenter((function() {
$("#userLoginPassword").attr("type", "text")
})), $(".zwiico-eye").mouseleave((function() {
$("#userLoginPassword").attr("type", "password")
}))
}));

View File

@ -180,11 +180,15 @@
<div class="col3">
<?php echo template::checkbox('profilAddCourseRestore', true, 'Restaurer un espace'); ?>
</div>
</div>
<div class="row">
<div class="col6">
<div class="col3">
<?php echo template::checkbox('profilAddCourseUsers', true, 'Gérer les participants'); ?>
</div>
<div class="col3">
<?php echo template::checkbox('profilAddCourseExport', true, 'Exporter un espace en html'); ?>
</div>
</div>
<div id="courseContainer">
<div class="row">
@ -192,7 +196,7 @@
<?php echo template::checkbox('profilAddCourseUserHistory', true, 'Voir historique d\'un participant'); ?>
</div>
<div class="col3">
<?php echo template::checkbox('profilAddCourseUserHistoryExport', true, 'Exporter historique d\'un participant'); ?>
<?php echo template::checkbox('profilAddCourseuserReportExport', true, 'Exporter historique d\'un participant'); ?>
</div>
<div class="col3">
<?php echo template::checkbox('profilAddCourseUserDelete', true, 'Désinscrire un participant'); ?>
@ -205,6 +209,9 @@
<div class="col3">
<?php echo template::checkbox('profilAddCourseUsersDelete', true, 'Désinscrire en masse'); ?>
</div>
<div class="col3">
<?php echo template::checkbox('profilAddCourseReset', true, 'Réinitialiser un espace'); ?>
</div>
</div>
</div>
</div>

View File

@ -54,6 +54,19 @@ $(document).ready(function () {
$(".containerModule").slideDown();
}
if ($('#profilEditCourseUsers').is(':checked')) {
// Activer les autres checkboxes
$('#profilEditCourseUserHistory, #profilEditCourseuserReportExport, #profilEditCourseUserDelete, #profilEditCourseUsersAdd, #profilEditCourseUsersDelete, #profilEditCourseReset').prop('disabled', false);
} else {
// Désactiver les autres checkboxes
$('#profilEditCourseUserHistory, #profilEditCourseuserReportExport, #profilEditCourseUserDelete, #profilEditCourseUsersAdd, #profilEditCourseUsersDelete, #profilEditCourseReset').prop('checked', false).prop('disabled', true);
// Désactiver les modules et tout décocher
$(".courseContainer").slideUp();
$('.courseContainer input[type="checkbox"]').prop('checked', false);
}
// EVENEMENTS
// À chaque inversion de l'état du checkbox avec l'id "profilEditFileManager", désactive ou active tous les éléments de la classe "filemanager" en fonction de l'état
$("#profilEditFileManager").change(function () {
if (!$(this).is(':checked')) {
@ -118,4 +131,20 @@ $(document).ready(function () {
}
});
// Gérer lévènement de modification de la checkbox #profilEditCourse
$('#profilEditCourseUsers').change(function () {
if ($(this).is(':checked')) {
// Activer les autres checkboxes
$('#profilEditCourseUserHistory, #profilEditCourseuserReportExport, #profilEditCourseUserDelete, #profilEditCourseUsersAdd, #profilEditCourseUsersDelete, #profilEditCourseReset').prop('disabled', false);
} else {
// Désactiver les autres checkboxes
$('#profilEditCourseUserHistory, #profilEditCourseuserReportExport, #profilEditCourseUserDelete, #profilEditCourseUsersAdd, #profilEditCourseUsersDelete, #profilEditCourseReset').prop('checked', false).prop('disabled', true);
// Désactiver les modules et tout décocher
$(".courseContainer").slideUp();
$('.courseContainer input[type="checkbox"]').prop('checked', false);
}
});
});

View File

@ -272,11 +272,16 @@
</div>
</div>
<div class="row">
<div class="col6">
<div class="col3">
<?php echo template::checkbox('profilEditCourseUsers', true, 'Gérer les participants', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'course', 'users']),
]); ?>
</div>
<div class="col3">
<?php echo template::checkbox('profilEditCourseExport', true, 'Exporter un espace en html', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'course', 'export']),
]); ?>
</div>
</div>
<div id="courseContainer">
<div class="row">
@ -286,8 +291,8 @@
]); ?>
</div>
<div class="col3">
<?php echo template::checkbox('profilEditCourseUserHistoryExport', true, 'Exporter historique d\'un participant', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'course', 'userHistoryExport']),
<?php echo template::checkbox('profilEditCourseuserReportExport', true, 'Exporter historique d\'un participant', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'course', 'userReportExport']),
]); ?>
</div>
<div class="col3">
@ -307,6 +312,11 @@
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'course', 'usersDelete']),
]); ?>
</div>
<div class="col3">
<?php echo template::checkbox('profilEditCourseReset', true, 'Réinitialiser un espace', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'course', 'reset']),
]); ?>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,18 @@
/**
* This file is part of Zwii.
*
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2024, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/** NE PAS EFFACER
* admin.css
*/

View File

@ -0,0 +1,101 @@
/**
* This file is part of Zwii.
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2024, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
$(document).ready((function () {
$('tr').click(function () {
// Cochez ou décochez la case à cocher dans cette ligne
$(this).find('input[type="checkbox"]').prop('checked', function (i, val) {
return !val; // Inverse l'état actuel de la case à cocher
});
});
$('#usersTagSelectAll').on('click', function () {
$('.checkboxSelect').prop('checked', true);
saveCheckboxState();
});
$('#usersTagSelectNone').on('click', function () {
$('.checkboxSelect').prop('checked', false);
saveCheckboxState();
});
$("#usersFilterGroup, #usersFilterFirstName, #usersFilterLastName").change(function () {
saveCheckboxState();
$("#usersTagForm").submit();
});
var table = $('#dataTables').DataTable({
language: {
url: "core/vendor/datatables/french.json"
},
locale: 'fr',
"lengthMenu": [[10, 25, 50, 100, 299, -1], [10, 25, 50, 100, 200, "Tout"]],
"columnDefs": [
{
target: 0,
orderable: false,
searchable: false,
}
]
});
// Handle checkbox change event
$('.checkboxSelect').on('change', function () {
// Save checkbox state to cookies or local storage
saveCheckboxState();
});
// Handle checkbox state on DataTables draw event
table.on('draw', function () {
// Restore checkbox state from cookies or local storage
restoreCheckboxState();
});
// Empty local storage after submit
$("#usersTagSubmit").on("click", function () {
localStorage.setItem('checkboxState', JSON.stringify({}));
});
// Restore checkbox state on page load
restoreCheckboxState();
function saveCheckboxState() {
// Récupérer d'abord les données existantes dans le localStorage
var existingData = JSON.parse(localStorage.getItem('checkboxState')) || {};
// Ajouter ou mettre à jour les données actuelles
$('.checkboxSelect').each(function () {
var checkboxId = $(this).attr('id');
var checked = $(this).prop('checked');
existingData[checkboxId] = checked;
});
// Sauvegarder les données mises à jour dans le localStorage
localStorage.setItem('checkboxState', JSON.stringify(existingData));
}
// Function to restore checkbox state
function restoreCheckboxState() {
var checkboxState = JSON.parse(localStorage.getItem('checkboxState')) || {};
// console.log(checkboxState);
for (var checkboxId in checkboxState) {
if (checkboxState.hasOwnProperty(checkboxId)) {
var checked = checkboxState[checkboxId];
// Update checkbox state based on stored information
$('#' + checkboxId).prop('checked', checked);
}
}
}
}));

View File

@ -0,0 +1,65 @@
<?php echo template::formOpen('usersTagForm'); ?>
<div class="row">
<div class="col1">
<?php echo template::button('userDeleteBack', [
'class' => 'buttonGrey',
'href' => helper::baseUrl() . 'user/' . $this->getUrl(2),
'value' => template::ico('left')
]); ?>
</div>
</div>
<div class="row">
<div class="col12">
<div class="block">
<h4>Étiquette de remplacement</h4>
<div class="row">
<div class="col8">
<?php echo template::text('usersTagLabel', [
'placeholder' => 'Les étiquettes saisis remplaceront celles existantes. Les étiquettes sont séparées par des espaces'
]); ?>
</div>
<div class="col2 offset2 verticalAlignBottom">
<?php echo template::submit('usersTagSubmit'); ?>
</div>
</div>
</div>
</div>
</div>
<div class="row" id="Bfrtip">
<div class="col3">
<?php echo template::select('usersFilterGroup', $module::$usersGroups, [
'label' => 'Groupes / Profils',
'selected' => isset($_POST['usersFilterGroup']) ? $_POST['usersFilterGroup'] : 'all',
]); ?>
</div>
<div class="col3">
<?php echo template::select('usersFilterFirstName', $module::$alphabet, [
'label' => 'Prénom commence par',
'selected' => isset($_POST['usersFilterFirstName']) ? $_POST['usersFilterFirstName'] : 'all',
]); ?>
</div>
<div class="col3">
<?php echo template::select('usersFilterLastName', $module::$alphabet, [
'label' => 'Nom commence par',
'selected' => isset($_POST['usersFilterLastName']) ? $_POST['usersFilterLastName'] : 'all',
]); ?>
</div>
<div class="col1 offset1 verticalAlignBottom">
<?php echo template::button('usersTagSelectAll', [
'value' => template::ico('square-check'),
'help' => 'Tout sélectionner'
]); ?>
</div>
<div class="col1 verticalAlignBottom">
<?php echo template::button('usersTagSelectNone', [
'value' => template::ico('square-check-empty'),
'help' => 'Tout désélectionner'
]); ?>
</div>
</div>
<?php if ($module::$users): ?>
<?php echo template::table([1, 2, 3, 3, 3], $module::$users, ['', 'Id', 'Prénom', 'Nom', 'Étiquettes'], ['id' => 'dataTables']); ?>
<?php else: ?>
<?php echo template::speech('Aucun inscrit'); ?>
<?php endif; ?>
<?php echo template::formClose(); ?>

View File

@ -0,0 +1,26 @@
/**
* This file is part of Zwii.
*
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2024, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/** NE PAS EFFACER
* admin.css
*/
#usersDeleteSubmit {
background-color: rgba(217, 95, 78, 1);
}
tr {
cursor: pointer;
}

View File

@ -0,0 +1,101 @@
/**
* This file is part of Zwii.
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2024, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
$(document).ready((function () {
$('tr').click(function () {
// Cochez ou décochez la case à cocher dans cette ligne
$(this).find('input[type="checkbox"]').prop('checked', function (i, val) {
return !val; // Inverse l'état actuel de la case à cocher
});
});
$('#usersDeleteSelectAll').on('click', function () {
$('.checkboxSelect').prop('checked', true);
saveCheckboxState();
});
$('#usersDeleteSelectNone').on('click', function () {
$('.checkboxSelect').prop('checked', false);
saveCheckboxState();
});
$("#usersFilterGroup, #usersFilterFirstName, #usersFilterLastName").change(function () {
saveCheckboxState();
$("#usersDeleteForm").submit();
});
var table = $('#dataTables').DataTable({
language: {
url: "core/vendor/datatables/french.json"
},
locale: 'fr',
"lengthMenu": [[10, 25, 50, 100, 299, -1], [10, 25, 50, 100, 200, "Tout"]],
"columnDefs": [
{
target: 0,
orderable: false,
searchable: false,
}
]
});
// Handle checkbox change event
$('.checkboxSelect').on('change', function () {
// Save checkbox state to cookies or local storage
saveCheckboxState();
});
// Handle checkbox state on DataTables draw event
table.on('draw', function () {
// Restore checkbox state from cookies or local storage
restoreCheckboxState();
});
// Empty local storage after submit
$("#usersDeleteSubmit").on("click", function () {
localStorage.setItem('checkboxState', JSON.stringify({}));
});
// Restore checkbox state on page load
restoreCheckboxState();
function saveCheckboxState() {
// Récupérer d'abord les données existantes dans le localStorage
var existingData = JSON.parse(localStorage.getItem('checkboxState')) || {};
// Ajouter ou mettre à jour les données actuelles
$('.checkboxSelect').each(function () {
var checkboxId = $(this).attr('id');
var checked = $(this).prop('checked');
existingData[checkboxId] = checked;
});
// Sauvegarder les données mises à jour dans le localStorage
localStorage.setItem('checkboxState', JSON.stringify(existingData));
}
// Function to restore checkbox state
function restoreCheckboxState() {
var checkboxState = JSON.parse(localStorage.getItem('checkboxState')) || {};
// console.log(checkboxState);
for (var checkboxId in checkboxState) {
if (checkboxState.hasOwnProperty(checkboxId)) {
var checked = checkboxState[checkboxId];
// Update checkbox state based on stored information
$('#' + checkboxId).prop('checked', checked);
}
}
}
}));

View File

@ -0,0 +1,55 @@
<?php echo template::formOpen('usersDeleteForm'); ?>
<div class="row">
<div class="col1">
<?php echo template::button('userDeleteBack', [
'class' => 'buttonGrey',
'href' => helper::baseUrl() . 'user/' . $this->getUrl(2),
'value' => template::ico('left')
]); ?>
</div>
<div class="col1 offset10">
<?php echo template::submit('usersDeleteSubmit', [
'class' => 'buttonRed',
'ico' => '',
'value' => template::ico('minus'),
]); ?>
</div>
</div>
<div class="row" id="Bfrtip">
<div class="col3">
<?php echo template::select('usersFilterGroup', $module::$usersGroups, [
'label' => 'Groupes / Profils',
'selected' => isset($_POST['usersFilterGroup']) ? $_POST['usersFilterGroup'] : 'all',
]); ?>
</div>
<div class="col3">
<?php echo template::select('usersFilterFirstName', $module::$alphabet, [
'label' => 'Prénom commence par',
'selected' => isset($_POST['usersFilterFirstName']) ? $_POST['usersFilterFirstName'] : 'all',
]); ?>
</div>
<div class="col3">
<?php echo template::select('usersFilterLastName', $module::$alphabet, [
'label' => 'Nom commence par',
'selected' => isset($_POST['usersFilterLastName']) ? $_POST['usersFilterLastName'] : 'all',
]); ?>
</div>
<div class="col1 offset1 verticalAlignBottom">
<?php echo template::button('usersDeleteSelectAll', [
'value' => template::ico('square-check'),
'help' => 'Tout sélectionner'
]); ?>
</div>
<div class="col1 verticalAlignBottom">
<?php echo template::button('usersDeleteSelectNone', [
'value' => template::ico('square-check-empty'),
'help' => 'Tout désélectionner'
]); ?>
</div>
</div>
<?php if ($module::$users): ?>
<?php echo template::table([1, 2, 3, 3, 3], $module::$users, ['', 'Id', 'Prénom', 'Nom', 'Étiquettes'], ['id' => 'dataTables']); ?>
<?php else: ?>
<?php echo template::speech('Aucun inscrit'); ?>
<?php endif; ?>
<?php echo template::formClose(); ?>

View File

@ -0,0 +1,13 @@
.dataTables_length {
margin-bottom: 10px;
width: 250px;
}
.dataTables_length label {
float: left;
}
.dataTables_length select {
margin-left: 5px;
width: 80px;
}

View File

@ -1,7 +1,7 @@
{
"processing": "Traitement en cours...",
"search": "Rechercher&nbsp;:",
"lengthMenu": "&Eacute;l&eacute;ments par page _MENU_",
"lengthMenu": "&Eacute;l&eacute;ments par page&nbsp;:&nbsp;_MENU_",
"info": "Affichage de l'&eacute;lement _START_ &agrave; _END_ sur _TOTAL_ &eacute;l&eacute;ments",
"infoEmpty": "Affichage de l'&eacute;lement 0 &agrave; 0 sur 0 &eacute;l&eacute;ments",
"infoFiltered": "(filtr&eacute; de _MAX_ &eacute;l&eacute;ments au total)",

View File

@ -2,5 +2,6 @@
"datatables.min.js",
"moment.min.js",
"datetime.min.js",
"datatables.min.css"
"datatables.min.css",
"datatables.custom.css"
]

View File

@ -1506,8 +1506,9 @@ class UploadHandler
}
$thumbResult = create_img($targetFile, $targetFileThumb, 122, 91);
$newWidth = 640;
$newHeight = 480;
$thumbResult = create_img($targetFile, $targetFileThumb, $newWidth, $newHeight);
if ( $thumbResult!==true)
{

View File

@ -19,7 +19,7 @@ setlocale(LC_CTYPE, $lang);
/* Lecture du groupe de l'utilisateur connecté pour attribuer les droits et les dossiers */
$userId = $_COOKIE['ZWII_USER_ID'];
$courseId = $_COOKIE['ZWII_SITE_CONTENT'];
$courseId = $_GET['lang'];
$u = json_decode(file_get_contents('../../../site/data/user.json'), true);
$g = json_decode(file_get_contents('../../../site/data/profil.json'), true);
@ -609,7 +609,7 @@ $config = array(
// path_from_filemanager/test/test1/
// PS if there isn't write permission in your destination folder you must set it
//
'fixed_image_creation' => true,
'fixed_image_creation' => false,
//activate or not the creation of one or more image resized with fixed path from filemanager folder
'fixed_path_from_filemanager' => array('../../../site/file/thumb/'),
//fixed path of the image folder from the current position on upload folder

View File

@ -10,23 +10,43 @@
if (typeof (privateKey) == 'undefined') {
var privateKey = null;
};
tinymce.init({
// Classe où appliquer l'éditeur
selector: ".editorWysiwyg",
// Aperçu dans le pied de page
setup: function (ed) {
ed.on('change', function (e) {
if (ed.id === 'themeFooterText') {
setup: function (editor) {
// Événement 'change'
editor.on('change', function (e) {
if (editor.id === 'themeFooterText') {
$("#footerText").html(tinyMCE.get('themeFooterText').getContent());
}
if (ed.id === 'themeHeaderText') {
if (editor.id === 'themeHeaderText') {
$("#featureContent").html(tinyMCE.get('themeHeaderText').getContent());
}
});
// Événement 'init'
// Cette partie permet de récupérer l'attribut de zwii_site_content qui contient le code de l'espace ouvert stocké dans la session php
editor.on('init', function () {
var siteContent = document.getElementById('zwii_site_content').getAttribute('data-variable');
siteContent = siteContent === null ? 'home' : siteContent;
// Charger le contenu CSS avec siteContent
var newStylesheetUrls = [
baseUrl + "core/layout/common.css",
baseUrl + "core/vendor/tinymce/content.css",
baseUrl + "site/data/" + siteContent + "/theme.css",
baseUrl + "site/data/custom.css"
];
// Supprime les anciennes feuilles de style si nécessaire
newStylesheetUrls.forEach(function(url) {
editor.dom.loadCSS(url); // Charger les nouvelles feuilles de style
});
});
},
// Langue
language: getCookie('ZWII_UI') === null ? "fr_FR" : getCookie('ZWII_UI'),
language:"fr_FR",
// Plugins
plugins: "advlist anchor autolink autoresize autosave codemirror contextmenu colorpicker fullscreen hr image imagetools link lists media paste searchreplace tabfocus table template textcolor visualblocks nonbreaking emoticons charmap textpattern",
// Contenu du menu
@ -35,7 +55,6 @@ tinymce.init({
edit: { title: 'Edit', items: 'undo redo | cut copy paste pastetext | selectall | searchreplace' },
view: { title: 'View', items: 'code | visualaid visualblocks | fullscreen' },
insert: { title: 'Insert', items: 'image link media pageembed template inserttable | charmap emoticons hr | nonbreaking anchor' },
//format: { title: 'Format', items: 'bold italic underline strikethrough superscript subscript codeformat | styles blocks fontfamily fontsize align lineheight | forecolor backcolor | removeformat' },
tools: { title: 'Tools', items: '' },
table: { title: 'Table', items: 'inserttable | cell row column | tableprops deletetable' },
help: { title: 'Help', items: 'help' }
@ -56,7 +75,6 @@ tinymce.init({
saveCursorPosition: false, // Insert caret marker
config: { // CodeMirror config object
fullscreen: true,
/*mode: 'application/x-httpd-php',*/
lineNumbers: true,
indentUnit: 4,
mode: "htmlmixed"
@ -71,12 +89,8 @@ tinymce.init({
'addon/search/searchcursor.js',
'addon/search/search.js',
],
/*
cssFiles: [
'theme/cobalt.css',
],*/
width: 800, // Default value is 800
height: 500 // Default value is 550
width: 800,
height: 500
},
// Cibles de la target
target_list: [
@ -89,7 +103,7 @@ tinymce.init({
{ title: 'Une popup (Lity)', value: 'data-lity' },
{ title: 'Une galerie d\'images (SimpleLightbox)', value: 'gallery' }
],
// Titre des image
// Titre des images
image_title: true,
// Pages internes
link_list: baseUrl + "core/vendor/tinymce/links.php",
@ -99,7 +113,7 @@ tinymce.init({
content_css: [
baseUrl + "core/layout/common.css",
baseUrl + "core/vendor/tinymce/content.css",
baseUrl + "site/data/"+ (getCookie("ZWII_SITE_CONTENT") === null ? 'home' : getCookie("ZWII_SITE_CONTENT")) +"/theme.css",
baseUrl + "site/data/home/theme.css", // Ceci sera mis à jour dans l'événement 'init'
baseUrl + "site/data/custom.css"
],
// Classe à ajouter à la balise body dans l'iframe
@ -302,7 +316,7 @@ tinymce.init({
});
},
// Langue
language: getCookie('ZWII_UI') === null ? "fr_FR" : getCookie('ZWII_UI'),
language: "fr_FR",
// Plugins
plugins: "advlist anchor autolink autoresize autosave colorpicker contextmenu hr lists paste searchreplace tabfocus template textcolor textpattern",
// Contenu de la barre d'outils

View File

@ -1,6 +1,7 @@
@charset "UTF-8";
.zwiico-plus-circled:before { content: '\2191'; } /* '↑' */
.zwiico-square-check:before { content: '\e800'; } /* '' */
.zwiico-plus:before { content: '\e801'; } /* '' */
.zwiico-cancel:before { content: '\e802'; } /* '' */
.zwiico-help:before { content: '\e803'; } /* '' */
@ -48,7 +49,9 @@
.zwiico-right-dir:before { content: '\e82d'; } /* '' */
.zwiico-chart-line:before { content: '\e82e'; } /* '' */
.zwiico-book:before { content: '\e82f'; } /* '' */
.zwiico-square-check-empty:before { content: '\e830'; } /* '' */
.zwiico-spin:before { content: '\e831'; } /* '' */
.zwiico-tags:before { content: '\e832'; } /* '' */
.zwiico-twitter:before { content: '\f099'; } /* '' */
.zwiico-facebook:before { content: '\f09a'; } /* '' */
.zwiico-docs:before { content: '\f0c5'; } /* '' */

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,6 @@
.zwiico-plus-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#x2191;&nbsp;'); }
.zwiico-square-check { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe800;&nbsp;'); }
.zwiico-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe801;&nbsp;'); }
.zwiico-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe802;&nbsp;'); }
.zwiico-help { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe803;&nbsp;'); }
@ -47,7 +48,9 @@
.zwiico-right-dir { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe82d;&nbsp;'); }
.zwiico-chart-line { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe82e;&nbsp;'); }
.zwiico-book { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe82f;&nbsp;'); }
.zwiico-square-check-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe830;&nbsp;'); }
.zwiico-spin { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe831;&nbsp;'); }
.zwiico-tags { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe832;&nbsp;'); }
.zwiico-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf099;&nbsp;'); }
.zwiico-facebook { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf09a;&nbsp;'); }
.zwiico-docs { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0c5;&nbsp;'); }

View File

@ -11,6 +11,7 @@
}
.zwiico-plus-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#x2191;&nbsp;'); }
.zwiico-square-check { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe800;&nbsp;'); }
.zwiico-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe801;&nbsp;'); }
.zwiico-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe802;&nbsp;'); }
.zwiico-help { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe803;&nbsp;'); }
@ -58,7 +59,9 @@
.zwiico-right-dir { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe82d;&nbsp;'); }
.zwiico-chart-line { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe82e;&nbsp;'); }
.zwiico-book { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe82f;&nbsp;'); }
.zwiico-square-check-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe830;&nbsp;'); }
.zwiico-spin { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe831;&nbsp;'); }
.zwiico-tags { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe832;&nbsp;'); }
.zwiico-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf099;&nbsp;'); }
.zwiico-facebook { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf09a;&nbsp;'); }
.zwiico-docs { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0c5;&nbsp;'); }

View File

@ -1,12 +1,12 @@
@charset "UTF-8";
@font-face {
font-family: 'zwiico';
src: url('../font/zwiico.eot?8645815');
src: url('../font/zwiico.eot?8645815#iefix') format('embedded-opentype'),
url('../font/zwiico.woff2?8645815') format('woff2'),
url('../font/zwiico.woff?8645815') format('woff'),
url('../font/zwiico.ttf?8645815') format('truetype'),
url('../font/zwiico.svg?8645815#zwiico') format('svg');
src: url('../font/zwiico.eot?24931130');
src: url('../font/zwiico.eot?24931130#iefix') format('embedded-opentype'),
url('../font/zwiico.woff2?24931130') format('woff2'),
url('../font/zwiico.woff?24931130') format('woff'),
url('../font/zwiico.ttf?24931130') format('truetype'),
url('../font/zwiico.svg?24931130#zwiico') format('svg');
font-weight: normal;
font-style: normal;
}
@ -16,7 +16,7 @@
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'zwiico';
src: url('../font/zwiico.svg?8645815#zwiico') format('svg');
src: url('../font/zwiico.svg?24931130#zwiico') format('svg');
}
}
*/
@ -56,6 +56,7 @@
}
.zwiico-plus-circled:before { content: '\2191'; } /* '↑' */
.zwiico-square-check:before { content: '\e800'; } /* '' */
.zwiico-plus:before { content: '\e801'; } /* '' */
.zwiico-cancel:before { content: '\e802'; } /* '' */
.zwiico-help:before { content: '\e803'; } /* '' */
@ -103,7 +104,9 @@
.zwiico-right-dir:before { content: '\e82d'; } /* '' */
.zwiico-chart-line:before { content: '\e82e'; } /* '' */
.zwiico-book:before { content: '\e82f'; } /* '' */
.zwiico-square-check-empty:before { content: '\e830'; } /* '' */
.zwiico-spin:before { content: '\e831'; } /* '' */
.zwiico-tags:before { content: '\e832'; } /* '' */
.zwiico-twitter:before { content: '\f099'; } /* '' */
.zwiico-facebook:before { content: '\f09a'; } /* '' */
.zwiico-docs:before { content: '\f0c5'; } /* '' */

Binary file not shown.

View File

@ -8,6 +8,8 @@
<missing-glyph horiz-adv-x="1000" />
<glyph glyph-name="plus-circled" unicode="&#x2191;" d="M420 770q174 0 297-123t123-297-123-297-297-123-297 123-123 297 123 297 297 123z m52-470l200 0 0 102-200 0 0 202-102 0 0-202-202 0 0-102 202 0 0-202 102 0 0 202z" horiz-adv-x="840" />
<glyph glyph-name="square-check" unicode="&#xe800;" d="M0-150l0 1000 646 0-164-164-318 0 0-672 672 0 0 319 164 164 0-647-1000 0z m234 623l133 131 129-129 361 363 133-132-361-364-133-133z" horiz-adv-x="1000" />
<glyph glyph-name="plus" unicode="&#xe801;" d="M786 439v-107q0-22-16-38t-38-15h-232v-233q0-22-16-37t-38-16h-107q-22 0-38 16t-15 37v233h-232q-23 0-38 15t-16 38v107q0 23 16 38t38 16h232v232q0 22 15 38t38 16h107q23 0 38-16t16-38v-232h232q23 0 38-16t16-38z" horiz-adv-x="785.7" />
<glyph glyph-name="cancel" unicode="&#xe802;" d="M724 112q0-22-15-38l-76-76q-16-15-38-15t-38 15l-164 165-164-165q-16-15-38-15t-38 15l-76 76q-16 16-16 38t16 38l164 164-164 164q-16 16-16 38t16 38l76 76q16 16 38 16t38-16l164-164 164 164q16 16 38 16t38-16l76-76q15-15 15-38t-15-38l-164-164 164-164q15-15 15-38z" horiz-adv-x="785.7" />
@ -102,8 +104,12 @@
<glyph glyph-name="book" unicode="&#xe82f;" d="M915 583q22-31 10-72l-154-505q-10-36-42-60t-69-25h-515q-43 0-83 30t-55 74q-14 37-1 71 0 2 1 15t3 20q0 5-2 12t-2 11q1 6 5 12t9 13 9 13q13 21 25 51t17 51q2 6 0 17t0 16q2 6 9 15t10 13q12 20 23 51t14 51q1 5-1 17t0 16q2 7 12 17t13 13q10 14 23 47t16 54q0 4-2 14t-1 15q1 4 5 10t10 13 10 11q4 7 9 17t8 20 9 20 11 18 15 13 20 6 26-3l0-1q21 5 28 5h425q41 0 64-32t10-72l-153-506q-20-66-40-85t-72-20h-485q-15 0-21-8-6-9-1-24 14-39 81-39h515q16 0 31 9t20 23l167 550q4 13 3 32 21-8 33-24z m-594-1q-2-7 1-12t11-6h339q8 0 15 6t9 12l12 36q2 7-1 12t-12 6h-339q-7 0-14-6t-9-12z m-46-143q-3-7 1-12t11-6h339q7 0 14 6t10 12l11 36q3 7-1 13t-11 5h-339q-7 0-14-5t-10-13z" horiz-adv-x="928.6" />
<glyph glyph-name="square-check-empty" unicode="&#xe830;" d="M0-150l0 1000 1000 0 0-1000-1000 0z m164 164l672 0 0 672-672 0 0-672z" horiz-adv-x="1000" />
<glyph glyph-name="spin" unicode="&#xe831;" d="M46 144l0 0c0 0-1 0-1 0-8 18-15 37-21 55-6 19-11 38-15 58-19 99-8 203 35 298 3 6 10 8 15 5 1 0 2 0 2-1l0 0 80-59c5-3 6-9 4-14-5-12-9-25-12-37-4-13-7-26-9-40-11-67-3-137 23-201 2-5 0-10-4-13l0 0-80-56c-5-4-12-2-16 3-1 0-1 1-1 2l0 0z m120 574l0 0c0 1 0 1 0 1 15 13 30 25 46 37 16 11 33 22 51 31 89 50 192 72 297 60 6-1 10-6 10-13 0-1-1-1-1-2l0 0-31-94c-2-5-8-8-13-7-13 0-27 0-40 0-14-1-27-2-40-4-68-11-133-40-186-84-4-3-10-3-14 0l0 0-79 58c-5 3-6 11-2 16 0 0 1 1 2 1l0 0z m588 65l0 0c0 0 1 0 1 0 17-10 34-21 50-32 16-12 31-25 46-38 74-69 127-160 148-262 2-6-2-12-9-13-1 0-1 0-2 0l0 0-100 1c-5 0-10 4-11 9-3 13-8 26-12 38-5 12-10 25-17 36-31 61-78 113-137 150-5 3-6 8-5 13l0 0 31 92c2 6 9 9 15 7 1 0 2-1 2-1l0 0z m244-535l0 0c0 0 0 0 0 0-4-20-9-39-15-57-7-19-14-37-22-55-44-92-114-170-205-221-6-3-13-1-16 4 0 1-1 2-1 2l0 0-30 94c-2 6 1 12 6 14 11 7 22 15 32 23 11 9 21 18 30 27 49 48 84 109 101 176 2 5 6 8 11 8l0 0 98-1c6 0 11-5 11-11 0-1 0-2 0-3l0 0z m-438-395l0 0c0 0 0 0 0 0-20-2-40-3-60-3-20 0-40 1-59 4-102 12-198 54-276 125-5 4-5 11 0 16 0 0 1 1 1 1l0 0 81 58c5 3 12 2 16-2 10-8 20-16 32-23 11-7 22-14 34-20 62-31 131-45 200-41 6 0 10-3 12-8l0 0 29-92c2-6-1-12-7-14-1-1-2-1-3-1l0 0z" horiz-adv-x="1000" />
<glyph glyph-name="tags" unicode="&#xe832;" d="M250 600q0 30-21 51t-50 20-51-20-21-51 21-50 51-21 50 21 21 50z m595-321q0-30-20-51l-274-274q-22-21-51-21-30 0-50 21l-399 399q-21 21-36 57t-15 65v232q0 29 21 50t50 22h233q29 0 65-15t57-36l399-399q20-21 20-50z m215 0q0-30-21-51l-274-274q-22-21-51-21-20 0-33 8t-29 25l262 262q21 21 21 51 0 29-21 50l-399 399q-21 21-57 36t-65 15h125q29 0 65-15t57-36l399-399q21-21 21-50z" horiz-adv-x="1071.4" />
<glyph glyph-name="twitter" unicode="&#xf099;" d="M904 622q-37-54-90-93 0-8 0-23 0-73-21-145t-64-139-103-117-144-82-181-30q-151 0-276 81 19-2 43-2 126 0 224 77-59 1-105 36t-64 89q19-3 34-3 24 0 48 6-63 13-104 62t-41 115v2q38-21 82-23-37 25-59 64t-22 86q0 49 25 91 68-83 164-133t208-55q-5 21-5 41 0 75 53 127t127 53q79 0 132-57 61 12 115 44-21-64-80-100 52 6 104 28z" horiz-adv-x="928.6" />
<glyph glyph-name="facebook" unicode="&#xf09a;" d="M535 843v-147h-87q-48 0-65-20t-17-60v-106h164l-22-165h-142v-424h-171v424h-142v165h142v122q0 104 58 161t155 57q82 0 127-7z" horiz-adv-x="571.4" />

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
core/vendor/zwiico/fontello-2efa58ff.zip vendored Normal file

Binary file not shown.

Binary file not shown.

View File

@ -19,6 +19,30 @@
*/
// Remplace la directive htaccess
ini_set('session.use_trans_sid', FALSE);
// Crée un identifiant unique pour chaque site en fonction du nom de domaine ou autre
$siteId = md5($_SERVER['SERVER_NAME'] . $_SERVER['SCRIPT_FILENAME']); // Ou utilise un autre identifiant unique pour chaque site
// Change le nom de la session en fonction de cet identifiant
session_name('zwii_session_' . $siteId);
// Récupère dynamiquement le chemin du dossier dans lequel le script est exécuté
$scriptPath = dirname($_SERVER['SCRIPT_NAME']);
// Si le chemin est vide (ce qui peut arriver si le site est à la racine), définis-le comme '/'
if ($scriptPath === '/' || $scriptPath === '\\' || $scriptPath === '.') {
$scriptPath = '/';
}
// Définissez le chemin du cookie de session dynamiquement
session_set_cookie_params([
'lifetime' => 0,
'path' => $scriptPath, // Utilise le chemin du script pour restreindre la session à ce répertoire
'domain' => $_SERVER['SERVER_NAME'], // Domaine par défaut
'secure' => isset($_SERVER['HTTPS']), // Pour HTTPS, si nécessaire
'httponly' => true,
'samesite' => 'Lax' // Ou 'Strict' ou 'None' selon tes besoins
]);
// Démarre la session
session_start();

View File

@ -16,7 +16,7 @@
class blog extends common
{
const VERSION = '7.6';
const VERSION = '8.0';
const REALNAME = 'Blog';
const DELETE = true;
const UPDATE = '0.0';
@ -174,10 +174,12 @@ class blog extends common
$this->setData(['module', $this->getUrl(0), 'config', 'timeFormat', '%H:%M']);
$this->setData(['module', $this->getUrl(0), 'config', 'versionData', '6.5']);
}
// Version 7.4
if (version_compare($this->getData(['module', $this->getUrl(0), 'config', 'versionData']), '7.4', '<')) {
// Version 8.0
if (version_compare($this->getData(['module', $this->getUrl(0), 'config', 'versionData']), '8.0', '<')) {
$this->setData(['module', $this->getUrl(0), 'config', 'buttonBack', true]);
$this->setData(['module', $this->getUrl(0), 'config', 'versionData', '7.4']);
$this->setData(['module', $this->getUrl(0), 'config', 'showTime', true]);
$this->setData(['module', $this->getUrl(0), 'config', 'showDate', true]);
$this->setData(['module', $this->getUrl(0), 'config', 'versionData', '8.0']);
}
}
@ -196,9 +198,11 @@ class blog extends common
$feeds = new \FeedWriter\RSS2();
// En-tête
$feeds->setTitle($this->getData(['page', $this->getUrl(0), 'title']));
$feeds->setTitle($this->getData(['page', $this->getUrl(0), 'title']) ? $this->getData(['page', $this->getUrl(0), 'title']): '');
$feeds->setLink(helper::baseUrl() . $this->getUrl(0));
if ($this->getData(['page', $this->getUrl(0), 'metaDescription'])) {
$feeds->setDescription($this->getData(['page', $this->getUrl(0), 'metaDescription']));
}
$feeds->setChannelElement('language', 'fr-FR');
$feeds->setDate(date('r', time()));
$feeds->addGenerator();
@ -361,7 +365,7 @@ class blog extends common
self::$dateFormat = $this->getData(['module', $this->getUrl(0), 'config', 'dateFormat']);
self::$timeFormat = $this->getData(['module', $this->getUrl(0), 'config', 'timeFormat']);
self::$comments[] = [
helper::dateUTF8(self::$dateFormat, $comment['createdOn']) . ' - ' . helper::dateUTF8(self::$timeFormat, $comment['createdOn']),
helper::dateUTF8(self::$dateFormat, $comment['createdOn'], self::$i18nUI) . ' - ' . helper::dateUTF8(self::$timeFormat, $comment['createdOn'], self::$i18nUI),
$comment['content'],
$comment['userId'] ? $this->getData(['user', $comment['userId'], 'firstname']) . ' ' . $this->getData(['user', $comment['userId'], 'lastname']) : $comment['author'],
$buttonApproval,
@ -530,7 +534,7 @@ class blog extends common
'<a href="' . helper::baseurl() . $this->getUrl(0) . '/' . $articleIds[$i] . '" target="_blank" >' .
$this->getData(['module', $this->getUrl(0), 'posts', $articleIds[$i], 'title']) .
'</a>',
helper::dateUTF8(self::$dateFormat, $this->getData(['module', $this->getUrl(0), 'posts', $articleIds[$i], 'publishedOn'])) . ' - ' . helper::dateUTF8(self::$timeFormat, $this->getData(['module', $this->getUrl(0), 'posts', $articleIds[$i], 'publishedOn'])),
helper::dateUTF8(self::$dateFormat, $this->getData(['module', $this->getUrl(0), 'posts', $articleIds[$i], 'publishedOn']), self::$i18nUI) . ' - ' . helper::dateUTF8(self::$timeFormat, $this->getData(['module', $this->getUrl(0), 'posts', $articleIds[$i], 'publishedOn']), self::$i18nUI),
self::$states[$this->getData(['module', $this->getUrl(0), 'posts', $articleIds[$i], 'state'])],
// Bouton pour afficher les commentaires de l'article
template::button('blogConfigComment' . $articleIds[$i], [
@ -576,7 +580,9 @@ class blog extends common
'itemsperPage' => $this->getInput('blogOptionItemsperPage', helper::FILTER_INT, true),
'dateFormat' => $this->getInput('blogOptionDateFormat'),
'timeFormat' => $this->getInput('blogOptionTimeFormat'),
'buttonBack' => $this->getInput('newsOptionButtonBack'),
'buttonBack' => $this->getInput('blogOptionButtonBack', helper::FILTER_BOOLEAN),
'showDate' => $this->getInput('blogOptionShowDate', helper::FILTER_BOOLEAN),
'showTime' => $this->getInput('blogOptionShowTime', helper::FILTER_BOOLEAN),
'versionData' => $this->getData(['module', $this->getUrl(0), 'config', 'versionData']),
]
]);
@ -594,7 +600,6 @@ class blog extends common
]);
}
/**
* Suppression
*/
@ -747,7 +752,7 @@ class blog extends common
) {
// Check la captcha
if (
$this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')
$this->isConnected() === false
//AND $this->getInput('blogArticlecaptcha', helper::FILTER_INT) !== $this->getInput('blogArticlecaptchaFirstNumber', helper::FILTER_INT) + $this->getInput('blogArticlecaptchaSecondNumber', helper::FILTER_INT))
and password_verify($this->getInput('blogArticleCaptcha', helper::FILTER_INT), $this->getInput('blogArticleCaptchaResult')) === false
) {
@ -755,7 +760,7 @@ class blog extends common
}
// Crée le commentaire
$commentId = helper::increment(uniqid(), $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'comment']));
$content = $this->getInput('blogArticleContent', false);
$content = $this->getInput('blogArticleContent', null, true);
$this->setData([
'module', $this->getUrl(0),
'posts', $this->getUrl(1),
@ -829,7 +834,7 @@ class blog extends common
// Signature de l'article
self::$articleSignature = $this->signature($this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'userId']));
// Signature du commentaire édité
if ($this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')) {
if ($this->isConnected() === true) {
self::$editCommentSignature = $this->signature($this->getUser('id'));
}
// Commentaires en fonction de la pagination

View File

@ -1,3 +1,16 @@
# Version 8.00
- Ajoute deux nouvelles options pour afficher ou masquer la date et l'heure de l'article.
- Corrige un bug d'affichage des articles lorsque le thème Moderne est sélectionné.
- Corrige un bug dans la méthode de tronquage de l'article, nécessite Zwii 13.5
- Corrige un mauvais format de la propriété buttonBack non stockée au type booléen.
# Version 7.11
- Le sélecteur de fichier affiche par défaut le chemin vers le fichier présent dans le champ.
# Version 7.10
- Empêche la validation d'un commentaire lorsque le contenu est vide.
# Versions 7.8 - 7.9
- Le flux RSS ne fonctionne pas si les méta de la page sont vides.
# Version 7.7
- Contrôle de la variable de session liée au contenu. Evite des erreurs lorsque plusieurs onglets sont ouverts.
# Version 7.6
- Mise à jour RSS Feed
# Version 7.5

View File

@ -1 +1 @@
{"name":"blog","realName":"Blog","version":"7.2","update":"0.0","delete":true,"dataDirectory":""}
{"name":"blog","realName":"Blog","version":"7.12","update":"0.0","delete":true,"dataDirectory":""}

0
module/blog/ressource/feed-icon-16.gif Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 652 B

After

Width:  |  Height:  |  Size: 652 B

0
module/blog/vendor/FeedWriter/ATOM.php vendored Normal file → Executable file
View File

0
module/blog/vendor/FeedWriter/Feed.php vendored Normal file → Executable file
View File

0
module/blog/vendor/FeedWriter/InvalidOperationException.php vendored Normal file → Executable file
View File

0
module/blog/vendor/FeedWriter/Item.php vendored Normal file → Executable file
View File

0
module/blog/vendor/FeedWriter/README.md vendored Normal file → Executable file
View File

0
module/blog/vendor/FeedWriter/RSS1.php vendored Normal file → Executable file
View File

0
module/blog/vendor/FeedWriter/RSS2.php vendored Normal file → Executable file
View File

Some files were not shown because too many files have changed in this diff Show More