views.py 12.6 KB
Newer Older
1 2
# -*- coding:utf-8 -*
#
Skia's avatar
Skia committed
3
# Copyright 2016,2017,2018
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#

Krophil's avatar
Krophil committed
25
from django.shortcuts import get_object_or_404
Skia's avatar
Skia committed
26 27
from django.views.generic import ListView, DetailView, RedirectView
from django.views.generic.edit import UpdateView, CreateView, DeleteView
Skia's avatar
Skia committed
28
from django.views.generic.detail import SingleObjectMixin
Skia's avatar
Skia committed
29
from django.utils.translation import ugettext_lazy as _
Krophil's avatar
Krophil committed
30
from django.core.urlresolvers import reverse_lazy
Skia's avatar
Skia committed
31 32 33 34
from django.utils import timezone
from django.conf import settings
from django import forms
from django.core.exceptions import PermissionDenied
35
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
Skia's avatar
Skia committed
36

Krophil's avatar
Krophil committed
37
from ajax_select import make_ajax_field
38

Krophil's avatar
Krophil committed
39
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
40
from core.views.forms import MarkdownInput
Skia's avatar
Skia committed
41
from forum.models import Forum, ForumMessage, ForumTopic, ForumMessageMeta
42 43 44 45 46 47 48 49 50 51
from haystack.query import SearchQuerySet


class ForumSearchView(ListView):
    template_name = "forum/search.jinja"

    def get_queryset(self):
        query = self.request.GET.get("query", "")
        q = SearchQuerySet().models(ForumMessage).filter(text=query)
        return [r.object for r in q]
Skia's avatar
Skia committed
52

Krophil's avatar
Krophil committed
53

Skia's avatar
Skia committed
54
class ForumMainView(ListView):
Sli's avatar
Sli committed
55 56 57
    queryset = Forum.objects.filter(parent=None).prefetch_related(
        "children___last_message__author", "children___last_message__topic"
    )
Skia's avatar
Skia committed
58 59
    template_name = "forum/main.jinja"

Krophil's avatar
Krophil committed
60

Skia's avatar
Skia committed
61 62
class ForumMarkAllAsRead(RedirectView):
    permanent = False
Sli's avatar
Sli committed
63
    url = reverse_lazy("forum:last_unread")
Skia's avatar
Skia committed
64 65 66 67 68 69

    def get(self, request, *args, **kwargs):
        try:
            fi = request.user.forum_infos
            fi.last_read_date = timezone.now()
            fi.save()
70
            for m in request.user.read_messages.filter(date__lt=fi.last_read_date):
Krophil's avatar
Krophil committed
71 72 73
                m.readers.remove(request.user)  # Clean up to keep table low in data
        except:
            pass
Skia's avatar
Skia committed
74 75
        return super(ForumMarkAllAsRead, self).get(request, *args, **kwargs)

Krophil's avatar
Krophil committed
76

Skia's avatar
Skia committed
77 78 79 80 81 82 83 84 85
class ForumFavoriteTopics(ListView):
    model = ForumTopic
    template_name = "forum/favorite_topics.jinja"
    paginate_by = settings.SITH_FORUM_PAGE_LENGTH / 2

    def get_queryset(self):
        topic_list = self.request.user.favorite_topics.all()
        return topic_list

86

Skia's avatar
Skia committed
87 88 89
class ForumLastUnread(ListView):
    model = ForumTopic
    template_name = "forum/last_unread.jinja"
Skia's avatar
Skia committed
90
    paginate_by = settings.SITH_FORUM_PAGE_LENGTH / 2
Skia's avatar
Skia committed
91 92

    def get_queryset(self):
Sli's avatar
Sli committed
93 94 95 96 97 98 99 100 101
        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")
        )
102
        return topic_list
Skia's avatar
Skia committed
103

Krophil's avatar
Krophil committed
104

105 106 107 108 109
class ForumNameField(forms.ModelChoiceField):
    def label_from_instance(self, obj):
        return obj.get_full_name()


110 111 112
class ForumForm(forms.ModelForm):
    class Meta:
        model = Forum
Sli's avatar
Sli committed
113 114 115 116 117 118 119 120 121 122 123 124
        fields = [
            "name",
            "parent",
            "number",
            "owner_club",
            "is_category",
            "edit_groups",
            "view_groups",
        ]

    edit_groups = make_ajax_field(Forum, "edit_groups", "groups", help_text="")
    view_groups = make_ajax_field(Forum, "view_groups", "groups", help_text="")
125
    parent = ForumNameField(Forum.objects.all())
126

Krophil's avatar
Krophil committed
127

128
class ForumCreateView(CanCreateMixin, CreateView):
Skia's avatar
Skia committed
129
    model = Forum
130
    form_class = ForumForm
Skia's avatar
Skia committed
131 132 133 134
    template_name = "core/create.jinja"

    def get_initial(self):
        init = super(ForumCreateView, self).get_initial()
135
        try:
Sli's avatar
Sli committed
136 137 138 139 140
            parent = Forum.objects.filter(id=self.request.GET["parent"]).first()
            init["parent"] = parent
            init["owner_club"] = parent.owner_club
            init["edit_groups"] = parent.edit_groups.all()
            init["view_groups"] = parent.view_groups.all()
Krophil's avatar
Krophil committed
141 142
        except:
            pass
Skia's avatar
Skia committed
143 144
        return init

Krophil's avatar
Krophil committed
145

146
class ForumEditForm(ForumForm):
Sli's avatar
Sli committed
147 148 149
    recursive = forms.BooleanField(
        label=_("Apply rights and club owner recursively"), required=False
    )
150

Krophil's avatar
Krophil committed
151

152
class ForumEditView(CanEditPropMixin, UpdateView):
Skia's avatar
Skia committed
153 154
    model = Forum
    pk_url_kwarg = "forum_id"
155
    form_class = ForumEditForm
Skia's avatar
Skia committed
156
    template_name = "core/edit.jinja"
Sli's avatar
Sli committed
157
    success_url = reverse_lazy("forum:main")
Skia's avatar
Skia committed
158

159 160
    def form_valid(self, form):
        ret = super(ForumEditView, self).form_valid(form)
Sli's avatar
Sli committed
161
        if form.cleaned_data["recursive"]:
162 163 164
            self.object.apply_rights_recursively()
        return ret

Krophil's avatar
Krophil committed
165

166 167 168 169
class ForumDeleteView(CanEditPropMixin, DeleteView):
    model = Forum
    pk_url_kwarg = "forum_id"
    template_name = "core/delete_confirm.jinja"
Sli's avatar
Sli committed
170
    success_url = reverse_lazy("forum:main")
171

Krophil's avatar
Krophil committed
172

173
class ForumDetailView(CanViewMixin, DetailView):
Skia's avatar
Skia committed
174 175 176 177
    model = Forum
    template_name = "forum/forum.jinja"
    pk_url_kwarg = "forum_id"

Skia's avatar
Skia committed
178 179
    def get_context_data(self, **kwargs):
        kwargs = super(ForumDetailView, self).get_context_data(**kwargs)
Sli's avatar
Sli committed
180 181 182
        qs = (
            self.object.topics.order_by("-_last_message__date")
            .select_related("_last_message__author", "author")
Krophil's avatar
Krophil committed
183
            .prefetch_related("forum__edit_groups")
Sli's avatar
Sli committed
184 185 186
        )
        paginator = Paginator(qs, settings.SITH_FORUM_PAGE_LENGTH)
        page = self.request.GET.get("topic_page")
Skia's avatar
Skia committed
187 188 189 190 191 192
        try:
            kwargs["topics"] = paginator.page(page)
        except PageNotAnInteger:
            kwargs["topics"] = paginator.page(1)
        except EmptyPage:
            kwargs["topics"] = paginator.page(paginator.num_pages)
Skia's avatar
Skia committed
193 194
        return kwargs

Krophil's avatar
Krophil committed
195

196 197 198
class TopicForm(forms.ModelForm):
    class Meta:
        model = ForumMessage
Sli's avatar
Sli committed
199 200 201
        fields = ["title", "message"]
        widgets = {"message": MarkdownInput}

Skia's avatar
Skia committed
202
    title = forms.CharField(required=True, label=_("Title"))
203

Krophil's avatar
Krophil committed
204

205
class ForumTopicCreateView(CanCreateMixin, CreateView):
Skia's avatar
Skia committed
206
    model = ForumMessage
207
    form_class = TopicForm
Skia's avatar
Skia committed
208
    template_name = "forum/reply.jinja"
Skia's avatar
Skia committed
209 210

    def dispatch(self, request, *args, **kwargs):
Sli's avatar
Sli committed
211 212 213
        self.forum = get_object_or_404(
            Forum, id=self.kwargs["forum_id"], is_category=False
        )
Skia's avatar
Skia committed
214 215 216 217 218
        if not request.user.can_view(self.forum):
            raise PermissionDenied
        return super(ForumTopicCreateView, self).dispatch(request, *args, **kwargs)

    def form_valid(self, form):
Sli's avatar
Sli committed
219 220 221
        topic = ForumTopic(
            _title=form.instance.title, author=self.request.user, forum=self.forum
        )
Skia's avatar
Skia committed
222 223
        topic.save()
        form.instance.topic = topic
Skia's avatar
Skia committed
224 225 226
        form.instance.author = self.request.user
        return super(ForumTopicCreateView, self).form_valid(form)

Krophil's avatar
Krophil committed
227

228
class ForumTopicEditView(CanEditMixin, UpdateView):
Skia's avatar
Skia committed
229
    model = ForumTopic
Sli's avatar
Sli committed
230
    fields = ["forum"]
Skia's avatar
Skia committed
231 232 233
    pk_url_kwarg = "topic_id"
    template_name = "core/edit.jinja"

Sli's avatar
Sli committed
234

Skia's avatar
Skia committed
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
class ForumTopicSubscribeView(CanViewMixin, SingleObjectMixin, RedirectView):
    model = ForumTopic
    pk_url_kwarg = "topic_id"
    permanent = False

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        if request.user in self.object.subscribed_users.all():
            self.object.subscribed_users.remove(request.user)
        else:
            self.object.subscribed_users.add(request.user)
        return super().get(request, *args, **kwargs)

    def get_redirect_url(self, *args, **kwargs):
        return self.object.get_absolute_url()

Krophil's avatar
Krophil committed
251

252
class ForumTopicDetailView(CanViewMixin, DetailView):
Skia's avatar
Skia committed
253 254 255 256
    model = ForumTopic
    pk_url_kwarg = "topic_id"
    template_name = "forum/topic.jinja"
    context_object_name = "topic"
Sli's avatar
Sli committed
257
    queryset = ForumTopic.objects.select_related("forum__parent")
Skia's avatar
Skia committed
258

Skia's avatar
Skia committed
259 260 261
    def get_context_data(self, **kwargs):
        kwargs = super(ForumTopicDetailView, self).get_context_data(**kwargs)
        try:
Skia's avatar
Skia committed
262
            msg = self.object.get_first_unread_message(self.request.user)
Sli's avatar
Sli committed
263
            kwargs["first_unread_message_id"] = msg.id
Skia's avatar
Skia committed
264
        except:
Sli's avatar
Sli committed
265 266 267 268 269 270 271 272
            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,
        )
        page = self.request.GET.get("page")
273
        try:
274 275 276 277 278
            kwargs["msgs"] = paginator.page(page)
        except PageNotAnInteger:
            kwargs["msgs"] = paginator.page(1)
        except EmptyPage:
            kwargs["msgs"] = paginator.page(paginator.num_pages)
Skia's avatar
Skia committed
279 280
        return kwargs

Krophil's avatar
Krophil committed
281

282 283 284 285 286 287 288 289 290
class ForumMessageView(SingleObjectMixin, RedirectView):
    model = ForumMessage
    pk_url_kwarg = "message_id"
    permanent = False

    def get_redirect_url(self, *args, **kwargs):
        self.object = self.get_object()
        return self.object.get_url()

Krophil's avatar
Krophil committed
291

Skia's avatar
Skia committed
292 293
class ForumMessageEditView(CanEditMixin, UpdateView):
    model = ForumMessage
Sli's avatar
Sli committed
294 295 296 297 298
    form_class = forms.modelform_factory(
        model=ForumMessage,
        fields=["title", "message"],
        widgets={"message": MarkdownInput},
    )
299
    template_name = "forum/reply.jinja"
Skia's avatar
Skia committed
300 301
    pk_url_kwarg = "message_id"

Skia's avatar
Skia committed
302
    def form_valid(self, form):
Sli's avatar
Sli committed
303 304 305
        ForumMessageMeta(
            message=self.object, user=self.request.user, action="EDIT"
        ).save()
Skia's avatar
Skia committed
306 307
        return super(ForumMessageEditView, self).form_valid(form)

308 309
    def get_context_data(self, **kwargs):
        kwargs = super(ForumMessageEditView, self).get_context_data(**kwargs)
Sli's avatar
Sli committed
310
        kwargs["topic"] = self.object.topic
311 312
        return kwargs

Krophil's avatar
Krophil committed
313

Skia's avatar
Skia committed
314 315 316 317 318 319
class ForumMessageDeleteView(SingleObjectMixin, RedirectView):
    model = ForumMessage
    pk_url_kwarg = "message_id"
    permanent = False

    def get_redirect_url(self, *args, **kwargs):
320
        self.object = self.get_object()
Skia's avatar
Skia committed
321
        if self.object.can_be_moderated_by(self.request.user):
Sli's avatar
Sli committed
322 323 324
            ForumMessageMeta(
                message=self.object, user=self.request.user, action="DELETE"
            ).save()
Skia's avatar
Skia committed
325 326
        return self.object.get_absolute_url()

Krophil's avatar
Krophil committed
327

Skia's avatar
Skia committed
328 329 330 331 332 333
class ForumMessageUndeleteView(SingleObjectMixin, RedirectView):
    model = ForumMessage
    pk_url_kwarg = "message_id"
    permanent = False

    def get_redirect_url(self, *args, **kwargs):
334
        self.object = self.get_object()
Skia's avatar
Skia committed
335
        if self.object.can_be_moderated_by(self.request.user):
Sli's avatar
Sli committed
336 337 338
            ForumMessageMeta(
                message=self.object, user=self.request.user, action="UNDELETE"
            ).save()
Skia's avatar
Skia committed
339 340
        return self.object.get_absolute_url()

Krophil's avatar
Krophil committed
341

342
class ForumMessageCreateView(CanCreateMixin, CreateView):
Skia's avatar
Skia committed
343
    model = ForumMessage
Sli's avatar
Sli committed
344 345 346 347 348
    form_class = forms.modelform_factory(
        model=ForumMessage,
        fields=["title", "message"],
        widgets={"message": MarkdownInput},
    )
Skia's avatar
Skia committed
349
    template_name = "forum/reply.jinja"
Skia's avatar
Skia committed
350 351

    def dispatch(self, request, *args, **kwargs):
Sli's avatar
Sli committed
352
        self.topic = get_object_or_404(ForumTopic, id=self.kwargs["topic_id"])
Skia's avatar
Skia committed
353 354 355 356 357 358 359
        if not request.user.can_view(self.topic):
            raise PermissionDenied
        return super(ForumMessageCreateView, self).dispatch(request, *args, **kwargs)

    def get_initial(self):
        init = super(ForumMessageCreateView, self).get_initial()
        try:
Sli's avatar
Sli committed
360 361 362 363 364 365 366 367 368 369 370 371
            message = (
                ForumMessage.objects.select_related("author")
                .filter(id=self.request.GET["quote_id"])
                .first()
            )
            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"
372 373
        except Exception as e:
            print(repr(e))
Skia's avatar
Skia committed
374 375 376 377 378 379 380
        return init

    def form_valid(self, form):
        form.instance.topic = self.topic
        form.instance.author = self.request.user
        return super(ForumMessageCreateView, self).form_valid(form)

Skia's avatar
Skia committed
381 382
    def get_context_data(self, **kwargs):
        kwargs = super(ForumMessageCreateView, self).get_context_data(**kwargs)
Sli's avatar
Sli committed
383
        kwargs["topic"] = self.topic
Skia's avatar
Skia committed
384
        return kwargs