Foro: buscador de temas y nombre de autor legible
- forum_search: busca temas por título y contenido (foros visibles) - db.get_display_name: nombre visible del autor = parte local del email de la cuenta (en vez de <bnetId>#1), usado en el panel de posts y la lista de temas - buscador en la portada del foro + plantilla search.html - docs/FORO.md actualizado Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+7
-2
@@ -23,7 +23,10 @@ Symfony *NovaWeb-main*, contra las **mismas tablas** en la base de datos
|
|||||||
- Crear tema, responder, editar y borrar el post propio.
|
- Crear tema, responder, editar y borrar el post propio.
|
||||||
- Moderación: fijar/desfijar, bloquear/desbloquear, mover, borrar/restaurar temas
|
- Moderación: fijar/desfijar, bloquear/desbloquear, mover, borrar/restaurar temas
|
||||||
y posts.
|
y posts.
|
||||||
|
- Buscador de temas (por título y contenido) en `/es/forum/search`.
|
||||||
- Perfil público básico (`/es/profile/<usuario>`).
|
- Perfil público básico (`/es/profile/<usuario>`).
|
||||||
|
- El autor se muestra con un **nombre legible** (parte local del email de la
|
||||||
|
cuenta) en vez de `<bnetId>#1`.
|
||||||
|
|
||||||
Rutas bajo `/es/forum...` (idénticas al original: `app_forum`, `forum_view`,
|
Rutas bajo `/es/forum...` (idénticas al original: `app_forum`, `forum_view`,
|
||||||
`forum_topic`, `forum_create_topic`, etc.).
|
`forum_topic`, `forum_create_topic`, etc.).
|
||||||
@@ -76,5 +79,7 @@ es seguro.
|
|||||||
|
|
||||||
## Notas / posibles mejoras
|
## Notas / posibles mejoras
|
||||||
|
|
||||||
- El autor se muestra como el nombre de cuenta de juego (`<bnetId>#1`); se podría
|
- Sistema de nicks propio del foro (ahora el nombre visible es la parte local del
|
||||||
mapear a un nick o al email como en «Mi cuenta».
|
email de la cuenta).
|
||||||
|
- Paginación/relevancia en los resultados de búsqueda (ahora lista los 50 temas
|
||||||
|
más recientes que coinciden).
|
||||||
|
|||||||
+46
-1
@@ -231,9 +231,11 @@ def get_author_info(poster_id):
|
|||||||
Datos del autor para mostrar en los posts: nivel GM, fecha de registro y
|
Datos del autor para mostrar en los posts: nivel GM, fecha de registro y
|
||||||
nº de mensajes. Cruza acore_auth (account/account_access) y acore_web.
|
nº de mensajes. Cruza acore_auth (account/account_access) y acore_web.
|
||||||
"""
|
"""
|
||||||
info = {'gmlevel': 0, 'joindate': None, 'post_count': 0, 'status': 'Miembro'}
|
info = {'gmlevel': 0, 'joindate': None, 'post_count': 0, 'status': 'Miembro',
|
||||||
|
'display_name': None}
|
||||||
if not poster_id:
|
if not poster_id:
|
||||||
return info
|
return info
|
||||||
|
info['display_name'] = get_display_name(poster_id)
|
||||||
try:
|
try:
|
||||||
with connections['acore_auth'].cursor() as cursor:
|
with connections['acore_auth'].cursor() as cursor:
|
||||||
cursor.execute("SELECT MAX(gmlevel) FROM account_access WHERE id = %s", [poster_id])
|
cursor.execute("SELECT MAX(gmlevel) FROM account_access WHERE id = %s", [poster_id])
|
||||||
@@ -308,6 +310,49 @@ def mark_read(user_id, topic_id):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_display_name(poster_id):
|
||||||
|
"""
|
||||||
|
Nombre legible del autor a partir de su cuenta: parte local del email
|
||||||
|
(antes de la @). Si no hay email, devuelve None (la plantilla usa el poster).
|
||||||
|
"""
|
||||||
|
if not poster_id:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
with connections['acore_auth'].cursor() as cursor:
|
||||||
|
cursor.execute(
|
||||||
|
"SELECT email, reg_mail FROM account WHERE id = %s", [poster_id]
|
||||||
|
)
|
||||||
|
row = cursor.fetchone()
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
if not row:
|
||||||
|
return None
|
||||||
|
email = (row[0] or row[1] or '').strip()
|
||||||
|
if '@' in email:
|
||||||
|
return email.split('@', 1)[0]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def search_topics(query, limit=50):
|
||||||
|
"""Busca temas por título o por contenido de sus posts (foros visibles)."""
|
||||||
|
like = f"%{query}%"
|
||||||
|
with _conn().cursor() as cursor:
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
SELECT DISTINCT t.id, t.name, t.poster, t.poster_id, t.forum_id,
|
||||||
|
t.created, f.name AS forum_name
|
||||||
|
FROM forum_topics t
|
||||||
|
JOIN forums f ON f.id = t.forum_id AND f.visibility = 1 AND f.deleted = 0
|
||||||
|
LEFT JOIN forum_posts p ON p.topic_id = t.id AND p.deleted = 0
|
||||||
|
WHERE t.deleted = 0 AND (t.name LIKE %s OR p.text LIKE %s)
|
||||||
|
ORDER BY t.updated_at DESC
|
||||||
|
LIMIT %s
|
||||||
|
""",
|
||||||
|
[like, like, limit],
|
||||||
|
)
|
||||||
|
return _dictfetchall(cursor)
|
||||||
|
|
||||||
|
|
||||||
def list_forums_for_move(exclude_forum_id=None):
|
def list_forums_for_move(exclude_forum_id=None):
|
||||||
"""Lista de foros visibles para el desplegable de 'mover tema'."""
|
"""Lista de foros visibles para el desplegable de 'mover tema'."""
|
||||||
with _conn().cursor() as cursor:
|
with _conn().cursor() as cursor:
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
{% if topic.moved %}<span class="forum-badge">Movido</span>{% endif %}
|
{% if topic.moved %}<span class="forum-badge">Movido</span>{% endif %}
|
||||||
{{ topic.name }}
|
{{ topic.name }}
|
||||||
</a>
|
</a>
|
||||||
<div class="desc">por {{ topic.poster }} · {{ topic.created }}</div>
|
<div class="desc">por {{ topic.display_poster }} · {{ topic.created }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
<div>{{ topic.post_count }} respuestas</div>
|
<div>{{ topic.post_count }} respuestas</div>
|
||||||
|
|||||||
@@ -12,6 +12,11 @@
|
|||||||
<div class="body-box-content">
|
<div class="body-box-content">
|
||||||
{% include 'forum/_style.html' %}
|
{% include 'forum/_style.html' %}
|
||||||
<div class="forum-wrap">
|
<div class="forum-wrap">
|
||||||
|
<form class="forum-search" action="{% url 'forum_search' %}" method="GET" style="margin-bottom:16px">
|
||||||
|
<input type="text" name="q" placeholder="Buscar en el foro..."
|
||||||
|
style="width:70%;background:rgba(0,0,0,.3);color:#e8e0d0;border:1px solid rgba(215,150,2,.4);padding:8px;border-radius:3px">
|
||||||
|
<button type="submit" class="forum-btn">Buscar</button>
|
||||||
|
</form>
|
||||||
{% for category in categories %}
|
{% for category in categories %}
|
||||||
<div class="forum-cat">
|
<div class="forum-cat">
|
||||||
<div class="forum-cat-title">{{ category.name }}</div>
|
<div class="forum-cat-title">{{ category.name }}</div>
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
{% include 'partials/head.html' %}
|
||||||
|
{% include 'partials/header.html' %}
|
||||||
|
{% include 'partials/video.html' %}
|
||||||
|
|
||||||
|
<div class="main-page">
|
||||||
|
<div class="middle-content">
|
||||||
|
<div class="body-content">
|
||||||
|
<div class="title-content"><h1>Buscar en el foro</h1></div>
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="body-box-content">
|
||||||
|
{% include 'forum/_style.html' %}
|
||||||
|
<div class="forum-wrap">
|
||||||
|
<form class="forum-search" action="{% url 'forum_search' %}" method="GET" style="margin-bottom:16px">
|
||||||
|
<input type="text" name="q" value="{{ query }}" placeholder="Buscar en el foro..."
|
||||||
|
style="width:70%;background:rgba(0,0,0,.3);color:#e8e0d0;border:1px solid rgba(215,150,2,.4);padding:8px;border-radius:3px">
|
||||||
|
<button type="submit" class="forum-btn">Buscar</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% if query %}
|
||||||
|
<p style="color:#b1997f">Resultados para «{{ query }}»: {{ results|length }}</p>
|
||||||
|
{% for topic in results %}
|
||||||
|
<div class="topic-row">
|
||||||
|
<div class="main">
|
||||||
|
<a class="name" href="{% url 'forum_topic_p1' topic_id=topic.id %}">{{ topic.name }}</a>
|
||||||
|
<div class="desc">en {{ topic.forum_name }} · por {{ topic.display_poster }} · {{ topic.created }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% empty %}
|
||||||
|
<p>No se encontraron temas.</p>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<p>Escribe al menos 2 caracteres para buscar.</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="forum-actions" style="margin-top:16px">
|
||||||
|
<a class="forum-btn" href="{% url 'app_forum' %}">← Volver al foro</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% include 'partials/social.html' %}
|
||||||
|
{% include 'partials/footer.html' %}
|
||||||
|
{% include 'partials/final.html' %}
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
<div class="forum-post {% if post.deleted %}forum-deleted{% endif %}" id="post-{{ post.id }}">
|
<div class="forum-post {% if post.deleted %}forum-deleted{% endif %}" id="post-{{ post.id }}">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<div class="pname">
|
<div class="pname">
|
||||||
<a href="{% url 'forum_profile' username=post.poster %}" style="color:#e0c07a">{{ post.poster }}</a>
|
<a href="{% url 'forum_profile' username=post.poster %}" style="color:#e0c07a">{{ post.author.display_name|default:post.poster }}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="prole">{{ post.author.status }}</div>
|
<div class="prole">{{ post.author.status }}</div>
|
||||||
<div class="pmeta">
|
<div class="pmeta">
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from . import views
|
|||||||
# que la genérica forum/<int:forum_id>/...
|
# que la genérica forum/<int:forum_id>/...
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('forum', views.index, name='app_forum'),
|
path('forum', views.index, name='app_forum'),
|
||||||
|
path('forum/search', views.search, name='forum_search'),
|
||||||
|
|
||||||
# Temas
|
# Temas
|
||||||
path('forum/topic/<int:topic_id>/reply', views.reply_to_topic, name='forum_post_reply'),
|
path('forum/topic/<int:topic_id>/reply', views.reply_to_topic, name='forum_post_reply'),
|
||||||
|
|||||||
@@ -75,6 +75,8 @@ def view_forum(request, forum_id, page=1):
|
|||||||
total = db.count_topics(forum_id)
|
total = db.count_topics(forum_id)
|
||||||
pag, offset = _paginate(total, page, settings.FORUM_TOPICS_PER_PAGE)
|
pag, offset = _paginate(total, page, settings.FORUM_TOPICS_PER_PAGE)
|
||||||
topics = db.get_topics(forum_id, offset, settings.FORUM_TOPICS_PER_PAGE)
|
topics = db.get_topics(forum_id, offset, settings.FORUM_TOPICS_PER_PAGE)
|
||||||
|
for t in topics:
|
||||||
|
t['display_poster'] = db.get_display_name(t['poster_id']) or t['poster']
|
||||||
|
|
||||||
return render(request, 'forum/forum.html', _base_context(request, {
|
return render(request, 'forum/forum.html', _base_context(request, {
|
||||||
'forum': forum,
|
'forum': forum,
|
||||||
@@ -83,6 +85,19 @@ def view_forum(request, forum_id, page=1):
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
def search(request):
|
||||||
|
query = (request.GET.get('q') or '').strip()
|
||||||
|
results = []
|
||||||
|
if len(query) >= 2:
|
||||||
|
results = db.search_topics(query)
|
||||||
|
for r in results:
|
||||||
|
r['display_poster'] = db.get_display_name(r['poster_id']) or r['poster']
|
||||||
|
return render(request, 'forum/search.html', _base_context(request, {
|
||||||
|
'query': query,
|
||||||
|
'results': results,
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
def view_topic(request, topic_id, page=1):
|
def view_topic(request, topic_id, page=1):
|
||||||
topic = db.get_topic(topic_id, include_hidden=perm.is_moderator(request))
|
topic = db.get_topic(topic_id, include_hidden=perm.is_moderator(request))
|
||||||
if not topic:
|
if not topic:
|
||||||
|
|||||||
Reference in New Issue
Block a user