diff --git a/forum/models.py b/forum/models.py index b7f0c8d8..296b5a99 100644 --- a/forum/models.py +++ b/forum/models.py @@ -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) - diff --git a/forum/urls.py b/forum/urls.py index 9096e3cb..43d7c90f 100644 --- a/forum/urls.py +++ b/forum/urls.py @@ -22,7 +22,7 @@ # # -from django.conf.urls import url, include +from django.conf.urls import url from forum.views import * diff --git a/forum/views.py b/forum/views.py index e90f9d9f..1c539e70 100644 --- a/forum/views.py +++ b/forum/views.py @@ -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 -