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->execute([$server_id, $name, $type, $allow_file_sharing, $ai_moderation_enabled, $message_limit, $icon, $category_id, $nextPos, $rules_role_id]);
$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);
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()]);
} elseif ($action === 'delete') {
$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->execute([$tag_id, $channel_id]);
echo json_encode(['success' => true]);
}
exit;
}
}

View File

@ -4,13 +4,10 @@ require_once 'auth/session.php';
require_once 'includes/permissions.php';
requireLogin();
$user_id = $_SESSION['user_id'];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$channel_id = $_POST['channel_id'] ?? 0;
$title = $_POST['title'] ?? '';
$is_private = isset($_POST['is_private']) && $_POST['is_private'] == '1' ? 1 : 0;
$content = $_POST['content'] ?? '';
$user_id = $_SESSION['user_id'];
if (!$channel_id || !$title) {
echo json_encode(['success' => false, 'error' => 'Missing data']);
@ -29,8 +26,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
try {
db()->beginTransaction();
$stmt = db()->prepare("INSERT INTO forum_threads (channel_id, user_id, title, is_private) VALUES (?, ?, ?, ?)");
$stmt->execute([$channel_id, $user_id, $title, $is_private]);
$stmt = db()->prepare("INSERT INTO forum_threads (channel_id, user_id, title) VALUES (?, ?, ?)");
$stmt->execute([$channel_id, $user_id, $title]);
$thread_id = db()->lastInsertId();
if (!empty($tag_ids)) {
@ -39,23 +36,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
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();
echo json_encode(['success' => true, 'thread_id' => $thread_id]);
} catch (Exception $e) {
@ -75,6 +55,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'PATCH' || $_SERVER['REQUEST_METHOD'] === 'DE
$action = 'delete';
}
$user_id = $_SESSION['user_id'];
if (!$thread_id) {
echo json_encode(['success' => false, 'error' => 'Missing thread_id']);
exit;
@ -151,4 +133,4 @@ if ($_SERVER['REQUEST_METHOD'] === 'PATCH' || $_SERVER['REQUEST_METHOD'] === 'DE
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}
exit;
}
}

View File

@ -2665,17 +2665,6 @@ document.addEventListener('DOMContentLoaded', () => {
}
} 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();
});

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);
}
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) {
$stmt = db()->prepare("SELECT server_id FROM channels WHERE id = ?");
$stmt->execute([$channel_id]);
@ -155,4 +127,4 @@ class Permissions {
// Fallback to base permissions
return self::hasPermission($user_id, $server_id, $permission);
}
}
}

View File

@ -234,11 +234,10 @@ if ($is_dm_view) {
$active_server = $s;
$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_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::ADMINISTRATOR) ||
Permissions::hasPermission($current_user_id, $active_server_id, Permissions::ADMINISTRATOR) ||
$is_owner;
$can_manage_support = $can_manage_server || Permissions::hasPermission($current_user_id, $active_server_id, Permissions::MANAGE_CHANNELS);
// Event permissions
$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;
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]);
$active_thread = $stmt->fetch();
@ -298,32 +297,26 @@ if ($is_dm_view) {
} 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->execute([$active_channel_id]);
} elseif ($channel_type === "forum" || $channel_type === "support") {
$autoroles = $stmt->fetchAll();
} elseif ($channel_type === 'forum') {
$tag_where = "";
$private_where = "";
$query_params = [$active_server_id, $active_server_id, $active_channel_id];
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))";
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("
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 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.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
(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
FROM forum_threads t
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
");
$stmt->execute($query_params);
@ -706,7 +699,6 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<?php endif; ?>
<span class="channel-name-text">
<?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'])): ?>
<span class="ms-1">(<?php echo $c['event_count']; ?>)</span>
<?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'] === '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"] === "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>';
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']};" : ""; ?>">
<?php echo htmlspecialchars($m['username']); ?>
<?php echo renderRoleIcon($m['role_icon'], '14px'); ?>
<span class="message-time"><?php echo date('H:i', strtotime($m['created_at'])); ?></span>
<?php if ($is_solution): ?>
<span class="badge bg-success ms-2">SOLUTION</span>
<?php endif; ?>
<div class="message-actions-menu">
<span class="message-time"><?php echo date('d/m/Y H:i', strtotime($m['created_at'])); ?></span>
<div class="message-actions-menu d-inline-flex ms-2" style="margin-left: 0 !important; opacity: 1; vertical-align: middle;">
<?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']; ?>">
<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>
<?php endif; ?>
</div>
<?php if ($is_solution): ?>
<span class="badge bg-success ms-2">SOLUTION</span>
<?php endif; ?>
</div>
<div class="message-text">
<?php echo parse_emotes($m['content'], $user['username']); ?>
@ -1374,11 +1365,11 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
</div>
<?php endif; ?>
</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="d-flex justify-content-between align-items-center mb-4">
<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">
<?php
$s_id = $active_server_id;
@ -1450,7 +1441,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
</div>
<div class="thread-activity text-end small text-muted">
<?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; ?>
</div>
</a>
@ -1493,7 +1484,22 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<?php echo htmlspecialchars($m['username']); ?>
<?php echo renderRoleIcon($m['role_icon'], '14px'); ?>
</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']): ?>
<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>
@ -2733,7 +2739,6 @@ document.addEventListener('DOMContentLoaded', () => {
<option value="rules">Salon de Règlement</option>
<option value="autorole">Salon d'Auto-rôles</option>
<option value="forum">Salon Forum</option>
<option value="support">Salon de Support</option>
<option value="event">Salon Événement</option>
<option value="category">Catégorie</option>
<option value="separator">Séparateur</option>
@ -2759,7 +2764,6 @@ document.addEventListener('DOMContentLoaded', () => {
<option value="fa-lock">🔒 Privé</option>
<option value="fa-star"> Étoile</option>
<option value="fa-heart">❤️ Cœur</option>
<option value="fa-ticket-alt">🎫 Support</option>
<option value="fa-gamepad">🎮 Jeux</option>
<option value="fa-music">🎵 Musique</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>
<input type="text" id="new-thread-title" class="form-control" placeholder="À quoi pensez-vous ?">
</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">
<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">
<!-- Tags loaded here -->
</div>
</div>
<div class="mb-3 d-none" id="new-thread-privacy-section">
<div class="form-check form-switch">
<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>
</div>
<div class="modal-footer">
<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>
</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; ?>