Release V1.7
This commit is contained in:
parent
9c3e061f45
commit
d77f94966d
@ -5,69 +5,9 @@ require_once 'includes/permissions.php';
|
||||
requireLogin();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = $_POST['action'] ?? '';
|
||||
$user_id = $_SESSION['user_id'];
|
||||
|
||||
if ($action === 'update_tags') {
|
||||
$thread_id = $_POST['thread_id'] ?? 0;
|
||||
$tag_ids = $_POST['tag_ids'] ?? [];
|
||||
if (is_string($tag_ids)) {
|
||||
$tag_ids = array_filter(explode(',', $tag_ids));
|
||||
}
|
||||
|
||||
if (!$thread_id) {
|
||||
echo json_encode(['success' => false, 'error' => 'Missing thread_id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Verify permission
|
||||
$stmt = db()->prepare("SELECT t.*, c.server_id FROM forum_threads t JOIN channels c ON t.channel_id = c.id WHERE t.id = ?");
|
||||
$stmt->execute([$thread_id]);
|
||||
$thread = $stmt->fetch();
|
||||
|
||||
if (!$thread) {
|
||||
echo json_encode(['success' => false, 'error' => 'Thread not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmtServer = db()->prepare("SELECT owner_id FROM servers WHERE id = ?");
|
||||
$stmtServer->execute([$thread['server_id']]);
|
||||
$server = $stmtServer->fetch();
|
||||
|
||||
$is_admin = Permissions::hasPermission($user_id, $thread['server_id'], Permissions::ADMINISTRATOR) ||
|
||||
Permissions::hasPermission($user_id, $thread['server_id'], Permissions::MANAGE_SERVER) ||
|
||||
Permissions::hasPermission($user_id, $thread['server_id'], Permissions::MANAGE_MESSAGES) ||
|
||||
$server['owner_id'] == $user_id;
|
||||
|
||||
if ($thread['user_id'] != $user_id && !$is_admin) {
|
||||
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
db()->beginTransaction();
|
||||
// Delete old tags
|
||||
$stmt = db()->prepare("DELETE FROM thread_tags WHERE thread_id = ?");
|
||||
$stmt->execute([$thread_id]);
|
||||
|
||||
// Insert new tags
|
||||
if (!empty($tag_ids)) {
|
||||
$stmtTag = db()->prepare("INSERT INTO thread_tags (thread_id, tag_id) VALUES (?, ?)");
|
||||
foreach ($tag_ids as $tag_id) {
|
||||
if ($tag_id) $stmtTag->execute([$thread_id, $tag_id]);
|
||||
}
|
||||
}
|
||||
db()->commit();
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (Exception $e) {
|
||||
db()->rollBack();
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
$channel_id = $_POST['channel_id'] ?? 0;
|
||||
$title = $_POST['title'] ?? '';
|
||||
$user_id = $_SESSION['user_id'];
|
||||
|
||||
if (!$channel_id || !$title) {
|
||||
echo json_encode(['success' => false, 'error' => 'Missing data']);
|
||||
@ -105,7 +45,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'PATCH' || $_SERVER['REQUEST_METHOD'] === 'DELETE' || (isset($_GET['action']) && in_array($_GET['action'], ['solve', 'pin', 'unpin', 'lock', 'unlock', 'delete']))) {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'PATCH' || $_SERVER['REQUEST_METHOD'] === 'DELETE' || (isset($_GET['action']) && in_array($_GET['action'], ['solve', 'pin', 'unpin', 'lock', 'unlock', 'delete', 'update_tags']))) {
|
||||
$data = json_decode(file_get_contents('php://input'), true) ?? $_POST;
|
||||
$thread_id = $data['thread_id'] ?? $_GET['thread_id'] ?? 0;
|
||||
$message_id = $data['message_id'] ?? null;
|
||||
@ -187,9 +127,25 @@ if ($_SERVER['REQUEST_METHOD'] === 'PATCH' || $_SERVER['REQUEST_METHOD'] === 'DE
|
||||
$stmt = db()->prepare("DELETE FROM forum_threads WHERE id = ?");
|
||||
$stmt->execute([$thread_id]);
|
||||
db()->commit();
|
||||
} elseif ($action === 'update_tags') {
|
||||
if ($thread['user_id'] != $user_id && !$is_admin) {
|
||||
echo json_encode(['success' => false, 'error' => 'Unauthorized']); exit;
|
||||
}
|
||||
$tag_ids = $data['tag_ids'] ?? [];
|
||||
db()->beginTransaction();
|
||||
$stmt = db()->prepare("DELETE FROM thread_tags WHERE thread_id = ?");
|
||||
$stmt->execute([$thread_id]);
|
||||
if (!empty($tag_ids)) {
|
||||
$stmtTag = db()->prepare("INSERT INTO thread_tags (thread_id, tag_id) VALUES (?, ?)");
|
||||
foreach ($tag_ids as $tag_id) {
|
||||
if ($tag_id) $stmtTag->execute([$thread_id, $tag_id]);
|
||||
}
|
||||
}
|
||||
db()->commit();
|
||||
}
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (Exception $e) {
|
||||
if (db()->inTransaction()) db()->rollBack();
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
|
||||
@ -4042,46 +4042,67 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const modal = new bootstrap.Modal(document.getElementById('pollVotersModal'));
|
||||
modal.show();
|
||||
}
|
||||
});
|
||||
|
||||
// Forum: Edit Thread Tags
|
||||
const editThreadTagsBtn = e.target.closest('.edit-thread-tags');
|
||||
if (editThreadTagsBtn) {
|
||||
const threadId = editThreadTagsBtn.dataset.threadId;
|
||||
const tagIds = editThreadTagsBtn.dataset.tagIds ? editThreadTagsBtn.dataset.tagIds.split(',') : [];
|
||||
const modal = document.getElementById('editThreadTagsModal');
|
||||
if (modal) {
|
||||
document.getElementById('edit-tags-thread-id').value = threadId;
|
||||
// Manage Thread Tags
|
||||
const manageThreadTagsBtn = document.getElementById('manage-thread-tags');
|
||||
if (manageThreadTagsBtn) {
|
||||
manageThreadTagsBtn.addEventListener('click', () => {
|
||||
const threadId = manageThreadTagsBtn.dataset.id;
|
||||
const currentTags = manageThreadTagsBtn.dataset.tags ? manageThreadTagsBtn.dataset.tags.split(',').filter(id => id !== "") : [];
|
||||
|
||||
const threadIdInput = document.getElementById('manage-tags-thread-id');
|
||||
if (threadIdInput) threadIdInput.value = threadId;
|
||||
|
||||
// Reset all checkboxes
|
||||
document.querySelectorAll('.tag-checkbox').forEach(cb => {
|
||||
cb.checked = currentTags.includes(cb.value);
|
||||
});
|
||||
|
||||
const modalEl = document.getElementById('manageThreadTagsModal');
|
||||
if (modalEl) {
|
||||
const modal = new bootstrap.Modal(modalEl);
|
||||
modal.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const saveThreadTagsBtn = document.getElementById('save-thread-tags-btn');
|
||||
if (saveThreadTagsBtn) {
|
||||
saveThreadTagsBtn.addEventListener('click', async () => {
|
||||
const threadIdInput = document.getElementById('manage-tags-thread-id');
|
||||
const threadId = threadIdInput ? threadIdInput.value : null;
|
||||
if (!threadId) return;
|
||||
|
||||
const selectedTags = Array.from(document.querySelectorAll('.tag-checkbox:checked')).map(cb => cb.value);
|
||||
|
||||
try {
|
||||
saveThreadTagsBtn.disabled = true;
|
||||
saveThreadTagsBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i> Enregistrement...';
|
||||
|
||||
// Clear and then check checkboxes
|
||||
const checkboxes = modal.querySelectorAll('.tag-checkbox');
|
||||
checkboxes.forEach(cb => {
|
||||
cb.checked = tagIds.includes(cb.value);
|
||||
const resp = await fetch('api_v1_threads.php?action=update_tags', {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
thread_id: threadId,
|
||||
tag_ids: selectedTags
|
||||
})
|
||||
});
|
||||
|
||||
const bsModal = new bootstrap.Modal(modal);
|
||||
bsModal.show();
|
||||
const result = await resp.json();
|
||||
if (result.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert(result.error || 'Erreur lors de la mise à jour des tags');
|
||||
saveThreadTagsBtn.disabled = false;
|
||||
saveThreadTagsBtn.innerHTML = 'Enregistrer';
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
alert('Une erreur est survenue');
|
||||
saveThreadTagsBtn.disabled = false;
|
||||
saveThreadTagsBtn.innerHTML = 'Enregistrer';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('edit-thread-tags-form')?.addEventListener('submit', async (ev) => {
|
||||
ev.preventDefault();
|
||||
const form = ev.target;
|
||||
const formData = new FormData(form);
|
||||
|
||||
try {
|
||||
const resp = await fetch('api_v1_threads.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert(data.error || 'Erreur lors de la mise à jour des tags');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
105
index.php
105
index.php
@ -257,11 +257,14 @@ if ($is_dm_view) {
|
||||
$active_thread = null;
|
||||
|
||||
if ($active_thread_id) {
|
||||
$stmt = db()->prepare("SELECT t.*, (SELECT GROUP_CONCAT(CONCAT(ft.id, ':', ft.name, ':', ft.color) SEPARATOR '|') FROM thread_tags tt JOIN forum_tags ft ON tt.tag_id = ft.id WHERE tt.thread_id = t.id) as tags, u.display_name as username, u.username as login_name FROM forum_threads t JOIN users u ON t.user_id = u.id WHERE t.id = ?");
|
||||
$stmt = db()->prepare("SELECT t.*, (SELECT GROUP_CONCAT(CONCAT(ft.name, ':', ft.color) SEPARATOR '|') FROM thread_tags tt JOIN forum_tags ft ON tt.tag_id = ft.id WHERE tt.thread_id = t.id) as tags, u.display_name as username, u.username as login_name FROM forum_threads t JOIN users u ON t.user_id = u.id WHERE t.id = ?");
|
||||
$stmt->execute([$active_thread_id]);
|
||||
$active_thread = $stmt->fetch();
|
||||
|
||||
if ($active_thread) {
|
||||
$stmt_t_ids = db()->prepare("SELECT tag_id FROM thread_tags WHERE thread_id = ?");
|
||||
$stmt_t_ids->execute([$active_thread_id]);
|
||||
$active_thread_tag_ids = $stmt_t_ids->fetchAll(PDO::FETCH_COLUMN);
|
||||
$stmt = db()->prepare("
|
||||
SELECT m.*, u.display_name as username, u.username as login_name, u.avatar_url,
|
||||
(SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color,
|
||||
@ -309,7 +312,7 @@ if ($is_dm_view) {
|
||||
}
|
||||
|
||||
$stmt = db()->prepare("
|
||||
SELECT t.*, (SELECT GROUP_CONCAT(CONCAT(ft.id, ':', ft.name, ':', ft.color) SEPARATOR '|') FROM thread_tags tt JOIN forum_tags ft ON tt.tag_id = ft.id WHERE tt.thread_id = t.id) as tags, u.display_name as username, u.avatar_url,
|
||||
SELECT t.*, (SELECT GROUP_CONCAT(CONCAT(ft.name, ':', ft.color) SEPARATOR '|') FROM thread_tags tt JOIN forum_tags ft ON tt.tag_id = ft.id WHERE tt.thread_id = t.id) as tags, u.display_name as username, u.avatar_url,
|
||||
(SELECT COUNT(*) FROM messages m WHERE m.thread_id = t.id) as message_count,
|
||||
(SELECT MAX(created_at) FROM messages m WHERE m.thread_id = t.id) as last_message_at,
|
||||
(SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color,
|
||||
@ -881,6 +884,11 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
<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 ($active_thread["user_id"] == $current_user_id || $can_manage_server): ?>
|
||||
<button class="btn btn-sm btn-outline-info" id="manage-thread-tags" data-id="<?php echo $active_thread["id"]; ?>" data-tags="<?php echo implode(",", $active_thread_tag_ids); ?>">
|
||||
<i class="fa-solid fa-tags me-1"></i> Gérer les tags
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?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'; ?>
|
||||
@ -1428,7 +1436,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
<?php if($thread['tags']):
|
||||
$tag_list = explode('|', $thread['tags']);
|
||||
foreach($tag_list as $tag_data):
|
||||
list($t_id, $t_name, $t_color) = explode(':', $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; ?>
|
||||
@ -1460,37 +1468,6 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
<div class="small text-muted mt-1">
|
||||
Par <?php echo htmlspecialchars($active_thread['username']); ?> • Dans #<?php echo htmlspecialchars($current_channel_name); ?>
|
||||
</div>
|
||||
<?php if($channel_type === 'forum'): ?>
|
||||
<div class="mt-2 d-flex flex-wrap gap-1 align-items-center">
|
||||
<?php if(!empty($active_thread['tags'])):
|
||||
$tag_list = explode('|', $active_thread['tags']);
|
||||
foreach($tag_list as $tag_data):
|
||||
list($t_id, $t_name, $t_color) = explode(':', $tag_data);
|
||||
?>
|
||||
<span class="badge rounded-pill" style="background-color: <?php echo htmlspecialchars($t_color); ?>; font-size: 0.7em;">
|
||||
<?php echo htmlspecialchars($t_name); ?>
|
||||
</span>
|
||||
<?php endforeach; endif; ?>
|
||||
|
||||
<?php if($active_thread['user_id'] == $current_user_id || $can_manage_channels): ?>
|
||||
<button class="btn btn-link btn-sm p-0 ms-1 text-muted text-decoration-none edit-thread-tags"
|
||||
data-thread-id="<?php echo $active_thread['id']; ?>"
|
||||
data-active-tags="<?php
|
||||
$active_tags_ids = [];
|
||||
if (!empty($active_thread['tags'])) {
|
||||
$tag_list_ids = explode('|', $active_thread['tags']);
|
||||
foreach($tag_list_ids as $tdid) {
|
||||
$parts_id = explode(':', $tdid);
|
||||
$active_tags_ids[] = $parts_id[0];
|
||||
}
|
||||
}
|
||||
echo implode(',', $active_tags_ids);
|
||||
?>" title="Gérer les tags">
|
||||
<i class="fa-solid fa-pen-to-square"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</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
|
||||
@ -3933,40 +3910,46 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Thread Tags Modal -->
|
||||
<div class="modal fade" id="editThreadTagsModal" tabindex="-1">
|
||||
<!-- Manage Thread Tags Modal -->
|
||||
<div class="modal fade" id="manageThreadTagsModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header border-0">
|
||||
<h5 class="modal-title">Modifier les tags</h5>
|
||||
<div class="modal-content border-0 shadow-lg" style="background-color: #313338; color: white;">
|
||||
<div class="modal-header border-0 pb-0">
|
||||
<h5 class="modal-title fw-bold">Gérer les tags de la discussion</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="edit-thread-tags-form">
|
||||
<input type="hidden" name="thread_id" id="edit-tags-thread-id">
|
||||
<input type="hidden" name="action" value="update_tags">
|
||||
<?php if (!empty($forum_tags)): ?>
|
||||
<div class="mb-3">
|
||||
<label class="form-label d-block text-muted small fw-bold text-uppercase">Tags disponibles</label>
|
||||
<div id="edit-thread-tags-list" class="d-flex flex-wrap gap-2 p-3 bg-dark bg-opacity-50 rounded">
|
||||
<?php foreach ($forum_tags as $tag): ?>
|
||||
<div class="form-check form-check-inline m-0">
|
||||
<input class="btn-check tag-checkbox" type="checkbox" name="tag_ids[]" value="<?php echo $tag['id']; ?>" id="edit-tag-<?php echo $tag['id']; ?>">
|
||||
<label class="btn btn-outline-secondary btn-sm rounded-pill px-3" for="edit-tag-<?php echo $tag['id']; ?>" style="--bs-btn-active-bg: <?php echo $tag['color']; ?>; --bs-btn-active-border-color: <?php echo $tag['color']; ?>;">
|
||||
<?php echo htmlspecialchars($tag['name']); ?>
|
||||
</label>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<p class="text-muted text-center py-3">Aucun tag n'est configuré pour ce salon.</p>
|
||||
<?php endif; ?>
|
||||
<button type="submit" class="btn btn-primary w-100 mt-2 py-2 fw-bold">Enregistrer les modifications</button>
|
||||
</form>
|
||||
<input type="hidden" id="manage-tags-thread-id">
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-uppercase fw-bold mb-2" style="font-size: 0.75em; color: #b5bac1;">Tags disponibles</label>
|
||||
<div id="thread-tags-selection-list" class="d-flex flex-wrap gap-2">
|
||||
<?php if (isset($forum_tags)): ?>
|
||||
<?php foreach($forum_tags as $tag): ?>
|
||||
<div class="tag-selection-item">
|
||||
<input type="checkbox" class="tag-checkbox btn-check" id="tag-check-<?php echo $tag["id"]; ?>" value="<?php echo $tag["id"]; ?>" autocomplete="off">
|
||||
<label class="btn btn-sm btn-outline-light rounded-pill px-3" for="tag-check-<?php echo $tag["id"]; ?>" style="border-color: <?php echo $tag["color"]; ?>; color: <?php echo $tag["color"]; ?>;">
|
||||
<?php echo htmlspecialchars($tag["name"]); ?>
|
||||
</label>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer border-0 pt-0">
|
||||
<button type="button" class="btn btn-link text-white text-decoration-none" data-bs-dismiss="modal">Annuler</button>
|
||||
<button type="button" class="btn btn-primary px-4" id="save-thread-tags-btn">Enregistrer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
.tag-selection-item .btn-check:checked + .btn {
|
||||
background-color: var(--bs-btn-border-color);
|
||||
color: #fff !important;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user