ReleaseV11+AnnonceManuelle
This commit is contained in:
parent
d761c251bf
commit
88493bedcd
@ -135,31 +135,70 @@ if ($_SERVER['REQUEST_METHOD'] === 'PUT') {
|
||||
$action = $data['action'] ?? 'edit';
|
||||
|
||||
try {
|
||||
$stmt = db()->prepare("SELECT user_id, channel_id FROM messages WHERE id = ?");
|
||||
$stmt->execute([$message_id]);
|
||||
$msg_data = $stmt->fetch();
|
||||
|
||||
if (!$msg_data) {
|
||||
echo json_encode(['success' => false, 'error' => 'Message not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'pin') {
|
||||
if (!Permissions::canDoInChannel($user_id, $msg_data['channel_id'], Permissions::MANAGE_MESSAGES)) {
|
||||
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
$stmt = db()->prepare("UPDATE messages SET is_pinned = 1 WHERE id = ?");
|
||||
$stmt->execute([$message_id]);
|
||||
echo json_encode(['success' => true]);
|
||||
exit;
|
||||
}
|
||||
if ($action === 'unpin') {
|
||||
if (!Permissions::canDoInChannel($user_id, $msg_data['channel_id'], Permissions::MANAGE_MESSAGES)) {
|
||||
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
$stmt = db()->prepare("UPDATE messages SET is_pinned = 0 WHERE id = ?");
|
||||
$stmt->execute([$message_id]);
|
||||
echo json_encode(['success' => true]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($msg_data['user_id'] != $user_id && !Permissions::canDoInChannel($user_id, $msg_data['channel_id'], Permissions::MANAGE_MESSAGES)) {
|
||||
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (empty($content)) {
|
||||
echo json_encode(['success' => false, 'error' => 'Content cannot be empty']);
|
||||
exit;
|
||||
}
|
||||
$stmt = db()->prepare("UPDATE messages SET content = ? WHERE id = ? AND user_id = ?");
|
||||
$stmt->execute([$content, $message_id, $user_id]);
|
||||
|
||||
if ($stmt->rowCount() > 0) {
|
||||
echo json_encode(['success' => true]);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'error' => 'Message not found or unauthorized']);
|
||||
|
||||
$metadata = null;
|
||||
if (isset($data['is_announcement']) && $data['is_announcement'] == '1') {
|
||||
if (!Permissions::canDoInChannel($user_id, $msg_data['channel_id'], Permissions::CREATE_ANNOUNCEMENT)) {
|
||||
echo json_encode(['success' => false, 'error' => 'You do not have permission to manage announcements in this channel.']);
|
||||
exit;
|
||||
}
|
||||
$metadata = json_encode([
|
||||
'is_manual_announcement' => true,
|
||||
'title' => $data['ann_title'] ?? '',
|
||||
'color' => $data['ann_color'] ?? '#5865f2',
|
||||
'description' => $content,
|
||||
'url' => $data['ann_link'] ?? ''
|
||||
]);
|
||||
}
|
||||
|
||||
if ($metadata) {
|
||||
$stmt = db()->prepare("UPDATE messages SET content = ?, metadata = ? WHERE id = ?");
|
||||
$stmt->execute([$content, $metadata, $message_id]);
|
||||
} else {
|
||||
$stmt = db()->prepare("UPDATE messages SET content = ? WHERE id = ?");
|
||||
$stmt->execute([$content, $message_id]);
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
@ -171,14 +210,24 @@ if ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
|
||||
$message_id = $data['id'] ?? 0;
|
||||
|
||||
try {
|
||||
$stmt = db()->prepare("DELETE FROM messages WHERE id = ? AND user_id = ?");
|
||||
$stmt->execute([$message_id, $user_id]);
|
||||
$stmt = db()->prepare("SELECT user_id, channel_id FROM messages WHERE id = ?");
|
||||
$stmt->execute([$message_id]);
|
||||
$msg_data = $stmt->fetch();
|
||||
|
||||
if ($stmt->rowCount() > 0) {
|
||||
echo json_encode(['success' => true]);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'error' => 'Message not found or unauthorized']);
|
||||
if (!$msg_data) {
|
||||
echo json_encode(['success' => false, 'error' => 'Message not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($msg_data['user_id'] != $user_id && !Permissions::canDoInChannel($user_id, $msg_data['channel_id'], Permissions::MANAGE_MESSAGES)) {
|
||||
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt = db()->prepare("DELETE FROM messages WHERE id = ?");
|
||||
$stmt->execute([$message_id]);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
@ -261,7 +310,21 @@ if (!empty($content)) {
|
||||
}
|
||||
|
||||
$metadata = null;
|
||||
if (!empty($content)) {
|
||||
if (isset($_POST['is_announcement']) && $_POST['is_announcement'] == '1') {
|
||||
if (!Permissions::canDoInChannel($user_id, $channel_id, Permissions::CREATE_ANNOUNCEMENT)) {
|
||||
echo json_encode(['success' => false, 'error' => 'You do not have permission to create announcements in this channel.']);
|
||||
exit;
|
||||
}
|
||||
$metadata = json_encode([
|
||||
'is_manual_announcement' => true,
|
||||
'title' => $_POST['ann_title'] ?? '',
|
||||
'color' => $_POST['ann_color'] ?? '#5865f2',
|
||||
'description' => $content,
|
||||
'url' => $_POST['ann_link'] ?? ''
|
||||
]);
|
||||
// Clear content for the message text itself if we want it only in the embed
|
||||
// But keeping it in content might be good for search/fallback
|
||||
} elseif (!empty($content)) {
|
||||
$urls = extractUrls($content);
|
||||
if (!empty($urls)) {
|
||||
// Fetch OG data for the first URL
|
||||
|
||||
@ -693,6 +693,98 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
xhr.send(formData);
|
||||
});
|
||||
|
||||
// Announcement News Button
|
||||
document.addEventListener('click', (e) => {
|
||||
const annBtn = e.target.closest('#announcement-news-btn');
|
||||
if (annBtn) {
|
||||
const idField = document.getElementById('announcement-id');
|
||||
if (idField) idField.value = '';
|
||||
const form = document.getElementById('announcement-form');
|
||||
if (form) form.reset();
|
||||
const submitBtn = document.getElementById('announcement-submit-btn');
|
||||
if (submitBtn) submitBtn.innerText = "Publier l'annonce";
|
||||
|
||||
const modal = new bootstrap.Modal(document.getElementById('createAnnouncementModal'));
|
||||
modal.show();
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// Announcement Form Submission
|
||||
const annForm = document.getElementById('announcement-form');
|
||||
annForm?.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const annId = document.getElementById('announcement-id').value;
|
||||
const title = document.getElementById('announcement-title').value.trim();
|
||||
const color = document.getElementById('announcement-color').value;
|
||||
const content = document.getElementById('announcement-content').value.trim();
|
||||
const link = document.getElementById('announcement-link').value.trim();
|
||||
|
||||
if (!title || !content) return;
|
||||
|
||||
const modalEl = document.getElementById('createAnnouncementModal');
|
||||
const modal = bootstrap.Modal.getInstance(modalEl);
|
||||
|
||||
let resp;
|
||||
if (annId) {
|
||||
resp = await fetch('api_v1_messages.php', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
id: annId,
|
||||
content: content,
|
||||
is_announcement: '1',
|
||||
ann_title: title,
|
||||
ann_color: color,
|
||||
ann_link: link
|
||||
})
|
||||
});
|
||||
} else {
|
||||
const formData = new FormData();
|
||||
formData.append('channel_id', currentChannel);
|
||||
formData.append('content', content);
|
||||
formData.append('is_announcement', '1');
|
||||
formData.append('ann_title', title);
|
||||
formData.append('ann_color', color);
|
||||
formData.append('ann_link', link);
|
||||
|
||||
resp = await fetch('api_v1_messages.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await resp.json();
|
||||
if (result.success) {
|
||||
if (annId) {
|
||||
// Update via WS or reload
|
||||
ws?.send(JSON.stringify({ type: 'message_edit', message_id: annId, content: content }));
|
||||
location.reload();
|
||||
} else {
|
||||
appendMessage(result.message);
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({
|
||||
type: 'message',
|
||||
data: JSON.stringify({
|
||||
...result.message,
|
||||
channel_id: currentChannel
|
||||
})
|
||||
}));
|
||||
}
|
||||
}
|
||||
modal.hide();
|
||||
annForm.reset();
|
||||
} else {
|
||||
alert(result.error || 'Erreur lors de la publication');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert('Une erreur est survenue');
|
||||
}
|
||||
});
|
||||
|
||||
// Handle Click Events
|
||||
document.addEventListener('click', (e) => {
|
||||
console.log('Global click at:', e.target);
|
||||
@ -805,6 +897,40 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (editBtn) {
|
||||
const msgId = editBtn.dataset.id;
|
||||
const msgItem = editBtn.closest('.message-item');
|
||||
|
||||
if (editBtn.classList.contains('edit-announcement')) {
|
||||
const modalEl = document.getElementById('createAnnouncementModal');
|
||||
const modal = new bootstrap.Modal(modalEl);
|
||||
|
||||
const titleEl = msgItem.querySelector('.embed-title');
|
||||
const title = titleEl ? titleEl.innerText : '';
|
||||
const description = msgItem.dataset.rawContent;
|
||||
const embedEl = msgItem.querySelector('.rich-embed');
|
||||
const color = embedEl ? embedEl.style.borderLeftColor : '#5865f2';
|
||||
|
||||
const rgbToHex = (rgb) => {
|
||||
if (!rgb || !rgb.startsWith('rgb')) return '#5865f2';
|
||||
const parts = rgb.match(/\d+/g);
|
||||
return "#" + parts.map(x => {
|
||||
const hex = parseInt(x).toString(16);
|
||||
return hex.length === 1 ? "0" + hex : hex;
|
||||
}).join("");
|
||||
};
|
||||
|
||||
const hexColor = rgbToHex(color);
|
||||
const url = titleEl ? (titleEl.getAttribute('href') || '') : '';
|
||||
|
||||
document.getElementById('announcement-id').value = msgId;
|
||||
document.getElementById('announcement-title').value = title;
|
||||
document.getElementById('announcement-color').value = hexColor;
|
||||
document.getElementById('announcement-content').value = description;
|
||||
document.getElementById('announcement-link').value = url;
|
||||
document.getElementById('announcement-submit-btn').innerText = "Modifier l'annonce";
|
||||
|
||||
modal.show();
|
||||
return;
|
||||
}
|
||||
|
||||
const textEl = msgItem.querySelector('.message-text');
|
||||
const originalContent = msgItem.dataset.rawContent || textEl.innerText;
|
||||
|
||||
@ -1462,6 +1588,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
eventPerms.forEach(p => {
|
||||
p.style.setProperty('display', channelType === 'event' ? 'block' : 'none', channelType === 'event' ? '' : 'important');
|
||||
});
|
||||
|
||||
// Show/Hide announcement permissions
|
||||
const annPerms = document.querySelectorAll('.announcement-permission-only');
|
||||
annPerms.forEach(p => {
|
||||
p.style.setProperty('display', channelType === 'announcement' ? 'block' : 'none', channelType === 'announcement' ? '' : 'important');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1487,6 +1619,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
p.style.setProperty('display', type === 'event' ? 'block' : 'none', type === 'event' ? '' : 'important');
|
||||
});
|
||||
|
||||
// Show/Hide announcement permissions
|
||||
const annPerms = document.querySelectorAll('.announcement-permission-only');
|
||||
annPerms.forEach(p => {
|
||||
p.style.setProperty('display', type === 'announcement' ? 'block' : 'none', type === 'announcement' ? '' : 'important');
|
||||
});
|
||||
|
||||
// Rules specific visibility
|
||||
const rulesRoleContainer = document.getElementById('edit-channel-rules-role-container');
|
||||
if (rulesRoleContainer) {
|
||||
@ -3092,12 +3230,41 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
let embedHtml = '';
|
||||
if (msg.metadata) {
|
||||
const meta = typeof msg.metadata === 'string' ? JSON.parse(msg.metadata) : msg.metadata;
|
||||
const borderColor = meta.color || 'var(--blurple)';
|
||||
|
||||
let metaHtml = '';
|
||||
if (meta.is_rss) {
|
||||
const parts = [];
|
||||
if (meta.category) parts.push(escapeHTML(meta.category));
|
||||
if (meta.date) parts.push(escapeHTML(meta.date));
|
||||
if (meta.author) parts.push(escapeHTML(meta.author));
|
||||
metaHtml = `<div class="embed-meta mb-2" style="font-size: 0.8em; color: var(--text-muted);">${parts.join(' · ')}</div>`;
|
||||
} else if (meta.is_manual_announcement) {
|
||||
metaHtml = `<div class="embed-meta mb-2" style="font-size: 0.8em; color: var(--text-muted);">${msg.timestamp || 'Just now'}</div>`;
|
||||
}
|
||||
|
||||
let titleHtml = '';
|
||||
if (meta.title) {
|
||||
if (meta.url) {
|
||||
titleHtml = `<a href="${meta.url}" target="_blank" class="embed-title d-block mb-1 text-decoration-none" style="font-weight: 600; color: #00a8fc; font-size: 1.1em;">${escapeHTML(meta.title)}</a>`;
|
||||
} else {
|
||||
titleHtml = `<div class="embed-title d-block mb-1" style="font-weight: 600; color: #00a8fc; font-size: 1.1em;">${escapeHTML(meta.title)}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
let footerHtml = '';
|
||||
if (meta.is_manual_announcement && meta.url) {
|
||||
footerHtml = `<div class="embed-footer mt-2"><a href="${meta.url}" target="_blank" class="btn btn-sm btn-outline-info" style="font-size: 0.8em;">Source / En savoir plus</a></div>`;
|
||||
}
|
||||
|
||||
embedHtml = `
|
||||
<div class="rich-embed mt-2 p-3 rounded" style="background: rgba(0,0,0,0.1); border-left: 4px solid var(--blurple); max-width: 520px;">
|
||||
${meta.site_name ? `<div class="embed-site-name mb-1" style="font-size: 0.75em; color: var(--text-muted); text-transform: uppercase; font-weight: bold;">${escapeHTML(meta.site_name)}</div>` : ''}
|
||||
${meta.title ? `<a href="${meta.url}" target="_blank" class="embed-title d-block mb-1 text-decoration-none" style="font-weight: 600; color: #00a8fc;">${escapeHTML(meta.title)}</a>` : ''}
|
||||
${meta.description ? `<div class="embed-description mb-2" style="font-size: 0.9em; color: var(--text-normal);">${escapeHTML(meta.description)}</div>` : ''}
|
||||
<div class="rich-embed mt-2 p-3 rounded" style="background: rgba(0,0,0,0.1); border-left: 4px solid ${borderColor}; max-width: 520px;">
|
||||
${(meta.site_name && !meta.is_rss && !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;">${escapeHTML(meta.site_name)}</div>` : ''}
|
||||
${titleHtml}
|
||||
${metaHtml}
|
||||
${meta.description ? `<div class="embed-description mb-2" style="font-size: 0.9em; color: var(--text-normal);">${parseMarkdown(meta.description)}</div>` : ''}
|
||||
${meta.image ? `<div class="embed-image"><img src="${meta.image}" class="rounded" style="max-width: 100%; max-height: 300px; object-fit: contain;"></div>` : ''}
|
||||
${footerHtml}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@ -3114,8 +3281,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const actionsHtml = (isMe || hasManageRights) ? `
|
||||
<div class="message-actions-menu">
|
||||
${pinHtml}
|
||||
${isMe ? `
|
||||
<span class="action-btn edit" title="Edit" data-id="${msg.id}">
|
||||
${(isMe || (isManualAnn && hasManageRights)) ? `
|
||||
<span class="action-btn edit ${isManualAnn ? 'edit-announcement' : ''}" title="Edit" data-id="${msg.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="Delete" data-id="${msg.id}">
|
||||
@ -3132,21 +3299,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
</span>
|
||||
` : '';
|
||||
|
||||
let userBadgesHtml = '';
|
||||
const bData = msg.badge_data || '';
|
||||
if (bData) {
|
||||
bData.split(':::').forEach(d => {
|
||||
const parts = d.split('|');
|
||||
const name = parts[0];
|
||||
const url = parts[1];
|
||||
userBadgesHtml += `<img src="${url}" class="ms-1" style="width: 32px; height: 32px; object-fit: contain; vertical-align: middle;" title="${escapeHTML(name)}">`;
|
||||
});
|
||||
}
|
||||
|
||||
const mentionRegex = new RegExp(`@${window.currentUsername}\\b`, 'g');
|
||||
const mentionHtml = `<span class="mention">@${window.currentUsername}</span>`;
|
||||
const contentWithMentions = parseCustomEmotes(msg.content).replace(mentionRegex, mentionHtml);
|
||||
|
||||
const isManualAnn = msg.metadata && (typeof msg.metadata === 'string' ? JSON.parse(msg.metadata) : msg.metadata).is_manual_announcement;
|
||||
|
||||
div.innerHTML = `
|
||||
<div class="message-avatar" style="${avatarStyle}"></div>
|
||||
<div class="message-content">
|
||||
@ -3154,12 +3312,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
<span class="message-username" style="color: ${msg.role_color || 'inherit'};">
|
||||
${escapeHTML(msg.username)}
|
||||
${renderRoleIconJS(msg.role_icon, '14px')}
|
||||
<span class="ms-1 d-inline-flex gap-1">${userBadgesHtml}</span>
|
||||
</span>
|
||||
<span class="message-timestamp">${msg.timestamp || 'Just now'}</span>
|
||||
${pinnedBadge}
|
||||
</div>
|
||||
<div class="message-text">${contentWithMentions}</div>
|
||||
${!isManualAnn ? `<div class="message-text">${contentWithMentions}</div>` : ''}
|
||||
${attachmentHtml}
|
||||
${embedHtml}
|
||||
<div class="message-reactions mt-1" data-message-id="${msg.id}"></div>
|
||||
|
||||
BIN
assets/pasted-20260220-202504-6801c34a.png
Normal file
BIN
assets/pasted-20260220-202504-6801c34a.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 125 KiB |
@ -16,6 +16,7 @@ class Permissions {
|
||||
const CREATE_EVENT = 4096;
|
||||
const EDIT_EVENT = 8192;
|
||||
const DELETE_EVENT = 16384;
|
||||
const CREATE_ANNOUNCEMENT = 32768;
|
||||
|
||||
public static function hasPermission($user_id, $server_id, $permission) {
|
||||
$stmt = db()->prepare("SELECT is_admin FROM users WHERE id = ?");
|
||||
|
||||
130
index.php
130
index.php
@ -239,6 +239,7 @@ if ($is_dm_view) {
|
||||
$can_create_event = Permissions::canDoInChannel($current_user_id, $active_channel_id, Permissions::CREATE_EVENT);
|
||||
$can_edit_event = Permissions::canDoInChannel($current_user_id, $active_channel_id, Permissions::EDIT_EVENT);
|
||||
$can_delete_event = Permissions::canDoInChannel($current_user_id, $active_channel_id, Permissions::DELETE_EVENT);
|
||||
$can_create_announcement = Permissions::canDoInChannel($current_user_id, $active_channel_id, Permissions::CREATE_ANNOUNCEMENT);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -1259,7 +1260,18 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="message-text">
|
||||
<?php echo parse_emotes($m['content'], $user['username']); ?>
|
||||
<?php
|
||||
$is_manual_ann = 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 (!$is_manual_ann) {
|
||||
echo parse_emotes($m['content'], $user['username']);
|
||||
}
|
||||
?>
|
||||
<?php if ($m['attachment_url']): ?>
|
||||
<div class="message-attachment mt-2">
|
||||
<?php
|
||||
@ -1279,12 +1291,16 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
<?php if (!empty($m['metadata'])):
|
||||
$meta = json_decode($m['metadata'], true);
|
||||
if ($meta): ?>
|
||||
<div class="rich-embed mt-2 p-3 rounded" style="background: rgba(0,0,0,0.1); border-left: 4px solid var(--blurple); max-width: 520px;">
|
||||
<?php if (!empty($meta['site_name']) && empty($meta['is_rss'])): ?>
|
||||
<div class="rich-embed mt-2 p-3 rounded" style="background: rgba(0,0,0,0.1); border-left: 4px solid <?php echo htmlspecialchars($meta['color'] ?? 'var(--blurple)'); ?>; 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'])): ?>
|
||||
<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 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);">
|
||||
@ -1297,8 +1313,18 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
?>
|
||||
</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);"><?php echo htmlspecialchars($meta['description']); ?></div>
|
||||
<div class="embed-description mb-2" style="font-size: 0.9em; color: var(--text-normal);"><?php echo parse_markdown($meta['description']); ?></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">
|
||||
@ -1330,8 +1356,8 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
<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): ?>
|
||||
<span class="action-btn edit" title="Modifier" data-id="<?php echo $m['id']; ?>">
|
||||
<?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']; ?>">
|
||||
@ -1393,6 +1419,12 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="8" y1="8" x2="16" y2="16"></line><line x1="16" y1="8" x2="8" y2="16"></line></svg>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($channel_type === 'announcement' && $can_create_announcement): ?>
|
||||
<button type="button" class="btn border-0 text-muted p-2" id="announcement-news-btn" title="Créer une annonce manuelle">
|
||||
<span style="font-size: 1.2rem;">📰</span>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($active_thread) && $active_thread['is_locked']): ?>
|
||||
<textarea id="chat-input" class="chat-input" placeholder="Cette discussion est verrouillée." autocomplete="off" rows="1" disabled style="background-color: rgba(0,0,0,0.1); cursor: not-allowed;"></textarea>
|
||||
<?php else: ?>
|
||||
@ -1432,6 +1464,52 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Créer une Annonce -->
|
||||
<div class="modal fade" id="createAnnouncementModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content border-0 shadow-lg bg-dark text-white">
|
||||
<div class="modal-header border-0">
|
||||
<h5 class="modal-title"><span class="me-2">📰</span> Nouvelle Annonce</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="announcement-form">
|
||||
<input type="hidden" id="announcement-id" value="">
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-muted small text-uppercase fw-bold">Titre de l'annonce</label>
|
||||
<input type="text" id="announcement-title" class="form-control bg-black text-white border-secondary" placeholder="Titre accrocheur..." required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-muted text-uppercase fw-bold">Couleur de la bordure</label>
|
||||
<div class="d-flex gap-2 flex-wrap">
|
||||
<input type="color" id="announcement-color" class="form-control form-control-color bg-transparent border-0" value="#5865f2" title="Choisir une couleur">
|
||||
<div class="color-presets d-flex gap-2 align-items-center">
|
||||
<span class="rounded-circle" style="width: 20px; height: 20px; background: #5865f2; cursor: pointer;" onclick="document.getElementById('announcement-color').value='#5865f2'"></span>
|
||||
<span class="rounded-circle" style="width: 20px; height: 20px; background: #23a559; cursor: pointer;" onclick="document.getElementById('announcement-color').value='#23a559'"></span>
|
||||
<span class="rounded-circle" style="width: 20px; height: 20px; background: #f04747; cursor: pointer;" onclick="document.getElementById('announcement-color').value='#f04747'"></span>
|
||||
<span class="rounded-circle" style="width: 20px; height: 20px; background: #faa61a; cursor: pointer;" onclick="document.getElementById('announcement-color').value='#faa61a'"></span>
|
||||
<span class="rounded-circle" style="width: 20px; height: 20px; background: #eb459e; cursor: pointer;" onclick="document.getElementById('announcement-color').value='#eb459e'"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-muted text-uppercase fw-bold">Texte de présentation</label>
|
||||
<textarea id="announcement-content" class="form-control bg-black text-white border-secondary" rows="5" placeholder="Contenu de votre annonce..." required></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-muted text-uppercase fw-bold">Lien source (optionnel)</label>
|
||||
<input type="url" id="announcement-link" class="form-control bg-black text-white border-secondary" placeholder="https://...">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer border-0">
|
||||
<button type="button" class="btn btn-link text-muted text-decoration-none" data-bs-dismiss="modal">Annuler</button>
|
||||
<button type="submit" id="announcement-submit-btn" form="announcement-form" class="btn btn-primary px-4">Publier l'annonce</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Paramètres utilisateur Modal -->
|
||||
<div class="modal fade" id="userParamètresModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
@ -2794,6 +2872,44 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="permission-item mb-3 p-2 rounded" style="background: var(--separator-soft);">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="pe-3">
|
||||
<div class="fw-bold" style="color: #ffffff; font-size: 0.9em;">Gérer les messages</div>
|
||||
<div style="font-size: 0.75em; color: #b5bac1;">Permet de supprimer ou d'épingler les messages des autres membres.</div>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm perm-tri-state" data-perm-bit="4">
|
||||
<input type="radio" class="btn-check" name="perm_4" id="perm_4_deny" value="deny">
|
||||
<label class="btn btn-outline-danger border-0" for="perm_4_deny" title="Refuser"><i class="fa-solid fa-xmark"></i></label>
|
||||
|
||||
<input type="radio" class="btn-check" name="perm_4" id="perm_4_neutral" value="neutral" checked>
|
||||
<label class="btn btn-outline-secondary border-0" for="perm_4_neutral" title="Neutre">/</label>
|
||||
|
||||
<input type="radio" class="btn-check" name="perm_4" id="perm_4_allow" value="allow">
|
||||
<label class="btn btn-outline-success border-0" for="perm_4_allow" title="Autoriser"><i class="fa-solid fa-check"></i></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="permission-item announcement-permission-only mb-3 p-2 rounded" style="background: var(--separator-soft); display: none;">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="pe-3">
|
||||
<div class="fw-bold" style="color: #ffffff; font-size: 0.9em;">Créer des annonces</div>
|
||||
<div style="font-size: 0.75em; color: #b5bac1;">Permet de créer des annonces manuelles stylisées dans ce salon.</div>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm perm-tri-state" data-perm-bit="32768">
|
||||
<input type="radio" class="btn-check" name="perm_32768" id="perm_32768_deny" value="deny">
|
||||
<label class="btn btn-outline-danger border-0" for="perm_32768_deny" title="Refuser"><i class="fa-solid fa-xmark"></i></label>
|
||||
|
||||
<input type="radio" class="btn-check" name="perm_32768" id="perm_32768_neutral" value="neutral" checked>
|
||||
<label class="btn btn-outline-secondary border-0" for="perm_32768_neutral" title="Neutre">/</label>
|
||||
|
||||
<input type="radio" class="btn-check" name="perm_32768" id="perm_32768_allow" value="allow">
|
||||
<label class="btn btn-outline-success border-0" for="perm_32768_allow" title="Autoriser"><i class="fa-solid fa-check"></i></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="permission-item mb-3 p-2 rounded" style="background: var(--separator-soft);">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="pe-3">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user