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
+3
View File
@@ -32,6 +32,9 @@
.forum-msg.error { background: rgba(196,30,58,.2); color: #ff9a9a; }
.forum-msg.success { background: rgba(46,160,67,.2); color: #9affb0; }
.forum-deleted { opacity: .55; }
.forum-inline { display: inline; }
.link-btn { background: none; border: none; color: #d79602; cursor: pointer; padding: 0; font: inherit; text-decoration: underline; }
.link-btn.danger { color: #ff9a9a; }
</style>
{% if messages %}
<div class="forum-messages forum-wrap">
+3 -2
View File
@@ -15,13 +15,14 @@
<p style="color:#b1997f">Foro: <strong>{{ forum.name }}</strong></p>
<form action="{% url 'forum_create_topic_submit' forum_id=forum.id %}" method="POST">
{% csrf_token %}
<input type="text" name="name" maxlength="45" placeholder="Título del tema" required>
<textarea name="text" placeholder="Contenido del tema..." required></textarea>
{{ form.name }}
{{ form.text }}
<div class="forum-actions">
<button type="submit" class="forum-btn">Publicar tema</button>
<a class="forum-btn" href="{% url 'forum_view_p1' forum_id=forum.id %}">Cancelar</a>
</div>
</form>
{{ form.media }}
</div>
</div>
</div>
+2 -1
View File
@@ -14,12 +14,13 @@
<div class="forum-wrap forum-form">
<form action="{% url 'forum_update_post' post_id=post.id %}" method="POST">
{% csrf_token %}
<textarea name="text" required>{{ post.text|safe }}</textarea>
{{ form.text }}
<div class="forum-actions">
<button type="submit" class="forum-btn">Guardar cambios</button>
<a class="forum-btn" href="{% url 'forum_topic_p1' topic_id=post.topic_id %}">Cancelar</a>
</div>
</form>
{{ form.media }}
</div>
</div>
</div>
+9 -8
View File
@@ -40,10 +40,10 @@
<span>
{% if post.can_edit and not post.deleted %}
<a href="{% url 'forum_edit_post' post_id=post.id %}">Editar</a>
· <a href="{% url 'forum_delete_post' post_id=post.id %}" onclick="return confirm('¿Borrar este mensaje?')">Borrar</a>
· <form class="forum-inline" action="{% url 'forum_delete_post' post_id=post.id %}" method="POST" onsubmit="return confirm('¿Borrar este mensaje?')">{% csrf_token %}<button type="submit" class="link-btn danger">Borrar</button></form>
{% endif %}
{% if post.deleted and forum_is_moderator %}
<a href="{% url 'forum_restore_post' post_id=post.id %}">Restaurar</a>
<form class="forum-inline" action="{% url 'forum_restore_post' post_id=post.id %}" method="POST">{% csrf_token %}<button type="submit" class="link-btn">Restaurar</button></form>
{% endif %}
</span>
</div>
@@ -69,11 +69,12 @@
<h3 style="color:#d79602">Responder</h3>
<form action="{% url 'forum_post_reply' topic_id=topic.id %}" method="POST">
{% csrf_token %}
<textarea name="text" placeholder="Escribe tu respuesta..." required></textarea>
{{ reply_form.text }}
<div class="forum-actions">
<button type="submit" class="forum-btn">Publicar respuesta</button>
</div>
</form>
{{ reply_form.media }}
</div>
{% elif topic.locked %}
<p style="color:#b1997f">Este tema está bloqueado. No se admiten nuevas respuestas.</p>
@@ -86,15 +87,15 @@
<h3 style="color:#d79602">Moderación</h3>
<div class="forum-actions">
{% if topic.locked %}
<a class="forum-btn" href="{% url 'forum_unlock_topic' topic_id=topic.id %}">Desbloquear</a>
<form class="forum-inline" action="{% url 'forum_unlock_topic' topic_id=topic.id %}" method="POST">{% csrf_token %}<button type="submit" class="forum-btn">Desbloquear</button></form>
{% else %}
<a class="forum-btn" href="{% url 'forum_lock_topic' topic_id=topic.id %}">Bloquear</a>
<form class="forum-inline" action="{% url 'forum_lock_topic' topic_id=topic.id %}" method="POST">{% csrf_token %}<button type="submit" class="forum-btn">Bloquear</button></form>
{% endif %}
<a class="forum-btn" href="{% url 'forum_toggle_sticky' topic_id=topic.id %}">{% if topic.sticky %}Quitar fijado{% else %}Fijar{% endif %}</a>
<form class="forum-inline" action="{% url 'forum_toggle_sticky' topic_id=topic.id %}" method="POST">{% csrf_token %}<button type="submit" class="forum-btn">{% if topic.sticky %}Quitar fijado{% else %}Fijar{% endif %}</button></form>
{% if topic.deleted %}
<a class="forum-btn" href="{% url 'forum_restore_topic' topic_id=topic.id %}">Restaurar tema</a>
<form class="forum-inline" action="{% url 'forum_restore_topic' topic_id=topic.id %}" method="POST">{% csrf_token %}<button type="submit" class="forum-btn">Restaurar tema</button></form>
{% else %}
<a class="forum-btn danger" href="{% url 'forum_delete_topic' topic_id=topic.id %}" onclick="return confirm('¿Borrar el tema completo?')">Borrar tema</a>
<form class="forum-inline" action="{% url 'forum_delete_topic' topic_id=topic.id %}" method="POST" onsubmit="return confirm('¿Borrar el tema completo?')">{% csrf_token %}<button type="submit" class="forum-btn danger">Borrar tema</button></form>
{% endif %}
</div>
{% if move_forums %}