Foro: editor CKEditor 5 en los mensajes y moderación por POST+CSRF

- forum/forms.py: TopicForm/ReplyForm/EditPostForm con CKEditor5Widget (rich text)
- vistas crear/responder/editar pasan el form; el HTML del editor se sanea con nh3
- acciones de moderación (bloquear, fijar, mover, borrar/restaurar temas y posts)
  ahora exigen POST y van con CSRF; las plantillas usan formularios en vez de enlaces GET
- docs/FORO.md actualizado

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-12 17:15:35 +00:00
parent 2c30844bf7
commit 874a0c1ad9
7 changed files with 73 additions and 23 deletions
+23 -5
View File
@@ -16,6 +16,7 @@ from django.urls import reverse
from . import db
from . import permissions as perm
from . import sanitize
from .forms import TopicForm, ReplyForm, EditPostForm
MIN_TEXT_LEN = 5
@@ -110,6 +111,7 @@ def view_topic(request, topic_id, page=1):
'pagination': pag,
'can_reply': perm.can_post(request) and not topic['locked'],
'move_forums': db.list_forums_for_move(topic['forum_id']) if perm.is_moderator(request) else [],
'reply_form': ReplyForm(),
}))
@@ -127,6 +129,7 @@ def create_topic(request, forum_id):
return redirect('app_forum')
return render(request, 'forum/create_topic.html', _base_context(request, {
'forum': forum,
'form': TopicForm(),
}))
@@ -204,6 +207,7 @@ def edit_post(request, post_id):
return redirect('forum_topic_p1', topic_id=post['topic_id'])
return render(request, 'forum/edit_post.html', _base_context(request, {
'post': post,
'form': EditPostForm(initial={'text': post['text']}),
}))
@@ -242,6 +246,8 @@ def delete_post(request, post_id):
if not perm.can_edit_post(request, post):
messages.error(request, 'No tienes permiso.')
return redirect('forum_topic_p1', topic_id=post['topic_id'])
if request.method != 'POST':
return redirect('forum_topic_p1', topic_id=post['topic_id'])
db.set_post_deleted(post_id, True)
messages.success(request, 'Post borrado.')
return redirect('forum_topic_p1', topic_id=post['topic_id'])
@@ -253,6 +259,8 @@ def restore_post(request, post_id):
post = db.get_post(post_id)
if not post:
return redirect('app_forum')
if request.method != 'POST':
return redirect('forum_topic_p1', topic_id=post['topic_id'])
db.set_post_deleted(post_id, False)
messages.success(request, 'Post restaurado.')
return redirect('forum_topic_p1', topic_id=post['topic_id'])
@@ -269,8 +277,18 @@ def _mod_only(request):
return None
def lock_topic(request, topic_id):
def _mod_post_only(request):
"""Exige moderador y método POST (CSRF)."""
block = _mod_only(request)
if block:
return block
if request.method != 'POST':
return redirect('app_forum')
return None
def lock_topic(request, topic_id):
block = _mod_post_only(request)
if block:
return block
db.set_topic_flag(topic_id, 'locked', True)
@@ -279,7 +297,7 @@ def lock_topic(request, topic_id):
def unlock_topic(request, topic_id):
block = _mod_only(request)
block = _mod_post_only(request)
if block:
return block
db.set_topic_flag(topic_id, 'locked', False)
@@ -288,7 +306,7 @@ def unlock_topic(request, topic_id):
def toggle_sticky(request, topic_id):
block = _mod_only(request)
block = _mod_post_only(request)
if block:
return block
topic = db.get_topic(topic_id, include_hidden=True)
@@ -299,7 +317,7 @@ def toggle_sticky(request, topic_id):
def delete_topic(request, topic_id):
block = _mod_only(request)
block = _mod_post_only(request)
if block:
return block
topic = db.get_topic(topic_id, include_hidden=True)
@@ -311,7 +329,7 @@ def delete_topic(request, topic_id):
def restore_topic(request, topic_id):
block = _mod_only(request)
block = _mod_post_only(request)
if block:
return block
db.set_topic_flag(topic_id, 'deleted', False)