Format forum

This commit is contained in:
Pierre Brunet 2017-06-12 09:58:24 +02:00
parent b3466237ca
commit 6a43c2cef6
3 changed files with 73 additions and 51 deletions

View File

@ -23,11 +23,9 @@
#
from django.db import models
from django.core import validators
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
from django.db import IntegrityError, transaction
from django.core.urlresolvers import reverse
from django.utils import timezone
from django.utils.functional import cached_property
@ -35,9 +33,10 @@ from django.utils.functional import cached_property
from datetime import datetime
import pytz
from core.models import User, MetaGroup, Group, SithFile
from core.models import User, Group
from club.models import Club
class Forum(models.Model):
"""
The Forum class, made as a tree to allow nice tidy organization
@ -52,14 +51,14 @@ class Forum(models.Model):
is_category = models.BooleanField(_('is a category'), default=False)
parent = models.ForeignKey('Forum', related_name='children', null=True, blank=True)
owner_club = models.ForeignKey(Club, related_name="owned_forums", verbose_name=_("owner club"),
default=settings.SITH_MAIN_CLUB_ID)
default=settings.SITH_MAIN_CLUB_ID)
edit_groups = models.ManyToManyField(Group, related_name="editable_forums", blank=True,
default=[settings.SITH_GROUP_OLD_SUBSCRIBERS_ID])
default=[settings.SITH_GROUP_OLD_SUBSCRIBERS_ID])
view_groups = models.ManyToManyField(Group, related_name="viewable_forums", blank=True,
default=[settings.SITH_GROUP_PUBLIC_ID])
default=[settings.SITH_GROUP_PUBLIC_ID])
number = models.IntegerField(_("number to choose a specific forum ordering"), default=1)
_last_message = models.ForeignKey('ForumMessage', related_name="forums_where_its_last",
verbose_name=_("the last message"), null=True, on_delete=models.SET_NULL)
verbose_name=_("the last message"), null=True, on_delete=models.SET_NULL)
_topic_number = models.IntegerField(_("number of topics"), default=0)
class Meta:
@ -112,16 +111,18 @@ class Forum(models.Model):
self.view_groups = self.parent.view_groups.all()
self.save()
_club_memberships = {} # This cache is particularly efficient:
# divided by 3 the number of requests on the main forum page
# after the first load
_club_memberships = {} # This cache is particularly efficient:
# divided by 3 the number of requests on the main forum page
# after the first load
def is_owned_by(self, user):
if user.is_in_group(settings.SITH_GROUP_FORUM_ADMIN_ID):
return True
try: m = Forum._club_memberships[self.id][user.id]
try:
m = Forum._club_memberships[self.id][user.id]
except:
m = self.owner_club.get_membership_for(user)
try: Forum._club_memberships[self.id][user.id] = m
try:
Forum._club_memberships[self.id][user.id] = m
except:
Forum._club_memberships[self.id] = {}
Forum._club_memberships[self.id][user.id] = m
@ -178,12 +179,13 @@ class Forum(models.Model):
l += c.get_children_list()
return l
class ForumTopic(models.Model):
forum = models.ForeignKey(Forum, related_name='topics')
author = models.ForeignKey(User, related_name='forum_topics')
description = models.CharField(_('description'), max_length=256, default="")
_last_message = models.ForeignKey('ForumMessage', related_name="+", verbose_name=_("the last message"),
null=True, on_delete=models.SET_NULL)
null=True, on_delete=models.SET_NULL)
_title = models.CharField(_('title'), max_length=64, blank=True)
_message_number = models.IntegerField(_("number of messages"), default=0)
@ -192,7 +194,7 @@ class ForumTopic(models.Model):
def save(self, *args, **kwargs):
super(ForumTopic, self).save(*args, **kwargs)
self.forum.set_topic_number() # Recompute the cached value
self.forum.set_topic_number() # Recompute the cached value
self.forum.set_last_message()
def is_owned_by(self, user):
@ -225,6 +227,7 @@ class ForumTopic(models.Model):
def title(self):
return self._title
class ForumMessage(models.Model):
"""
"A ForumMessage object represents a message in the forum" -- Cpt. Obvious
@ -244,7 +247,7 @@ class ForumMessage(models.Model):
return "%s (%s) - %s" % (self.id, self.author, self.title)
def save(self, *args, **kwargs):
self._deleted = self.is_deleted() # Recompute the cached value
self._deleted = self.is_deleted() # Recompute the cached value
super(ForumMessage, self).save(*args, **kwargs)
if self.is_last_in_topic():
self.topic._last_message_id = self.id
@ -259,15 +262,15 @@ class ForumMessage(models.Model):
def is_last_in_topic(self):
return bool(self.id == self.topic.messages.order_by('date').last().id)
def is_owned_by(self, user): # Anyone can create a topic: it's better to
# check the rights at the forum level, since it's more controlled
def is_owned_by(self, user): # Anyone can create a topic: it's better to
# check the rights at the forum level, since it's more controlled
return self.topic.forum.is_owned_by(user) or user.id == self.author.id
def can_be_edited_by(self, user):
return user.can_edit(self.topic.forum)
def can_be_viewed_by(self, user):
return not self._deleted # No need to check the real rights since it's already done by the Topic view
return not self._deleted # No need to check the real rights since it's already done by the Topic view
def can_be_moderated_by(self, user):
return self.topic.forum.is_owned_by(user) or user.id == self.author.id
@ -282,10 +285,11 @@ class ForumMessage(models.Model):
return int(self.topic.messages.filter(id__lt=self.id).count() / settings.SITH_FORUM_PAGE_LENGTH) + 1
def mark_as_read(self, user):
try: # Need the try/except because of AnonymousUser
try: # Need the try/except because of AnonymousUser
if not self.is_read(user):
self.readers.add(user)
except: pass
except:
pass
def is_read(self, user):
return (self.date < user.forum_infos.last_read_date) or (user in self.readers.all())
@ -296,11 +300,13 @@ class ForumMessage(models.Model):
return meta.action == "DELETE"
return False
MESSAGE_META_ACTIONS = [
('EDIT', _("Message edited by")),
('DELETE', _("Message deleted by")),
('UNDELETE', _("Message undeleted by")),
]
('EDIT', _("Message edited by")),
('DELETE', _("Message deleted by")),
('UNDELETE', _("Message undeleted by")),
]
class ForumMessageMeta(models.Model):
user = models.ForeignKey(User, related_name="forum_message_metas")
@ -322,8 +328,7 @@ 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))
month=1, day=1, tzinfo=pytz.UTC))
def __str__(self):
return str(self.user)

View File

@ -22,7 +22,7 @@
#
#
from django.conf.urls import url, include
from django.conf.urls import url
from forum.views import *

View File

@ -22,30 +22,30 @@
#
#
from django.shortcuts import render, get_object_or_404
from django.shortcuts import get_object_or_404
from django.views.generic import ListView, DetailView, RedirectView
from django.views.generic.edit import UpdateView, CreateView, DeleteView
from django.views.generic.detail import SingleObjectMixin
from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse, reverse_lazy
from django.core.urlresolvers import reverse_lazy
from django.utils import timezone
from django.conf import settings
from django import forms
from django.db import models
from django.core.exceptions import PermissionDenied
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from ajax_select import make_ajax_form, make_ajax_field
from ajax_select import make_ajax_field
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
from core.views.forms import MarkdownInput
from core.models import Page
from forum.models import Forum, ForumMessage, ForumTopic, ForumMessageMeta
class ForumMainView(ListView):
queryset = Forum.objects.filter(parent=None).prefetch_related("children___last_message__author", "children___last_message__topic")
template_name = "forum/main.jinja"
class ForumMarkAllAsRead(RedirectView):
permanent = False
url = reverse_lazy('forum:last_unread')
@ -56,10 +56,12 @@ class ForumMarkAllAsRead(RedirectView):
fi.last_read_date = timezone.now()
fi.save()
for m in request.user.read_messages.filter(date__lt=fi.last_read_date):
m.readers.remove(request.user) # Clean up to keep table low in data
except: pass
m.readers.remove(request.user) # Clean up to keep table low in data
except:
pass
return super(ForumMarkAllAsRead, self).get(request, *args, **kwargs)
class ForumLastUnread(ListView):
model = ForumTopic
template_name = "forum/last_unread.jinja"
@ -67,12 +69,13 @@ class ForumLastUnread(ListView):
def get_queryset(self):
topic_list = self.model.objects.filter(_last_message__date__gt=self.request.user.forum_infos.last_read_date)\
.exclude(_last_message__readers=self.request.user)\
.order_by('-_last_message__date')\
.select_related('_last_message__author', 'author')\
.prefetch_related('forum__edit_groups')
.exclude(_last_message__readers=self.request.user)\
.order_by('-_last_message__date')\
.select_related('_last_message__author', 'author')\
.prefetch_related('forum__edit_groups')
return topic_list
class ForumForm(forms.ModelForm):
class Meta:
model = Forum
@ -80,6 +83,7 @@ class ForumForm(forms.ModelForm):
edit_groups = make_ajax_field(Forum, 'edit_groups', 'groups', help_text="")
view_groups = make_ajax_field(Forum, 'view_groups', 'groups', help_text="")
class ForumCreateView(CanCreateMixin, CreateView):
model = Forum
form_class = ForumForm
@ -93,12 +97,15 @@ class ForumCreateView(CanCreateMixin, CreateView):
init['owner_club'] = parent.owner_club
init['edit_groups'] = parent.edit_groups.all()
init['view_groups'] = parent.view_groups.all()
except: pass
except:
pass
return init
class ForumEditForm(ForumForm):
recursive = forms.BooleanField(label=_("Apply rights and club owner recursively"), required=False)
class ForumEditView(CanEditPropMixin, UpdateView):
model = Forum
pk_url_kwarg = "forum_id"
@ -112,12 +119,14 @@ class ForumEditView(CanEditPropMixin, UpdateView):
self.object.apply_rights_recursively()
return ret
class ForumDeleteView(CanEditPropMixin, DeleteView):
model = Forum
pk_url_kwarg = "forum_id"
template_name = "core/delete_confirm.jinja"
success_url = reverse_lazy('forum:main')
class ForumDetailView(CanViewMixin, DetailView):
model = Forum
template_name = "forum/forum.jinja"
@ -126,10 +135,10 @@ class ForumDetailView(CanViewMixin, DetailView):
def get_context_data(self, **kwargs):
kwargs = super(ForumDetailView, self).get_context_data(**kwargs)
qs = self.object.topics.order_by('-_last_message__date')\
.select_related('_last_message__author', 'author')\
.prefetch_related("forum__edit_groups")
.select_related('_last_message__author', 'author')\
.prefetch_related("forum__edit_groups")
paginator = Paginator(qs,
settings.SITH_FORUM_PAGE_LENGTH)
settings.SITH_FORUM_PAGE_LENGTH)
page = self.request.GET.get('topic_page')
try:
kwargs["topics"] = paginator.page(page)
@ -139,15 +148,17 @@ class ForumDetailView(CanViewMixin, DetailView):
kwargs["topics"] = paginator.page(paginator.num_pages)
return kwargs
class TopicForm(forms.ModelForm):
class Meta:
model = ForumMessage
fields = ['title', 'message']
widgets = {
'message': MarkdownInput,
}
}
title = forms.CharField(required=True, label=_("Title"))
class ForumTopicCreateView(CanCreateMixin, CreateView):
model = ForumMessage
form_class = TopicForm
@ -166,12 +177,14 @@ class ForumTopicCreateView(CanCreateMixin, CreateView):
form.instance.author = self.request.user
return super(ForumTopicCreateView, self).form_valid(form)
class ForumTopicEditView(CanEditMixin, UpdateView):
model = ForumTopic
fields = ['forum']
pk_url_kwarg = "topic_id"
template_name = "core/edit.jinja"
class ForumTopicDetailView(CanViewMixin, DetailView):
model = ForumTopic
pk_url_kwarg = "topic_id"
@ -186,9 +199,9 @@ class ForumTopicDetailView(CanViewMixin, DetailView):
kwargs['first_unread_message_id'] = msg.id
except:
kwargs['first_unread_message_id'] = float("inf")
paginator = Paginator(self.object.messages.select_related('author__avatar_pict')\
.prefetch_related('topic__forum__edit_groups', 'readers').order_by('date'),
settings.SITH_FORUM_PAGE_LENGTH)
paginator = Paginator(self.object.messages.select_related('author__avatar_pict')
.prefetch_related('topic__forum__edit_groups', 'readers').order_by('date'),
settings.SITH_FORUM_PAGE_LENGTH)
page = self.request.GET.get('page')
try:
kwargs["msgs"] = paginator.page(page)
@ -198,6 +211,7 @@ class ForumTopicDetailView(CanViewMixin, DetailView):
kwargs["msgs"] = paginator.page(paginator.num_pages)
return kwargs
class ForumMessageView(SingleObjectMixin, RedirectView):
model = ForumMessage
pk_url_kwarg = "message_id"
@ -207,9 +221,10 @@ class ForumMessageView(SingleObjectMixin, RedirectView):
self.object = self.get_object()
return self.object.get_url()
class ForumMessageEditView(CanEditMixin, UpdateView):
model = ForumMessage
form_class = forms.modelform_factory(model=ForumMessage, fields=['title', 'message',], widgets={'message': MarkdownInput})
form_class = forms.modelform_factory(model=ForumMessage, fields=['title', 'message', ], widgets={'message': MarkdownInput})
template_name = "forum/reply.jinja"
pk_url_kwarg = "message_id"
@ -222,6 +237,7 @@ class ForumMessageEditView(CanEditMixin, UpdateView):
kwargs['topic'] = self.object.topic
return kwargs
class ForumMessageDeleteView(SingleObjectMixin, RedirectView):
model = ForumMessage
pk_url_kwarg = "message_id"
@ -233,6 +249,7 @@ class ForumMessageDeleteView(SingleObjectMixin, RedirectView):
ForumMessageMeta(message=self.object, user=self.request.user, action="DELETE").save()
return self.object.get_absolute_url()
class ForumMessageUndeleteView(SingleObjectMixin, RedirectView):
model = ForumMessage
pk_url_kwarg = "message_id"
@ -244,9 +261,10 @@ class ForumMessageUndeleteView(SingleObjectMixin, RedirectView):
ForumMessageMeta(message=self.object, user=self.request.user, action="UNDELETE").save()
return self.object.get_absolute_url()
class ForumMessageCreateView(CanCreateMixin, CreateView):
model = ForumMessage
form_class = forms.modelform_factory(model=ForumMessage, fields=['title', 'message',], widgets={'message': MarkdownInput})
form_class = forms.modelform_factory(model=ForumMessage, fields=['title', 'message', ], widgets={'message': MarkdownInput})
template_name = "forum/reply.jinja"
def dispatch(self, request, *args, **kwargs):
@ -262,7 +280,7 @@ class ForumMessageCreateView(CanCreateMixin, CreateView):
init['message'] = "> ##### %s\n" % (_("%(author)s said") % {'author': message.author.get_short_name()})
init['message'] += "\n".join([
"> " + line for line in message.message.split('\n')
])
])
init['message'] += "\n\n"
except Exception as e:
print(repr(e))
@ -277,4 +295,3 @@ class ForumMessageCreateView(CanCreateMixin, CreateView):
kwargs = super(ForumMessageCreateView, self).get_context_data(**kwargs)
kwargs['topic'] = self.topic
return kwargs