diff --git a/aprelik.php b/aprelik.php
new file mode 100644
index 0000000..2275d8c
--- /dev/null
+++ b/aprelik.php
@@ -0,0 +1,425 @@
+
+
+
+
+
+
+Запись в Апрелик 2026-2027 — USLUGI.FR
+
+
+
+
+
+
+
+

+
+
Студия-мастерская
«Апрелик»
+
Русский язык и театр в Париже
+
+
+
Набор открыт
+
Запись на 2026-2027
учебный год
+
+
Заполните форму — заявка поступит напрямую в школу.
+
+
+
+
+
\ No newline at end of file
diff --git a/aprelik/index.php b/aprelik/index.php
new file mode 100644
index 0000000..67ae15f
--- /dev/null
+++ b/aprelik/index.php
@@ -0,0 +1,2 @@
+ 0 && mb_strlen($value) > $maxLength) {
+ $value = mb_substr($value, 0, $maxLength);
+ }
+
+ return $value;
+}
+
+function aprelik_get_pdo(): PDO
+{
+ foreach (['pdo', 'db', 'database', 'conn'] as $name) {
+ if (isset($GLOBALS[$name]) && $GLOBALS[$name] instanceof PDO) {
+ return $GLOBALS[$name];
+ }
+ }
+
+ throw new RuntimeException('PDO connection not found in bootstrap_global.php');
+}
+
+if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
+ aprelik_redirect('/aprelik.php');
+}
+
+if (! empty($_POST['website'] ?? '')) {
+ $_SESSION['aprelik_success'] = 'Спасибо. Форма отправлена.';
+ aprelik_redirect('/aprelik.php');
+}
+
+$data = [
+ 'child_last_name' => aprelik_clean($_POST['child_last_name'] ?? '', 120),
+ 'child_first_name' => aprelik_clean($_POST['child_first_name'] ?? '', 120),
+ 'child_birthdate' => aprelik_clean($_POST['child_birthdate'] ?? '', 10),
+
+ 'parent1_last_name' => aprelik_clean($_POST['parent1_last_name'] ?? '', 120),
+ 'parent1_first_name' => aprelik_clean($_POST['parent1_first_name'] ?? '', 120),
+ 'parent1_language' => aprelik_clean($_POST['parent1_language'] ?? '', 120),
+
+ 'parent2_last_name' => aprelik_clean($_POST['parent2_last_name'] ?? '', 120),
+ 'parent2_first_name' => aprelik_clean($_POST['parent2_first_name'] ?? '', 120),
+ 'parent2_language' => aprelik_clean($_POST['parent2_language'] ?? '', 120),
+
+ 'street_address' => aprelik_clean($_POST['street_address'] ?? '', 255),
+ 'postal_code' => aprelik_clean($_POST['postal_code'] ?? '', 30),
+ 'city' => aprelik_clean($_POST['city'] ?? '', 120),
+
+ 'parent1_phone' => aprelik_clean($_POST['parent1_phone'] ?? '', 40),
+ 'parent2_phone' => aprelik_clean($_POST['parent2_phone'] ?? '', 40),
+ 'email' => aprelik_clean($_POST['email'] ?? '', 190),
+
+ 'consent_rgpd' => ! empty($_POST['consent_rgpd']) ? 1 : 0,
+ 'consent_email_diffusion' => ! empty($_POST['consent_email_diffusion']) ? 1 : 0,
+
+ 'school_day_address' => aprelik_clean($_POST['school_day_address'] ?? '', 255),
+ 'age_group' => aprelik_clean($_POST['age_group'] ?? '', 80),
+ 'notes' => aprelik_clean($_POST['notes'] ?? '', 5000),
+];
+
+$errors = [];
+
+if ($data['child_last_name'] === '') {
+ $errors[] = 'Укажите фамилию ребёнка.';
+}
+
+if ($data['child_first_name'] === '') {
+ $errors[] = 'Укажите имя ребёнка.';
+}
+
+if ($data['child_birthdate'] === '') {
+ $errors[] = 'Укажите дату рождения ребёнка.';
+} else {
+ $dt = DateTime::createFromFormat('Y-m-d', $data['child_birthdate']);
+ if (! $dt || $dt->format('Y-m-d') !== $data['child_birthdate']) {
+ $errors[] = 'Некорректная дата рождения.';
+ }
+}
+
+if ($data['parent1_last_name'] === '') {
+ $errors[] = 'Укажите фамилию родителя 1.';
+}
+
+if ($data['parent1_first_name'] === '') {
+ $errors[] = 'Укажите имя родителя 1.';
+}
+
+if ($data['parent1_language'] === '') {
+ $errors[] = 'Укажите язык родителя 1.';
+}
+
+if ($data['street_address'] === '') {
+ $errors[] = 'Укажите адрес.';
+}
+
+if ($data['postal_code'] === '') {
+ $errors[] = 'Укажите индекс.';
+}
+
+if ($data['city'] === '') {
+ $errors[] = 'Укажите город.';
+}
+
+if ($data['parent1_phone'] === '') {
+ $errors[] = 'Укажите телефон родителя 1.';
+}
+
+if ($data['email'] === '' || filter_var($data['email'], FILTER_VALIDATE_EMAIL) === false) {
+ $errors[] = 'Укажите корректный email.';
+}
+
+if ($data['school_day_address'] === '') {
+ $errors[] = 'Выберите адрес занятий.';
+}
+
+if ($data['age_group'] === '') {
+ $errors[] = 'Выберите возрастную группу.';
+}
+
+if ($data['consent_email_diffusion'] !== 1) {
+ $errors[] = 'Нужно согласиться на информационную рассылку.';
+}
+
+if ($data['consent_rgpd'] !== 1) {
+ $errors[] = 'Нужно согласиться на обработку персональных данных.';
+}
+
+if ($errors !== []) {
+ $_SESSION['aprelik_errors'] = $errors;
+ $_SESSION['aprelik_old'] = $data;
+ aprelik_redirect('/aprelik.php');
+}
+
+try {
+ $pdo = aprelik_get_pdo();
+
+ $sql = "
+ INSERT INTO aprelik (
+ child_last_name,
+ child_first_name,
+ child_birthdate,
+ parent1_last_name,
+ parent1_first_name,
+ parent1_language,
+ parent2_last_name,
+ parent2_first_name,
+ parent2_language,
+ street_address,
+ postal_code,
+ city,
+ parent1_phone,
+ parent2_phone,
+ email,
+ consent_rgpd,
+ consent_email_diffusion,
+ ip_address,
+ user_agent,
+ school_day_address,
+ age_group,
+ notes
+ ) VALUES (
+ :child_last_name,
+ :child_first_name,
+ :child_birthdate,
+ :parent1_last_name,
+ :parent1_first_name,
+ :parent1_language,
+ :parent2_last_name,
+ :parent2_first_name,
+ :parent2_language,
+ :street_address,
+ :postal_code,
+ :city,
+ :parent1_phone,
+ :parent2_phone,
+ :email,
+ :consent_rgpd,
+ :consent_email_diffusion,
+ :ip_address,
+ :user_agent,
+ :school_day_address,
+ :age_group,
+ :notes
+ )
+ ON DUPLICATE KEY UPDATE
+ child_last_name = VALUES(child_last_name),
+ child_first_name = VALUES(child_first_name),
+ child_birthdate = VALUES(child_birthdate),
+ parent1_last_name = VALUES(parent1_last_name),
+ parent1_first_name = VALUES(parent1_first_name),
+ parent1_language = VALUES(parent1_language),
+ parent2_last_name = VALUES(parent2_last_name),
+ parent2_first_name = VALUES(parent2_first_name),
+ parent2_language = VALUES(parent2_language),
+ street_address = VALUES(street_address),
+ postal_code = VALUES(postal_code),
+ city = VALUES(city),
+ parent1_phone = VALUES(parent1_phone),
+ parent2_phone = VALUES(parent2_phone),
+ consent_rgpd = VALUES(consent_rgpd),
+ consent_email_diffusion = VALUES(consent_email_diffusion),
+ ip_address = VALUES(ip_address),
+ user_agent = VALUES(user_agent),
+ school_day_address = VALUES(school_day_address),
+ age_group = VALUES(age_group),
+ notes = VALUES(notes),
+ updated_at = CURRENT_TIMESTAMP
+ ";
+
+ $stmt = $pdo->prepare($sql);
+ $stmt->execute([
+ ':child_last_name' => $data['child_last_name'],
+ ':child_first_name' => $data['child_first_name'],
+ ':child_birthdate' => $data['child_birthdate'],
+ ':parent1_last_name' => $data['parent1_last_name'],
+ ':parent1_first_name' => $data['parent1_first_name'],
+ ':parent1_language' => $data['parent1_language'],
+ ':parent2_last_name' => $data['parent2_last_name'] !== '' ? $data['parent2_last_name'] : null,
+ ':parent2_first_name' => $data['parent2_first_name'] !== '' ? $data['parent2_first_name'] : null,
+ ':parent2_language' => $data['parent2_language'] !== '' ? $data['parent2_language'] : null,
+ ':street_address' => $data['street_address'],
+ ':postal_code' => $data['postal_code'],
+ ':city' => $data['city'],
+ ':parent1_phone' => $data['parent1_phone'],
+ ':parent2_phone' => $data['parent2_phone'] !== '' ? $data['parent2_phone'] : null,
+ ':email' => mb_strtolower($data['email']),
+ ':consent_rgpd' => $data['consent_rgpd'],
+ ':consent_email_diffusion' => $data['consent_email_diffusion'],
+ ':ip_address' => substr((string) ($_SERVER['REMOTE_ADDR'] ?? ''), 0, 45) ?: null,
+ ':user_agent' => substr((string) ($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 255) ?: null,
+ ':school_day_address' => $data['school_day_address'],
+ ':age_group' => $data['age_group'],
+ ':notes' => $data['notes'] !== '' ? $data['notes'] : null,
+ ]);
+
+ $_SESSION['aprelik_success'] = 'Спасибо. Ваша заявка сохранена.';
+ aprelik_redirect('/aprelik.php');
+} catch (Throwable $e) {
+ $_SESSION['aprelik_errors'] = ['Произошла ошибка при сохранении формы.'];
+ $_SESSION['aprelik_old'] = $data;
+ aprelik_redirect('/aprelik.php');
+}
diff --git a/connexion.php b/connexion.php
index fe60525..ad8edaa 100644
--- a/connexion.php
+++ b/connexion.php
@@ -1,62 +1,99 @@
time()) {
+ $remaining = $blockedUntil - time();
+ $erreur = sprintf(
+ $translations['trop_de_tentatives_connexion'] ?? 'Trop de tentatives. Réessayez dans %d secondes.',
+ $remaining
+ );
} else {
- // 1) On vérifie l’utilisateur
- $stmt = $pdo->prepare("SELECT * FROM compte WHERE email = :email LIMIT 1");
- $stmt->execute(['email' => $email]);
- $compte = $stmt->fetch();
- if ($compte && password_verify($mot_de_passe, $compte['mot_de_passe'])) {
- if (empty($compte['email_valide'])) {
- $_SESSION['pending_validation_resend'] = [
- 'compte_id' => (int) $compte['id'],
- 'email' => (string) $compte['email'],
- 'authorized' => true,
- 'expires_at' => time() + 900, // 15 min
- ];
-
- $erreur = $translations['tvalidat_erreur_email_non_valide'] ?? $translations['erreur_email_non_valide'] ?? 'Veuillez valider votre adresse email avant de vous connecter.';
- } else {
- session_regenerate_id(true); // ✅ anti session fixation
- // 2) Connexion OK → on initialise la session
- $_SESSION['compte_id'] = $compte['id'];
- $_SESSION['role'] = strtolower(trim($compte['role'] ?? ''));
- $_SESSION['reference_id'] = $compte['reference_id'];
- $_SESSION['email'] = $compte['email'];
- $_SESSION['last_activity'] = time();
-
- // 3) On gère la redirection prioritaire vers la page demandée
- $next = $_POST['next'] ?? $_GET['next'] ?? ($_SESSION['redirect_to'] ?? null);
- $next = next_safe($next);
- if ($next) {
- unset($_SESSION['redirect_to']);
- header('Location: ' . $next);
- exit;
- }
-
- // 4) Sinon, fallback par rôle
- if ($_SESSION['role'] === 'client') {
- header('Location: /public/client/mes_rendez_vous.php');exit;
- } elseif ($_SESSION['role'] === 'admin') {
- header('Location: /admin/index.php');exit;
- } elseif ($_SESSION['role'] === 'secretaire') {
- header('Location: /public/cabinet/dashboard.php');exit;
- } else {
- header('Location: /public/praticien/agenda.php');exit;
- }
- }
+ if ($email === '' || $mot_de_passe === '') {
+ $erreur = $translations['erreur_champs_vides'];
} else {
- $erreur = $translations['erreur_identifiants'];
+
+ $stmt = $pdo->prepare("SELECT * FROM compte WHERE email = :email LIMIT 1");
+ $stmt->execute(['email' => $email]);
+ $compte = $stmt->fetch();
+
+ if ($compte && password_verify($mot_de_passe, $compte['mot_de_passe'])) {
+
+ // ✅ reset brute force
+ $_SESSION['login_tries'] = 0;
+ unset($_SESSION['login_blocked_until']);
+
+ if (empty($compte['email_valide'])) {
+
+ $_SESSION['pending_validation_resend'] = [
+ 'compte_id' => (int) $compte['id'],
+ 'email' => (string) $compte['email'],
+ 'authorized' => true,
+ 'expires_at' => time() + 900,
+ ];
+
+ $erreur = $translations['tvalidat_erreur_email_non_valide'] ?? $translations['erreur_email_non_valide'] ?? 'Veuillez valider votre adresse email.';
+
+ } else {
+
+ session_regenerate_id(true);
+
+ $_SESSION['compte_id'] = $compte['id'];
+ $_SESSION['role'] = strtolower(trim($compte['role'] ?? ''));
+ $_SESSION['reference_id'] = $compte['reference_id'];
+ $_SESSION['email'] = $compte['email'];
+ $_SESSION['last_activity'] = time();
+
+ // 🔁 redirection next
+ $next = $_POST['next'] ?? $_GET['next'] ?? ($_SESSION['redirect_to'] ?? null);
+ $next = next_safe($next);
+
+ if ($next) {
+ unset($_SESSION['redirect_to']);
+ header('Location: ' . $next);
+ exit;
+ }
+
+ // 🎯 fallback rôle
+ if ($_SESSION['role'] === 'client') {
+ header('Location: /public/client/mes_rendez_vous.php');exit;
+ } elseif ($_SESSION['role'] === 'admin') {
+ header('Location: /admin/index.php');exit;
+ } elseif ($_SESSION['role'] === 'secretaire') {
+ header('Location: /public/cabinet/dashboard.php');exit;
+ } else {
+ header('Location: /public/praticien/agenda.php');exit;
+ }
+ }
+
+ } else {
+
+ // ❌ Mauvais login
+ $_SESSION['login_tries']++;
+
+ // ⏱️ Blocage après 5 tentatives
+ if ($_SESSION['login_tries'] >= 3) {
+ $_SESSION['login_blocked_until'] = time() + 600; // 10 min
+ }
+
+ // 🐢 ralentissement progressif
+ sleep(min($_SESSION['login_tries'], 3));
+
+ $erreur = $translations['erreur_identifiants'];
+ }
}
}
}
@@ -66,17 +103,13 @@
-
-
-
-
-
-
-
+
+
+
+
-
-
+
@@ -84,130 +117,126 @@