ZwiiCMS/core/module/user/user.php

1442 lines
51 KiB
PHP
Raw Normal View History

2018-04-02 08:29:19 +02:00
<?php
/**
* 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
2021-02-17 13:49:58 +01:00
* @author Frédéric Tempez <frederic.tempez@outlook.com>
2024-01-14 19:31:28 +01:00
* @copyright Copyright (C) 2018-2024, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
2018-04-02 08:29:19 +02:00
*/
2022-09-29 08:45:59 +02:00
class user extends common
{
2018-04-02 08:29:19 +02:00
public static $actions = [
'add' => self::GROUP_ADMIN,
'delete' => self::GROUP_ADMIN,
2020-10-04 17:14:15 +02:00
'import' => self::GROUP_ADMIN,
'index' => self::GROUP_ADMIN,
2023-02-10 22:27:08 +01:00
'template' => self::GROUP_ADMIN,
2018-04-02 08:29:19 +02:00
'edit' => self::GROUP_MEMBER,
2020-10-04 17:14:15 +02:00
'logout' => self::GROUP_MEMBER,
2018-04-02 08:29:19 +02:00
'forgot' => self::GROUP_VISITOR,
'login' => self::GROUP_VISITOR,
2023-02-10 22:27:08 +01:00
'reset' => self::GROUP_VISITOR,
2023-03-31 17:02:56 +02:00
'profil' => self::GROUP_ADMIN,
'profilEdit' => self::GROUP_ADMIN,
'profilAdd' => self::GROUP_ADMIN,
2023-06-28 17:52:13 +02:00
'profilDelete' => self::GROUP_ADMIN,
2018-04-02 08:29:19 +02:00
];
2018-04-02 08:29:19 +02:00
public static $users = [];
2020-07-10 16:57:21 +02:00
//Paramètres pour choix de la signature
2020-07-02 19:53:52 +02:00
public static $signature = [
2020-07-10 16:57:21 +02:00
self::SIGNATURE_ID => 'Identifiant',
self::SIGNATURE_PSEUDO => 'Pseudo',
self::SIGNATURE_FIRSTLASTNAME => 'Prénom Nom',
self::SIGNATURE_LASTFIRSTNAME => 'Nom Prénom'
2020-07-02 19:53:52 +02:00
];
public static $userId = '';
2023-03-10 15:02:49 +01:00
public static $userGroups = [];
2023-05-02 23:37:51 +02:00
public static $userProfils = [];
public static $userProfilsComments = [];
2023-05-02 23:37:51 +02:00
2020-08-10 09:10:20 +02:00
public static $userLongtime = false;
2020-07-02 19:53:52 +02:00
2020-10-05 16:56:30 +02:00
public static $separators = [
';' => ';',
',' => ',',
':' => ':'
];
public static $languagesInstalled = [];
2023-03-22 17:46:08 +01:00
public static $sharePath = [
'/site/file/source/'
];
2023-03-31 17:02:56 +02:00
public static $groupProfils = [
self::GROUP_MEMBER => 'Membre',
2023-07-25 11:12:35 +02:00
self::GROUP_EDITOR => 'Éditeur'
2023-03-31 17:02:56 +02:00
];
public static $listModules = [];
2023-08-05 14:50:05 +02:00
public static $profils = [];
2023-10-09 19:38:35 +02:00
public static $alphabet = [];
2023-11-30 13:30:10 +01:00
public static $usersGroups = [
'all' => 'Tous'
2023-10-09 19:38:35 +02:00
];
2018-04-02 08:29:19 +02:00
/**
* Ajout
*/
2022-09-29 08:45:59 +02:00
public function add()
{
2018-04-02 08:29:19 +02:00
// Soumission du formulaire
if (
2023-06-30 09:09:39 +02:00
$this->getUser('permission', __CLASS__, __FUNCTION__) === true &&
$this->isPost()
) {
2022-09-29 08:45:59 +02:00
$check = true;
2018-04-02 08:29:19 +02:00
// L'identifiant d'utilisateur est indisponible
$userId = $this->getInput('userAddId', helper::FILTER_ID, true);
2022-09-29 08:45:59 +02:00
if ($this->getData(['user', $userId])) {
2018-04-02 08:29:19 +02:00
self::$inputNotices['userAddId'] = 'Identifiant déjà utilisé';
2022-09-29 08:45:59 +02:00
$check = false;
2018-04-02 08:29:19 +02:00
}
// Double vérification pour le mot de passe
2022-09-29 08:45:59 +02:00
if ($this->getInput('userAddPassword', helper::FILTER_STRING_SHORT, true) !== $this->getInput('userAddConfirmPassword', helper::FILTER_STRING_SHORT, true)) {
2018-04-02 08:29:19 +02:00
self::$inputNotices['userAddConfirmPassword'] = 'Incorrect';
$check = false;
}
2018-04-02 08:29:19 +02:00
// Crée l'utilisateur
$userFirstname = $this->getInput('userAddFirstname', helper::FILTER_STRING_SHORT, true);
$userLastname = $this->getInput('userAddLastname', helper::FILTER_STRING_SHORT, true);
$userMail = $this->getInput('userAddMail', helper::FILTER_MAIL, true);
2020-09-24 18:01:34 +02:00
2023-05-02 23:37:51 +02:00
// Profil
$group = $this->getInput('userAddGroup', helper::FILTER_INT, true);
$profil = 0;
if ($group === 1 || $group === 2) {
2023-05-02 23:37:51 +02:00
$profil = $this->getInput('userAddProfil' . $group, helper::FILTER_INT);
}
2020-09-24 11:34:26 +02:00
// Stockage des données
$this->setData([
'user',
$userId,
[
'firstname' => $userFirstname,
'forgot' => 0,
2023-05-02 23:37:51 +02:00
'group' => $group,
'profil' => $profil,
2020-09-24 11:34:26 +02:00
'lastname' => $userLastname,
2020-10-31 17:55:54 +01:00
'pseudo' => $this->getInput('userAddPseudo', helper::FILTER_STRING_SHORT, true),
'signature' => $this->getInput('userAddSignature', helper::FILTER_INT, true),
2020-09-24 11:34:26 +02:00
'mail' => $userMail,
'password' => $this->getInput('userAddPassword', helper::FILTER_PASSWORD, true),
2023-05-02 23:37:51 +02:00
'connectFail' => null,
'connectTimeout' => null,
'accessUrl' => null,
'accessTimer' => null,
'accessCsrf' => null,
'language' => $this->getInput('userAddLanguage', helper::FILTER_STRING_SHORT),
2024-02-11 18:53:57 +01:00
'tags' => $this->getInput('userAddTags', helper::FILTER_STRING_SHORT),
2020-09-24 11:34:26 +02:00
]
]);
// Envoie le mail
2018-04-02 08:29:19 +02:00
$sent = true;
2022-09-29 08:45:59 +02:00
if ($this->getInput('userAddSendMail', helper::FILTER_BOOLEAN) && $check === true) {
2018-04-02 08:29:19 +02:00
$sent = $this->sendMail(
$userMail,
2023-10-09 19:38:35 +02:00
'Compte créé sur ' . $this->getData(['config', 'title']),
2018-04-02 08:29:19 +02:00
'Bonjour <strong>' . $userFirstname . ' ' . $userLastname . '</strong>,<br><br>' .
2023-10-09 19:38:35 +02:00
'Un administrateur vous a créé un compte sur le site ' . $this->getData(['config', 'title']) . '. Vous trouverez ci-dessous les détails de votre compte.<br><br>' .
2023-03-10 10:19:22 +01:00
'<strong>Identifiant du compte :</strong> ' . $this->getInput('userAddId') . '<br>' .
'<small>Nous ne conservons pas les mots de passe, en conséquence nous vous conseillons de conserver ce message tant que vous ne vous êtes pas connecté. Vous pourrez modifier votre mot de passe après votre première connexion.</small>',
2023-02-23 16:02:06 +01:00
null,
2023-07-16 18:47:50 +02:00
$this->getData(['config', 'smtp', 'from'])
2018-04-02 08:29:19 +02:00
);
}
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . 'user',
2022-10-11 10:33:44 +02:00
'notification' => $sent === true ? helper::translate('Utilisateur créé') : $sent,
'state' => $sent === true ? true : null
2018-04-02 08:29:19 +02:00
]);
}
2023-05-02 23:37:51 +02:00
// Langues disponibles pour l'interface de l'utilisateur
2023-05-02 23:37:51 +02:00
self::$languagesInstalled = $this->getData(['language']);
if (self::$languagesInstalled) {
foreach (self::$languagesInstalled as $lang => $datas) {
self::$languagesInstalled[$lang] = self::$languages[$lang];
}
}
2023-05-02 23:37:51 +02:00
// Profils disponibles
foreach ($this->getData(['profil']) as $profilId => $profilData) {
2023-05-29 18:43:30 +02:00
if ($profilId < self::GROUP_MEMBER) {
continue;
}
if ($profilId === self::GROUP_ADMIN) {
self::$userProfils[$profilId][self::GROUP_ADMIN] = $profilData['name'];
self::$userProfilsComments[$profilId][self::GROUP_ADMIN] = $profilData['comment'];
2023-05-02 23:37:51 +02:00
continue;
}
foreach ($profilData as $key => $value) {
self::$userProfils[$profilId][$key] = $profilData[$key]['name'];
2023-05-29 09:18:32 +02:00
self::$userProfilsComments[$profilId][$key] = $profilData[$key]['name'] . ' : ' . $profilData[$key]['comment'];
}
}
2023-05-02 23:37:51 +02:00
2018-04-02 08:29:19 +02:00
// Valeurs en sortie
$this->addOutput([
2022-10-02 10:59:42 +02:00
'title' => helper::translate('Nouvel utilisateur'),
2018-04-02 08:29:19 +02:00
'view' => 'add'
]);
}
/**
* Suppression
*/
2022-09-29 08:45:59 +02:00
public function delete()
{
2018-04-02 08:29:19 +02:00
// Accès refusé
2022-09-29 08:45:59 +02:00
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true ||
2018-04-02 08:29:19 +02:00
// L'utilisateur n'existe pas
2019-01-15 13:51:30 +01:00
$this->getData(['user', $this->getUrl(2)]) === null
2018-04-02 08:29:19 +02:00
// Groupe insuffisant
2023-07-05 18:04:42 +02:00
and ($this->getUrl('group') < self::GROUP_EDITOR)
2018-04-02 08:29:19 +02:00
) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
}
// Bloque la suppression de son propre compte
2022-09-29 08:45:59 +02:00
elseif ($this->getUser('id') === $this->getUrl(2)) {
2018-04-02 08:29:19 +02:00
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . 'user',
2022-10-11 10:33:44 +02:00
'notification' => helper::translate('Impossible de supprimer votre propre compte')
2018-04-02 08:29:19 +02:00
]);
}
// Suppression
else {
2019-01-15 13:51:30 +01:00
$this->deleteData(['user', $this->getUrl(2)]);
2018-04-02 08:29:19 +02:00
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . 'user',
2022-10-11 10:33:44 +02:00
'notification' => helper::translate('Utilisateur supprimé'),
2018-04-02 08:29:19 +02:00
'state' => true
]);
}
}
/**
* Édition
*/
2022-09-29 08:45:59 +02:00
public function edit()
{
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true
2018-04-02 08:29:19 +02:00
) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
} else {
if (
// L'utilisateur n'existe pas
$this->getData(['user', $this->getUrl(2)]) === null
// Droit d'édition
and (
// Impossible de s'auto-éditer
($this->getUser('id') === $this->getUrl(2)
and $this->getUrl('group') <= self::GROUP_VISITOR
)
// Impossible d'éditer un autre utilisateur
2023-07-05 18:04:42 +02:00
or ($this->getUrl('group') < self::GROUP_EDITOR)
)
) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
}
// Accès autorisé
else {
// Soumission du formulaire
if (
$this->isPost()
) {
$oldPassword = $this->getData(['user', $this->getUrl(2), 'password']);
// Double vérification pour le mot de passe
2023-11-07 21:48:37 +01:00
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']))) {
// La confirmation correspond au mot de passe
if ($this->getInput('userEditNewPassword') === $this->getInput('userEditConfirmPassword')) {
$newPassword = $this->getInput('userEditNewPassword', helper::FILTER_PASSWORD, true);
// Déconnexion de l'utilisateur si il change le mot de passe de son propre compte
if ($this->getUser('id') === $this->getUrl(2)) {
helper::deleteCookie('ZWII_USER_ID');
helper::deleteCookie('ZWII_USER_PASSWORD');
}
} else {
self::$inputNotices['userEditConfirmPassword'] = helper::translate('Incorrect');
}
} else {
2023-11-07 21:48:37 +01:00
self::$inputNotices['userEditOldPassword'] = helper::translate('Incorrect');
}
}
} else {
if (
2023-12-01 16:25:45 +01:00
!empty($this->getInput('userEditNewPassword'))
&& $this->getInput('userEditNewPassword') === $this->getInput('userEditConfirmPassword')
) {
2023-11-08 09:50:39 +01:00
$newPassword = $this->getInput('userEditNewPassword', helper::FILTER_PASSWORD);
2023-11-07 21:48:37 +01:00
// Déconnexion de l'utilisateur si il change le mot de passe de son propre compte
if ($this->getUser('id') === $this->getUrl(2)) {
helper::deleteCookie('ZWII_USER_ID');
helper::deleteCookie('ZWII_USER_PASSWORD');
2018-04-02 08:29:19 +02:00
}
}
}
// Modification du groupe
if (
$this->getUser('group') === self::GROUP_ADMIN
and $this->getUrl(2) !== $this->getUser('id')
) {
$newGroup = $this->getInput('userEditGroup', helper::FILTER_INT, true);
2022-09-29 08:45:59 +02:00
} else {
$newGroup = $this->getData(['user', $this->getUrl(2), 'group']);
2018-04-02 08:29:19 +02:00
}
// Modification de nom Prénom
if ($this->getUser('group') === self::GROUP_ADMIN) {
$newfirstname = $this->getInput('userEditFirstname', helper::FILTER_STRING_SHORT, true);
$newlastname = $this->getInput('userEditLastname', helper::FILTER_STRING_SHORT, true);
} else {
$newfirstname = $this->getData(['user', $this->getUrl(2), 'firstname']);
$newlastname = $this->getData(['user', $this->getUrl(2), 'lastname']);
}
// Profil
$profil = 0;
if ($newGroup === 1 || $newGroup === 2) {
$profil = $this->getInput('userEditProfil' . $newGroup, helper::FILTER_INT);
}
// Modifie l'utilisateur
$this->setData([
'user',
$this->getUrl(2),
[
'firstname' => $newfirstname,
'forgot' => 0,
'group' => $newGroup,
'profil' => $profil,
'lastname' => $newlastname,
'pseudo' => $this->getInput('userEditPseudo', helper::FILTER_STRING_SHORT, true),
'signature' => $this->getInput('userEditSignature', helper::FILTER_INT, true),
'mail' => $this->getInput('userEditMail', helper::FILTER_MAIL, true),
'password' => empty($newPassword) ? $oldPassword : $newPassword,
'connectFail' => $this->getData(['user', $this->getUrl(2), 'connectFail']),
'connectTimeout' => $this->getData(['user', $this->getUrl(2), 'connectTimeout']),
'accessUrl' => $this->getData(['user', $this->getUrl(2), 'accessUrl']),
'accessTimer' => $this->getData(['user', $this->getUrl(2), 'accessTimer']),
'accessCsrf' => $this->getData(['user', $this->getUrl(2), 'accessCsrf']),
'files' => $this->getInput('userEditFiles', helper::FILTER_BOOLEAN),
'language' => $this->getInput('userEditLanguage', helper::FILTER_STRING_SHORT),
2023-11-30 13:30:10 +01:00
'tags' => $this->getInput('userEditTags', helper::FILTER_STRING_SHORT),
2024-09-28 18:42:41 +02:00
'authKey' => $this->getData(['user', $this->getUrl(2), 'authKey']),
]
]);
// Redirection spécifique si l'utilisateur change son mot de passe
if ($this->getUser('id') === $this->getUrl(2) and $this->getInput('userEditNewPassword')) {
$redirect = helper::baseUrl() . 'user/login/' . str_replace('/', '_', $this->getUrl());
}
// Redirection si retour en arrière possible
2023-07-28 11:12:58 +02:00
elseif ($this->getUser('group') === self::GROUP_ADMIN) {
$redirect = helper::baseUrl() . 'user';
}
// Redirection normale
else {
$redirect = helper::baseUrl();
}
// Valeurs en sortie
$this->addOutput([
'redirect' => $redirect,
'notification' => helper::translate('Modifications enregistrées'),
'state' => true
]);
2018-04-02 08:29:19 +02:00
}
// Langues disponibles pour l'interface de l'utilisateur
self::$languagesInstalled = $this->getData(['language']);
if (self::$languagesInstalled) {
foreach (self::$languagesInstalled as $lang => $datas) {
self::$languagesInstalled[$lang] = self::$languages[$lang];
}
2018-04-02 08:29:19 +02:00
}
// Profils disponibles
foreach ($this->getData(['profil']) as $profilId => $profilData) {
if ($profilId < self::GROUP_MEMBER) {
continue;
}
if ($profilId === self::GROUP_ADMIN) {
self::$userProfils[$profilId][self::GROUP_ADMIN] = $profilData['name'];
self::$userProfilsComments[$profilId][self::GROUP_ADMIN] = $profilData['comment'];
continue;
}
foreach ($profilData as $key => $value) {
self::$userProfils[$profilId][$key] = $profilData[$key]['name'];
self::$userProfilsComments[$profilId][$key] = $profilData[$key]['name'] . ' : ' . $profilData[$key]['comment'];
}
2018-04-02 08:29:19 +02:00
}
2018-04-02 08:29:19 +02:00
// Valeurs en sortie
$this->addOutput([
'title' => $this->getData(['user', $this->getUrl(2), 'firstname']) . ' ' . $this->getData(['user', $this->getUrl(2), 'lastname']),
'view' => 'edit'
2018-04-02 08:29:19 +02:00
]);
}
}
}
/**
* Mot de passe perdu
*/
2022-09-29 08:45:59 +02:00
public function forgot()
{
2018-04-02 08:29:19 +02:00
// Soumission du formulaire
if (
$this->isPost()
) {
2018-04-02 08:29:19 +02:00
$userId = $this->getInput('userForgotId', helper::FILTER_ID, true);
2024-01-18 18:33:12 +01:00
$sent = false;
2022-09-29 08:45:59 +02:00
if ($this->getData(['user', $userId])) {
2018-04-02 08:29:19 +02:00
// 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])));
// Envoi le mail
$sent = $this->sendMail(
$this->getData(['user', $userId, 'mail']),
'Réinitialisation de votre mot de passe',
'Bonjour <strong>' . $this->getData(['user', $userId, 'firstname']) . ' ' . $this->getData(['user', $userId, 'lastname']) . '</strong>,<br><br>' .
2023-03-10 10:19:22 +01:00
'Vous avez demandé à changer le mot de passe lié à votre compte. Vous trouverez ci-dessous un lien vous permettant de modifier celui-ci.<br><br>' .
'<a href="' . helper::baseUrl() . 'user/reset/' . $userId . '/' . $uniqId . '" target="_blank">' . helper::baseUrl() . 'user/reset/' . $userId . '/' . $uniqId . '</a><br><br>' .
'<small>Si nous n\'avez pas demandé à réinitialiser votre mot de passe, veuillez ignorer ce mail.</small>',
2023-02-23 16:02:06 +01:00
null,
2023-07-16 18:47:50 +02:00
$this->getData(['config', 'smtp', 'from'])
2018-04-02 08:29:19 +02:00
);
2018-04-02 08:29:19 +02:00
}
// L'utilisateur n'existe pas, on ne le précise pas
// Valeurs en sortie
$this->addOutput([
'notification' => helper::translate('Un mail a été envoyé pour confirmer la réinitialisation'),
'state' => ($sent === true ? true : null)
]);
2018-04-02 08:29:19 +02:00
}
// Valeurs en sortie
$this->addOutput([
2022-10-02 10:59:42 +02:00
'title' => helper::translate('Mot de passe oublié'),
'view' => 'forgot',
2018-04-02 08:29:19 +02:00
'display' => self::DISPLAY_LAYOUT_LIGHT,
]);
}
/**
* Liste des utilisateurs
*/
2022-09-29 08:45:59 +02:00
public function index()
{
2023-10-09 19:38:35 +02:00
// Liste des groupes et des profils
2023-11-30 13:30:10 +01:00
$usersGroups = $this->getData(['profil']);
foreach ($usersGroups as $groupId => $groupValue) {
2023-10-09 19:38:35 +02:00
switch ($groupId) {
case "-1":
case "0":
2023-10-12 19:03:40 +02:00
break;
2023-10-09 19:38:35 +02:00
case "3":
2023-11-30 13:30:10 +01:00
self::$usersGroups['30'] = 'Administrateur';
$profils['30'] = 0;
2023-10-09 19:38:35 +02:00
break;
case "1":
case "2":
foreach ($groupValue as $profilId => $profilValue) {
if ($profilId) {
2023-11-30 13:30:10 +01:00
self::$usersGroups[$groupId . $profilId] = sprintf(helper::translate('Groupe %s - Profil %s'), self::$groupPublics[$groupId], $profilValue['name']);
$profils[$groupId . $profilId] = 0;
2023-10-09 19:38:35 +02:00
}
}
}
}
// Liste alphabétique
self::$alphabet = range('A', 'Z');
$alphabet = range('A', 'Z');
self::$alphabet = array_combine($alphabet, self::$alphabet);
2023-11-30 13:30:10 +01:00
self::$alphabet = array_merge(['all' => 'Toute'], self::$alphabet);
2023-10-09 19:38:35 +02:00
// Liste des membres
$userIdsLastNames = helper::arrayColumn($this->getData(['user']), 'lastname');
ksort($userIdsLastNames);
foreach ($userIdsLastNames as $userId => $userLastNames) {
if ($this->getData(['user', $userId, 'group'])) {
2023-11-30 13:30:10 +01:00
// Compte les rôles
2023-11-30 14:26:19 +01:00
if (isset($profils[$this->getData(['user', $userId, 'group']) . $this->getData(['user', $userId, 'profil'])])) {
$profils[$this->getData(['user', $userId, 'group']) . $this->getData(['user', $userId, 'profil'])]++;
}
2023-11-30 13:30:10 +01:00
2023-10-09 19:38:35 +02:00
// Filtres
if ($this->isPost()) {
// 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('userFilterGroup', helper::FILTER_INT) > 0
&& $this->getInput('userFilterGroup', helper::FILTER_STRING_SHORT) !== $group . $profil
)
continue;
// Première lettre du prénom
if (
$this->getInput('userFilterFirstName', helper::FILTER_STRING_SHORT) !== 'all'
&& $this->getInput('userFilterFirstName', helper::FILTER_STRING_SHORT) !== strtoupper(substr($firstName, 0, 1))
)
continue;
// Première lettre du nom
if (
$this->getInput('userFilterLastName', helper::FILTER_STRING_SHORT) !== 'all'
&& $this->getInput('userFilterLastName', helper::FILTER_STRING_SHORT) !== strtoupper(substr($lastName, 0, 1))
)
continue;
}
2023-11-30 13:30:10 +01:00
2023-10-09 19:38:35 +02:00
// Formatage de la liste
self::$users[] = [
$userId,
2023-10-09 19:38:35 +02:00
$this->getData(['user', $userId, 'firstname']) . ' ' . $userLastNames,
helper::translate(self::$groups[(int) $this->getData(['user', $userId, 'group'])]),
empty($this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name']))
2023-11-07 11:25:41 +01:00
? helper::translate(self::$groups[(int) $this->getData(['user', $userId, 'group'])])
: $this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name']),
2023-11-30 13:30:10 +01:00
$this->getData(['user', $userId, 'tags']),
template::button('userEdit' . $userId, [
2023-06-19 19:46:00 +02:00
'href' => helper::baseUrl() . 'user/edit/' . $userId,
2022-02-18 12:43:48 +01:00
'value' => template::ico('pencil'),
2022-10-02 10:59:42 +02:00
'help' => 'Éditer'
]),
template::button('userDelete' . $userId, [
'class' => 'userDelete buttonRed',
2023-06-19 19:46:00 +02:00
'href' => helper::baseUrl() . 'user/delete/' . $userId,
'value' => template::ico('trash'),
2022-09-05 09:00:23 +02:00
'help' => 'Supprimer'
])
];
2023-11-30 13:30:10 +01:00
}
}
// 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] . ')';
}
2018-04-02 08:29:19 +02:00
}
2023-05-02 23:37:51 +02:00
2018-04-02 08:29:19 +02:00
// Valeurs en sortie
$this->addOutput([
2022-10-02 10:59:42 +02:00
'title' => helper::translate('Utilisateurs'),
2023-11-30 13:52:22 +01:00
'view' => 'index',
'vendor' => [
'datatables'
]
2018-04-02 08:29:19 +02:00
]);
}
2023-03-10 10:19:22 +01:00
/**
* Table des groupes
*/
2023-03-31 17:02:56 +02:00
public function profil()
2023-03-10 10:19:22 +01:00
{
2023-03-29 19:28:03 +02:00
2023-12-01 16:25:45 +01:00
// Ne pas supprimer un profil utililsé
// recherche les membres du groupe
$groups = helper::arrayColumn($this->getData(['user']), 'group');
$groups = array_keys($groups, $this->getUrl(2));
$profilUsed = true;
// Stoppe si le profil est affecté
foreach ($groups as $userId) {
if ((string) $this->getData(['user', $userId, 'profil']) === $this->getUrl(3)) {
2024-08-24 17:29:17 +02:00
$profilUsed = false;
2023-12-01 16:25:45 +01:00
}
}
foreach ($this->getData(['profil']) as $groupId => $groupData) {
2023-03-29 19:28:03 +02:00
// Membres sans permissions spécifiques
if (
2023-03-31 14:36:19 +02:00
$groupId == self::GROUP_BANNED ||
$groupId == self::GROUP_VISITOR ||
$groupId == self::GROUP_ADMIN
2023-03-29 19:28:03 +02:00
) {
self::$userGroups[$groupId] = [
$groupId,
helper::translate($groupData['name']),
nl2br(helper::translate($groupData['comment'])),
2023-03-31 17:02:56 +02:00
template::button('profilEdit' . $groupId, [
2023-03-29 19:28:03 +02:00
'value' => template::ico('pencil'),
'help' => 'Éditer',
'disabled' => $groupData['readonly'],
]),
template::button('profilDelete' . $groupId, [
2023-03-29 19:28:03 +02:00
'value' => template::ico('trash'),
'help' => 'Supprimer',
'disabled' => $groupData['readonly'],
])
2023-05-02 23:37:51 +02:00
];
2023-03-31 14:36:19 +02:00
} elseif (
$groupId == self::GROUP_MEMBER ||
2023-07-05 18:04:42 +02:00
$groupId == self::GROUP_EDITOR
2023-03-31 14:36:19 +02:00
) {
2023-03-29 19:28:03 +02:00
// Enumérer les sous groupes MEMBER et MODERATOR
2023-08-05 14:50:05 +02:00
foreach ($groupData as $profilId => $profilData) {
self::$userGroups[$groupId . '.' . $profilId] = [
$groupId . '-' . $profilId,
2023-10-09 19:38:35 +02:00
helper::translate(self::$groups[$groupId]) . '<br />Profil : ' . helper::translate($profilData['name']),
nl2br(helper::translate($profilData['comment'])),
2023-08-05 14:50:05 +02:00
template::button('profilEdit' . $groupId . $profilId, [
'href' => helper::baseUrl() . 'user/profilEdit/' . $groupId . '/' . $profilId,
2023-03-29 19:28:03 +02:00
'value' => template::ico('pencil'),
'help' => 'Éditer',
2023-08-08 17:44:44 +02:00
'disabled' => $profilData['readonly'],
2023-03-29 19:28:03 +02:00
]),
2023-08-05 14:50:05 +02:00
template::button('profilDelete' . $groupId . $profilId, [
'class' => 'profilDelete buttonRed',
2023-08-05 14:50:05 +02:00
'href' => helper::baseUrl() . 'user/profilDelete/' . $groupId . '/' . $profilId,
2023-03-29 19:28:03 +02:00
'value' => template::ico('trash'),
'help' => 'Supprimer',
2023-12-01 16:25:45 +01:00
'disabled' => $profilData['permanent'] && $profilUsed,
2023-03-29 19:28:03 +02:00
])
];
}
}
2023-03-10 10:19:22 +01:00
}
// Valeurs en sortie
$this->addOutput([
2023-03-31 17:02:56 +02:00
'title' => helper::translate('Profils des groupes'),
'view' => 'profil'
2023-03-10 10:19:22 +01:00
]);
}
2023-03-10 15:02:49 +01:00
/**
* Edition d'un groupe
*/
2023-03-31 17:02:56 +02:00
public function profilEdit()
2023-03-10 15:02:49 +01:00
{
2023-03-16 11:38:46 +01:00
2023-03-10 17:23:05 +01:00
// Soumission du formulaire
if (
2023-06-30 09:09:39 +02:00
$this->getUser('permission', __CLASS__, __FUNCTION__) === true &&
$this->isPost()
) {
2023-08-05 14:50:05 +02:00
// Effacer les données du numéro de profil ancien
$group = $this->getInput('profilEditGroup', helper::FILTER_STRING_SHORT, true);
2023-10-05 18:58:09 +02:00
// Les profils 1 sont désactivés dans le formulaire
2023-10-09 19:38:35 +02:00
$profil = empty($this->getInput('profilEditProfil')) ? '1' : $this->getInput('profilEditProfil');
2023-08-05 14:50:05 +02:00
$oldProfil = $this->getInput('profilEditOldProfil', helper::FILTER_STRING_SHORT);
// Gère le chemin
$fileManager = $this->getInput('profilEditFileManager', helper::FILTER_BOOLEAN);
$path = $this->getInput('profilEditPath');
2023-10-09 19:38:35 +02:00
if (
$group < self::GROUP_MEMBER
2023-10-09 19:38:35 +02:00
&& $fileManager
&& empty($path)
2023-10-09 19:38:35 +02:00
) {
$fileManager = false;
}
2023-12-01 15:45:59 +01:00
// Effacer l'ancien profil si le rang a changé
2023-12-01 15:20:24 +01:00
if (
$profil !== $oldProfil &&
$this->deleteData(['profil', $group, $oldProfil])
) {
2023-08-05 14:50:05 +02:00
$this->deleteData(['profil', $group, $oldProfil]);
}
// Données du formulaire
$data = [
'name' => $this->getInput('profilEditName', helper::FILTER_STRING_SHORT, true),
'readonly' => false,
2023-12-01 15:45:59 +01:00
'permanent' => $profil === '1' ? true : false,
'comment' => $this->getInput('profilEditComment', helper::FILTER_STRING_SHORT, true),
'filemanager' => $fileManager,
'file' => [
'download' => $this->getInput('profilEditDownload', helper::FILTER_BOOLEAN),
'edit' => $this->getInput('profilEditEdit', helper::FILTER_BOOLEAN),
'create' => $this->getInput('profilEditCreate', helper::FILTER_BOOLEAN),
'rename' => $this->getInput('profilEditRename', helper::FILTER_BOOLEAN),
'upload' => $this->getInput('profilEditUpload', helper::FILTER_BOOLEAN),
'delete' => $this->getInput('profilEditDelete', helper::FILTER_BOOLEAN),
'preview' => $this->getInput('profilEditPreview', helper::FILTER_BOOLEAN),
'duplicate' => $this->getInput('profilEditDuplicate', helper::FILTER_BOOLEAN),
'extract' => $this->getInput('profilEditExtract', helper::FILTER_BOOLEAN),
'copycut' => $this->getInput('profilEditCopycut', helper::FILTER_BOOLEAN),
'chmod' => $this->getInput('profilEditChmod', helper::FILTER_BOOLEAN),
],
'folder' => [
'create' => $this->getInput('profilEditFolderCreate', helper::FILTER_BOOLEAN),
'delete' => $this->getInput('profilEditFolderDelete', helper::FILTER_BOOLEAN),
'rename' => $this->getInput('profilEditFolderRename', helper::FILTER_BOOLEAN),
'copycut' => $this->getInput('profilEditFolderCopycut', helper::FILTER_BOOLEAN),
'chmod' => $this->getInput('profilEditFolderChmod', helper::FILTER_BOOLEAN),
'path' => preg_replace('/^\\./', '', $path), // Supprime le point pour préserver le chemin
],
'page' => [
'add' => $this->getInput('profilEditPageAdd', helper::FILTER_BOOLEAN),
'edit' => $this->getInput('profilEditPageEdit', helper::FILTER_BOOLEAN),
'delete' => $this->getInput('profilEditPageDelete', helper::FILTER_BOOLEAN),
'duplicate' => $this->getInput('profilEditPageDuplicate', helper::FILTER_BOOLEAN),
'module' => $this->getInput('profilEditPageModule', helper::FILTER_BOOLEAN),
'cssEditor' => $this->getInput('profilEditPagecssEditor', helper::FILTER_BOOLEAN),
'jsEditor' => $this->getInput('profilEditPagejsEditor', helper::FILTER_BOOLEAN),
],
'user' => [
'edit' => $this->getInput('profilEditUserEdit', helper::FILTER_BOOLEAN),
]
];
// Données des modules
$dataModules = helper::getModules();
if (is_array($dataModules)) {
foreach ($dataModules as $moduleId => $moduleValue) {
if (file_exists('module/' . $moduleId . '/profil/main/edit.inc.php')) {
include('module/' . $moduleId . '/profil/main/edit.inc.php');
if (is_array($moduleData[$moduleId])) {
$data = array_merge($data, [$moduleId => $moduleData[$moduleId]]);
}
}
}
}
//Sauvegarder le données
2023-03-17 15:55:15 +01:00
$this->setData([
2023-03-31 17:02:56 +02:00
'profil',
2023-08-05 14:50:05 +02:00
$group,
$profil,
$data
2023-03-10 17:23:05 +01:00
]);
2023-03-31 17:02:56 +02:00
2023-03-17 15:55:15 +01:00
// Valeurs en sortie
$this->addOutput([
2023-08-05 14:50:05 +02:00
'redirect' => helper::baseUrl() . 'user/profil',
2023-03-17 15:55:15 +01:00
'notification' => helper::translate('Modifications enregistrées'),
'state' => true
]);
2023-03-10 17:23:05 +01:00
}
2023-03-10 15:02:49 +01:00
// Chemin vers les dossiers du gestionnaire de fichier
2024-03-14 10:28:42 +01:00
self::$sharePath = $this->getSubdirectories('site/file/source');
2023-03-22 17:46:08 +01:00
self::$sharePath = array_flip(self::$sharePath);
2024-03-14 10:28:42 +01:00
self::$sharePath = array_merge(['site/file/source/' => 'Tous les dossiers'], self::$sharePath);
2023-05-11 18:46:46 +02:00
self::$sharePath = array_merge([null => 'Aucun dossier'], self::$sharePath);
2023-03-22 17:46:08 +01:00
// Liste des modules installés
self::$listModules = helper::getModules();
self::$listModules = array_keys(self::$listModules);
2023-11-30 13:30:10 +01:00
2023-11-07 11:25:41 +01:00
// Charge les dialogues du module pour afficher les traductions
foreach (self::$listModules as $moduleId) {
if (
is_dir(self::MODULE_DIR . $moduleId . '/i18n')
&& file_exists(self::MODULE_DIR . $moduleId . '/i18n/' . self::$i18nUI . '.json')
) {
$d = json_decode(file_get_contents(self::MODULE_DIR . $moduleId . '/i18n/' . self::$i18nUI . '.json'), true);
self::$dialog = array_merge(self::$dialog, $d);
}
}
// Tri alphabétique
sort(self::$listModules);
2023-07-24 21:26:24 +02:00
2023-08-05 14:50:05 +02:00
/**
* Génération des profils disponibles
* Tableau des profils attribués
* Extraire les numéros de profils
* Générer un tableau $p des profils possibles selon le plafond
* Ne garder que la différence sauf le profil de l'utilisateur édité que l'on ajoute
*/
self::$profils = $this->getData(['profil', $this->getUrl(2)]);
// Supprime le profil utilisateur
unset(self::$profils[$this->getUrl(3)]);
self::$profils = array_keys(self::$profils);
$p = range(1, self::MAX_PROFILS - 1);
self::$profils = array_diff($p, self::$profils);
sort(self::$profils);
// Restructure le tableau pour faire correspondre la clé et la valeur
$p = array();
foreach (self::$profils as $key => $value) {
$p[$value] = $value;
}
self::$profils = $p;
2023-03-28 13:43:42 +02:00
// Valeurs en sortie;
2023-03-10 15:02:49 +01:00
$this->addOutput([
2023-07-19 09:28:26 +02:00
'title' => sprintf(helper::translate('Édition du profil %s'), $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'name'])),
2023-03-31 17:02:56 +02:00
'view' => 'profilEdit'
2023-03-10 15:02:49 +01:00
]);
}
2023-03-31 17:02:56 +02:00
/**
* Ajouter un profil de permission
*/
2023-05-02 23:37:51 +02:00
public function profilAdd()
{
2023-05-10 16:50:02 +02:00
// Soumission du formulaire
if (
2023-06-30 09:09:39 +02:00
$this->getUser('permission', __CLASS__, __FUNCTION__) === true &&
$this->isPost()
) {
2023-05-10 16:58:56 +02:00
// Nombre de profils de ce groupe
$group = $this->getInput('profilAddGroup');
2023-08-05 14:50:05 +02:00
$profil = count($this->getData(['profil', $group]));
2023-10-06 09:15:21 +02:00
// Gère le chemin
2023-10-06 09:15:50 +02:00
$fileManager = $this->getInput('profilAddFileManager', helper::FILTER_BOOLEAN);
$path = $this->getInput('profilAddPath');
2023-10-09 19:38:35 +02:00
if (
$group < self::GROUP_MEMBER
2023-10-09 19:38:35 +02:00
&& $fileManager
2023-10-06 09:15:21 +02:00
&& empty($path)
2023-10-09 19:38:35 +02:00
) {
$fileManager = false;
2023-10-06 09:15:21 +02:00
}
2023-10-09 19:38:35 +02:00
2023-08-05 14:50:05 +02:00
if ($profil < self::MAX_PROFILS) {
$profil = (string) ($profil + 1);
// Données du formulaire
$data = [
'name' => $this->getInput('profilAddName', helper::FILTER_STRING_SHORT, true),
'readonly' => false,
'permanent' => false,
'comment' => $this->getInput('profilAddComment', helper::FILTER_STRING_SHORT, true),
2023-10-06 09:15:21 +02:00
'filemanager' => $fileManager,
2023-08-05 14:50:05 +02:00
'file' => [
'download' => $this->getInput('profilAddDownload', helper::FILTER_BOOLEAN),
'edit' => $this->getInput('profilAddEdit', helper::FILTER_BOOLEAN),
'create' => $this->getInput('profilAddCreate', helper::FILTER_BOOLEAN),
'rename' => $this->getInput('profilAddRename', helper::FILTER_BOOLEAN),
'upload' => $this->getInput('profilAddUpload', helper::FILTER_BOOLEAN),
'delete' => $this->getInput('profilAddDelete', helper::FILTER_BOOLEAN),
'preview' => $this->getInput('profilAddPreview', helper::FILTER_BOOLEAN),
'duplicate' => $this->getInput('profilAddDuplicate', helper::FILTER_BOOLEAN),
'extract' => $this->getInput('profilAddExtract', helper::FILTER_BOOLEAN),
'copycut' => $this->getInput('profilAddCopycut', helper::FILTER_BOOLEAN),
'chmod' => $this->getInput('profilAddChmod', helper::FILTER_BOOLEAN),
],
'folder' => [
'create' => $this->getInput('profilAddFolderCreate', helper::FILTER_BOOLEAN),
'delete' => $this->getInput('profilAddFolderDelete', helper::FILTER_BOOLEAN),
'rename' => $this->getInput('profilAddFolderRename', helper::FILTER_BOOLEAN),
'copycut' => $this->getInput('profilAddFolderCopycut', helper::FILTER_BOOLEAN),
'chmod' => $this->getInput('profilAddFolderChmod', helper::FILTER_BOOLEAN),
'path' => preg_replace('/^\\./', '', $path), // Supprime le point pour préserver le chemin
2023-08-05 14:50:05 +02:00
],
'page' => [
'add' => $this->getInput('profilAddPageAdd', helper::FILTER_BOOLEAN),
'edit' => $this->getInput('profilAddPageEdit', helper::FILTER_BOOLEAN),
'delete' => $this->getInput('profilAddPageDelete', helper::FILTER_BOOLEAN),
'duplicate' => $this->getInput('profilAddPageDuplicate', helper::FILTER_BOOLEAN),
'module' => $this->getInput('profilAddPageModule', helper::FILTER_BOOLEAN),
'cssEditor' => $this->getInput('profilAddPagecssEditor', helper::FILTER_BOOLEAN),
'jsEditor' => $this->getInput('profilAddPagejsEditor', helper::FILTER_BOOLEAN),
],
'user' => [
'edit' => $this->getInput('profilAddUserEdit', helper::FILTER_BOOLEAN),
]
];
2023-08-05 14:50:05 +02:00
// Données des modules
$dataModules = helper::getModules();
if (is_array($dataModules)) {
foreach ($dataModules as $moduleId => $moduleValue) {
if (file_exists('module/' . $moduleId . '/profil/main/add.inc.php')) {
include('module/' . $moduleId . '/profil/main/add.inc.php');
if (is_array($moduleData[$moduleId])) {
$data = array_merge($data, [$moduleId => $moduleData[$moduleId]]);
}
}
}
}
2023-08-05 14:50:05 +02:00
// Sauvegarder les données
$this->setData([
'profil',
$group,
$profil,
$data
]);
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . 'user/profil',
'notification' => helper::translate('Modifications enregistrées'),
'state' => true
]);
} else {
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . 'user/profil',
'notification' => helper::translate('Nombre de profils maximum : ') . self::MAX_PROFILS,
'state' => false
]);
}
2023-05-10 16:50:02 +02:00
}
2023-03-31 17:02:56 +02:00
// Chemin vers les dossiers du gestionnaire de fichier
2024-03-14 10:28:42 +01:00
self::$sharePath = $this->getSubdirectories('site/file/source');
2023-11-07 11:25:41 +01:00
self::$sharePath = array_flip(self::$sharePath);
2024-03-14 10:28:42 +01:00
self::$sharePath = array_merge(['site/file/source/' => 'Tous les dossiers'], self::$sharePath);
2023-11-07 11:25:41 +01:00
self::$sharePath = array_merge([null => 'Aucun dossier'], self::$sharePath);
// Liste des modules installés
self::$listModules = helper::getModules();
self::$listModules = array_keys(self::$listModules);
2023-11-30 13:30:10 +01:00
2023-11-07 11:25:41 +01:00
// Charge les dialogues du module pour afficher les traductions
foreach (self::$listModules as $moduleId) {
if (
is_dir(self::MODULE_DIR . $moduleId . '/i18n')
&& file_exists(self::MODULE_DIR . $moduleId . '/i18n/' . self::$i18nUI . '.json')
) {
$d = json_decode(file_get_contents(self::MODULE_DIR . $moduleId . '/i18n/' . self::$i18nUI . '.json'), true);
self::$dialog = array_merge(self::$dialog, $d);
}
}
// Tri alphabétique
sort(self::$listModules);
2023-03-31 17:02:56 +02:00
// Valeurs en sortie;
$this->addOutput([
'title' => "Ajouter un profil",
'view' => 'profilAdd'
]);
}
2023-03-10 10:19:22 +01:00
2023-06-28 17:52:13 +02:00
/**
* Effacement de profil
*/
public function profilDelete()
{
2023-12-01 16:25:45 +01:00
// Ne pas supprimer un profil utililsé
// recherche les membres du groupe
$groups = helper::arrayColumn($this->getData(['user']), 'group');
$groups = array_keys($groups, $this->getUrl(2));
2024-08-24 17:29:17 +02:00
$flag = true;
2023-12-01 16:25:45 +01:00
// Stoppe si le profil est affecté
foreach ($groups as $userId) {
if ((string) $this->getData(['user', $userId, 'profil']) === $this->getUrl(3)) {
2024-08-24 17:29:17 +02:00
$flag = false;
2023-12-01 16:25:45 +01:00
}
}
2023-06-28 17:52:13 +02:00
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true ||
$this->getData(['profil', $this->getUrl(2), $this->getUrl(3)]) === null ||
$this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'permanent']) === true
2023-06-28 17:52:13 +02:00
) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
// Suppression
} else {
2023-12-01 16:25:45 +01:00
if ($flag) {
$this->deleteData(['profil', $this->getUrl(2), $this->getUrl(3)]);
}
2024-08-24 17:29:17 +02:00
2023-06-28 17:52:13 +02:00
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . $this->getUrl(0) . '/profil',
2023-12-01 16:25:45 +01:00
'notification' => $flag ? helper::translate('Profil supprimé') : helper::translate('Action interdite'),
'state' => $flag
2023-06-28 17:52:13 +02:00
]);
}
}
2018-04-02 08:29:19 +02:00
/**
* Connexion
*/
2022-09-29 08:45:59 +02:00
public function login()
{
2018-04-02 08:29:19 +02:00
// Soumission du formulaire
2021-01-03 18:41:25 +01:00
$logStatus = '';
if (
$this->isPost()
) {
2020-12-30 17:41:27 +01:00
// Lire Id du compte
$userId = $this->getInput('userLoginId', helper::FILTER_ID, true);
// Check le captcha
2022-09-29 08:45:59 +02:00
if (
$this->getData(['config', 'connect', 'captcha'])
and password_verify($this->getInput('userLoginCaptcha', helper::FILTER_INT), $this->getInput('userLoginCaptchaResult')) === false
) {
2021-01-03 18:41:25 +01:00
$captcha = false;
2020-10-09 08:37:34 +02:00
} else {
2021-01-03 18:41:25 +01:00
$captcha = true;
}
/**
* Aucun compte existant
*/
2022-09-29 08:45:59 +02:00
if (!$this->getData(['user', $userId])) {
$logStatus = 'Compte inconnu';
2021-01-03 18:41:25 +01:00
//Stockage de l'IP
$this->setData([
'blacklist',
$userId,
[
2022-09-29 08:45:59 +02:00
'connectFail' => $this->getData(['blacklist', $userId, 'connectFail']) + 1,
2021-01-03 18:41:25 +01:00
'lastFail' => time(),
'ip' => helper::getIp()
]
]);
// Verrouillage des IP
$ipBlackList = helper::arrayColumn($this->getData(['blacklist']), 'ip');
2022-09-29 08:45:59 +02:00
if (
$this->getData(['blacklist', $userId, 'connectFail']) >= $this->getData(['config', 'connect', 'attempt'])
and in_array($this->getData(['blacklist', $userId, 'ip']), $ipBlackList)
) {
2021-01-03 18:41:25 +01:00
$logStatus = 'Compte inconnu verrouillé';
// Valeurs en sortie
$this->addOutput([
2022-10-11 10:33:44 +02:00
'notification' => helper::translate('Compte verrouillé'),
2021-01-03 18:41:25 +01:00
'redirect' => helper::baseUrl(),
'state' => false
]);
2021-01-03 18:41:25 +01:00
} else {
// Valeurs en sortie
$this->addOutput([
2022-10-11 10:33:44 +02:00
'notification' => helper::translate('Captcha, identifiant ou mot de passe incorrects')
2021-01-03 18:41:25 +01:00
]);
}
2022-09-29 08:45:59 +02:00
/**
* Le compte existe
*/
} else {
2021-01-03 18:41:25 +01:00
// Cas 4 : le délai de blocage est dépassé et le compte est au max - Réinitialiser
2022-09-29 08:45:59 +02:00
if (
2023-03-10 10:19:22 +01:00
$this->getData(['user', $userId, 'connectTimeout']) + $this->getData(['config', 'connect', 'timeout']) < time()
2022-09-29 08:45:59 +02:00
and $this->getData(['user', $userId, 'connectFail']) === $this->getData(['config', 'connect', 'attempt'])
) {
2024-09-28 18:42:41 +02:00
$this->setData(['user', $userId, 'connectFail', 0], false);
$this->setData(['user', $userId, 'connectTimeout', 0], false);
2021-01-03 18:41:25 +01:00
}
// 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
if (
2022-09-29 08:45:59 +02:00
($this->getData(['user', $userId, 'connectTimeout']) + $this->getData(['config', 'connect', 'timeout'])) < time()
and $this->getData(['user', $userId, 'connectFail']) < $this->getData(['config', 'connect', 'attempt'])
and password_verify(html_entity_decode($this->getInput('userLoginPassword', helper::FILTER_STRING_SHORT, true)), $this->getData(['user', $userId, 'password']))
2022-09-29 08:45:59 +02:00
and $this->getData(['user', $userId, 'group']) >= self::GROUP_MEMBER
and $captcha === true
2021-01-03 18:41:25 +01:00
) {
// RAZ
2024-09-28 18:42:41 +02:00
$this->setData(['user', $userId, 'connectFail', 0], false);
$this->setData(['user', $userId, 'connectTimeout', 0], false);
2024-09-06 17:32:29 +02:00
// Clé d'authenfication
$authKey = uniqid('', true) . bin2hex(random_bytes(8));
$this->setData(['user', $userId, 'authKey', $authKey]);
2024-08-24 17:29:17 +02:00
// Validité du cookie
2022-09-29 08:45:59 +02:00
$expire = $this->getInput('userLoginLongTime', helper::FILTER_BOOLEAN) === true ? strtotime("+1 year") : 0;
2024-08-24 17:29:17 +02:00
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);
2024-09-06 17:32:29 +02:00
//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);
2024-08-24 17:29:17 +02:00
break;
default:
// Cookie persistant
setcookie('ZWII_USER_ID', $userId, $expire, helper::baseUrl(false, false));
2024-09-06 17:32:29 +02:00
//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));
2024-08-24 17:29:17 +02:00
break;
}
2021-01-03 18:41:25 +01:00
// Accès multiples avec le même compte
2024-09-28 18:42:41 +02:00
$this->setData(['user', $userId, 'accessCsrf', $_SESSION['csrf']], false);
2021-01-03 18:41:25 +01:00
// Valeurs en sortie lorsque le site est en maintenance et que l'utilisateur n'est pas administrateur
2022-09-29 08:45:59 +02:00
if (
2021-01-03 18:41:25 +01:00
$this->getData(['config', 'maintenance'])
2022-09-29 08:45:59 +02:00
and $this->getData(['user', $userId, 'group']) < self::GROUP_ADMIN
2021-01-03 18:41:25 +01:00
) {
2020-07-17 17:27:34 +02:00
$this->addOutput([
2022-10-11 10:33:44 +02:00
'notification' => helper::translate('Seul un administrateur peut se connecter lors d\'une maintenance'),
2020-07-17 17:27:34 +02:00
'redirect' => helper::baseUrl(),
'state' => false
]);
2020-07-02 19:53:52 +02:00
} else {
2021-01-03 18:41:25 +01:00
$logStatus = 'Connexion réussie';
$pageId = $this->getUrl(2);
2024-08-24 17:29:17 +02:00
if (
$this->getData(['config', 'page404']) === $pageId
|| $this->getData(['config', 'page403']) === $pageId
) {
$pageId = '';
}
$redirect = ($pageId && strpos($pageId, 'user_reset') !== 0) ? helper::baseUrl() . str_replace('_', '/', str_replace('__', '#', $pageId)) : helper::baseUrl();
2020-07-17 17:27:34 +02:00
// Valeurs en sortie
$this->addOutput([
2022-10-11 10:33:44 +02:00
'notification' => sprintf(helper::translate('Bienvenue %s %s'), $this->getData(['user', $userId, 'firstname']), $this->getData(['user', $userId, 'lastname'])),
2023-07-22 09:45:23 +02:00
'redirect' => $redirect,
2021-01-03 18:41:25 +01:00
'state' => true
2020-07-17 17:27:34 +02:00
]);
}
2022-09-29 08:45:59 +02:00
// Sinon notification d'échec
2021-01-03 18:41:25 +01:00
} else {
2022-10-11 10:33:44 +02:00
$notification = helper::translate('Captcha, identifiant ou mot de passe incorrects');
2021-01-03 18:41:25 +01:00
$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
2024-09-28 18:42:41 +02:00
if ($this->getData(['user', $userId, 'connectFail']) < $this->getData(['config', 'connect', 'attempt'], false)) {
$this->setData(['user', $userId, 'connectFail', $this->getdata(['user', $userId, 'connectFail']) + 1], false);
2020-07-17 17:27:34 +02:00
}
2021-01-03 18:41:25 +01:00
// Cas 2 la limite du nombre de connexion est atteinte : placer le timer
2022-09-29 08:45:59 +02:00
if ($this->getdata(['user', $userId, 'connectFail']) == $this->getData(['config', 'connect', 'attempt'])) {
2024-09-28 18:42:41 +02:00
$this->setData(['user', $userId, 'connectTimeout', time()], false);
2020-07-17 17:27:34 +02:00
}
2021-01-03 18:41:25 +01:00
// Cas 3 le délai de bloquage court
2023-03-29 19:28:03 +02:00
if ($this->getData(['user', $userId, 'connectTimeout']) + $this->getData(['config', 'connect', 'timeout']) > time()) {
$notification = sprintf(helper::translate('Accès bloqué %d minutes'), ($this->getData(['config', 'connect', 'timeout']) / 60));
2021-01-03 18:41:25 +01:00
}
// Valeurs en sortie
$this->addOutput([
'notification' => $notification
]);
2020-07-02 19:53:52 +02:00
}
2018-04-02 08:29:19 +02:00
}
2024-09-28 18:42:41 +02:00
// Force la sauvegarde
$this->saveDB('user');
2018-04-02 08:29:19 +02:00
}
2021-01-03 18:41:25 +01:00
// Journalisation
2023-04-23 18:51:37 +02:00
$this->saveLog($logStatus);
2021-01-03 18:41:25 +01:00
// Stockage des cookies
if (!empty($_COOKIE['ZWII_USER_ID'])) {
self::$userId = $_COOKIE['ZWII_USER_ID'];
}
2018-04-02 08:29:19 +02:00
// Valeurs en sortie
$this->addOutput([
2022-10-02 10:59:42 +02:00
'title' => helper::translate('Connexion'),
'view' => 'login',
2018-04-02 08:29:19 +02:00
'display' => self::DISPLAY_LAYOUT_LIGHT,
]);
}
2023-03-10 10:19:22 +01:00
2018-04-02 08:29:19 +02:00
/**
* Déconnexion
*/
2022-09-29 08:45:59 +02:00
public function logout()
{
2022-05-19 15:20:57 +02:00
helper::deleteCookie('ZWII_USER_ID');
2024-09-06 17:32:29 +02:00
//helper::deleteCookie('ZWII_USER_PASSWORD');
helper::deleteCookie('ZWII_AUTH_KEY');
2023-04-25 11:07:38 +02:00
// Détruit la session
2019-01-13 17:54:42 +01:00
session_destroy();
2023-04-25 11:07:38 +02:00
2018-04-02 08:29:19 +02:00
// Valeurs en sortie
$this->addOutput([
'notification' => helper::translate('Déconnexion !'),
2018-04-02 08:29:19 +02:00
'redirect' => helper::baseUrl(false),
'state' => true
]);
}
/**
* Réinitialisation du mot de passe
*/
2022-09-29 08:45:59 +02:00
public function reset()
{
2018-04-02 08:29:19 +02:00
// Accès refusé
2022-09-29 08:45:59 +02:00
if (
2018-04-02 08:29:19 +02:00
// L'utilisateur n'existe pas
$this->getData(['user', $this->getUrl(2)]) === null
// Lien de réinitialisation trop vieux
2022-09-29 08:45:59 +02:00
or $this->getData(['user', $this->getUrl(2), 'forgot']) + 86400 < time()
2018-04-02 08:29:19 +02:00
// Id unique incorrecte
2022-09-29 08:45:59 +02:00
or $this->getUrl(3) !== md5(json_encode($this->getData(['user', $this->getUrl(2)])))
2018-04-02 08:29:19 +02:00
) {
2018-04-02 08:29:19 +02:00
// Valeurs en sortie
$this->addOutput([
2023-12-01 16:25:45 +01:00
'redirect' => helper::baseurl(),
2023-11-09 10:43:13 +01:00
'notification' => helper::translate('Impossible de réinitialiser le mot de passe de ce compte !'),
'state' => false
//'access' => false
2018-04-02 08:29:19 +02:00
]);
}
// Accès autorisé
else {
// Soumission du formulaire
if (
// Tous les users peuvent réinitialiser
2023-07-04 19:03:27 +02:00
// $this->getUser('permission', __CLASS__, __FUNCTION__) === true &&
$this->isPost()
) {
2018-04-02 08:29:19 +02:00
// Double vérification pour le mot de passe
2022-09-29 08:45:59 +02:00
if ($this->getInput('userResetNewPassword')) {
2018-04-02 08:29:19 +02:00
// La confirmation ne correspond pas au mot de passe
2022-09-29 08:45:59 +02:00
if ($this->getInput('userResetNewPassword', helper::FILTER_STRING_SHORT, true) !== $this->getInput('userResetConfirmPassword', helper::FILTER_STRING_SHORT, true)) {
2018-04-02 08:29:19 +02:00
$newPassword = $this->getData(['user', $this->getUrl(2), 'password']);
self::$inputNotices['userResetConfirmPassword'] = 'Incorrect';
2022-09-29 08:45:59 +02:00
} else {
2018-04-02 08:29:19 +02:00
$newPassword = $this->getInput('userResetNewPassword', helper::FILTER_PASSWORD, true);
}
// Modifie le mot de passe
2024-09-28 18:42:41 +02:00
$this->setData(['user', $this->getUrl(2), 'password', $newPassword], false);
2018-04-02 08:29:19 +02:00
// Réinitialise la date de la demande
2024-09-28 18:42:41 +02:00
$this->setData(['user', $this->getUrl(2), 'forgot', 0], false);
2020-11-22 18:56:14 +01:00
// Réinitialise le blocage
2024-09-28 18:42:41 +02:00
$this->setData(['user', $this->getUrl(2), 'connectFail', 0], false);
2022-09-29 08:45:59 +02:00
$this->setData(['user', $this->getUrl(2), 'connectTimeout', 0]);
2018-04-02 08:29:19 +02:00
// Valeurs en sortie
$this->addOutput([
2022-10-11 10:33:44 +02:00
'notification' => helper::translate('Nouveau mot de passe enregistré'),
'redirect' => helper::baseUrl() . 'user/login/' . str_replace('/', '_', $this->getUrl()),
2018-04-02 08:29:19 +02:00
'state' => true
]);
}
}
// Valeurs en sortie
$this->addOutput([
2022-10-02 10:59:42 +02:00
'title' => helper::translate('Réinitialisation du mot de passe'),
'view' => 'reset',
2020-10-07 19:01:57 +02:00
'display' => self::DISPLAY_LAYOUT_LIGHT,
2018-04-02 08:29:19 +02:00
]);
}
}
2020-10-04 17:14:15 +02:00
/**
* Importation CSV d'utilisateurs
*/
2022-09-29 08:45:59 +02:00
public function import()
{
// Soumission du formulaire
2020-10-06 15:56:49 +02:00
$notification = '';
$success = true;
if (
2023-06-30 09:09:39 +02:00
$this->getUser('permission', __CLASS__, __FUNCTION__) === true &&
$this->isPost()
) {
// Lecture du CSV et construction du tableau
2022-09-29 08:45:59 +02:00
$file = $this->getInput('userImportCSVFile', helper::FILTER_STRING_SHORT, true);
2020-10-06 08:29:14 +02:00
$filePath = self::FILE_DIR . 'source/' . $file;
2022-09-29 08:45:59 +02:00
if ($file and file_exists($filePath)) {
2020-10-08 21:16:28 +02:00
// Analyse et extraction du CSV
2023-03-10 10:19:22 +01:00
$rows = array_map(function ($row) {
2022-09-29 08:45:59 +02:00
return str_getcsv($row, $this->getInput('userImportSeparator'));
}, file($filePath));
2020-10-05 16:56:30 +02:00
$header = array_shift($rows);
2023-03-10 10:19:22 +01:00
$csv = array();
2022-09-29 08:45:59 +02:00
foreach ($rows as $row) {
2020-10-05 16:56:30 +02:00
$csv[] = array_combine($header, $row);
}
2020-10-08 21:16:28 +02:00
// Traitement des données
2022-09-29 08:45:59 +02:00
foreach ($csv as $item) {
2020-10-08 21:16:28 +02:00
// Données valides
2022-09-29 08:45:59 +02:00
if (
array_key_exists('id', $item)
and array_key_exists('prenom', $item)
and array_key_exists('nom', $item)
and array_key_exists('groupe', $item)
2023-11-30 13:30:10 +01:00
and array_key_exists('profil', $item)
2022-09-29 08:45:59 +02:00
and array_key_exists('email', $item)
2023-10-09 19:38:35 +02:00
and array_key_exists('passe', $item)
2023-11-30 13:30:10 +01:00
and array_key_exists('tags', $item)
and isset($item['id'])
and isset($item['nom'])
and isset($item['prenom'])
and isset($item['email'])
and isset($item['groupe'])
and isset($item['profil'])
and isset($item['passe'])
and isset($item['tags'])
2020-10-19 09:53:28 +02:00
) {
2020-12-04 20:57:02 +01:00
// Validation du groupe
$item['groupe'] = (int) $item['groupe'];
2023-11-30 13:30:10 +01:00
$item['profil'] = (int) $item['profil'];
2023-03-10 10:19:22 +01:00
$item['groupe'] = ($item['groupe'] >= self::GROUP_BANNED and $item['groupe'] <= self::GROUP_ADMIN)
2022-09-29 08:45:59 +02:00
? $item['groupe'] : 1;
2020-10-08 21:16:28 +02:00
// L'utilisateur existe
2022-09-29 08:45:59 +02:00
if ($this->getData(['user', helper::filter($item['id'], helper::FILTER_ID)])) {
2020-10-08 21:16:28 +02:00
// Notification du doublon
$item['notification'] = template::ico('cancel');
// Création du tableau de confirmation
self::$users[] = [
2022-09-29 08:45:59 +02:00
helper::filter($item['id'], helper::FILTER_ID),
2020-10-08 21:16:28 +02:00
$item['nom'],
$item['prenom'],
self::$groups[$item['groupe']],
2023-11-30 13:52:22 +01:00
empty($this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name']))
? helper::translate(self::$groups[(int) $this->getData(['user', $userId, 'group'])])
: $this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name']),
2020-10-08 21:16:28 +02:00
$item['prenom'],
2022-09-29 08:45:59 +02:00
helper::filter($item['email'], helper::FILTER_MAIL),
2023-11-30 13:30:10 +01:00
$item['tags'],
2020-10-08 21:16:28 +02:00
$item['notification']
];
// L'utilisateur n'existe pas
} else {
// Nettoyage de l'identifiant
2022-09-29 08:45:59 +02:00
$userId = helper::filter($item['id'], helper::FILTER_ID);
2020-10-08 21:16:28 +02:00
// Enregistre le user
$create = $this->setData([
2020-10-08 21:16:28 +02:00
'user',
2023-03-10 10:19:22 +01:00
$userId,
[
2020-10-08 21:16:28 +02:00
'firstname' => $item['prenom'],
'forgot' => 0,
2022-09-29 08:45:59 +02:00
'group' => $item['groupe'],
2023-11-30 13:30:10 +01:00
'profil' => $item['profil'],
2020-10-08 21:16:28 +02:00
'lastname' => $item['nom'],
'mail' => $item['email'],
'pseudo' => $item['prenom'],
2023-03-10 10:19:22 +01:00
'signature' => 1,
// Pseudo
2023-10-09 19:38:35 +02:00
'password' => helper::filter($item['passe'], helper::FILTER_PASSWORD),
2023-03-10 10:19:22 +01:00
// A modifier à la première connexion
2021-05-20 21:52:09 +02:00
"connectFail" => null,
"connectTimeout" => null,
"accessUrl" => null,
"accessTimer" => null,
2023-11-30 13:30:10 +01:00
"accessCsrf" => null,
'tags' => $item['tags']
2022-09-29 08:45:59 +02:00
]
2024-09-28 18:42:41 +02:00
], false);
2020-10-08 21:16:28 +02:00
// Icône de notification
2023-03-10 10:19:22 +01:00
$item['notification'] = $create ? template::ico('check') : template::ico('cancel');
2020-10-08 21:16:28 +02:00
// Envoi du mail
2022-09-29 08:45:59 +02:00
if (
$create
and $this->getInput('userImportNotification', helper::FILTER_BOOLEAN) === true
) {
2020-10-08 21:16:28 +02:00
$sent = $this->sendMail(
$item['email'],
2023-10-09 19:38:35 +02:00
'Compte créé sur ' . $this->getData(['config', 'title']),
2020-10-08 21:16:28 +02:00
'Bonjour <strong>' . $item['prenom'] . ' ' . $item['nom'] . '</strong>,<br><br>' .
2023-10-09 19:38:35 +02:00
'Un administrateur vous a créé un compte sur le site ' . $this->getData(['config', 'title']) . '. Vous trouverez ci-dessous les détails de votre compte.<br><br>' .
2023-03-10 10:19:22 +01:00
'<strong>Identifiant du compte :</strong> ' . $userId . '<br>' .
'<small>Un mot de passe provisoire vous été attribué, à la première connexion cliquez sur Mot de passe Oublié.</small>',
null,
2023-07-16 18:47:50 +02:00
$this->getData(['config', 'smtp', 'from'])
2020-10-08 21:16:28 +02:00
);
if ($sent === true) {
// Mail envoyé changement de l'icône
2022-09-29 08:45:59 +02:00
$item['notification'] = template::ico('mail');
2020-10-08 21:16:28 +02:00
}
}
// Création du tableau de confirmation
self::$users[] = [
$userId,
$item['nom'],
$item['prenom'],
self::$groups[$item['groupe']],
2023-11-30 13:52:22 +01:00
empty($this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name']))
? helper::translate(self::$groups[(int) $this->getData(['user', $userId, 'group'])])
: $this->getData(['profil', $this->getData(['user', $userId, 'group']), $this->getData(['user', $userId, 'profil']), 'name']),
$item['prenom'],
$item['email'],
2023-11-30 13:30:10 +01:00
$item['tags'],
$item['notification']
];
}
2020-10-05 16:56:30 +02:00
}
2023-11-30 13:30:10 +01:00
2020-10-05 16:56:30 +02:00
}
2024-09-28 18:42:41 +02:00
// Force la sauvegarde
$this->saveDB('user');
2020-10-08 12:49:09 +02:00
if (empty(self::$users)) {
2023-03-10 10:19:22 +01:00
$notification = helper::translate('Rien à importer, erreur de format ou fichier incorrect');
2020-10-08 12:49:09 +02:00
$success = false;
} else {
2023-03-10 10:19:22 +01:00
$notification = helper::translate('Importation effectuée');
2020-10-08 12:49:09 +02:00
$success = true;
}
2020-10-05 16:56:30 +02:00
} else {
2022-10-11 10:33:44 +02:00
$notification = helper::translate('Erreur de lecture, vérifiez les permissions');
2020-10-05 16:56:30 +02:00
$success = false;
}
}
2020-10-04 17:14:15 +02:00
// Valeurs en sortie
$this->addOutput([
2022-10-02 10:59:42 +02:00
'title' => 'Importation d\'utilisateurs',
2020-10-05 16:56:30 +02:00
'view' => 'import',
'notification' => $notification,
'state' => $success
2020-10-04 17:14:15 +02:00
]);
}
2023-02-10 22:27:08 +01:00
/**
* Télécharge un modèle
2023-03-10 10:19:22 +01:00
*/
public function template()
{
2023-10-05 11:29:20 +02:00
if ($this->getUser('permission', __CLASS__, __FUNCTION__) === true) {
$file = 'template.csv';
$path = 'core/module/user/ressource/';
// Téléchargement du CSV
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Transfer-Encoding: binary');
header('Content-Disposition: attachment; filename="' . $file . '"');
header('Content-Length: ' . filesize($path . $file));
readfile($path . $file);
exit();
}
2023-02-10 22:27:08 +01:00
}
2023-03-01 16:11:36 +01:00
2023-03-22 17:46:08 +01:00
/**
* Liste les dossier contenus dans RFM
*/
2023-05-11 18:46:46 +02:00
private function getSubdirectories($dir, $basePath = '')
2023-03-29 19:28:03 +02:00
{
2023-03-22 17:46:08 +01:00
$subdirs = array();
// Ouvrez le répertoire spécifié
$dh = opendir($dir);
// Parcourez tous les fichiers et répertoires dans le répertoire
while (($file = readdir($dh)) !== false) {
2023-03-29 19:28:03 +02:00
// Ignorer les entrées de répertoire parent et actuel
if ($file == '.' || $file == '..') {
continue;
2024-08-24 17:29:17 +02:00
}
2023-03-29 19:28:03 +02:00
// Construisez le chemin complet du fichier ou du répertoire
$path = $dir . '/' . $file;
// Vérifiez si c'est un répertoire
if (is_dir($path)) {
// Construisez la clé et la valeur pour le tableau associatif
2023-05-11 19:07:34 +02:00
$key = $basePath === '' ? ucfirst($file) : $basePath . '/' . $file;
2023-03-29 19:28:03 +02:00
$value = $path . '/';
// Ajouter la clé et la valeur au tableau associatif
$subdirs[$key] = $value;
// Appeler la fonction récursivement pour ajouter les sous-répertoires
$subdirs = array_merge($subdirs, $this->getSubdirectories($path, $key));
}
2023-03-22 17:46:08 +01:00
}
// Fermez le gestionnaire de dossier
closedir($dh);
return $subdirs;
2023-03-29 19:28:03 +02:00
}
2023-03-10 10:19:22 +01:00
}