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" %} {% 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 %} {% block content %}
<h3>{% trans %}Sales{% endtrans %}</h3> <h3>{% trans %}Sales{% endtrans %}</h3>

View File

@ -1,12 +1,13 @@
import random import random
from datetime import date, timedelta from datetime import date, timedelta
from datetime import timezone as tz
from decimal import Decimal from decimal import Decimal
from typing import Iterator from typing import Iterator
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from django.conf import settings from django.conf import settings
from django.core.management.base import BaseCommand 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.db.models.functions import Coalesce
from django.utils.timezone import make_aware, now from django.utils.timezone import make_aware, now
from faker import Faker from faker import Faker
@ -22,6 +23,7 @@ from counter.models import (
Refilling, Refilling,
Selling, Selling,
) )
from forum.models import Forum, ForumMessage, ForumTopic
from pedagogy.models import UV from pedagogy.models import UV
from subscription.models import Subscription from subscription.models import Subscription
@ -97,6 +99,8 @@ class Command(BaseCommand):
self.create_sales(sellers) self.create_sales(sellers)
self.stdout.write("Creating permanences...") self.stdout.write("Creating permanences...")
self.create_permanences(sellers) self.create_permanences(sellers)
self.stdout.write("Filling the forum...")
self.create_forums()
self.stdout.write("Done") self.stdout.write("Done")
@ -288,7 +292,8 @@ class Command(BaseCommand):
since=Subquery( since=Subquery(
Subscription.objects.filter(member__customer=OuterRef("pk")) Subscription.objects.filter(member__customer=OuterRef("pk"))
.annotate(res=Min("subscription_start")) .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) 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; 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-------------------------------*/
footer { footer {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -170,7 +170,7 @@ class Forum(models.Model):
def is_owned_by(self, user): def is_owned_by(self, user):
if user.is_anonymous: if user.is_anonymous:
return False 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 return True
try: try:
m = Forum._club_memberships[self.id][user.id] m = Forum._club_memberships[self.id][user.id]
@ -273,10 +273,10 @@ class ForumTopic(models.Model):
return self.forum.is_owned_by(user) return self.forum.is_owned_by(user)
def can_be_edited_by(self, 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): 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: def get_first_unread_message(self, user: User) -> ForumMessage | None:
if not hasattr(user, "forum_infos"): if not hasattr(user, "forum_infos"):
@ -355,7 +355,7 @@ class ForumMessage(models.Model):
return not self._deleted return not self._deleted
def can_be_moderated_by(self, user): 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): def get_url(self):
return ( 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 %} {% trans %}Favorite topics{% endtrans %}
{% endblock %} {% endblock %}
{% block additional_css %}
<link rel="stylesheet" href="{{ scss('forum/css/forum.scss') }}">
{% endblock %}
{% block content %} {% block content %}
<p> <p>
<a href="{{ url('forum:main') }}">Forum</a> > <a href="{{ url('forum:main') }}">Forum</a> >

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,11 @@
{% from 'forum/macros.jinja' import display_message, display_breadcrumb, display_search_bar %} {% 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 %} {% block content %}
<div id="forum"> <div id="forum">
{{ display_search_bar(request) }} {{ display_search_bar(request) }}

View File

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

View File

@ -306,14 +306,15 @@ class ForumTopicDetailView(CanViewMixin, DetailView):
queryset = ForumTopic.objects.select_related("forum__parent") queryset = ForumTopic.objects.select_related("forum__parent")
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
topic: ForumTopic = self.object
kwargs = super().get_context_data(**kwargs) 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: if msg is None:
kwargs["first_unread_message_id"] = math.inf kwargs["first_unread_message_id"] = math.inf
else: else:
kwargs["first_unread_message_id"] = msg.id kwargs["first_unread_message_id"] = msg.id
paginator = Paginator( 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") .prefetch_related("topic__forum__edit_groups", "readers")
.order_by("date"), .order_by("date"),
settings.SITH_FORUM_PAGE_LENGTH, settings.SITH_FORUM_PAGE_LENGTH,

View File

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

View File

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

View File

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

View File

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