Autosave: 20260220-234654

This commit is contained in:
Flatlogic Bot 2026-02-20 23:46:55 +00:00
parent 98ca1a26f2
commit 6a5c3bdd28
11 changed files with 1494 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

19
check_divs.php Normal file
View File

@ -0,0 +1,19 @@
<?php
$content = file_get_contents('index.php');
$open = substr_count($content, '<div');
$close = substr_count($content, '</div');
echo "Open: $open, Close: $close\n";
// Trace where it goes wrong
$lines = explode("\n", $content);
$depth = 0;
foreach ($lines as $i => $line) {
$line_open = substr_count($line, '<div');
$line_close = substr_count($line, '</div');
$old_depth = $depth;
$depth += $line_open - $line_close;
if (($i + 1) >= 760 && ($i + 1) <= 780) {
echo "Line " . ($i + 1) . ": Depth $old_depth -> $depth | " . trim($line) . "\n";
}
}
echo "Final depth: $depth\n";

27
check_tags.py Normal file
View File

@ -0,0 +1,27 @@
import sys
def check_html_balance(filename):
with open(filename, 'r') as f:
content = f.read()
# Simple regex to find tags, ignoring PHP for now
import re
tags = re.findall(r'<(/?div|/?span|/?form|/?section|/?article|/?aside|/?header|/?footer|/?nav|/?main)', content)
stack = []
for i, tag in enumerate(tags):
if tag.startswith('/'):
if not stack:
print(f"Error: Closing tag {tag} with no opening tag at index {i}")
else:
stack.pop()
else:
stack.append(tag)
if stack:
print(f"Error: Unbalanced tags remaining: {stack}")
else:
print("Tags are balanced (excluding potential PHP issues)")
check_html_balance('index.php')

View File

@ -0,0 +1,12 @@
-- Migration to add poll votes table
CREATE TABLE IF NOT EXISTS `poll_votes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`message_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`option_index` int(11) NOT NULL,
`created_at` timestamp NULL DEFAULT current_timestamp(),
PRIMARY KEY (`id`),
UNIQUE KEY `user_poll_option` (`message_id`,`user_id`,`option_index`),
CONSTRAINT `poll_votes_ibfk_1` FOREIGN KEY (`message_id`) REFERENCES `messages` (`id`) ON DELETE CASCADE,
CONSTRAINT `poll_votes_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

265
fix_index.py Normal file
View File

@ -0,0 +1,265 @@
import sys
with open('index.php', 'r') as f:
lines = f.readlines()
start_marker = "<?php foreach($messages as $m):"
# We want the one for normal channels, which is the second occurrence (first is for threads)
occurrences = [i for i, line in enumerate(lines) if start_marker in line]
if len(occurrences) < 2:
print(f"Error: Found only {len(occurrences)} occurrences of start marker")
sys.exit(1)
# The one we want to fix is usually the last one if I've been duplicating it
# But let's be careful. The thread one is inside if($active_thread)
# The normal one is inside the final else.
# Let's find the main else at line 1271
else_line = 0
for i, line in enumerate(lines):
if "<?php else: ?>" in line and i > 1200: # around there
else_line = i
break
if not else_line:
print("Error: Could not find the final else line")
sys.exit(1)
# Now find the first foreach after this else
start_line = 0
for i in range(else_line, len(lines)):
if start_marker in lines[i]:
start_line = i
break
if not start_line:
print("Error: Could not find start marker after else")
sys.exit(1)
# Now find the end of this block. It should end with <?php endforeach; ?>
# But I might have multiple ones now.
# We want to find the one that is followed by <?php endif; ?> (closing the main if)
# and then </div> (closing chat-container or messages-list)
end_line = 0
for i in range(start_line, len(lines)):
if "<?php endforeach; ?>" in lines[i]:
# Check if next line (or soon after) is <?php endif; ?>
is_real_end = False
for j in range(i+1, min(i+10, len(lines))):
if "<?php endif; ?>" in lines[j]:
is_real_end = True
end_line = j
break
if is_real_end:
break
if not end_line:
print("Error: Could not find end marker")
sys.exit(1)
print(f"Replacing lines {start_line+1} to {end_line+1}")
new_block = """ <?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']);
}
?>
<?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>
<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; ?>"""
# Adjust end_line to be the last endif before the start of next section
# The original file has <?php endif; ?> at line 1486 in my previous read.
# Let's verify.
with open('index.php', 'w') as f:
f.writelines(lines[:start_line])
f.write(new_block)
f.write("\n")
f.writelines(lines[end_line+1:])

222
fix_index_v2.py Normal file
View File

@ -0,0 +1,222 @@
import sys
with open('index.php', 'r') as f:
lines = f.readlines()
# Section starts after line 1294 (index 1294)
# Section ends before chat-input-container (Line 1749 -> index 1748)
start_index = 1294
end_index = 0
for i, line in enumerate(lines):
if '<div class="chat-input-container">' in line:
end_index = i
break
if not end_index:
print("Error: Could not find end marker")
sys.exit(1)
print(f"Replacing lines {start_index+1} to {end_index}")
new_content = """ <?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']);
}
?>
<?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>
<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; ?>
</div>
<div id="typing-indicator" class="typing-indicator"></div>
"""
with open('index.php', 'w') as f:
f.writelines(lines[:start_index])
f.write(new_content)
f.write("\n")
f.writelines(lines[end_index:])

815
fix_index_v3.py Normal file
View File

@ -0,0 +1,815 @@
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)

104
index.php_partial_fix Normal file
View File

@ -0,0 +1,104 @@
<?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; ?>

30
test_poll.php Normal file
View File

@ -0,0 +1,30 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
$_SESSION['user_id'] = 2; // Simulate logged in user
$_SERVER['REQUEST_METHOD'] = 'POST';
$_POST = [
'channel_id' => 1,
'content' => 'bha a vous de choisir !',
'is_poll' => '1',
'poll_title' => 'Test du sondage !',
'poll_color' => '#f45571',
'poll_choice_type' => 'single',
'poll_end_date' => '20/02/2026 23:45',
'poll_options' => '["Oui je le veux !","Non je ne le veux pas !","Je ne sais pas encore !"]'
];
function getallheaders() { return []; }
// Mock session
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
$_SESSION['user_id'] = 2;
// Include the file
ob_start();
include 'api_v1_messages.php';
$output = ob_get_clean();
echo $output;