Kommentar-Sichtbarkeit: Öffentlich/Angemeldete/Nur ich + Badges + Server-Filter
This commit is contained in:
parent
ad97a76824
commit
5d2a0338ee
@ -153,9 +153,12 @@ async def add_comment(user_id: str, user_name: str, drucksache: str,
|
||||
|
||||
|
||||
async def get_comments(drucksache: str, user_id: Optional[str] = None) -> list[dict]:
|
||||
"""Get comments for a drucksache. Filters by visibility:
|
||||
- 'all' comments are always visible
|
||||
- group-scoped comments only visible if user is in that group (TODO)
|
||||
"""Get comments for a drucksache, server-seitig nach Sichtbarkeit gefiltert.
|
||||
|
||||
- 'all': immer sichtbar
|
||||
- 'authenticated': nur wenn user_id gesetzt (eingeloggt)
|
||||
- 'private': nur für den Autor
|
||||
- 'group:XYZ': für Gruppenmitglieder (TODO: Keycloak-Gruppen-Check)
|
||||
"""
|
||||
async with aiosqlite.connect(settings.db_path) as db:
|
||||
db.row_factory = aiosqlite.Row
|
||||
@ -163,7 +166,22 @@ async def get_comments(drucksache: str, user_id: Optional[str] = None) -> list[d
|
||||
"SELECT * FROM comments WHERE drucksache=? ORDER BY created_at",
|
||||
(drucksache,),
|
||||
)
|
||||
return [dict(r) for r in await rows.fetchall()]
|
||||
all_comments = [dict(r) for r in await rows.fetchall()]
|
||||
|
||||
# Server-seitig filtern (Defense in Depth — Client filtert auch)
|
||||
result = []
|
||||
for c in all_comments:
|
||||
vis = c.get("visibility", "all")
|
||||
if vis == "all":
|
||||
result.append(c)
|
||||
elif vis == "authenticated" and user_id:
|
||||
result.append(c)
|
||||
elif vis == "private" and user_id and c["user_id"] == user_id:
|
||||
result.append(c)
|
||||
elif vis.startswith("group:") and user_id:
|
||||
# TODO: Keycloak-Gruppen-Membership prüfen
|
||||
result.append(c)
|
||||
return result
|
||||
|
||||
|
||||
async def delete_comment(comment_id: int, user_id: str) -> bool:
|
||||
|
||||
@ -1385,14 +1385,31 @@
|
||||
if (!container) return;
|
||||
try {
|
||||
const comments = await fetch(`/api/comments?drucksache=${encodeURIComponent(drucksache)}`).then(r => r.json());
|
||||
if (comments.length === 0) {
|
||||
// Client-seitig filtern nach Sichtbarkeit
|
||||
const visible = comments.filter(c => {
|
||||
if (c.visibility === 'all') return true;
|
||||
if (!currentUser) return false;
|
||||
if (c.visibility === 'authenticated') return true;
|
||||
if (c.visibility === 'private') return c.user_id === currentUser.sub;
|
||||
// group:XYZ — TODO: gegen Keycloak-Gruppen prüfen
|
||||
if (c.visibility?.startsWith('group:')) return true; // Platzhalter
|
||||
return false;
|
||||
});
|
||||
if (visible.length === 0) {
|
||||
container.innerHTML = '<span style="color:#aaa;font-size:0.85rem;">Noch keine Kommentare.</span>';
|
||||
return;
|
||||
}
|
||||
container.innerHTML = comments.map(c => `
|
||||
const visBadge = (v) => {
|
||||
if (v === 'private') return '<span style="font-size:0.7rem;background:#f0f0f0;padding:0.1rem 0.3rem;border-radius:2px;" title="Nur für dich sichtbar">👤</span>';
|
||||
if (v === 'authenticated') return '<span style="font-size:0.7rem;background:#e8f4f8;padding:0.1rem 0.3rem;border-radius:2px;" title="Nur für angemeldete Nutzer">🔒</span>';
|
||||
if (v?.startsWith('group:')) return `<span style="font-size:0.7rem;background:#f0e6ff;padding:0.1rem 0.3rem;border-radius:2px;" title="Gruppe: ${v.substring(6)}">👥 ${v.substring(6)}</span>`;
|
||||
return '<span style="font-size:0.7rem;background:#e8ffe8;padding:0.1rem 0.3rem;border-radius:2px;" title="Öffentlich sichtbar">🌐</span>';
|
||||
};
|
||||
container.innerHTML = visible.map(c => `
|
||||
<div style="padding:0.4rem 0;border-bottom:1px solid #f0f0f0;font-size:0.85rem;">
|
||||
<strong>${c.user_name || 'Anonym'}</strong>
|
||||
<span style="color:#aaa;font-size:0.75rem;margin-left:0.5rem;">${new Date(c.created_at).toLocaleString('de-DE')}</span>
|
||||
${visBadge(c.visibility)}
|
||||
<span style="color:#aaa;font-size:0.75rem;margin-left:0.3rem;">${new Date(c.created_at).toLocaleString('de-DE')}</span>
|
||||
${currentUser && currentUser.sub === c.user_id ? `<button onclick="deleteCommentUI(${c.id},'${drucksache}')" style="float:right;background:none;border:none;color:#dc3545;cursor:pointer;font-size:0.75rem;">✕</button>` : ''}
|
||||
<div style="margin-top:0.2rem;">${c.text}</div>
|
||||
</div>
|
||||
@ -1401,12 +1418,15 @@
|
||||
}
|
||||
|
||||
async function addCommentUI(drucksache) {
|
||||
const input = document.getElementById('comment-input-' + drucksache.replace('/', '-'));
|
||||
const safeDrs = drucksache.replace('/', '-');
|
||||
const input = document.getElementById('comment-input-' + safeDrs);
|
||||
const visSelect = document.getElementById('comment-visibility-' + safeDrs);
|
||||
if (!input || !input.value.trim()) return;
|
||||
const visibility = visSelect ? visSelect.value : 'all';
|
||||
await fetch('/api/comment', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body: `drucksache=${encodeURIComponent(drucksache)}&text=${encodeURIComponent(input.value)}`
|
||||
body: `drucksache=${encodeURIComponent(drucksache)}&text=${encodeURIComponent(input.value)}&visibility=${visibility}`
|
||||
});
|
||||
input.value = '';
|
||||
loadComments(drucksache);
|
||||
@ -1940,11 +1960,17 @@
|
||||
<span style="color:#aaa;font-size:0.85rem;">Lade Kommentare...</span>
|
||||
</div>
|
||||
${currentUser ? `
|
||||
<div style="display:flex;gap:0.5rem;">
|
||||
<div style="display:flex;gap:0.5rem;flex-wrap:wrap;align-items:center;">
|
||||
<input id="comment-input-${item.drucksache.replace('/','-')}" type="text"
|
||||
placeholder="Kommentar schreiben..."
|
||||
style="flex:1;padding:0.4rem;border:1px solid #ddd;border-radius:4px;font-size:0.85rem;"
|
||||
style="flex:1;min-width:150px;padding:0.4rem;border:1px solid #ddd;border-radius:4px;font-size:0.85rem;"
|
||||
onkeydown="if(event.key==='Enter')addCommentUI('${item.drucksache}')">
|
||||
<select id="comment-visibility-${item.drucksache.replace('/','-')}"
|
||||
style="padding:0.4rem;border:1px solid #ddd;border-radius:4px;font-size:0.8rem;color:#666;">
|
||||
<option value="all">🌐 Öffentlich</option>
|
||||
<option value="authenticated">🔒 Angemeldete</option>
|
||||
<option value="private">👤 Nur ich</option>
|
||||
</select>
|
||||
<button onclick="addCommentUI('${item.drucksache}')"
|
||||
style="padding:0.4rem 0.8rem;background:var(--color-blue);color:white;border:none;border-radius:4px;cursor:pointer;font-size:0.85rem;">
|
||||
Senden
|
||||
|
||||
Loading…
Reference in New Issue
Block a user