Add last unread function

This commit is contained in:
Skia 2017-01-29 00:16:41 +01:00
parent 4cc57c183e
commit 254126fd79
14 changed files with 229 additions and 36 deletions

View File

@ -18,7 +18,7 @@ from subscription.models import Subscription
from counter.models import Customer, ProductType, Product, Counter from counter.models import Customer, ProductType, Product, Counter
from com.models import Sith, Weekmail from com.models import Sith, Weekmail
from election.models import Election, Role, Candidature, ElectionList from election.models import Election, Role, Candidature, ElectionList
from forum.models import Forum, ForumMessage from forum.models import Forum, ForumMessage, ForumTopic
class Command(BaseCommand): class Command(BaseCommand):
@ -438,8 +438,10 @@ Welcome to the wiki page!
room.save() room.save()
Forum(name="AE", description="Réservé au bureau AE", parent=room).save() Forum(name="AE", description="Réservé au bureau AE", parent=room).save()
Forum(name="BdF", description="Réservé au bureau BdF", parent=room).save() Forum(name="BdF", description="Réservé au bureau BdF", parent=room).save()
Forum(name="Hall de discussions", description="Pour toutes les discussions", parent=room).save() hall = Forum(name="Hall de discussions", description="Pour toutes les discussions", parent=room)
hall.save()
various = Forum(name="Divers", description="Pour causer de rien", is_category=True) various = Forum(name="Divers", description="Pour causer de rien", is_category=True)
various.save() various.save()
Forum(name="Promos", description="Réservé aux Promos", parent=various).save() Forum(name="Promos", description="Réservé aux Promos", parent=various).save()
ForumTopic(forum=hall)

View File

@ -426,6 +426,16 @@ class User(AbstractBaseUser):
def subscribed(self): def subscribed(self):
return self.is_in_group(settings.SITH_MAIN_MEMBERS_GROUP) return self.is_in_group(settings.SITH_MAIN_MEMBERS_GROUP)
@property
def forum_infos(self):
try:
return self._forum_infos
except:
from forum.models import ForumUserInfo
infos = ForumUserInfo(user=self)
infos.save()
return infos
class AnonymousUser(AuthAnonymousUser): class AnonymousUser(AuthAnonymousUser):
def __init__(self, request): def __init__(self, request):
super(AnonymousUser, self).__init__() super(AnonymousUser, self).__init__()

View File

@ -412,6 +412,9 @@ textarea {
margin: 2px; margin: 2px;
background: skyblue; background: skyblue;
} }
.unread {
background: #e6ddad;
}
.message h5 { .message h5 {
font-size: 100%; font-size: 100%;
} }

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
from django.conf import settings
from django.utils.timezone import utc
import datetime
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('forum', '0003_forum_owner_club'),
]
operations = [
migrations.CreateModel(
name='ForumUserInfo',
fields=[
('id', models.AutoField(serialize=False, verbose_name='ID', auto_created=True, primary_key=True)),
('last_read_date', models.DateTimeField(verbose_name='last read date', default=datetime.datetime(1999, 1, 1, 0, 0, tzinfo=utc))),
('user', models.OneToOneField(related_name='_forum_infos', to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('forum', '0004_forumuserinfo'),
]
operations = [
migrations.AddField(
model_name='forumuserinfo',
name='read_messages',
field=models.ManyToManyField(to='forum.ForumMessage', related_name='readers', verbose_name='read messages'),
),
]

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('forum', '0005_forumuserinfo_read_messages'),
]
operations = [
migrations.RemoveField(
model_name='forumuserinfo',
name='read_messages',
),
migrations.AddField(
model_name='forummessage',
name='readers',
field=models.ManyToManyField(to=settings.AUTH_USER_MODEL, related_name='read_messages', verbose_name='readers'),
),
]

View File

@ -7,6 +7,9 @@ from django.db import IntegrityError, transaction
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils import timezone from django.utils import timezone
from datetime import datetime
import pytz
from core.models import User, MetaGroup, Group, SithFile from core.models import User, MetaGroup, Group, SithFile
from club.models import Club from club.models import Club
@ -136,10 +139,14 @@ class ForumMessage(models.Model):
title = models.CharField(_("title"), default="", max_length=64, blank=True) title = models.CharField(_("title"), default="", max_length=64, blank=True)
message = models.TextField(_("message"), default="") message = models.TextField(_("message"), default="")
date = models.DateTimeField(_('date'), default=timezone.now) date = models.DateTimeField(_('date'), default=timezone.now)
readers = models.ManyToManyField(User, related_name="read_messages", verbose_name=_("readers"))
class Meta: class Meta:
ordering = ['id'] ordering = ['id']
def __str__(self):
return "%s" % (self.title)
def is_owned_by(self, user): def is_owned_by(self, user):
return self.topic.is_owned_by(user) or user.id == self.author.id return self.topic.is_owned_by(user) or user.id == self.author.id
@ -150,5 +157,14 @@ class ForumMessage(models.Model):
return user.can_view(self.topic) return user.can_view(self.topic)
def get_absolute_url(self): def get_absolute_url(self):
return self.topic.get_absolute_url() return self.topic.get_absolute_url() + "#first_unread"
def mark_as_read(self, user):
self.readers.add(user)
class ForumUserInfo(models.Model):
user = models.OneToOneField(User, related_name="_forum_infos")
last_read_date = models.DateTimeField(_('last read date'), default=datetime(year=settings.SITH_SCHOOL_START_YEAR,
month=1, day=1, tzinfo=pytz.UTC))
# read_messages = models.ManyToManyField(ForumMessage, related_name="readers", verbose_name=_("read messages"))

View File

@ -1,6 +1,9 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{% from 'forum/macros.jinja' import display_forum %} {% from 'forum/macros.jinja' import display_forum, display_topic %}
{% from 'core/macros.jinja' import user_profile_link %}
{% block title %}
{{ forum }}
{% endblock %}
{% block content %} {% block content %}
<div> <div>
@ -21,34 +24,7 @@
{{ display_forum(f, user) }} {{ display_forum(f, user) }}
{% endfor %} {% endfor %}
{% for t in topics %} {% for t in topics %}
<div class="topic"> {{ display_topic(t, user) }}
<div class="ib w_medium">
<a class="ib w_big" href="{{ url('forum:view_topic', topic_id=t.id) }}">
<h5>{{ t.title }}</h5>
<p>{{ t.description }}</p>
</a>
{% if user.is_owner(t) %}
<div class="ib" style="text-align: center;">
<a href="{{ url('forum:edit_topic', topic_id=t.id) }}">Edit</a>
</div>
{% endif %}
</div>
<div class="ib w_medium">
<div class="ib w_medium">
<div class="ib w_medium" style="text-align: center;">
{{ user_profile_link(t.author) }}
</div>
<div class="ib w_medium" style="text-align: center;">
{{ t.messages.count() }}
</div>
</div>
<div class="ib w_medium" style="text-align: center;">
{% set last_msg = t.messages.order_by('id').last() %}
{{ user_profile_link(last_msg.author) }} <br/>
{{ last_msg.date|date(DATETIME_FORMAT) }} {{ last_msg.date|time(DATETIME_FORMAT) }}
</div>
</div>
</div>
{% endfor %} {% endfor %}
{% endblock %} {% endblock %}

View File

@ -0,0 +1,24 @@
{% extends "core/base.jinja" %}
{% from 'forum/macros.jinja' import display_topic %}
{% block title %}
{% trans %}Last unread messages{% endtrans %}
{% endblock %}
{% block content %}
<p>
<a href="{{ url('forum:main') }}">Forum</a> >
</p>
<h3>{% trans %}Forum{% endtrans %}</h3>
<h4>{% trans %}Last unread messages{% endtrans %}</h4>
<p>
<a class="ib" href="{{ url('forum:mark_all_as_read') }}">{% trans %}Mark all as read{% endtrans %}</a>
<a class="ib" href="{{ url('forum:last_unread') }}">{% trans %}Refresh{% endtrans %}</a>
</p>
{% for t in forumtopic_list %}
{{ display_topic(t, user, True) }}
{% endfor %}
{% endblock %}

View File

@ -1,3 +1,5 @@
{% from 'core/macros.jinja' import user_profile_link %}
{% macro display_forum(forum, user) %} {% macro display_forum(forum, user) %}
<div class="forum {% if forum.is_category %}category{% endif %}"> <div class="forum {% if forum.is_category %}category{% endif %}">
<div class="ib w_big"> <div class="ib w_big">
@ -35,4 +37,38 @@
</div> </div>
{% endmacro %} {% endmacro %}
{% macro display_topic(topic, user, first_unread=False) %}
<div class="topic">
<div class="ib w_medium">
{% if first_unread %}
<a class="ib w_big" href="{{ url('forum:view_topic', topic_id=topic.id) }}#first_unread">
{% else %}
<a class="ib w_big" href="{{ url('forum:view_topic', topic_id=topic.id) }}">
{% endif %}
<h5>{{ topic.title }}</h5>
<p>{{ topic.description }}</p>
</a>
{% if user.is_owner(topic) %}
<div class="ib" style="text-align: center;">
<a href="{{ url('forum:edit_topic', topic_id=topic.id) }}">{% trans %}Edit{% endtrans %}</a>
</div>
{% endif %}
</div>
<div class="ib w_medium">
<div class="ib w_medium">
<div class="ib w_medium" style="text-align: center;">
{{ user_profile_link(topic.author) }}
</div>
<div class="ib w_medium" style="text-align: center;">
{{ topic.messages.count() }}
</div>
</div>
<div class="ib w_medium" style="text-align: center;">
{% set last_msg = topic.messages.order_by('id').last() %}
{{ user_profile_link(last_msg.author) }} <br/>
{{ last_msg.date|date(DATETIME_FORMAT) }} {{ last_msg.date|time(DATETIME_FORMAT) }}
</div>
</div>
</div>
{% endmacro %}

View File

@ -2,16 +2,25 @@
{% from 'core/macros.jinja' import user_profile_link %} {% from 'core/macros.jinja' import user_profile_link %}
{% from 'forum/macros.jinja' import display_forum %} {% from 'forum/macros.jinja' import display_forum %}
{% block title %}
{% trans %}Forum{% endtrans %}
{% endblock %}
{% block content %} {% block content %}
<p> <p>
<a href="{{ url('forum:main') }}">Forum</a> > <a href="{{ url('forum:main') }}">Forum</a> >
</p> </p>
<h3>{% trans %}Forum{% endtrans %}</h3> <h3>{% trans %}Forum{% endtrans %}</h3>
<p> <p>
{% if user.is_in_group(settings.SITH_GROUP_FORUM_ADMIN_ID) or user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) %} <a class="ib" href="{{ url('forum:last_unread') }}">{% trans %}View last unread messages{% endtrans %}</a>
<a href="{{ url('forum:new_forum') }}">New forum</a> <a class="ib" href="{{ url('forum:mark_all_as_read') }}">{% trans %}Mark all as read{% endtrans %}</a>
{% endif %} <a class="ib" href="{{ url('forum:main') }}">{% trans %}Refresh{% endtrans %}</a>
</p> </p>
{% if user.is_in_group(settings.SITH_GROUP_FORUM_ADMIN_ID) or user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) %}
<p>
<a href="{{ url('forum:new_forum') }}">{% trans %}New forum{% endtrans %}</a>
</p>
{% endif %}
{% for f in forum_list %} {% for f in forum_list %}
<div style="padding: 4px; margin: 4px"> <div style="padding: 4px; margin: 4px">
{{ display_forum(f, user) }} {{ display_forum(f, user) }}

View File

@ -1,6 +1,10 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{% from 'core/macros.jinja' import user_profile_link %} {% from 'core/macros.jinja' import user_profile_link %}
{% block title %}
{{ topic }}
{% endblock %}
{% block head %} {% block head %}
{{ super() }} {{ super() }}
<style type="text/css" media="all"> <style type="text/css" media="all">
@ -32,8 +36,16 @@
<h3>{{ topic.title }}</h3> <h3>{{ topic.title }}</h3>
<p>{{ topic.description }}</p> <p>{{ topic.description }}</p>
<p><a href="{{ url('forum:new_message', topic_id=topic.id) }}">Reply</a></p> <p><a href="{{ url('forum:new_message', topic_id=topic.id) }}">Reply</a></p>
{% set unread = False %}
{% for m in topic.messages.all() %} {% for m in topic.messages.all() %}
{% if m.id == first_unread_message_id %}
<div class="message unread" id="first_unread">
{% set unread = True %}
{% elif unread %}
<div class="message unread">
{% else %}
<div class="message"> <div class="message">
{% endif %}
<div class="msg_author"> <div class="msg_author">
{% if m.author.profile_pict %} {% if m.author.profile_pict %}
<img src="{{ m.author.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}" id="picture" /> <img src="{{ m.author.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}" id="picture" />
@ -64,6 +76,7 @@
</div> </div>
</div> </div>
</div> </div>
{# m.mark_as_read(user) or "" #}
{% endfor %} {% endfor %}
{% endblock %} {% endblock %}

View File

@ -5,6 +5,8 @@ from forum.views import *
urlpatterns = [ urlpatterns = [
url(r'^$', ForumMainView.as_view(), name='main'), url(r'^$', ForumMainView.as_view(), name='main'),
url(r'^new_forum$', ForumCreateView.as_view(), name='new_forum'), url(r'^new_forum$', ForumCreateView.as_view(), name='new_forum'),
url(r'^mark_all_as_read$', ForumMarkAllAsRead.as_view(), name='mark_all_as_read'),
url(r'^last_unread$', ForumLastUnread.as_view(), name='last_unread'),
url(r'^(?P<forum_id>[0-9]+)$', ForumDetailView.as_view(), name='view_forum'), url(r'^(?P<forum_id>[0-9]+)$', ForumDetailView.as_view(), name='view_forum'),
url(r'^(?P<forum_id>[0-9]+)/edit$', ForumEditView.as_view(), name='edit_forum'), url(r'^(?P<forum_id>[0-9]+)/edit$', ForumEditView.as_view(), name='edit_forum'),
url(r'^(?P<forum_id>[0-9]+)/delete$', ForumDeleteView.as_view(), name='delete_forum'), url(r'^(?P<forum_id>[0-9]+)/delete$', ForumDeleteView.as_view(), name='delete_forum'),

View File

@ -16,6 +16,29 @@ class ForumMainView(CanViewMixin, ListView):
queryset = Forum.objects.filter(parent=None) queryset = Forum.objects.filter(parent=None)
template_name = "forum/main.jinja" template_name = "forum/main.jinja"
class ForumMarkAllAsRead(RedirectView):
permanent = False
url = reverse_lazy('forum:last_unread')
def get(self, request, *args, **kwargs):
try:
fi = request.user.forum_infos
fi.last_read_date = timezone.now()
fi.save()
except: pass
return super(ForumMarkAllAsRead, self).get(request, *args, **kwargs)
class ForumLastUnread(ListView):
model = ForumTopic
template_name = "forum/last_unread.jinja"
def get_queryset(self):
l = ForumMessage.objects.exclude(readers=self.request.user).filter(
date__gt=self.request.user.forum_infos.last_read_date).values_list('topic') # TODO try to do better
return self.model.objects.filter(id__in=l).annotate(models.Max('messages__date')).order_by('-messages__date__max')
# return self.model.objects.exclude(messages__readers=self.request.user).distinct().annotate(
# models.Max('messages__date')).order_by('-messages__date__max')
class ForumCreateView(CanCreateMixin, CreateView): class ForumCreateView(CanCreateMixin, CreateView):
model = Forum model = Forum
fields = ['name', 'parent', 'owner_club', 'is_category', 'edit_groups', 'view_groups'] fields = ['name', 'parent', 'owner_club', 'is_category', 'edit_groups', 'view_groups']
@ -95,6 +118,15 @@ class ForumTopicDetailView(CanViewMixin, DetailView):
template_name = "forum/topic.jinja" template_name = "forum/topic.jinja"
context_object_name = "topic" context_object_name = "topic"
def get_context_data(self, **kwargs):
kwargs = super(ForumTopicDetailView, self).get_context_data(**kwargs)
msg = self.object.messages.exclude(readers=self.request.user).order_by('id').first()
try:
kwargs['first_unread_message_id'] = msg.id
except:
kwargs['first_unread_message_id'] = -1
return kwargs
class ForumMessageEditView(CanEditMixin, UpdateView): class ForumMessageEditView(CanEditMixin, UpdateView):
model = ForumMessage model = ForumMessage
fields = ['title', 'message'] fields = ['title', 'message']