Release V1.6

This commit is contained in:
Flatlogic Bot 2026-03-14 23:41:10 +00:00
parent 424728da32
commit 2ee58fd533
8 changed files with 44 additions and 234 deletions

View File

@ -127,13 +127,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$stmt = db()->prepare("INSERT INTO channels (server_id, name, type, allow_file_sharing, ai_moderation_enabled, message_limit, icon, category_id, position, rules_role_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); $stmt = db()->prepare("INSERT INTO channels (server_id, name, type, allow_file_sharing, ai_moderation_enabled, message_limit, icon, category_id, position, rules_role_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$server_id, $name, $type, $allow_file_sharing, $ai_moderation_enabled, $message_limit, $icon, $category_id, $nextPos, $rules_role_id]); $stmt->execute([$server_id, $name, $type, $allow_file_sharing, $ai_moderation_enabled, $message_limit, $icon, $category_id, $nextPos, $rules_role_id]);
$channel_id = db()->lastInsertId(); $channel_id = db()->lastInsertId();
if ($type === 'support') {
$stmtTags = db()->prepare("INSERT INTO forum_tags (channel_id, name, color) VALUES (?, ?, ?)");
$stmtTags->execute([$channel_id, 'Ticket prioritaire', '#dc3545']);
$stmtTags->execute([$channel_id, 'Ticket non prioritaire', '#6c757d']);
$stmtTags->execute([$channel_id, 'Ticket urgent', '#ffc107']);
}
header('Location: index.php?server_id=' . $server_id . '&channel_id=' . $channel_id); header('Location: index.php?server_id=' . $server_id . '&channel_id=' . $channel_id);
exit; exit;
@ -142,4 +135,4 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
} }
} }
header('Location: index.php'); header('Location: index.php');

View File

@ -46,28 +46,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
echo json_encode(['success' => true, 'tag_id' => db()->lastInsertId()]); echo json_encode(['success' => true, 'tag_id' => db()->lastInsertId()]);
} elseif ($action === 'delete') { } elseif ($action === 'delete') {
$tag_id = $data['tag_id'] ?? 0; $tag_id = $data['tag_id'] ?? 0;
// Check if it's a default tag in a support channel
$stmtCheck = db()->prepare("
SELECT t.name, c.type
FROM forum_tags t
JOIN channels c ON t.channel_id = c.id
WHERE t.id = ?
");
$stmtCheck->execute([$tag_id]);
$tag = $stmtCheck->fetch();
if ($tag && $tag['type'] === 'support') {
$defaults = ['Ticket prioritaire', 'Ticket non prioritaire', 'Ticket urgent'];
if (in_array($tag['name'], $defaults)) {
echo json_encode(['success' => false, 'error' => 'Default support tags cannot be deleted']);
exit;
}
}
$stmt = db()->prepare("DELETE FROM forum_tags WHERE id = ? AND channel_id = ?"); $stmt = db()->prepare("DELETE FROM forum_tags WHERE id = ? AND channel_id = ?");
$stmt->execute([$tag_id, $channel_id]); $stmt->execute([$tag_id, $channel_id]);
echo json_encode(['success' => true]); echo json_encode(['success' => true]);
} }
exit; exit;
} }

View File

@ -4,13 +4,10 @@ require_once 'auth/session.php';
require_once 'includes/permissions.php'; require_once 'includes/permissions.php';
requireLogin(); requireLogin();
$user_id = $_SESSION['user_id'];
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$channel_id = $_POST['channel_id'] ?? 0; $channel_id = $_POST['channel_id'] ?? 0;
$title = $_POST['title'] ?? ''; $title = $_POST['title'] ?? '';
$is_private = isset($_POST['is_private']) && $_POST['is_private'] == '1' ? 1 : 0; $user_id = $_SESSION['user_id'];
$content = $_POST['content'] ?? '';
if (!$channel_id || !$title) { if (!$channel_id || !$title) {
echo json_encode(['success' => false, 'error' => 'Missing data']); echo json_encode(['success' => false, 'error' => 'Missing data']);
@ -29,8 +26,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
try { try {
db()->beginTransaction(); db()->beginTransaction();
$stmt = db()->prepare("INSERT INTO forum_threads (channel_id, user_id, title, is_private) VALUES (?, ?, ?, ?)"); $stmt = db()->prepare("INSERT INTO forum_threads (channel_id, user_id, title) VALUES (?, ?, ?)");
$stmt->execute([$channel_id, $user_id, $title, $is_private]); $stmt->execute([$channel_id, $user_id, $title]);
$thread_id = db()->lastInsertId(); $thread_id = db()->lastInsertId();
if (!empty($tag_ids)) { if (!empty($tag_ids)) {
@ -39,23 +36,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($tag_id) $stmtTag->execute([$thread_id, $tag_id]); if ($tag_id) $stmtTag->execute([$thread_id, $tag_id]);
} }
} }
if ($content) {
$stmtMsg = db()->prepare("INSERT INTO messages (channel_id, thread_id, user_id, content) VALUES (?, ?, ?, ?)");
$stmtMsg->execute([$channel_id, $thread_id, $user_id, $content]);
$message_id = db()->lastInsertId();
// Default reactions for support tickets
$stmtChan = db()->prepare("SELECT type FROM channels WHERE id = ?");
$stmtChan->execute([$channel_id]);
$chan = $stmtChan->fetch();
if ($chan && $chan['type'] === 'support') {
$stmtReact = db()->prepare("INSERT IGNORE INTO message_reactions (message_id, user_id, emoji) VALUES (?, ?, ?)");
$stmtReact->execute([$message_id, $user_id, '👍']);
$stmtReact->execute([$message_id, $user_id, '👎']);
}
}
db()->commit(); db()->commit();
echo json_encode(['success' => true, 'thread_id' => $thread_id]); echo json_encode(['success' => true, 'thread_id' => $thread_id]);
} catch (Exception $e) { } catch (Exception $e) {
@ -75,6 +55,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'PATCH' || $_SERVER['REQUEST_METHOD'] === 'DE
$action = 'delete'; $action = 'delete';
} }
$user_id = $_SESSION['user_id'];
if (!$thread_id) { if (!$thread_id) {
echo json_encode(['success' => false, 'error' => 'Missing thread_id']); echo json_encode(['success' => false, 'error' => 'Missing thread_id']);
exit; exit;
@ -151,4 +133,4 @@ if ($_SERVER['REQUEST_METHOD'] === 'PATCH' || $_SERVER['REQUEST_METHOD'] === 'DE
echo json_encode(['success' => false, 'error' => $e->getMessage()]); echo json_encode(['success' => false, 'error' => $e->getMessage()]);
} }
exit; exit;
} }

View File

@ -2665,17 +2665,6 @@ document.addEventListener('DOMContentLoaded', () => {
} }
} catch (e) { console.error(e); } } catch (e) { console.error(e); }
const privacySection = document.getElementById("new-thread-privacy-section");
if (window.activeChannelType === "support") {
privacySection.classList.remove("d-none");
document.getElementById("newThreadModal").querySelector(".modal-title").textContent = "Nouveau Ticket";
document.getElementById("submit-new-thread-btn").textContent = "Créer le ticket";
} else {
privacySection.classList.add("d-none");
document.getElementById("newThreadModal").querySelector(".modal-title").textContent = "Nouvelle discussion";
document.getElementById("submit-new-thread-btn").textContent = "Créer la discussion";
}
newThreadModal.show(); newThreadModal.show();
}); });

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

View File

@ -53,34 +53,6 @@ class Permissions {
return self::canDoInChannel($user_id, $channel_id, self::SEND_MESSAGES); return self::canDoInChannel($user_id, $channel_id, self::SEND_MESSAGES);
} }
public static function canViewThread($user_id, $thread_id) {
$stmt = db()->prepare("
SELECT t.*, c.type as channel_type, c.server_id
FROM forum_threads t
JOIN channels c ON t.channel_id = c.id
WHERE t.id = ?
");
$stmt->execute([$thread_id]);
$thread = $stmt->fetch();
if (!$thread) return false;
// If it's not private, anyone who can view the channel can view the thread
if (!$thread['is_private']) {
return self::canViewChannel($user_id, $thread['channel_id']);
}
// If private:
// 1. Creator can view
if ($thread['user_id'] == $user_id) return true;
// 2. Admins/Moderators can view
if (self::hasPermission($user_id, $thread['server_id'], self::ADMINISTRATOR)) return true;
if (self::hasPermission($user_id, $thread['server_id'], self::MANAGE_MESSAGES)) return true;
if (self::hasPermission($user_id, $thread['server_id'], self::MANAGE_CHANNELS)) return true;
return false;
}
public static function canDoInChannel($user_id, $channel_id, $permission) { public static function canDoInChannel($user_id, $channel_id, $permission) {
$stmt = db()->prepare("SELECT server_id FROM channels WHERE id = ?"); $stmt = db()->prepare("SELECT server_id FROM channels WHERE id = ?");
$stmt->execute([$channel_id]); $stmt->execute([$channel_id]);
@ -155,4 +127,4 @@ class Permissions {
// Fallback to base permissions // Fallback to base permissions
return self::hasPermission($user_id, $server_id, $permission); return self::hasPermission($user_id, $server_id, $permission);
} }
} }

View File

@ -234,11 +234,10 @@ if ($is_dm_view) {
$active_server = $s; $active_server = $s;
$is_owner = ($s['owner_id'] == $current_user_id); $is_owner = ($s['owner_id'] == $current_user_id);
$can_manage_channels = Permissions::hasPermission($current_user_id, $active_server_id, Permissions::MANAGE_CHANNELS) || $is_owner; $can_manage_channels = Permissions::hasPermission($current_user_id, $active_server_id, Permissions::MANAGE_CHANNELS) || $is_owner;
$can_manage_server = Permissions::hasPermission($current_user_id, $active_server_id, Permissions::MANAGE_SERVER) || $can_manage_server = Permissions::hasPermission($current_user_id, $active_server_id, Permissions::MANAGE_SERVER) ||
Permissions::hasPermission($current_user_id, $active_server_id, Permissions::MANAGE_MESSAGES) || Permissions::hasPermission($current_user_id, $active_server_id, Permissions::MANAGE_MESSAGES) ||
Permissions::hasPermission($current_user_id, $active_server_id, Permissions::ADMINISTRATOR) || Permissions::hasPermission($current_user_id, $active_server_id, Permissions::ADMINISTRATOR) ||
$is_owner; $is_owner;
$can_manage_support = $can_manage_server || Permissions::hasPermission($current_user_id, $active_server_id, Permissions::MANAGE_CHANNELS);
// Event permissions // Event permissions
$can_create_event = Permissions::canDoInChannel($current_user_id, $active_channel_id, Permissions::CREATE_EVENT); $can_create_event = Permissions::canDoInChannel($current_user_id, $active_channel_id, Permissions::CREATE_EVENT);
@ -258,7 +257,7 @@ if ($is_dm_view) {
$active_thread = null; $active_thread = null;
if ($active_thread_id) { if ($active_thread_id) {
$stmt = db()->prepare("SELECT t.*, u.display_name as username, u.username as login_name FROM forum_threads t JOIN users u ON t.user_id = u.id WHERE t.id = ?"); $stmt = db()->prepare("SELECT t.*, (SELECT GROUP_CONCAT(CONCAT(ft.name, ':', ft.color) SEPARATOR '|') FROM thread_tags tt JOIN forum_tags ft ON tt.tag_id = ft.id WHERE tt.thread_id = t.id) as tags, u.display_name as username, u.username as login_name FROM forum_threads t JOIN users u ON t.user_id = u.id WHERE t.id = ?");
$stmt->execute([$active_thread_id]); $stmt->execute([$active_thread_id]);
$active_thread = $stmt->fetch(); $active_thread = $stmt->fetch();
@ -298,32 +297,26 @@ if ($is_dm_view) {
} elseif ($channel_type === 'autorole') { } elseif ($channel_type === 'autorole') {
$stmt = db()->prepare("SELECT ca.*, r.name as role_name FROM channel_autoroles ca JOIN roles r ON ca.role_id = r.id WHERE ca.channel_id = ? ORDER BY ca.id ASC"); $stmt = db()->prepare("SELECT ca.*, r.name as role_name FROM channel_autoroles ca JOIN roles r ON ca.role_id = r.id WHERE ca.channel_id = ? ORDER BY ca.id ASC");
$stmt->execute([$active_channel_id]); $stmt->execute([$active_channel_id]);
} elseif ($channel_type === "forum" || $channel_type === "support") { $autoroles = $stmt->fetchAll();
} elseif ($channel_type === 'forum') {
$tag_where = ""; $tag_where = "";
$private_where = "";
$query_params = [$active_server_id, $active_server_id, $active_channel_id]; $query_params = [$active_server_id, $active_server_id, $active_channel_id];
if (!empty($selected_tag_ids)) { if (!empty($selected_tag_ids)) {
$placeholders = implode(",", array_fill(0, count($selected_tag_ids), "?")); $placeholders = implode(',', array_fill(0, count($selected_tag_ids), '?'));
$tag_where = " AND EXISTS (SELECT 1 FROM thread_tags tt WHERE tt.thread_id = t.id AND tt.tag_id IN ($placeholders))"; $tag_where = " AND EXISTS (SELECT 1 FROM thread_tags tt WHERE tt.thread_id = t.id AND tt.tag_id IN ($placeholders))";
foreach ($selected_tag_ids as $tid) $query_params[] = $tid; foreach ($selected_tag_ids as $tid) $query_params[] = $tid;
} }
if (!$can_manage_support) {
$private_where = " AND (t.is_private = 0 OR t.user_id = ?)";
$query_params[] = $current_user_id;
}
$stmt = db()->prepare(" $stmt = db()->prepare("
SELECT t.*, u.display_name as username, u.avatar_url, SELECT t.*, (SELECT GROUP_CONCAT(CONCAT(ft.name, ':', ft.color) SEPARATOR '|') FROM thread_tags tt JOIN forum_tags ft ON tt.tag_id = ft.id WHERE tt.thread_id = t.id) as tags, u.display_name as username, u.avatar_url,
(SELECT COUNT(*) FROM messages m WHERE m.thread_id = t.id) as message_count, (SELECT COUNT(*) FROM messages m WHERE m.thread_id = t.id) as message_count,
(SELECT MAX(created_at) FROM messages m WHERE m.thread_id = t.id) as last_message_at, (SELECT MAX(created_at) FROM messages m WHERE m.thread_id = t.id) as last_message_at,
(SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color, (SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color,
(SELECT r.icon_url FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_icon, (SELECT r.icon_url FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_icon
(SELECT GROUP_CONCAT(CONCAT(ft.name, "-", ft.color) SEPARATOR "|") FROM thread_tags tt JOIN forum_tags ft ON tt.tag_id = ft.id WHERE tt.thread_id = t.id) as tags
FROM forum_threads t FROM forum_threads t
JOIN users u ON t.user_id = u.id JOIN users u ON t.user_id = u.id
WHERE t.channel_id = ? $tag_where $private_where WHERE t.channel_id = ? $tag_where
ORDER BY t.is_pinned DESC, last_message_at DESC ORDER BY t.is_pinned DESC, last_message_at DESC
"); ");
$stmt->execute($query_params); $stmt->execute($query_params);
@ -706,7 +699,6 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<?php endif; ?> <?php endif; ?>
<span class="channel-name-text"> <span class="channel-name-text">
<?php echo htmlspecialchars($c['name']); ?> <?php echo htmlspecialchars($c['name']); ?>
elseif ($c["type"] === "support") echo "<i class=\"fa-solid fa-ticket-alt\"></i>";
<?php if ($c['type'] === 'event' && !empty($c['event_count'])): ?> <?php if ($c['type'] === 'event' && !empty($c['event_count'])): ?>
<span class="ms-1">(<?php echo $c['event_count']; ?>)</span> <span class="ms-1">(<?php echo $c['event_count']; ?>)</span>
<?php endif; ?> <?php endif; ?>
@ -853,7 +845,6 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
elseif ($active_channel['type'] === 'autorole') echo '<i class="fa-solid fa-shield-halved"></i>'; elseif ($active_channel['type'] === 'autorole') echo '<i class="fa-solid fa-shield-halved"></i>';
elseif ($active_channel['type'] === 'forum') echo '<i class="fa-solid fa-comments"></i>'; elseif ($active_channel['type'] === 'forum') echo '<i class="fa-solid fa-comments"></i>';
elseif ($active_channel['type'] === 'voice') echo '<i class="fa-solid fa-volume-up"></i>'; elseif ($active_channel['type'] === 'voice') echo '<i class="fa-solid fa-volume-up"></i>';
elseif ($active_channel["type"] === "support") echo "<i class=\"fa-solid fa-ticket-alt\"></i>";
elseif ($active_channel['type'] === 'poll') echo '<i class="fa-solid fa-square-poll-vertical"></i>'; elseif ($active_channel['type'] === 'poll') echo '<i class="fa-solid fa-square-poll-vertical"></i>';
else echo '<i class="fa-solid fa-hashtag"></i>'; else echo '<i class="fa-solid fa-hashtag"></i>';
@ -935,11 +926,8 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<div class="message-author" style="<?php echo !empty($m['role_color']) ? "color: {$m['role_color']};" : ""; ?>"> <div class="message-author" style="<?php echo !empty($m['role_color']) ? "color: {$m['role_color']};" : ""; ?>">
<?php echo htmlspecialchars($m['username']); ?> <?php echo htmlspecialchars($m['username']); ?>
<?php echo renderRoleIcon($m['role_icon'], '14px'); ?> <?php echo renderRoleIcon($m['role_icon'], '14px'); ?>
<span class="message-time"><?php echo date('H:i', strtotime($m['created_at'])); ?></span> <span class="message-time"><?php echo date('d/m/Y H:i', strtotime($m['created_at'])); ?></span>
<?php if ($is_solution): ?> <div class="message-actions-menu d-inline-flex ms-2" style="margin-left: 0 !important; opacity: 1; vertical-align: middle;">
<span class="badge bg-success ms-2">SOLUTION</span>
<?php endif; ?>
<div class="message-actions-menu">
<?php if (($active_thread['user_id'] == $current_user_id || $can_manage_server) && $m['user_id'] != $active_thread['user_id']): ?> <?php if (($active_thread['user_id'] == $current_user_id || $can_manage_server) && $m['user_id'] != $active_thread['user_id']): ?>
<span class="action-btn mark-solution <?php echo $is_solution ? 'active' : ''; ?>" title="<?php echo $is_solution ? 'Retirer comme solution' : 'Marquer comme solution'; ?>" data-thread-id="<?php echo $active_thread['id']; ?>" data-message-id="<?php echo $m['id']; ?>"> <span class="action-btn mark-solution <?php echo $is_solution ? 'active' : ''; ?>" title="<?php echo $is_solution ? 'Retirer comme solution' : 'Marquer comme solution'; ?>" data-thread-id="<?php echo $active_thread['id']; ?>" data-message-id="<?php echo $m['id']; ?>">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"></polyline></svg> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"></polyline></svg>
@ -954,6 +942,9 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
</span> </span>
<?php endif; ?> <?php endif; ?>
</div> </div>
<?php if ($is_solution): ?>
<span class="badge bg-success ms-2">SOLUTION</span>
<?php endif; ?>
</div> </div>
<div class="message-text"> <div class="message-text">
<?php echo parse_emotes($m['content'], $user['username']); ?> <?php echo parse_emotes($m['content'], $user['username']); ?>
@ -1374,11 +1365,11 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
</div> </div>
<?php endif; ?> <?php endif; ?>
</div> </div>
<?php elseif($channel_type === 'forum" || $channel_type === "support' && !$active_thread): ?> <?php elseif($channel_type === 'forum' && !$active_thread): ?>
<div class="forum-container p-4"> <div class="forum-container p-4">
<div class="d-flex justify-content-between align-items-center mb-4"> <div class="d-flex justify-content-between align-items-center mb-4">
<div> <div>
<h2 class="mb-2"><?php echo ($channel_type === 'support') ? '🎫' : '🏛️'; ?> <?php echo htmlspecialchars($current_channel_name); ?></h2> <h2 class="mb-2">🏛️ <?php echo htmlspecialchars($current_channel_name); ?></h2>
<div class="btn-group btn-group-sm forum-filters flex-wrap gap-1"> <div class="btn-group btn-group-sm forum-filters flex-wrap gap-1">
<?php <?php
$s_id = $active_server_id; $s_id = $active_server_id;
@ -1450,7 +1441,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
</div> </div>
<div class="thread-activity text-end small text-muted"> <div class="thread-activity text-end small text-muted">
<?php if($thread['last_activity_at']): ?> <?php if($thread['last_activity_at']): ?>
Dernière activité : <?php echo date('H:i', strtotime($thread['last_activity_at'])); ?> Dernière activité : <?php echo date('d/m/Y H:i', strtotime($thread['last_activity_at'])); ?>
<?php endif; ?> <?php endif; ?>
</div> </div>
</a> </a>
@ -1493,7 +1484,22 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<?php echo htmlspecialchars($m['username']); ?> <?php echo htmlspecialchars($m['username']); ?>
<?php echo renderRoleIcon($m['role_icon'], '14px'); ?> <?php echo renderRoleIcon($m['role_icon'], '14px'); ?>
</span> </span>
<span class="message-time"><?php echo date('H:i', strtotime($m['created_at'])); ?></span> <span class="message-time"><?php echo date('d/m/Y H:i', strtotime($m['created_at'])); ?></span>
<?php if ($m['user_id'] == $current_user_id || ($active_server_id != 'dms' && $can_manage_channels)): ?>
<div class="message-actions-menu d-inline-flex ms-2" style="margin-left: 0 !important; opacity: 1; vertical-align: middle;">
<span class="action-btn pin <?php echo $m['is_pinned'] ? 'active' : ''; ?>" title="<?php echo $m['is_pinned'] ? 'Désépingler' : 'Épingler'; ?>" data-id="<?php echo $m['id']; ?>" data-pinned="<?php echo $m['is_pinned'] ? '1' : '0'; ?>">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></svg>
</span>
<?php if ($m['user_id'] == $current_user_id || ($is_manual_ann && $can_manage_channels)): ?>
<span class="action-btn edit <?php echo $is_manual_ann ? 'edit-announcement' : ''; ?>" title="Modifier" data-id="<?php echo $m['id']; ?>">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
</span>
<span class="action-btn delete" title="Supprimer" data-id="<?php echo $m['id']; ?>">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
</span>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if ($m['is_pinned']): ?> <?php if ($m['is_pinned']): ?>
<span class="pinned-badge ms-2" title="Message épinglé"> <span class="pinned-badge ms-2" title="Message épinglé">
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path></svg> <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path></svg>
@ -2733,7 +2739,6 @@ document.addEventListener('DOMContentLoaded', () => {
<option value="rules">Salon de Règlement</option> <option value="rules">Salon de Règlement</option>
<option value="autorole">Salon d'Auto-rôles</option> <option value="autorole">Salon d'Auto-rôles</option>
<option value="forum">Salon Forum</option> <option value="forum">Salon Forum</option>
<option value="support">Salon de Support</option>
<option value="event">Salon Événement</option> <option value="event">Salon Événement</option>
<option value="category">Catégorie</option> <option value="category">Catégorie</option>
<option value="separator">Séparateur</option> <option value="separator">Séparateur</option>
@ -2759,7 +2764,6 @@ document.addEventListener('DOMContentLoaded', () => {
<option value="fa-lock">🔒 Privé</option> <option value="fa-lock">🔒 Privé</option>
<option value="fa-star"> Étoile</option> <option value="fa-star"> Étoile</option>
<option value="fa-heart">❤️ Cœur</option> <option value="fa-heart">❤️ Cœur</option>
<option value="fa-ticket-alt">🎫 Support</option>
<option value="fa-gamepad">🎮 Jeux</option> <option value="fa-gamepad">🎮 Jeux</option>
<option value="fa-music">🎵 Musique</option> <option value="fa-music">🎵 Musique</option>
<option value="fa-video">📹 Vidéo</option> <option value="fa-video">📹 Vidéo</option>
@ -3543,21 +3547,14 @@ document.addEventListener('DOMContentLoaded', () => {
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Titre de la discussion</label> <label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Titre de la discussion</label>
<input type="text" id="new-thread-title" class="form-control" placeholder="À quoi pensez-vous ?"> <input type="text" id="new-thread-title" class="form-control" placeholder="À quoi pensez-vous ?">
</div> </div>
<div class="mb-3">
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Tags</label>
<div id="new-thread-tags-list" class="d-flex flex-wrap gap-2 p-2 bg-dark rounded">
<div class="mb-3"> <div class="mb-3">
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Tags</label> <label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Tags</label>
<div id="new-thread-tags-list" class="d-flex flex-wrap gap-2 p-2 bg-dark rounded"> <div id="new-thread-tags-list" class="d-flex flex-wrap gap-2 p-2 bg-dark rounded">
<!-- Tags loaded here --> <!-- Tags loaded here -->
</div> </div>
</div> </div>
<div class="mb-3 d-none" id="new-thread-privacy-section"> </div>
<div class="form-check form-switch"> <div class="modal-footer">
<input class="form-check-input" type="checkbox" id="new-thread-is-private" checked>
<label class="form-check-label" for="new-thread-is-private">Ticket privé (visible uniquement par le staff et vous)</label>
</div>
</div>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
<button type="button" id="submit-new-thread-btn" class="btn btn-primary">Créer la discussion</button> <button type="button" id="submit-new-thread-btn" class="btn btn-primary">Créer la discussion</button>
</div> </div>

View File

@ -1,104 +0,0 @@
<?php if (!empty($m['metadata'])):
$meta = json_decode($m['metadata'], true);
if ($meta):
$borderColor = htmlspecialchars($meta['color'] ?? 'var(--blurple)');
if (!empty($meta['is_poll'])):
?>
<div class="poll-container rich-embed mt-2 p-3 rounded" style="border-left: 4px solid <?php echo $borderColor; ?>;">
<div class="embed-site-name mb-1" style="font-size: 0.75em; color: var(--text-muted); text-transform: uppercase; font-weight: bold;">📊 SONDAGE</div>
<?php if (!empty($meta['title'])): ?>
<div class="embed-title d-block mb-1" style="font-weight: 600; color: #00a8fc; font-size: 1.1em;"><?php echo htmlspecialchars($meta['title']); ?></div>
<?php endif; ?>
<?php if (!empty($meta['description'])): ?>
<div class="embed-description mb-2" style="font-size: 0.9em; color: var(--text-normal); max-height: 100px; overflow: hidden; position: relative;"><?php echo parse_markdown($meta['description']); ?></div>
<div class="read-more-btn small mb-2" style="display: none; cursor: pointer; color: var(--blurple); font-weight: 600;">Voir plus</div>
<?php endif; ?>
<div class="poll-options-list">
<?php
$total_votes = 0;
if (isset($m['votes_data'])) {
foreach($m['votes_data'] as $v) $total_votes += $v['vote_count'];
}
$options = $meta['options'] ?? [];
foreach($options as $idx => $opt):
$count = 0;
$user_voted = false;
if (isset($m['votes_data'])) {
foreach($m['votes_data'] as $v) {
if ($v['option_index'] == $idx) {
$count = $v['vote_count'];
$user_ids = explode(',', $v['user_ids'] ?? '');
if (in_array($current_user_id, $user_ids)) {
$user_voted = true;
}
break;
}
}
}
$percent = $total_votes > 0 ? round(($count / $total_votes) * 100) : 0;
$is_expired = !empty($meta['end_date']) && strtotime($meta['end_date']) < time();
?>
<div class="poll-option <?php echo $user_voted ? 'voted' : ''; ?> <?php echo $is_expired ? 'expired' : ''; ?>" data-message-id="<?php echo $m['id']; ?>" data-option-index="<?php echo $idx; ?>">
<div class="poll-progress-container">
<div class="poll-progress-bar" style="width: <?php echo $percent; ?>%"></div>
<span class="poll-option-text"><?php echo htmlspecialchars($opt); ?></span>
<span class="poll-option-percent"><?php echo $percent; ?>% (<?php echo $count; ?>)</span>
</div>
</div>
<?php endforeach; ?>
</div>
<div class="poll-footer mt-2" style="font-size: 0.75em; color: var(--text-muted); display: flex; justify-content: space-between; align-items: center;">
<span><?php echo $total_votes; ?> vote<?php echo $total_votes > 1 ? 's' : ''; ?> · <?php echo ($meta['choice_type'] ?? 'single') === 'multiple' ? 'Plusieurs choix' : 'Choix unique'; ?></span>
<?php if (!empty($meta['end_date'])): ?>
<span class="poll-deadline <?php echo $is_expired ? 'text-danger' : ''; ?>">
<?php echo $is_expired ? 'Terminé' : 'Expire le ' . date('d/m/Y H:i', strtotime($meta['end_date'])); ?>
</span>
<?php endif; ?>
</div>
</div>
<?php else: ?>
<div class="rich-embed mt-2 p-3 rounded" style="background: rgba(0,0,0,0.1); border-left: 4px solid <?php echo $borderColor; ?>; max-width: 520px;">
<?php if (!empty($meta['site_name']) && empty($meta['is_rss']) && empty($meta['is_manual_announcement'])): ?>
<div class="embed-site-name mb-1" style="font-size: 0.75em; color: var(--text-muted); text-transform: uppercase; font-weight: bold;"><?php echo htmlspecialchars($meta['site_name']); ?></div>
<?php endif; ?>
<?php if (!empty($meta['title'])): ?>
<?php if (!empty($meta['url'])): ?>
<a href="<?php echo htmlspecialchars($meta['url']); ?>" target="_blank" class="embed-title d-block mb-1 text-decoration-none" style="font-weight: 600; color: #00a8fc; font-size: 1.1em;"><?php echo htmlspecialchars($meta['title']); ?></a>
<?php else: ?>
<div class="embed-title d-block mb-1" style="font-weight: 600; color: #00a8fc; font-size: 1.1em;"><?php echo htmlspecialchars($meta['title']); ?></div>
<?php endif; ?>
<?php endif; ?>
<?php if (!empty($meta['is_rss'])): ?>
<div class="embed-meta mb-2" style="font-size: 0.8em; color: var(--text-muted);">
<?php
$parts = [];
if (!empty($meta['category'])) $parts[] = htmlspecialchars($meta['category']);
if (!empty($meta['date'])) $parts[] = htmlspecialchars($meta['date']);
if (!empty($meta['author'])) $parts[] = htmlspecialchars($meta['author']);
echo implode(' · ', $parts);
?>
</div>
<?php endif; ?>
<?php if (!empty($meta['is_manual_announcement'])): ?>
<div class="embed-meta mb-2" style="font-size: 0.8em; color: var(--text-muted);">
<?php echo date('d/m/Y H:i', strtotime($m['created_at'])); ?>
</div>
<?php endif; ?>
<?php if (!empty($meta['description'])): ?>
<div class="embed-description mb-2" style="font-size: 0.9em; color: var(--text-normal); max-height: 100px; overflow: hidden; position: relative;"><?php echo parse_markdown($meta['description']); ?></div>
<div class="read-more-btn small mb-2" style="display: none; cursor: pointer; color: var(--blurple); font-weight: 600;">Voir plus</div>
<?php endif; ?>
<?php if (!empty($meta['is_manual_announcement']) && !empty($meta['url'])): ?>
<div class="embed-footer mt-2">
<a href="<?php echo htmlspecialchars($meta['url']); ?>" target="_blank" class="btn btn-sm btn-outline-info" style="font-size: 0.8em;">Source / En savoir plus</a>
</div>
<?php endif; ?>
<?php if (!empty($meta['image'])): ?>
<div class="embed-image">
<img src="<?php echo htmlspecialchars($meta['image']); ?>" class="rounded" style="max-width: 100%; max-height: 300px; object-fit: contain;">
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<?php endif; endif; ?>