Merge pull request #831 from ae-utbm/forum-css-rework

Forum css rework
This commit is contained in:
thomas girod 2024-09-30 12:13:52 +02:00 committed by GitHub
commit bb953a6139
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 428 additions and 282 deletions

View File

@ -1,5 +1,33 @@
{% extends "core/base.jinja" %}
{% from 'core/macros.jinja' import user_profile_link, paginate %}
{% from 'core/macros.jinja' import user_profile_link %}
{# This page uses a custom macro instead of the core `paginate_jinja` and `paginate_alpine`
because it works with a somewhat dynamic form,
but was written before Alpine was introduced in the project.
TODO : rewrite the pagination used in this template an Alpine one
#}
{% macro paginate(page_obj, paginator, js_action) %}
{% set js = js_action|default('') %}
{% if page_obj.has_previous() or page_obj.has_next() %}
{% if page_obj.has_previous() %}
<a {% if js %} type="submit" onclick="{{ js }}" {% endif %} href="?page={{ page_obj.previous_page_number() }}">{% trans %}Previous{% endtrans %}</a>
{% else %}
<span class="disabled">{% trans %}Previous{% endtrans %}</span>
{% endif %}
{% for i in paginator.page_range %}
{% if page_obj.number == i %}
<span class="active">{{ i }} <span class="sr-only">({% trans %}current{% endtrans %})</span></span>
{% else %}
<a {% if js %} type="submit" onclick="{{ js }}" {% endif %} href="?page={{ i }}">{{ i }}</a>
{% endif %}
{% endfor %}
{% if page_obj.has_next() %}
<a {% if js %} type="submit" onclick="{{ js }}" {% endif %} href="?page={{ page_obj.next_page_number() }}">{% trans %}Next{% endtrans %}</a>
{% else %}
<span class="disabled">{% trans %}Next{% endtrans %}</span>
{% endif %}
{% endif %}
{% endmacro %}
{% block content %}
<h3>{% trans %}Sales{% endtrans %}</h3>

View File

@ -1,12 +1,13 @@
import random
from datetime import date, timedelta
from datetime import timezone as tz
from decimal import Decimal
from typing import Iterator
from dateutil.relativedelta import relativedelta
from django.conf import settings
from django.core.management.base import BaseCommand
from django.db.models import Exists, F, Min, OuterRef, Subquery, Sum
from django.db.models import Count, Exists, F, Min, OuterRef, Subquery, Sum
from django.db.models.functions import Coalesce
from django.utils.timezone import make_aware, now
from faker import Faker
@ -22,6 +23,7 @@ from counter.models import (
Refilling,
Selling,
)
from forum.models import Forum, ForumMessage, ForumTopic
from pedagogy.models import UV
from subscription.models import Subscription
@ -97,6 +99,8 @@ class Command(BaseCommand):
self.create_sales(sellers)
self.stdout.write("Creating permanences...")
self.create_permanences(sellers)
self.stdout.write("Filling the forum...")
self.create_forums()
self.stdout.write("Done")
@ -288,7 +292,8 @@ class Command(BaseCommand):
since=Subquery(
Subscription.objects.filter(member__customer=OuterRef("pk"))
.annotate(res=Min("subscription_start"))
.values("res")[:1]
.values("res")
.order_by("res")[:1]
)
)
)
@ -381,3 +386,72 @@ class Command(BaseCommand):
)
)
Permanency.objects.bulk_create(perms)
def create_forums(self):
forumers = random.sample(list(User.objects.all()), 100)
most_actives = random.sample(forumers, 10)
categories = list(Forum.objects.filter(is_category=True))
new_forums = [
Forum(name=self.faker.text(20), parent=random.choice(categories))
for _ in range(15)
]
Forum.objects.bulk_create(new_forums)
forums = list(Forum.objects.filter(is_category=False))
new_topics = [
ForumTopic(
_title=self.faker.text(20),
author=random.choice(most_actives),
forum=random.choice(forums),
)
for _ in range(100)
]
ForumTopic.objects.bulk_create(new_topics)
topics = list(ForumTopic.objects.all())
def get_author():
if random.random() > 0.5:
return random.choice(most_actives)
return random.choice(forumers)
messages = []
for t in topics:
nb_messages = max(1, int(random.normalvariate(mu=90, sigma=50)))
dates = sorted(
[
self.faker.date_time_between("-15y", "-1d", tzinfo=tz.utc)
for _ in range(nb_messages)
],
reverse=True,
)
messages.extend(
[
ForumMessage(
topic=t,
author=get_author(),
date=d,
message="\n\n".join(
self.faker.paragraphs(random.randint(1, 4))
),
)
for d in dates
]
)
ForumMessage.objects.bulk_create(messages)
ForumTopic.objects.update(
_message_number=Subquery(
ForumMessage.objects.filter(topic_id=OuterRef("pk"))
.values("topic_id")
.annotate(res=Count("*"))
.values("res")
),
_last_message_id=Subquery(
ForumMessage.objects.order_by("-date").values("id")[:1]
),
)
for f in Forum.objects.filter(parent__isnull=False):
# this is a N+1 queries, but it's ok,
# since there are quite a few forums
# and trying to do it with a single query
# would result in a big whibbly-woobly hacky queryset
f.set_last_message()
f.set_topic_number()

View File

@ -1256,165 +1256,6 @@ textarea {
display: inline;
}
/*------------------------------FORUM----------------------------------*/
#forum {
.button {
background-color: rgb(230, 230, 230);
padding: 10px;
font-weight: bold;
border-radius: 5px;
&:hover {
background-color: rgb(211, 211, 211);
}
}
.topic {
border: solid $primary-neutral-color 1px;
padding: 1px;
margin: 1px;
p {
margin: 1px;
font-size: smaller;
}
a {
color: $black-color;
}
a:hover {
text-decoration: underline;
}
}
.tools {
font-size: x-small;
border: none;
font-weight: bold;
a {
padding: 1px;
}
}
.title {
font-size: small;
font-weight: bold;
padding: 2px;
}
.last_message date {
white-space: nowrap;
}
.last_message span {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
width: 100%;
display: block;
}
.forum {
background: $primary-neutral-light-color;
padding: 1px;
margin: 1px;
p {
margin: 1px;
font-size: smaller;
}
a {
color: $black-color;
}
a:hover {
text-decoration: underline;
}
}
.search_bar {
margin: 10px 0;
display: flex;
flex-wrap: wrap;
height: 20px;
align-items: center;
}
.search_check {
margin-left: 10px;
}
.search_bouton {
margin-left: 10px;
}
.category {
margin-top: 5px;
background: $secondary-color;
color: white;
border-radius: 10px 10px 0 0;
.title {
text-transform: uppercase;
}
}
.message {
padding: 1px;
margin: 1px;
background: $secondary-neutral-light-color;
&:nth-child(odd) {
background: $primary-neutral-light-color;
}
.title {
font-size: 100%;
}
&.unread {
background: #e9eea1;
}
}
.msg_author.deleted {
background: #ffcfcf;
}
.msg_content {
&.deleted {
background: #ffefef;
}
display: inline-block;
width: 80%;
vertical-align: top;
}
.msg_author {
display: inline-block;
width: 19%;
text-align: center;
img {
max-width: 70%;
margin: 0 auto;
}
}
.msg_header {
display: inline-block;
width: 100%;
font-size: small;
}
.msg_meta {
font-size: small;
list-style-type: none;
li {
padding: 1px;
margin: 1px;
}
}
.forum_signature {
color: hsl(0, 0%, 75%);
border-top: 1px solid hsl(0, 0%, 75%);
a {
color: hsl(0, 0%, 75%);
&:hover {
text-decoration: underline;
}
}
}
}
/*--------------------------------FOOTER-------------------------------*/
footer {

View File

@ -11,6 +11,7 @@
<link rel="stylesheet" href="{{ scss('core/markdown.scss') }}">
<link rel="stylesheet" href="{{ scss('core/header.scss') }}">
<link rel="stylesheet" href="{{ scss('core/navbar.scss') }}">
<link rel="stylesheet" href="{{ scss('core/pagination.scss') }}">
<link rel="stylesheet" href="{{ static('vendored/select2/select2.min.css') }}">
{% block jquery_css %}

View File

@ -156,27 +156,47 @@
</nav>
{% endmacro %}
{% macro paginate(page_obj, paginator, js_action) %}
{% set js = js_action|default('') %}
{% if page_obj.has_previous() or page_obj.has_next() %}
{% if page_obj.has_previous() %}
<a {% if js %} type="submit" onclick="{{ js }}" {% endif %} href="?page={{ page_obj.previous_page_number() }}">{% trans %}Previous{% endtrans %}</a>
{% macro paginate_jinja(current_page, paginator) %}
{# Add pagination buttons for pages without Alpine.
This must be coupled with a view that handles pagination
with the Django Paginator object.
Parameters:
current_page (django.core.paginator.Page): the current page object
paginator (django.core.paginator.Paginator): the paginator object
#}
<nav class="pagination">
{% if current_page.has_previous() %}
<a href="?page={{ current_page.previous_page_number() }}">
<button>
<i class="fa fa-caret-left"></i>
</button>
</a>
{% else %}
<span class="disabled">{% trans %}Previous{% endtrans %}</span>
<button disabled="disabled"><i class="fa fa-caret-left"></i></button>
{% endif %}
{% for i in paginator.page_range %}
{% if page_obj.number == i %}
<span class="active">{{ i }} <span class="sr-only">({% trans %}current{% endtrans %})</span></span>
{% for i in paginator.get_elided_page_range(current_page.number) %}
{% if i == current_page.number %}
<button class="active">{{ i }}</button>
{% elif i == paginator.ELLIPSIS %}
<strong>{{ paginator.ELLIPSIS }}</strong>
{% else %}
<a {% if js %} type="submit" onclick="{{ js }}" {% endif %} href="?page={{ i }}">{{ i }}</a>
<a href="?page={{ i }}">
<button>{{ i }}</button>
</a>
{% endif %}
{% endfor %}
{% if page_obj.has_next() %}
<a {% if js %} type="submit" onclick="{{ js }}" {% endif %} href="?page={{ page_obj.next_page_number() }}">{% trans %}Next{% endtrans %}</a>
{% if current_page.has_next() %}
<a href="?page={{ current_page.next_page_number() }}">
<button>
<i class="fa fa-caret-right"></i>
</button>
</a>
{% else %}
<span class="disabled">{% trans %}Next{% endtrans %}</span>
<button disabled="disabled"><i class="fa fa-caret-right"></i></button>
{% endif %}
{% endif %}
</nav>
{% endmacro %}
{% macro select_all_checkbox(form_id) %}

View File

@ -1,5 +1,5 @@
{% extends "core/base.jinja" %}
{% from 'core/macros.jinja' import user_profile_link, paginate %}
{% from 'core/macros.jinja' import user_profile_link, paginate_jinja %}
{% block title %}
{% trans %}Cash register summary list{% endtrans %}
@ -57,7 +57,7 @@
</table>
<br>
{% if is_paginated %}
{{ paginate(page_obj, paginator) }}
{{ paginate_jinja(page_obj, paginator) }}
{% endif %}
{% else %}
{% trans %}There is no cash register summary in this website.{% endtrans %}

View File

@ -1,5 +1,5 @@
{% extends "core/base.jinja" %}
{% from 'core/macros.jinja' import paginate %}
{% from "core/macros.jinja" import paginate_jinja %}
{% block title %}
{%- trans %}Reloads list{% endtrans %} -- {{ counter.name }}
@ -28,7 +28,7 @@
{%- endfor %}
</table>
{% if is_paginated %}
{{ paginate(page_obj, paginator) }}
{{ paginate_jinja(page_obj, paginator) }}
{% endif %}
{%- endblock %}

View File

@ -1,5 +1,5 @@
{% extends "core/base.jinja" %}
{% from 'core/macros.jinja' import paginate %}
{% from "core/macros.jinja" import paginate_jinja %}
{% block title %}
{%- trans %}Election list{% endtrans %}
@ -46,7 +46,7 @@
</section>
{%- endfor %}
{% if is_paginated %}
{{ paginate(page_obj, paginator) }}
{{ paginate_jinja(page_obj, paginator) }}
{% endif %}
{%- endblock %}

View File

@ -170,7 +170,7 @@ class Forum(models.Model):
def is_owned_by(self, user):
if user.is_anonymous:
return False
if user.is_in_group(pk=settings.SITH_GROUP_FORUM_ADMIN_ID):
if user.is_root or user.is_in_group(pk=settings.SITH_GROUP_FORUM_ADMIN_ID):
return True
try:
m = Forum._club_memberships[self.id][user.id]
@ -273,10 +273,10 @@ class ForumTopic(models.Model):
return self.forum.is_owned_by(user)
def can_be_edited_by(self, user):
return user.can_edit(self.forum)
return user.is_root or user.can_edit(self.forum)
def can_be_viewed_by(self, user):
return user.can_view(self.forum)
return user.is_root or user.can_view(self.forum)
def get_first_unread_message(self, user: User) -> ForumMessage | None:
if not hasattr(user, "forum_infos"):
@ -355,7 +355,7 @@ class ForumMessage(models.Model):
return not self._deleted
def can_be_moderated_by(self, user):
return self.topic.forum.is_owned_by(user) or user.id == self.author.id
return self.topic.forum.is_owned_by(user) or user.id == self.author_id
def get_url(self):
return (

View File

@ -0,0 +1,166 @@
@import "core/static/core/colors";
#forum {
.button {
background-color: rgb(230, 230, 230);
padding: 10px;
font-weight: bold;
border-radius: 5px;
&:hover {
background-color: rgb(211, 211, 211);
}
}
.topic {
border: solid $primary-neutral-color 1px;
padding: 1px;
margin: 1px;
p {
margin: 1px;
font-size: smaller;
}
a {
color: $black-color;
}
a:hover {
text-decoration: underline;
}
}
.tools {
font-size: x-small;
border: none;
font-weight: bold;
a {
padding: 1px;
}
}
.title {
font-size: small;
font-weight: bold;
padding: 2px;
}
.last_message span {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
width: 100%;
display: block;
}
.forum {
background: $primary-neutral-light-color;
padding: 1px;
margin: 1px;
p {
margin: 1px;
font-size: smaller;
}
a {
color: $black-color;
}
a:hover {
text-decoration: underline;
}
}
.search_bar {
margin: 10px 0;
display: flex;
flex-wrap: wrap;
align-items: center;
}
.search_check {
margin-left: 10px;
}
.search_bouton {
margin-left: 10px;
}
.category {
margin-top: 5px;
background: $secondary-color;
color: white;
border-radius: 10px 10px 0 0;
.title {
text-transform: uppercase;
}
}
.message {
padding: 15px;
margin: 20px;
display: flex;
flex-direction: column;
gap: 10px;
background: $secondary-neutral-light-color;
border-radius: 5px;
border: 1px darken($secondary-neutral-light-color, 10%) solid;
&:nth-child(odd) {
background: $primary-neutral-light-color;
}
.message-header {
display: flex;
gap: 20px;
border-bottom: 0.0625rem grey dotted;
padding-bottom: 10px;
img {
width: 50px;
height: 50px;
border-radius: 50%;
}
.message-metadata {
display: flex;
flex-direction: column;
justify-content: center;
}
.message-options {
flex: 2;
display: flex;
justify-content: right;
gap: 15px;
}
}
.message-content {
padding: 0 20px;
&.delete > * {
margin-top: 0;
}
.markdown blockquote h5 {
padding-top: 0;
margin-top: 0;
}
}
.forum-signature {
margin: 0;
font-size: small;
font-style: italic;
border-top: 0.0625rem grey dotted;
}
&.unread {
background: #e9eea1;
}
}
}

View File

@ -5,6 +5,10 @@
{% trans %}Favorite topics{% endtrans %}
{% endblock %}
{% block additional_css %}
<link rel="stylesheet" href="{{ scss('forum/css/forum.scss') }}">
{% endblock %}
{% block content %}
<p>
<a href="{{ url('forum:main') }}">Forum</a> >

View File

@ -5,6 +5,10 @@
{{ forum }}
{% endblock %}
{% block additional_css %}
<link rel="stylesheet" href="{{ scss('forum/css/forum.scss') }}">
{% endblock %}
{% block content %}
{{ display_breadcrumb(forum) }}
<div id="forum">

View File

@ -5,6 +5,10 @@
{% trans %}Last unread messages{% endtrans %}
{% endblock %}
{% block additional_css %}
<link rel="stylesheet" href="{{ scss('forum/css/forum.scss') }}">
{% endblock %}
{% block content %}
<p>
<a href="{{ url('forum:main') }}">Forum</a> >

View File

@ -97,73 +97,81 @@
{% endmacro %}
{% macro display_message(m, user, unread=False) %}
{% if user.can_view(m) %}
<div id="msg_{{ m.id }}" class="message {% if unread %}unread{% endif %}">
<div class="msg_author {% if m.deleted %}deleted{% endif %}">
{% set user_is_admin = m.topic.is_owned_by(user) %}
<article id="msg_{{ m.id }}" class="message {% if unread %}unread{% endif %}">
{% if user_is_admin or not m._deleted %}
<div class="message-header">
{% if m.author.avatar_pict %}
<img src="{{ m.author.avatar_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}" id="picture" />
{% elif m.author.profile_pict %}
<img src="{{ m.author.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}" id="picture" />
{% else %}
<img src="{{ static('core/img/unknown.jpg') }}" alt="{% trans %}Profile{% endtrans %}" id="picture" />
{% endif %}
<br/>
<strong><a href="{{ m.author.get_absolute_url() }}">{{ m.author.get_short_name() }}</a></strong>
</div>
<div class="msg_content {% if m.deleted %}deleted{% endif %}" {% if m.id == first_unread_message_id %}id="first_unread"{% endif %}>
<div class="msg_header">
<div class="ib w_big title">
<a href="{{ m.get_absolute_url() }}">
{{ m.date|localtime|date(DATETIME_FORMAT) }}
{{ m.date|localtime|time(DATETIME_FORMAT) }}
{%- if m.title -%}
- {{ m.title }}
{%- endif -%}
<div class="message-metadata">
<a href="{{ m.author.get_absolute_url() }}">
<strong>{{ m.author.get_short_name() }}</strong>
</a>
<a href="{{ m.get_absolute_url() }}">
{{ m.date|localtime|date(DATETIME_FORMAT) }}
{{ m.date|localtime|time(DATETIME_FORMAT) }}
</a>
</div>
<div class="message-options">
<a href="{{ url('forum:new_message', topic_id=m.topic.id) }}?quote_id={{ m.id }}">
<i class="fa fa-quote-right"></i>
</a>
{% if user.can_edit(m) %}
<a href="{{ url('forum:edit_message', message_id=m.id) }}">
<i class="fa fa-pencil"></i>
</a>
</div>
<div class="ib w_small">
<span><a href="{{ m.get_absolute_url() }}">#{{ m.id }}</a></span>
<br/>
<span><a href="{{ url('forum:new_message', topic_id=m.topic.id) }}?quote_id={{ m.id }}">
{% trans %}Reply as quote{% endtrans %}</a></span>
{% if user.can_edit(m) %}
<span> <a href="{{ url('forum:edit_message', message_id=m.id) }}">{% trans %}Edit{% endtrans %}</a></span>
{% endif %}
{% if m.can_be_moderated_by(user) %}
{% if m.deleted %}
<span> <a href="{{ url('forum:undelete_message', message_id=m.id) }}">{% trans %}Undelete{% endtrans %}</a></span>
{% else %}
<span> <a href="{{ url('forum:delete_message', message_id=m.id) }}">{% trans %}Delete{% endtrans %}</a></span>
{% endif %}
{% endif %}
</div>
</div>
<hr>
<div>
{{ m.message|markdown }}
{% endif %}
{% if user_is_admin and m._deleted %}
<span>
<a href="{{ url('forum:undelete_message', message_id=m.id) }}">
{% trans %}Undelete{% endtrans %}
</a>
</span>
{% endif %}
{% if not m._deleted and (user_is_admin or user.id == m.author_id) %}
<span>
<a href="{{ url('forum:delete_message', message_id=m.id) }}">
<i class="fa fa-trash"></i>
</a>
</span>
{% endif %}
</div>
</div>
{% endif %} {# close `user.can_view(m) or user_is_admin` #}
{% if user.can_view(m) %}
<div
class="message-content {%- if m.deleted -%}deleted{%- endif -%}"
{%- if m.id == first_unread_message_id -%}id="first_unread"{%- endif -%}
>
{{ m.message|markdown }}
{% if m.can_be_moderated_by(user) %}
<ul class="msg_meta">
{% for meta in m.metas.select_related('user').order_by('id') %}
<li style="background: {% if m.author == meta.user %}#bfffbf{% else %}#ffffbf{% endif %}">
{{ meta.get_action_display() }} {{ meta.user.get_short_name() }}
{{ meta.get_action_display() }} {{ meta.user.get_short_name() }}
{% trans %} at {% endtrans %}{{ meta.date|localtime|time(DATETIME_FORMAT) }}
{% trans %} the {% endtrans %}{{ meta.date|localtime|date(DATETIME_FORMAT)}}</li>
{% trans %} the {% endtrans %}{{ meta.date|localtime|date(DATETIME_FORMAT)}}
</li>
{% endfor %}
</ul>
{% endif %}
<div class="forum_signature">{{ m.author.forum_signature|markdown }}</div>
</div>
</div>
{% else %}
<div id="msg_{{ m.id }}" class="message">
<div class="msg_author deleted">
</div>
<div class="msg_content deleted">
<p class="ib w_big">{% trans %}Deleted or unreadable message.{% endtrans %}</p>
<p class="ib w_small">{{ m.date|localtime|date(DATETIME_FORMAT) }} {{ m.date|localtime|time(DATETIME_FORMAT) }}</p>
</div>
</div>
{% endif %}
{{ m.mark_as_read(user) or "" }}
{% if m.author.forum_signature %}
<div class="forum-signature">{{ m.author.forum_signature|markdown }}</div>
{% endif %}
{% else %}
<div class="message-content delete">
<p>{% trans %}Deleted or unreadable message.{% endtrans %}</p>
</div>
{% endif %}
</article>
{{ m.mark_as_read(user) or "" }}
{% endmacro %}
{% macro display_search_bar(request) %}

View File

@ -6,6 +6,10 @@
{% trans %}Forum{% endtrans %}
{% endblock %}
{% block additional_css %}
<link rel="stylesheet" href="{{ scss('forum/css/forum.scss') }}">
{% endblock %}
{% block content %}
<p>
<a href="{{ url('forum:main') }}">{% trans %}Forum{% endtrans %}</a> >

View File

@ -9,6 +9,11 @@
{% endif %}
{% endblock %}
{% block additional_css %}
<link rel="stylesheet" href="{{ scss('forum/css/forum.scss') }}">
{% endblock %}
{% block content %}
{% if topic %}
{{ display_search_bar(request) }}

View File

@ -2,6 +2,11 @@
{% from 'forum/macros.jinja' import display_message, display_breadcrumb, display_search_bar %}
{% block additional_css %}
<link rel="stylesheet" href="{{ scss('forum/css/forum.scss') }}">
{% endblock %}
{% block content %}
<div id="forum">
{{ display_search_bar(request) }}

View File

@ -1,28 +1,15 @@
{% extends "core/base.jinja" %}
{% from 'core/macros.jinja' import user_profile_link %}
{% from 'forum/macros.jinja' import display_message, display_breadcrumb, display_search_bar %}
{% from 'core/macros.jinja' import paginate_jinja %}
{% block title %}
{{ topic }}
{% endblock %}
{% block head %}
{{ super() }}
<style type="text/css" media="all">
.topic {
border: solid skyblue 1px;
padding: 2px;
margin: 2px;
}
.forum {
background: lightblue;
padding: 2px;
margin: 2px;
}
.category {
background: skyblue;
}
</style>
{% block additional_css %}
<link rel="stylesheet" href="{{ scss('forum/css/forum.scss') }}">
{% endblock %}
{% block content %}
@ -40,30 +27,24 @@
</p>
{{ display_search_bar(request) }}
<p style="text-align: right; background: #d8e7f3;">
{% for p in msgs.paginator.page_range %}
<span class="ib" style="background: {% if p == msgs.number %}white{% endif %}; margin: 0;"><a href="?page={{ p }}">{{ p }}</a></span>
{% endfor %}
</p>
{{ paginate_jinja(msgs, msgs.paginator) }}
{% for m in msgs %}
{% if m.id == first_unread_message_id %}
<span id="first_unread"></span>
{% endif %}
{% if m.id >= first_unread_message_id %}
{{ display_message(m, user, True) }}
{% else %}
{{ display_message(m, user, False) }}
{% endif %}
{% endfor %}
<main class="message-list">
{% for m in msgs %}
{% if m.id == first_unread_message_id %}
<span id="first_unread"></span>
{% endif %}
{% if m.id >= first_unread_message_id %}
{{ display_message(m, user, True) }}
{% else %}
{{ display_message(m, user, False) }}
{% endif %}
{% endfor %}
</main>
<p><a class="ib button" href="{{ url('forum:new_message', topic_id=topic.id) }}">{% trans %}Reply{% endtrans %}</a></p>
<p style="text-align: right; background: #d8e7f3;">
{% for p in msgs.paginator.page_range %}
<span class="ib" style="background: {% if p == msgs.number %}white{% endif %}; margin: 0;"><a href="?page={{ p }}">{{ p }}</a></span>
{% endfor %}
</p>
{{ paginate_jinja(msgs, msgs.paginator) }}
</div>
{% endblock %}

View File

@ -306,14 +306,15 @@ class ForumTopicDetailView(CanViewMixin, DetailView):
queryset = ForumTopic.objects.select_related("forum__parent")
def get_context_data(self, **kwargs):
topic: ForumTopic = self.object
kwargs = super().get_context_data(**kwargs)
msg = self.object.get_first_unread_message(self.request.user)
msg = topic.get_first_unread_message(self.request.user)
if msg is None:
kwargs["first_unread_message_id"] = math.inf
else:
kwargs["first_unread_message_id"] = msg.id
paginator = Paginator(
self.object.messages.select_related("author__avatar_pict")
topic.messages.select_related("author__avatar_pict", "topic__forum")
.prefetch_related("topic__forum__edit_groups", "readers")
.order_by("date"),
settings.SITH_FORUM_PAGE_LENGTH,

View File

@ -1,5 +1,5 @@
{% from "core/macros.jinja" import user_mini_profile, paginate %}
{% extends "core/base.jinja" %}
{% from "core/macros.jinja" import user_mini_profile, paginate_jinja %}
{% block title %}
{% trans %}Search user{% endtrans %}
@ -18,7 +18,9 @@
{% endfor %}
</div>
{{ paginate(page_obj, paginator) }}
{% if page_obj.has_other_pages() %}
{{ paginate_jinja(page_obj, paginator) }}
{% endif %}
<hr>
{% endif %}
<h2>{% trans %}Search user{% endtrans %}</h2>

View File

@ -7,7 +7,6 @@
{% block additional_css %}
<link rel="stylesheet" href="{{ scss('pedagogy/css/pedagogy.scss') }}">
<link rel="stylesheet" href="{{ scss('core/pagination.scss') }}">
{% endblock %}
{% block head %}

View File

@ -1,5 +1,5 @@
{% extends "core/base.jinja" %}
{% from 'core/macros.jinja' import paginate %}
{% from "core/macros.jinja" import paginate_jinja %}
{% block title %}
{% trans %}Operation logs{% endtrans %}
@ -28,5 +28,5 @@
</table>
<br>
{{ paginate(page_obj, paginator) }}
{{ paginate_jinja(page_obj, paginator) }}
{% endblock content %}

View File

@ -3,7 +3,6 @@
{%- block additional_css -%}
<link rel="stylesheet" href="{{ scss('sas/css/album.scss') }}">
<link rel="stylesheet" href="{{ scss('core/pagination.scss') }}">
{%- endblock -%}
{% block title %}