diff --git a/api_v1_accept_rules.php b/api_v1_accept_rules.php
new file mode 100644
index 0000000..fd704af
--- /dev/null
+++ b/api_v1_accept_rules.php
@@ -0,0 +1,59 @@
+ false, 'error' => 'ID de canal manquant']);
+ exit;
+ }
+
+ // Fetch channel details to get rules_role_id
+ $stmt = db()->prepare("SELECT * FROM channels WHERE id = ? AND type = 'rules'");
+ $stmt->execute([$channel_id]);
+ $channel = $stmt->fetch();
+
+ if (!$channel) {
+ echo json_encode(['success' => false, 'error' => 'Canal de règles introuvable']);
+ exit;
+ }
+
+ if (empty($channel['rules_role_id'])) {
+ echo json_encode(['success' => false, 'error' => 'Aucun rôle n\'est configuré pour ce canal']);
+ exit;
+ }
+
+ $role_id = $channel['rules_role_id'];
+
+ try {
+ db()->beginTransaction();
+
+ // 1. Record acceptance
+ $stmtAcc = db()->prepare("INSERT IGNORE INTO rule_acceptances (user_id, channel_id) VALUES (?, ?)");
+ $stmtAcc->execute([$user_id, $channel_id]);
+
+ // 2. Assign role
+ // Check if user already has this role
+ $stmtRoleCheck = db()->prepare("SELECT 1 FROM user_roles WHERE user_id = ? AND role_id = ?");
+ $stmtRoleCheck->execute([$user_id, $role_id]);
+
+ if (!$stmtRoleCheck->fetch()) {
+ $stmtRole = db()->prepare("INSERT INTO user_roles (user_id, role_id) VALUES (?, ?)");
+ $stmtRole->execute([$user_id, $role_id]);
+ }
+
+ db()->commit();
+ echo json_encode(['success' => true]);
+ } catch (Exception $e) {
+ db()->rollBack();
+ echo json_encode(['success' => false, 'error' => 'Erreur lors de l\'attribution du rôle : ' . $e->getMessage()]);
+ }
+ exit;
+}
+
+echo json_encode(['success' => false, 'error' => 'Méthode non autorisée']);
diff --git a/api_v1_channels.php b/api_v1_channels.php
index 43d7054..dfb854c 100644
--- a/api_v1_channels.php
+++ b/api_v1_channels.php
@@ -56,6 +56,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$icon = $_POST['icon'] ?? null;
if ($icon === '') $icon = null;
$category_id = !empty($_POST['category_id']) ? (int)$_POST['category_id'] : null;
+ $rules_role_id = !empty($_POST['rules_role_id']) ? (int)$_POST['rules_role_id'] : null;
// Check if user has permission to manage channels
$stmt = db()->prepare("SELECT server_id FROM channels WHERE id = ?");
@@ -67,8 +68,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Allow spaces, accents and mixed case
$name = trim($name);
// Explicitly exclude position from update to prevent jumping to bottom
- $stmt = db()->prepare("UPDATE channels SET name = ?, type = ?, status = ?, allow_file_sharing = ?, message_limit = ?, icon = ?, category_id = ? WHERE id = ?");
- $stmt->execute([$name, $type, $status, $allow_file_sharing, $message_limit, $icon, $category_id, $channel_id]);
+ $stmt = db()->prepare("UPDATE channels SET name = ?, type = ?, status = ?, allow_file_sharing = ?, message_limit = ?, icon = ?, category_id = ?, rules_role_id = ? WHERE id = ?");
+ $stmt->execute([$name, $type, $status, $allow_file_sharing, $message_limit, $icon, $category_id, $rules_role_id, $channel_id]);
}
header('Location: index.php?server_id=' . $server_id . '&channel_id=' . $channel_id);
exit;
@@ -103,6 +104,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$icon = $_POST['icon'] ?? null;
if ($icon === '') $icon = null;
$category_id = !empty($_POST['category_id']) ? (int)$_POST['category_id'] : null;
+ $rules_role_id = !empty($_POST['rules_role_id']) ? (int)$_POST['rules_role_id'] : null;
// Get next position
$stmtPos = db()->prepare("SELECT MAX(position) as max_pos FROM channels WHERE server_id = ?");
@@ -110,8 +112,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$maxPos = $stmtPos->fetch();
$nextPos = ($maxPos['max_pos'] ?? -1) + 1;
- $stmt = db()->prepare("INSERT INTO channels (server_id, name, type, allow_file_sharing, message_limit, icon, category_id, position) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
- $stmt->execute([$server_id, $name, $type, $allow_file_sharing, $message_limit, $icon, $category_id, $nextPos]);
+ $stmt = db()->prepare("INSERT INTO channels (server_id, name, type, allow_file_sharing, message_limit, icon, category_id, position, rules_role_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
+ $stmt->execute([$server_id, $name, $type, $allow_file_sharing, $message_limit, $icon, $category_id, $nextPos, $rules_role_id]);
$channel_id = db()->lastInsertId();
header('Location: index.php?server_id=' . $server_id . '&channel_id=' . $channel_id);
diff --git a/api_v1_withdraw_rules.php b/api_v1_withdraw_rules.php
new file mode 100644
index 0000000..aef376d
--- /dev/null
+++ b/api_v1_withdraw_rules.php
@@ -0,0 +1,50 @@
+ false, 'error' => 'ID de canal manquant']);
+ exit;
+ }
+
+ // Fetch channel details to get rules_role_id
+ $stmt = db()->prepare("SELECT * FROM channels WHERE id = ? AND type = 'rules'");
+ $stmt->execute([$channel_id]);
+ $channel = $stmt->fetch();
+
+ if (!$channel) {
+ echo json_encode(['success' => false, 'error' => 'Canal de règles introuvable']);
+ exit;
+ }
+
+ $role_id = $channel['rules_role_id'];
+
+ try {
+ db()->beginTransaction();
+
+ // 1. Remove acceptance record
+ $stmtAcc = db()->prepare("DELETE FROM rule_acceptances WHERE user_id = ? AND channel_id = ?");
+ $stmtAcc->execute([$user_id, $channel_id]);
+
+ // 2. Remove role if it was configured
+ if ($role_id) {
+ $stmtRole = db()->prepare("DELETE FROM user_roles WHERE user_id = ? AND role_id = ?");
+ $stmtRole->execute([$user_id, $role_id]);
+ }
+
+ db()->commit();
+ echo json_encode(['success' => true]);
+ } catch (Exception $e) {
+ db()->rollBack();
+ echo json_encode(['success' => false, 'error' => 'Erreur lors du retrait : ' . $e->getMessage()]);
+ }
+ exit;
+}
+
+echo json_encode(['success' => false, 'error' => 'Méthode non autorisée']);
diff --git a/assets/js/main.js b/assets/js/main.js
index a80faf1..3f261c9 100644
--- a/assets/js/main.js
+++ b/assets/js/main.js
@@ -1023,9 +1023,16 @@ document.addEventListener('DOMContentLoaded', () => {
modal.querySelector('#edit-channel-limit').value = btn.dataset.limit || '';
modal.querySelector('#edit-channel-status').value = btn.dataset.status || '';
modal.querySelector('#edit-channel-icon').value = btn.dataset.icon || '';
+ modal.querySelector('#edit-channel-rules-role').value = btn.dataset.rulesRole || '';
modal.querySelector('#edit-channel-category-id').value = btn.dataset.category || '';
modal.querySelector('#delete-channel-id').value = channelId;
+ // Toggle rules role visibility
+ const rulesRoleContainer = document.getElementById('edit-channel-rules-role-container');
+ if (rulesRoleContainer) {
+ rulesRoleContainer.style.display = (channelType === 'rules') ? 'block' : 'none';
+ }
+
// Reset delete zone
document.getElementById('delete-confirm-zone').style.display = 'none';
@@ -1950,18 +1957,94 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
+ // Rules Acceptance
+ document.getElementById('accept-rules-btn')?.addEventListener('click', async () => {
+ const btn = document.getElementById('accept-rules-btn');
+ btn.disabled = true;
+ btn.innerHTML = ' Traitement...';
+
+ try {
+ const resp = await fetch('api_v1_accept_rules.php', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ channel_id: window.activeChannelId })
+ });
+ const data = await resp.json();
+ if (data.success) {
+ const container = document.getElementById('rules-acceptance-container');
+ container.innerHTML = '
Vous avez accepté les règles.
';
+ // Reload roles in members list if possible, or just reload page
+ setTimeout(() => location.reload(), 1500);
+ } else {
+ alert(data.error || 'Erreur lors de l\'acceptation');
+ btn.disabled = false;
+ btn.innerHTML = ' J\'accepte les règles';
+ }
+ } catch (e) {
+ console.error(e);
+ btn.disabled = false;
+ btn.innerHTML = ' J\'accepte les règles';
+ }
+ });
+
+ document.getElementById('withdraw-rules-btn')?.addEventListener('click', async () => {
+ if (!confirm('Êtes-vous sûr de vouloir retirer votre acceptation des règles ? Vous perdrez le rôle associé.')) return;
+
+ const btn = document.getElementById('withdraw-rules-btn');
+ btn.disabled = true;
+ btn.innerHTML = ' Traitement...';
+
+ try {
+ const resp = await fetch('api_v1_withdraw_rules.php', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ channel_id: window.activeChannelId })
+ });
+ const data = await resp.json();
+ if (data.success) {
+ location.reload();
+ } else {
+ alert(data.error || 'Erreur lors du retrait');
+ btn.disabled = false;
+ btn.innerHTML = ' Retirer mon acceptation';
+ }
+ } catch (e) {
+ console.error(e);
+ btn.disabled = false;
+ btn.innerHTML = ' Retirer mon acceptation';
+ }
+ });
+
// Channel Selection Type
const addChannelBtns = document.querySelectorAll('.add-channel-btn');
addChannelBtns.forEach(btn => {
btn.addEventListener('click', () => {
const type = btn.dataset.type;
- const select = document.getElementById('channel-type-select');
+ const select = document.getElementById('add-channel-type'); // Corrected ID from index.php
if (select) {
- select.value = type === 'voice' ? 'voice' : 'chat';
+ select.value = type === 'voice' ? 'voice' : (type || 'chat');
+ // Trigger change to update visibility
+ select.dispatchEvent(new Event('change'));
}
});
});
+ const addChannelTypeSelect = document.getElementById('add-channel-type');
+ addChannelTypeSelect?.addEventListener('change', (e) => {
+ const container = document.getElementById('add-channel-rules-role-container');
+ if (container) {
+ container.style.display = (e.target.value === 'rules') ? 'block' : 'none';
+ }
+ });
+
+ const editChannelTypeSelect = document.getElementById('edit-channel-type');
+ editChannelTypeSelect?.addEventListener('change', (e) => {
+ const container = document.getElementById('edit-channel-rules-role-container');
+ if (container) {
+ container.style.display = (e.target.value === 'rules') ? 'block' : 'none';
+ }
+ });
+
// User Settings - Avatar Search
const avatarSearchBtn = document.getElementById('search-avatar-btn');
const avatarSearchQuery = document.getElementById('avatar-search-query');
diff --git a/db/migrations/20260216_rules_acceptance.sql b/db/migrations/20260216_rules_acceptance.sql
new file mode 100644
index 0000000..8979a5f
--- /dev/null
+++ b/db/migrations/20260216_rules_acceptance.sql
@@ -0,0 +1,12 @@
+-- Add rules_role_id to channels and create rule_acceptances table
+ALTER TABLE channels ADD COLUMN rules_role_id INT NULL;
+
+CREATE TABLE IF NOT EXISTS rule_acceptances (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ user_id INT NOT NULL,
+ channel_id INT NOT NULL,
+ accepted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UNIQUE KEY user_channel (user_id, channel_id),
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
+ FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE
+);
diff --git a/debug_reorder.log b/debug_reorder.log
index b86d5af..59e9ff7 100644
--- a/debug_reorder.log
+++ b/debug_reorder.log
@@ -6,3 +6,6 @@
2026-02-15 23:39:45 - Server: 1 - Orders: [{"id":"11","position":0,"category_id":null},{"id":"12","position":1,"category_id":null},{"id":"10","position":2,"category_id":null},{"id":"1","position":3,"category_id":"10"},{"id":"6","position":4,"category_id":"10"},{"id":"2","position":5,"category_id":"10"},{"id":"9","position":6,"category_id":null},{"id":"3","position":7,"category_id":null}]
2026-02-15 23:40:11 - Server: 1 - Orders: [{"id":"11","position":0,"category_id":null},{"id":"12","position":1,"category_id":null},{"id":"10","position":2,"category_id":null},{"id":"1","position":3,"category_id":"10"},{"id":"6","position":4,"category_id":"10"},{"id":"2","position":5,"category_id":"10"},{"id":"13","position":6,"category_id":null},{"id":"9","position":7,"category_id":null},{"id":"3","position":8,"category_id":null}]
2026-02-15 23:40:20 - Server: 1 - Orders: [{"id":"11","position":0,"category_id":null},{"id":"12","position":1,"category_id":null},{"id":"10","position":2,"category_id":null},{"id":"1","position":3,"category_id":"10"},{"id":"6","position":4,"category_id":"10"},{"id":"2","position":5,"category_id":"10"},{"id":"14","position":6,"category_id":null},{"id":"13","position":7,"category_id":null},{"id":"9","position":8,"category_id":null},{"id":"3","position":9,"category_id":null}]
+2026-02-16 00:15:14 - Server: 1 - Orders: [{"id":"11","position":0,"category_id":null},{"id":"12","position":1,"category_id":null},{"id":"10","position":2,"category_id":null},{"id":"1","position":3,"category_id":"10"},{"id":"6","position":4,"category_id":"10"},{"id":"15","position":5,"category_id":"10"},{"id":"2","position":6,"category_id":"10"},{"id":"14","position":7,"category_id":null},{"id":"13","position":8,"category_id":null},{"id":"9","position":9,"category_id":null},{"id":"3","position":10,"category_id":null}]
+2026-02-16 00:17:23 - Server: 1 - Orders: [{"id":"11","position":0,"category_id":null},{"id":"12","position":1,"category_id":null},{"id":"10","position":2,"category_id":null},{"id":"1","position":3,"category_id":"10"},{"id":"6","position":4,"category_id":"10"},{"id":"15","position":5,"category_id":"10"},{"id":"2","position":6,"category_id":"10"},{"id":"14","position":7,"category_id":null},{"id":"13","position":8,"category_id":null},{"id":"9","position":9,"category_id":null},{"id":"3","position":10,"category_id":null}]
+2026-02-16 00:17:31 - Server: 1 - Orders: [{"id":"11","position":0,"category_id":null},{"id":"12","position":1,"category_id":null},{"id":"10","position":2,"category_id":null},{"id":"1","position":3,"category_id":"10"},{"id":"6","position":4,"category_id":"10"},{"id":"15","position":5,"category_id":"10"},{"id":"2","position":6,"category_id":"10"},{"id":"14","position":7,"category_id":null},{"id":"13","position":8,"category_id":null},{"id":"9","position":9,"category_id":null},{"id":"3","position":10,"category_id":null}]
diff --git a/index.php b/index.php
index e87abea..5d29f85 100644
--- a/index.php
+++ b/index.php
@@ -201,6 +201,11 @@ if ($is_dm_view) {
");
$stmt->execute([$active_server_id, $active_server_id, $active_server_id, $active_server_id]);
$members = $stmt->fetchAll();
+
+ // Fetch all server roles
+ $stmt = db()->prepare("SELECT * FROM roles WHERE server_id = ? ORDER BY position DESC");
+ $stmt->execute([$active_server_id]);
+ $server_roles = $stmt->fetchAll();
}
// SEO & Env tags
@@ -349,6 +354,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
data-limit="0"
data-status=""
data-icon=""
+ data-rules-role=""
data-category="">
@@ -390,6 +396,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
data-limit=""
data-status=""
data-icon=""
+ data-rules-role=""
data-category="">
@@ -425,6 +432,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
data-limit="0"
data-status=""
data-icon=""
+ data-rules-role=""
data-category=""
data-theme="">
@@ -608,6 +616,31 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
+
+
+ prepare("SELECT 1 FROM rule_acceptances WHERE user_id = ? AND channel_id = ?");
+ $stmtAcc->execute([$current_user_id, $active_channel_id]);
+ $has_accepted = $stmtAcc->fetch();
+ ?>
+
+
+
+ Vous avez accepté les règles.
+
+
+
+
+
+
Veuillez accepter les règles pour obtenir l'accès complet.
+
+
+
+
@@ -1138,6 +1171,15 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
+
+
+
+
@@ -1243,6 +1285,15 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
+
+
+
+
diff --git a/requests.log b/requests.log
index fde3b26..8b37c7f 100644
--- a/requests.log
+++ b/requests.log
@@ -61,3 +61,36 @@
2026-02-15 23:59:43 - GET /?fl_project=38443 - POST: []
2026-02-16 00:02:15 - GET /index.php?server_id=1&channel_id=11 - POST: []
2026-02-16 00:02:20 - GET /index.php?server_id=1&channel_id=11 - POST: []
+2026-02-16 00:06:51 - GET /?fl_project=38443 - POST: []
+2026-02-16 00:11:43 - GET /?fl_project=38443 - POST: []
+2026-02-16 00:13:38 - GET /index.php?server_id=1&channel_id=11 - POST: []
+2026-02-16 00:13:47 - GET /index.php?server_id=1&channel_id=11 - POST: []
+2026-02-16 00:13:53 - GET /index.php?server_id=1&channel_id=11 - POST: []
+2026-02-16 00:14:45 - GET /index.php?server_id=1&channel_id=11 - POST: []
+2026-02-16 00:15:11 - GET /index.php?server_id=1&channel_id=15 - POST: []
+2026-02-16 00:15:42 - GET /index.php?server_id=1&channel_id=11 - POST: []
+2026-02-16 00:15:56 - GET /index.php?server_id=1&channel_id=11 - POST: []
+2026-02-16 00:16:00 - GET /index.php?server_id=1&channel_id=11 - POST: []
+2026-02-16 00:16:00 - GET /index.php?server_id=1&channel_id=11 - POST: []
+2026-02-16 00:17:19 - GET /index.php?server_id=1&channel_id=15 - POST: []
+2026-02-16 00:17:27 - GET /index.php?server_id=1&channel_id=15 - POST: []
+2026-02-16 00:17:35 - GET /index.php?server_id=1&channel_id=6 - POST: []
+2026-02-16 00:17:46 - GET /index.php?server_id=1&channel_id=15 - POST: []
+2026-02-16 00:17:58 - GET /index.php?server_id=1&channel_id=6 - POST: []
+2026-02-16 00:18:04 - GET /index.php?server_id=1&channel_id=15 - POST: []
+2026-02-16 00:18:12 - GET /index.php?server_id=1&channel_id=6 - POST: []
+2026-02-16 00:18:15 - GET /index.php?server_id=1&channel_id=1 - POST: []
+2026-02-16 00:18:17 - GET /index.php?server_id=1&channel_id=15 - POST: []
+2026-02-16 00:18:19 - GET /index.php?server_id=1&channel_id=2 - POST: []
+2026-02-16 00:18:25 - GET /index.php?server_id=1&channel_id=1 - POST: []
+2026-02-16 00:18:27 - GET /index.php?server_id=1&channel_id=11 - POST: []
+2026-02-16 00:19:28 - GET /?fl_project=38443 - POST: []
+2026-02-16 00:23:03 - GET /index.php?server_id=1&channel_id=11 - POST: []
+2026-02-16 00:23:08 - GET /index.php?server_id=1&channel_id=11 - POST: []
+2026-02-16 00:23:14 - GET /index.php?server_id=1&channel_id=11 - POST: []
+2026-02-16 00:23:19 - GET /index.php?server_id=1&channel_id=11 - POST: []
+2026-02-16 00:24:37 - GET /index.php?server_id=1&channel_id=1 - POST: []
+2026-02-16 00:24:58 - GET /index.php?server_id=1&channel_id=1 - POST: []
+2026-02-16 00:25:16 - GET /index.php?server_id=1&channel_id=6 - POST: []
+2026-02-16 00:25:18 - GET /index.php?server_id=1&channel_id=15 - POST: []
+2026-02-16 00:25:27 - GET /index.php?server_id=1&channel_id=2 - POST: []