38443-vm/fix_index_v3.py
2026-02-20 23:46:55 +00:00

816 lines
68 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import sys
import re
with open('index.php', 'r') as f:
content = f.read()
# First, let's restore the thread loop (the first one)
thread_loop_pattern = r'<\?php foreach\(\$messages as \$m\):.*?\<\?php endforeach; \?\>'
# We need a non-greedy regex that handles nested PHP tags
# Actually, it's easier to find the markers.
# Let's find the first foreach($messages as $m)
# and the first endforeach after it.
# I'll just use a simpler approach:
# Find everything between <div class="messages-list" id="messages-list">
# and <div id="typing-indicator" class="typing-indicator"></div>
# and replace it with a clean version of both loops.
start_marker = '<div class="messages-list" id="messages-list">'
end_marker = '<div id="typing-indicator" class="typing-indicator"></div>'
start_pos = content.find(start_marker)
end_pos = content.find(end_marker)
if start_pos == -1 or end_pos == -1:
print("Error: Could not find markers")
sys.exit(1)
# Now we need to keep the structure between those markers but clean it.
# The structure is:
# if($active_thread):
# ...
# foreach($messages as $m): (thread loop)
# ...
# endforeach
# ...
# elseif($channel_type === 'event'):
# ...
# else: (normal chat)
# ...
# foreach($messages as $m): (normal loop)
# ...
# endforeach
# endif
clean_inner = """
<?php if($active_thread): ?>
<div class="thread-view-container p-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<a href="?server_id=<?php echo $active_server_id; ?>&channel_id=<?php echo $active_channel_id; ?><?php echo isset($_GET['status']) ? '&status='.htmlspecialchars($_GET['status']) : ''; ?>" class="btn btn-sm btn-outline-secondary">← Retour au forum</a>
<div class="d-flex gap-2">
<?php if (Permissions::canDoInChannel($current_user_id, $active_channel_id, Permissions::PIN_THREADS)): ?>
<button class="btn btn-sm <?php echo $active_thread['is_pinned'] ? 'btn-primary' : 'btn-outline-primary'; ?>" id="toggle-pin-thread" data-id="<?php echo $active_thread['id']; ?>" data-pinned="<?php echo $active_thread['is_pinned']; ?>">
<i class="fa-solid fa-thumbtack me-1"></i> <?php echo $active_thread['is_pinned'] ? 'Désépingler' : 'Épingler'; ?>
</button>
<?php endif; ?>
<?php if (Permissions::canDoInChannel($current_user_id, $active_channel_id, Permissions::LOCK_THREADS)): ?>
<button class="btn btn-sm <?php echo $active_thread['is_locked'] ? 'btn-warning' : 'btn-outline-warning'; ?>" id="toggle-lock-thread" data-id="<?php echo $active_thread['id']; ?>" data-locked="<?php echo $active_thread['is_locked']; ?>">
<i class="fa-solid <?php echo $active_thread['is_locked'] ? 'fa-unlock' : 'fa-lock'; ?> me-1"></i> <?php echo $active_thread['is_locked'] ? 'Déverrouiller' : 'Verrouiller'; ?>
</button>
<?php endif; ?>
<?php if ($active_thread['user_id'] == $current_user_id || $can_manage_server): ?>
<button class="btn btn-sm btn-outline-danger" id="delete-thread-btn" data-id="<?php echo $active_thread['id']; ?>" data-channel-id="<?php echo $active_channel_id; ?>" data-server-id="<?php echo $active_server_id; ?>">
<i class="fa-solid fa-trash me-1"></i> Supprimer
</button>
<?php endif; ?>
</div>
</div>
<h3>
<?php if($active_thread['is_pinned']): ?><i class="fa-solid fa-thumbtack text-primary me-2 small"></i><?php endif; ?>
<?php if($active_thread['is_locked']): ?><i class="fa-solid fa-lock text-warning me-2 small"></i><?php endif; ?>
Discussion : <?php echo htmlspecialchars($active_thread['title']); ?>
</h3>
<?php
// Fetch tags for this thread
$stmt_t = db()->prepare("SELECT ft.* FROM forum_tags ft JOIN thread_tags tt ON ft.id = tt.tag_id WHERE tt.thread_id = ?");
$stmt_t->execute([$active_thread['id']]);
$thread_tags = $stmt_t->fetchAll();
if ($thread_tags):
foreach ($thread_tags as $t):
?>
<span class="badge rounded-pill me-1" style="background-color: <?php echo htmlspecialchars($t['color']); ?>;"><?php echo htmlspecialchars($t['name']); ?></span>
<?php endforeach; endif; ?>
<div class="messages-list-inner mt-4">
<?php foreach($messages as $m):
$mention_pattern = '/@' . preg_quote($user['username'], '/') . '\\b/';
$is_mentioned = preg_match($mention_pattern, $m['content']);
$is_solution = ($active_thread['solution_message_id'] == $m['id']);
?>
<div class="message-item <?php echo $is_mentioned ? 'mentioned' : ''; ?> <?php echo $is_solution ? 'is-solution' : ''; ?>" data-id="<?php echo $m['id']; ?>" data-raw-content="<?php echo htmlspecialchars($m['content']); ?>">
<div class="message-avatar" style="<?php echo $m['avatar_url'] ? "background-image: url('{$m['avatar_url']}');" : ""; ?>"></div>
<div class="message-content">
<div class="message-header">
<span 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>
<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>
<div class="message-text">
<?php
$is_manual_ann = false;
$is_poll = false;
if (!empty($m['metadata'])) {
$meta_check = json_decode($m['metadata'], true);
if (isset($meta_check['is_manual_announcement']) && $meta_check['is_manual_announcement']) {
$is_manual_ann = true;
}
if (isset($meta_check['is_poll']) && $meta_check['is_poll']) {
$is_poll = true;
}
}
if (!$is_manual_ann && !$is_poll) {
echo parse_emotes($m['content'], $user['username']);
}
?>
</div>
<?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 += (int)$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 = (int)$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; ?>
<div class="message-reactions mt-1" data-message-id="<?php echo $m['id']; ?>">
<?php
// Fetch reactions for this message
$stmt_react = db()->prepare("SELECT emoji, COUNT(*) as count, GROUP_CONCAT(user_id) as users FROM message_reactions WHERE message_id = ? GROUP BY emoji");
$stmt_react->execute([$m['id']]);
$reactions = $stmt_react->fetchAll();
foreach ($reactions as $r):
$reacted = in_array($current_user_id, explode(',', $r['users']));
?>
<span class="reaction-badge <?php echo $reacted ? 'active' : ''; ?>" data-emoji="<?php echo htmlspecialchars($r['emoji']); ?>">
<?php echo parse_emotes($r['emoji']); ?> <span class="count"><?php echo $r['count']; ?></span>
</span>
<?php endforeach; ?>
<span class="add-reaction-btn" title="Ajouter une réaction">+</span>
</div>
<div class="message-actions-menu">
<?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>
</span>
<?php endif; ?>
<?php if ($m['user_id'] == $current_user_id): ?>
<span class="action-btn edit" 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>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php elseif($channel_type === 'event'): ?>
<div class="events-container p-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h2 class="mb-0"><i class="fa-solid fa-calendar-days me-2"></i>Événements</h2>
<p class="text-muted small mb-0">Découvrez et gérez les événements à venir.</p>
</div>
<?php if ($can_create_event): ?>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addEventModal">
<i class="fa-solid fa-plus me-1"></i> Ajouter un événement
</button>
<?php endif; ?>
</div>
<div class="events-grid row g-4">
<?php if (empty($events)): ?>
<div class="col-12 text-center py-5">
<div class="opacity-25 mb-3">
<i class="fa-solid fa-calendar-xmark" style="font-size: 4rem;"></i>
</div>
<h4 class="text-muted">Aucun événement prévu pour le moment.</h4>
<?php if ($can_create_event): ?>
<p class="text-muted">Cliquez sur "Ajouter un événement" pour commencer.</p>
<?php endif; ?>
</div>
<?php else: ?>
<?php foreach($events as $event): ?>
<div class="col-12 col-md-6 col-lg-4">
<div class="card bg-dark border-secondary h-100 overflow-hidden shadow-sm event-card">
<div class="event-banner" style="height: 120px; <?php
if ($event['banner_url']) {
echo "background-image: url('{$event['banner_url']}'); background-size: cover; background-position: center;";
} else {
echo "background-color: " . ($event['banner_color'] ?: 'var(--blurple)') . ";";
}
?>"></div>
<div class="card-body">
<div class="mb-2">
<h5 class="card-title text-white mb-0"><?php echo htmlspecialchars($event['title']); ?></h5>
</div>
<div class="event-meta small text-muted mb-3">
<div class="d-flex align-items-center mb-1">
<i class="fa-solid fa-clock me-2"></i>
<span>
<?php echo date('d/m/Y', strtotime($event['start_date'])); ?> à <?php echo date('H:i', strtotime($event['start_time'])); ?>
</span>
</div>
<?php if (!$event['is_permanent']): ?>
<div class="d-flex align-items-center mb-1">
<i class="fa-solid fa-hourglass-end me-2"></i>
<span>
Fin: <?php echo date('d/m/Y', strtotime($event['end_date'])); ?> à <?php echo date('H:i', strtotime($event['end_time'])); ?>
</span>
</div>
<?php endif; ?>
<?php if ($event['frequency']): ?>
<div class="d-flex align-items-center text-success">
<i class="fa-solid fa-calendar-check me-2"></i>
<span>
<?php
$days = explode(',', $event['frequency']);
$dayNames = ['Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim'];
$mappedDays = array_map(function($d) use ($dayNames) { return $dayNames[$d-1] ?? ''; }, $days);
echo implode(', ', $mappedDays);
?>
</span>
</div>
<?php endif; ?>
</div>
<div class="card-text text-muted small mb-1 event-description">
<?php echo parse_markdown($event['description']); ?>
</div>
<div class="read-more-btn small mb-3">Voir plus</div>
<?php if ($event['enable_reactions']): ?>
<div class="mt-3 pt-3 border-top border-secondary">
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="small text-muted">Participants (<?php echo $event['participation_count']; ?>)</span>
<div class="participant-avatars d-flex">
<?php
$stmt_p = db()->prepare("SELECT u.avatar_url, u.display_name FROM event_participations ep JOIN users u ON ep.user_id = u.id WHERE ep.event_id = ? LIMIT 5");
$stmt_p->execute([$event['id']]);
$participants = $stmt_p->fetchAll();
$p_idx = 0;
foreach ($participants as $p):
?>
<div class="message-avatar border border-dark" title="<?php echo htmlspecialchars($p['display_name']); ?>" style="width: 24px; height: 24px; <?php echo $p['avatar_url'] ? "background-image: url('{$p['avatar_url']}');" : ""; ?>; margin-left: <?php echo $p_idx > 0 ? '-8px' : '0'; ?>; position: relative; z-index: <?php echo 10 - $p_idx; ?>;"></div>
<?php $p_idx++; endforeach; ?>
<?php if ($event['participation_count'] > 5): ?>
<div class="bg-secondary rounded-circle d-flex align-items-center justify-content-center text-white small border border-dark" style="width: 24px; height: 24px; margin-left: -8px; font-size: 10px; position: relative; z-index: 1;">+<?php echo $event['participation_count'] - 5; ?></div>
<?php endif; ?>
</div>
</div>
<button class="btn btn-sm <?php echo $event['is_participating'] ? 'btn-success' : 'btn-outline-primary'; ?> w-100 participate-btn" data-id="<?php echo $event['id']; ?>">
<i class="fa-solid <?php echo $event['is_participating'] ? 'fa-check-circle' : 'fa-hand-pointer'; ?> me-2"></i>
<?php echo $event['is_participating'] ? 'Je participe' : 'Participer'; ?>
</button>
</div>
<?php endif; ?>
</div>
<div class="card-footer bg-transparent border-secondary d-flex justify-content-between align-items-center gap-2">
<div class="small text-muted fst-italic">Organisé par <?php echo htmlspecialchars($event['username']); ?></div>
<div class="d-flex gap-2">
<?php if ($can_edit_event || $event['user_id'] == $current_user_id): ?>
<button class="btn btn-sm btn-outline-info edit-event-btn"
data-id="<?php echo $event['id']; ?>"
data-title="<?php echo htmlspecialchars($event['title']); ?>"
data-description="<?php echo htmlspecialchars($event['description']); ?>"
data-start-date="<?php echo $event['start_date']; ?>"
data-start-time="<?php echo $event['start_time']; ?>"
data-end-date="<?php echo $event['end_date']; ?>"
data-end-time="<?php echo $event['end_time']; ?>"
data-is-permanent="<?php echo $event['is_permanent']; ?>"
data-frequency="<?php echo $event['frequency']; ?>"
data-banner-color="<?php echo $event['banner_color']; ?>"
data-banner-url="<?php echo $event['banner_url']; ?>"
data-enable-reactions="<?php echo $event['enable_reactions']; ?>"
>
<i class="fa-solid fa-pen-to-square"></i>
</button>
<?php endif; ?>
<?php if ($can_delete_event || $event['user_id'] == $current_user_id): ?>
<button class="btn btn-sm btn-outline-danger delete-event-btn" data-id="<?php echo $event['id']; ?>">
<i class="fa-solid fa-trash"></i>
</button>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<?php elseif($channel_type === 'rules'): ?>
<div class="rules-container p-4">
<h2 class="mb-4">📜 <?php echo htmlspecialchars($current_channel_name); ?></h2>
<div id="rules-list-sortable">
<?php $i = 1; foreach($rules as $rule): ?>
<div class="rule-item mb-3 p-3 rounded bg-dark border-start border-4 border-primary d-flex justify-content-between align-items-center" data-id="<?php echo $rule['id']; ?>">
<div class="rule-content flex-grow-1">
<span class="rule-number fw-bold me-2"><?php echo $i++; ?>.</span>
<?php echo parse_emotes($rule['content']); ?>
</div>
<?php if($can_manage_channels): ?>
<div class="rule-actions ms-3 d-flex gap-2">
<button class="btn btn-sm btn-outline-secondary move-rule-btn" data-id="<?php echo $rule['id']; ?>" data-dir="up">↑</button>
<button class="btn btn-sm btn-outline-secondary move-rule-btn" data-id="<?php echo $rule['id']; ?>" data-dir="down">↓</button>
<button class="btn btn-sm btn-outline-light edit-rule-btn" data-id="<?php echo $rule['id']; ?>">Modifier</button>
<button class="btn btn-sm btn-outline-danger delete-rule-btn" data-id="<?php echo $rule['id']; ?>">×</button>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<?php if($can_manage_channels): ?>
<div id="add-rule-form" style="display: none;" class="mt-3 p-3 rounded bg-dark border border-secondary">
<textarea id="new-rule-content" class="form-control bg-dark text-white mb-2" placeholder="Saisissez la règle ici..." rows="3"></textarea>
<div class="d-flex gap-2">
<button class="btn btn-success btn-sm" id="save-new-rule-btn">Enregistrer</button>
<button class="btn btn-secondary btn-sm" id="cancel-new-rule-btn">Annuler</button>
</div>
</div>
<button class="btn btn-primary mt-3" id="add-rule-btn">+ Ajouter une règle</button>
<?php endif; ?>
<?php if (!empty($active_channel['rules_role_id'])): ?>
<?php
$stmtAcc = db()->prepare("SELECT 1 FROM rule_acceptances WHERE user_id = ? AND channel_id = ?");
$stmtAcc->execute([$current_user_id, $active_channel_id]);
$has_accepted = $stmtAcc->fetch();
?>
<div class="mt-5 pt-4 border-top border-secondary text-center" id="rules-acceptance-container">
<?php if ($has_accepted): ?>
<div class="alert alert-success d-inline-block">
<i class="fa-solid fa-check-circle me-2"></i> Vous avez accepté les règles.
</div>
<div class="mt-2">
<button class="btn btn-sm btn-outline-danger" id="withdraw-rules-btn">
<i class="fa-solid fa-undo me-1"></i> Retirer mon acceptation
</button>
</div>
<?php else: ?>
<p class="text-muted mb-3">Veuillez accepter les règles pour obtenir l'accès complet.</p>
<button class="btn btn-lg btn-success px-5" id="accept-rules-btn">
<i class="fa-solid fa-check me-2"></i> J'accepte les règles
</button>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<?php elseif($channel_type === 'autorole'): ?>
<div class="autoroles-container p-4">
<h2 class="mb-4">🛡️ <?php echo htmlspecialchars($current_channel_name); ?></h2>
<p class="text-muted mb-4">Cliquez sur un bouton pour vous attribuer ou vous retirer un rôle.</p>
<div class="d-flex flex-wrap gap-3" id="autorole-buttons-list">
<?php foreach($autoroles as $ar):
// Check if user has this role
$stmtHasRole = db()->prepare("SELECT 1 FROM user_roles WHERE user_id = ? AND role_id = ?");
$stmtHasRole->execute([$current_user_id, $ar['role_id']]);
$has_role = $stmtHasRole->fetch();
?>
<div class="autorole-card p-1 rounded" style="background-color: #2b2d31; border: 1px solid #4e5058; min-width: 200px;">
<button class="btn autorole-toggle-btn d-flex align-items-center gap-2 px-4 py-3 w-100 <?php echo $has_role ? 'btn-primary' : 'btn-outline-secondary'; ?>"
data-role-id="<?php echo $ar['role_id']; ?>"
data-id="<?php echo $ar['id']; ?>"
style="<?php echo $has_role ? 'background-color: var(--blurple); border: none;' : 'background-color: #2b2d31; border: none; color: white;'; ?>">
<span style="font-size: 1.5em;"><?php echo parse_emotes($ar['icon']); ?></span>
<div class="text-start">
<div class="fw-bold"><?php echo htmlspecialchars($ar['title']); ?></div>
<div class="small opacity-75"><?php echo htmlspecialchars($ar['role_name']); ?></div>
</div>
</button>
<?php if($can_manage_channels): ?>
<div class="d-flex justify-content-end gap-2 px-2 pb-2 border-top border-secondary pt-2 mt-1">
<button type="button" class="btn btn-sm btn-link text-info p-0 edit-autorole-btn"
data-id="<?php echo $ar['id']; ?>"
data-icon="<?php echo htmlspecialchars($ar['icon']); ?>"
data-title="<?php echo htmlspecialchars($ar['title']); ?>"
data-role-id="<?php echo $ar['role_id']; ?>"
data-bs-toggle="modal" data-bs-target="#editAutoroleModal">
<i class="fa-solid fa-pen-to-square"></i> Modifier
</button>
<form action="api_v1_autoroles.php" method="POST" class="m-0">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="<?php echo $ar['id']; ?>">
<input type="hidden" name="channel_id" value="<?php echo $active_channel_id; ?>">
<input type="hidden" name="server_id" value="<?php echo $active_server_id; ?>">
<button type="submit" class="btn btn-sm btn-link text-danger p-0 ms-1" title="Supprimer Autorole" onclick="return confirm('Supprimer cet autorole ?')">
<i class="fa-solid fa-trash"></i> Supprimer
</button>
</form>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<?php if($can_manage_channels): ?>
<div class="mt-5 pt-4 border-top border-secondary">
<button class="btn btn-outline-primary" data-bs-toggle="modal" data-bs-target="#addAutoroleModal">
<i class="fa-solid fa-plus me-2"></i> Ajouter un autorole
</button>
</div>
<?php endif; ?>
</div>
<?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 htmlspecialchars($current_channel_name); ?></h2>
<div class="btn-group btn-group-sm forum-filters flex-wrap gap-1">
<?php
$s_id = $active_server_id;
$c_id = $active_channel_id;
?>
<a href="?server_id=<?php echo $s_id; ?>&channel_id=<?php echo $c_id; ?>" class="btn btn-outline-secondary <?php echo empty($selected_tag_ids) ? 'active' : ''; ?>">Tous</a>
<?php foreach($forum_tags as $tag):
$is_active = in_array($tag['id'], $selected_tag_ids);
if ($is_active) {
$new_tags = array_diff($selected_tag_ids, [$tag['id']]);
} else {
$new_tags = array_merge($selected_tag_ids, [$tag['id']]);
}
$tags_query = !empty($new_tags) ? '&tags=' . implode(',', $new_tags) : '';
$tag_url = "?server_id=$s_id&channel_id=$c_id$tags_query";
?>
<a href="<?php echo $tag_url; ?>" class="btn btn-outline-secondary <?php echo $is_active ? 'active' : ''; ?>" style="<?php echo $is_active ? "background-color: {$tag['color']}; border-color: {$tag['color']}; color: white;" : ""; ?>">
<?php echo htmlspecialchars($tag['name']); ?>
</a>
<?php endforeach; ?>
</div>
</div>
<div class="d-flex gap-2">
<?php if(Permissions::canDoInChannel($current_user_id, $active_channel_id, Permissions::MANAGE_TAGS)): ?>
<button class="btn btn-outline-secondary" id="manage-tags-btn">Gérer les tags</button>
<?php endif; ?>
<?php if(Permissions::canDoInChannel($current_user_id, $active_channel_id, Permissions::CREATE_THREAD)): ?>
<button class="btn btn-primary" id="new-thread-btn">Nouvelle Discussion</button>
<?php endif; ?>
</div>
</div>
<div class="thread-list">
<?php if(empty($threads)): ?>
<div class="text-center text-muted mt-5">Pas encore de discussions. Commencez-en une !</div>
<?php endif; ?>
<?php foreach($threads as $thread): ?>
<a href="?server_id=<?php echo $active_server_id; ?>&channel_id=<?php echo $active_channel_id; ?>&thread_id=<?php echo $thread['id']; ?><?php echo !empty($selected_tag_ids) ? '&tags='.implode(',', $selected_tag_ids) : ''; ?>" class="thread-item d-flex align-items-center p-3 mb-2 rounded bg-dark text-decoration-none text-white border-start border-4 <?php echo $thread['is_pinned'] ? 'border-primary' : 'border-secondary'; ?>">
<div class="thread-icon me-3">
<?php if($thread['is_pinned']): ?>
<i class="fa-solid fa-thumbtack text-primary"></i>
<?php elseif($thread['is_locked']): ?>
<i class="fa-solid fa-lock text-warning"></i>
<?php else: ?>
💬
<?php endif; ?>
</div>
<div class="thread-info flex-grow-1">
<div class="thread-title fw-bold">
<?php if($thread['solution_message_id']): ?>
<span class="text-success me-1" title="Résolu">✔</span>
<?php endif; ?>
<?php if($thread['is_locked'] && !$thread['is_pinned']): ?>
<i class="fa-solid fa-lock small me-1 text-muted"></i>
<?php endif; ?>
<?php echo htmlspecialchars($thread['title']); ?>
<?php if($thread['tags']):
$tag_list = explode('|', $thread['tags']);
foreach($tag_list as $tag_data):
list($t_name, $t_color) = explode(':', $tag_data);
?>
<span class="badge rounded-pill ms-1" style="background-color: <?php echo htmlspecialchars($t_color); ?>; font-size: 0.6em;"><?php echo htmlspecialchars($t_name); ?></span>
<?php endforeach; endif; ?>
</div>
<div class="thread-meta small text-muted">
Lancé par <span style="<?php echo !empty($thread['role_color']) ? "color: {$thread['role_color']};" : ""; ?>"><?php echo htmlspecialchars($thread['username']); ?></span>
<?php echo renderRoleIcon($thread['role_icon'], '11px'); ?>
• <?php echo $thread['message_count']; ?> messages
</div>
</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'])); ?>
<?php endif; ?>
</div>
</a>
<?php endforeach; ?>
</div>
</div>
<?php else: ?>
<?php if ($active_thread): ?>
<div class="p-3 border-bottom border-secondary d-flex justify-content-between align-items-center bg-dark bg-opacity-25 sticky-top" style="z-index: 10;">
<div>
<h4 class="mb-0">
<?php if($active_thread['is_pinned']): ?><i class="fa-solid fa-thumbtack text-primary me-2 small"></i><?php endif; ?>
<?php if($active_thread['is_locked']): ?><i class="fa-solid fa-lock text-warning me-2 small"></i><?php endif; ?>
<?php echo htmlspecialchars($active_thread['title']); ?>
</h4>
<div class="small text-muted mt-1">
Par <?php echo htmlspecialchars($active_thread['username']); ?> • Dans #<?php echo htmlspecialchars($current_channel_name); ?>
</div>
</div>
<a href="?server_id=<?php echo $active_server_id; ?>&channel_id=<?php echo $active_channel_id; ?><?php echo !empty($selected_tag_ids) ? '&tags='.implode(',', $selected_tag_ids) : ''; ?>" class="btn btn-outline-secondary btn-sm">
<i class="fa-solid fa-arrow-left me-1"></i> Retour au forum
</a>
</div>
<?php endif; ?>
<?php if(empty($messages)): ?>
<div style="text-align: center; color: var(--text-muted); margin-top: 40px;">
<h4>Bienvenue dans #<?php echo htmlspecialchars($current_channel_name); ?> !</h4>
<p>C'est le début du salon #<?php echo htmlspecialchars($current_channel_name); ?>.</p>
</div>
<?php endif; ?>
<?php foreach($messages as $m):
$mention_pattern = '/@' . preg_quote($user['username'], '/') . '\\b/';
$is_mentioned = preg_match($mention_pattern, $m['content']);
?>
<div class="message-item <?php echo $is_mentioned ? 'mentioned' : ''; ?> <?php echo $m['is_pinned'] ? 'pinned' : ''; ?> <?php echo $channel_type === 'announcement' ? 'announcement-style' : ''; ?>" data-id="<?php echo $m['id']; ?>" data-raw-content="<?php echo htmlspecialchars($m['content']); ?>">
<div class="message-avatar" style="<?php echo $m['avatar_url'] ? "background-image: url('{$m['avatar_url']}');" : ""; ?>"></div>
<div class="message-content">
<div class="message-header">
<span 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>
<span class="message-time"><?php echo date('H:i', strtotime($m['created_at'])); ?></span>
<?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>
Épinglé
</span>
<?php endif; ?>
</div>
<div class="message-text">
<?php
$is_manual_ann = false;
$is_poll = false;
if (!empty($m['metadata'])) {
$meta_check = json_decode($m['metadata'], true);
if (isset($meta_check['is_manual_announcement']) && $meta_check['is_manual_announcement']) {
$is_manual_ann = true;
}
if (isset($meta_check['is_poll']) && $meta_check['is_poll']) {
$is_poll = true;
}
}
if (!$is_manual_ann && !$is_poll) {
echo parse_emotes($m['content'], $user['username']);
}
?>
</div>
<?php if ($m['attachment_url']): ?>
<div class="message-attachment mt-2">
<?php
$ext = strtolower(pathinfo($m['attachment_url'], PATHINFO_EXTENSION));
if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp'])):
?>
<img src="<?php echo htmlspecialchars($m['attachment_url']); ?>" class="img-fluid rounded message-img-preview" alt="Attachment" style="max-height: 300px; cursor: pointer;" onclick="window.open(this.src)">
<?php else: ?>
<a href="<?php echo htmlspecialchars($m['attachment_url']); ?>" target="_blank" class="attachment-link d-inline-flex align-items-center p-2 rounded bg-dark text-white text-decoration-none">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="me-2"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline></svg>
<?php echo basename($m['attachment_url']); ?>
</a>
<?php endif; ?>
</div>
<?php endif; ?>
<?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 += (int)$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 = (int)$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; ?>
<div class="message-reactions mt-1" data-message-id="<?php echo $m['id']; ?>">
<?php
// Fetch reactions for this message
$stmt_react = db()->prepare("SELECT emoji, COUNT(*) as count, GROUP_CONCAT(user_id) as users FROM message_reactions WHERE message_id = ? GROUP BY emoji");
$stmt_react->execute([$m['id']]);
$reactions = $stmt_react->fetchAll();
foreach ($reactions as $r):
$reacted = in_array($current_user_id, explode(',', $r['users']));
?>
<span class="reaction-badge <?php echo $reacted ? 'active' : ''; ?>" data-emoji="<?php echo htmlspecialchars($r['emoji']); ?>">
<?php echo parse_emotes($r['emoji']); ?> <span class="count"><?php echo $r['count']; ?></span>
</span>
<?php endforeach; ?>
<span class="add-reaction-btn" title="Ajouter une réaction">+</span>
</div>
</div>
<?php if ($m['user_id'] == $current_user_id || ($active_server_id != 'dms' && $can_manage_channels)): ?>
<div class="message-actions-menu">
<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; ?>
</div>
<?php endforeach; ?>
<?php endif; ?>
"""
new_content = clean_inner.strip()
content = content[:start_pos + len(start_marker)] + "\n" + new_content + "\n" + content[end_pos:]
with open('index.php', 'w') as f:
f.write(content)