views.py 16.6 KB
Newer Older
Skia's avatar
Skia committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# -*- coding:utf-8 -*
#
# Copyright 2017
# - 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.
#
#

25
from django.http import Http404, HttpResponseRedirect
Krophil's avatar
Krophil committed
26
from django.shortcuts import get_object_or_404, redirect
27
from django.core.urlresolvers import reverse_lazy, reverse
28
from django.views.generic import DetailView, RedirectView, TemplateView, View
Krophil's avatar
Krophil committed
29
from django.views.generic.edit import UpdateView, CreateView, DeleteView
Skia's avatar
Skia committed
30
31
from django.utils.translation import ugettext_lazy as _
from django import forms
Skia's avatar
Skia committed
32
from django.conf import settings
33
from django.forms.models import modelform_factory
Skia's avatar
Skia committed
34

35
36
from ajax_select.fields import AutoCompleteSelectField

Skia's avatar
Skia committed
37
38
from datetime import date

Skia's avatar
Skia committed
39
from trombi.models import Trombi, TrombiUser, TrombiComment, TrombiClubMembership
Krophil's avatar
Krophil committed
40
from core.views.forms import SelectDate
Sli's avatar
Sli committed
41
42
43
44
45
46
47
from core.views import (
    CanViewMixin,
    CanEditMixin,
    CanEditPropMixin,
    TabedViewMixin,
    CanCreateMixin,
    QuickNotifMixin,
Sli's avatar
Sli committed
48
    UserIsLoggedMixin,
Sli's avatar
Sli committed
49
)
Skia's avatar
Skia committed
50
51
52
from core.models import User
from club.models import Club

Krophil's avatar
Krophil committed
53

Skia's avatar
Skia committed
54
55
56
57
58
59
class TrombiTabsMixin(TabedViewMixin):
    def get_tabs_title(self):
        return _("Trombi")

    def get_list_of_tabs(self):
        tab_list = []
Sli's avatar
Sli committed
60
61
62
        tab_list.append(
            {"url": reverse("trombi:user_tools"), "slug": "tools", "name": _("Tools")}
        )
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
        if hasattr(self.request.user, "trombi_user"):
            tab_list.append(
                {
                    "url": reverse("trombi:profile"),
                    "slug": "profile",
                    "name": _("My profile"),
                }
            )
            tab_list.append(
                {
                    "url": reverse("trombi:pictures"),
                    "slug": "pictures",
                    "name": _("My pictures"),
                }
            )
Skia's avatar
Skia committed
78
79
80
        try:
            trombi = self.request.user.trombi_user.trombi
            if self.request.user.is_owner(trombi):
Sli's avatar
Sli committed
81
82
83
84
85
86
87
88
89
                tab_list.append(
                    {
                        "url": reverse(
                            "trombi:detail", kwargs={"trombi_id": trombi.id}
                        ),
                        "slug": "admin_tools",
                        "name": _("Admin tools"),
                    }
                )
Krophil's avatar
Krophil committed
90
91
        except:
            pass
Skia's avatar
Skia committed
92
93
        return tab_list

Krophil's avatar
Krophil committed
94

95
96
97
98
99
100
101
102
103
104
105
106
107
class UserIsInATrombiMixin(View):
    """
    This view check if the requested user has a trombi_user attribute
    """

    def dispatch(self, request, *args, **kwargs):

        if not hasattr(self.request.user, "trombi_user"):
            raise Http404()

        return super(UserIsInATrombiMixin, self).dispatch(request, *args, **kwargs)


Skia's avatar
Skia committed
108
class TrombiForm(forms.ModelForm):
Skia's avatar
Skia committed
109
    class Meta:
Skia's avatar
Skia committed
110
        model = Trombi
Sli's avatar
Sli committed
111
112
113
114
115
116
117
        fields = [
            "subscription_deadline",
            "comments_deadline",
            "max_chars",
            "show_profiles",
        ]
        widgets = {"subscription_deadline": SelectDate, "comments_deadline": SelectDate}
Krophil's avatar
Krophil committed
118

Skia's avatar
Skia committed
119

Krophil's avatar
Krophil committed
120
class TrombiCreateView(CanCreateMixin, CreateView):
Skia's avatar
Skia committed
121
    """
Skia's avatar
Skia committed
122
    Create a trombi for a club
Skia's avatar
Skia committed
123
    """
Sli's avatar
Sli committed
124

Skia's avatar
Skia committed
125
126
    model = Trombi
    form_class = TrombiForm
Sli's avatar
Sli committed
127
    template_name = "core/create.jinja"
Skia's avatar
Skia committed
128
129
130
131
132
133
134

    def post(self, request, *args, **kwargs):
        """
        Affect club
        """
        form = self.get_form()
        if form.is_valid():
Sli's avatar
Sli committed
135
            club = get_object_or_404(Club, id=self.kwargs["club_id"])
Skia's avatar
Skia committed
136
137
138
139
140
141
            form.instance.club = club
            ret = self.form_valid(form)
            return ret
        else:
            return self.form_invalid(form)

Krophil's avatar
Krophil committed
142

Skia's avatar
Skia committed
143
class TrombiEditView(CanEditPropMixin, TrombiTabsMixin, UpdateView):
Skia's avatar
Skia committed
144
145
    model = Trombi
    form_class = TrombiForm
Sli's avatar
Sli committed
146
147
    template_name = "core/edit.jinja"
    pk_url_kwarg = "trombi_id"
Skia's avatar
Skia committed
148
    current_tab = "admin_tools"
Skia's avatar
Skia committed
149

Skia's avatar
Skia committed
150
    def get_success_url(self):
Krophil's avatar
Krophil committed
151
152
        return super(TrombiEditView, self).get_success_url() + "?qn_success"

Skia's avatar
Skia committed
153

154
class AddUserForm(forms.Form):
Sli's avatar
Sli committed
155
156
157
158
    user = AutoCompleteSelectField(
        "users", required=True, label=_("Select user"), help_text=None
    )

159

Skia's avatar
Skia committed
160
class TrombiDetailView(CanEditMixin, QuickNotifMixin, TrombiTabsMixin, DetailView):
Skia's avatar
Skia committed
161
    model = Trombi
Sli's avatar
Sli committed
162
163
    template_name = "trombi/detail.jinja"
    pk_url_kwarg = "trombi_id"
Skia's avatar
Skia committed
164
    current_tab = "admin_tools"
Skia's avatar
Skia committed
165

166
167
168
169
170
    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        form = AddUserForm(request.POST)
        if form.is_valid():
            try:
Sli's avatar
Sli committed
171
                TrombiUser(user=form.cleaned_data["user"], trombi=self.object).save()
Skia's avatar
Skia committed
172
                self.quick_notif_list.append("qn_success")
Sli's avatar
Sli committed
173
            except:  # We don't care about duplicate keys
Skia's avatar
Skia committed
174
                self.quick_notif_list.append("qn_fail")
175
176
177
178
        return super(TrombiDetailView, self).get(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        kwargs = super(TrombiDetailView, self).get_context_data(**kwargs)
Sli's avatar
Sli committed
179
        kwargs["form"] = AddUserForm()
180
181
        return kwargs

Sli's avatar
Sli committed
182

Skia's avatar
Skia committed
183
184
class TrombiExportView(CanEditMixin, TrombiTabsMixin, DetailView):
    model = Trombi
Sli's avatar
Sli committed
185
186
    template_name = "trombi/export.jinja"
    pk_url_kwarg = "trombi_id"
Skia's avatar
Skia committed
187
    current_tab = "admin_tools"
Krophil's avatar
Krophil committed
188

Sli's avatar
Sli committed
189

Skia's avatar
Skia committed
190
191
class TrombiDeleteUserView(CanEditPropMixin, TrombiTabsMixin, DeleteView):
    model = TrombiUser
Sli's avatar
Sli committed
192
193
    pk_url_kwarg = "user_id"
    template_name = "core/delete_confirm.jinja"
Skia's avatar
Skia committed
194
    current_tab = "admin_tools"
Skia's avatar
Skia committed
195

Skia's avatar
Skia committed
196
    def get_success_url(self):
Sli's avatar
Sli committed
197
198
199
200
        return (
            reverse("trombi:detail", kwargs={"trombi_id": self.object.trombi.id})
            + "?qn_success"
        )
Skia's avatar
Skia committed
201

Krophil's avatar
Krophil committed
202

Sli's avatar
Sli committed
203
204
205
class TrombiModerateCommentsView(
    CanEditPropMixin, QuickNotifMixin, TrombiTabsMixin, DetailView
):
Skia's avatar
Skia committed
206
    model = Trombi
Sli's avatar
Sli committed
207
208
    template_name = "trombi/comment_moderation.jinja"
    pk_url_kwarg = "trombi_id"
Skia's avatar
Skia committed
209
    current_tab = "admin_tools"
Skia's avatar
Skia committed
210
211
212

    def get_context_data(self, **kwargs):
        kwargs = super(TrombiModerateCommentsView, self).get_context_data(**kwargs)
Sli's avatar
Sli committed
213
214
215
        kwargs["comments"] = TrombiComment.objects.filter(
            is_moderated=False, author__trombi__id=self.object.id
        ).exclude(target__user__id=self.request.user.id)
Skia's avatar
Skia committed
216
217
        return kwargs

Krophil's avatar
Krophil committed
218

Skia's avatar
Skia committed
219
220
221
222
class TrombiModerateForm(forms.Form):
    reason = forms.CharField(help_text=_("Explain why you rejected the comment"))
    action = forms.CharField(initial="delete", widget=forms.widgets.HiddenInput)

Krophil's avatar
Krophil committed
223

Skia's avatar
Skia committed
224
225
class TrombiModerateCommentView(DetailView):
    model = TrombiComment
Sli's avatar
Sli committed
226
227
    template_name = "core/edit.jinja"
    pk_url_kwarg = "comment_id"
Skia's avatar
Skia committed
228
229
230
231
232
233
234
235
236

    def dispatch(self, request, *args, **kwargs):
        self.object = self.get_object()
        if not request.user.is_owner(self.object.author.trombi):
            raise Http404()
        return super(TrombiModerateCommentView, self).dispatch(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        if "action" in request.POST:
Sli's avatar
Sli committed
237
            if request.POST["action"] == "accept":
Skia's avatar
Skia committed
238
239
                self.object.is_moderated = True
                self.object.save()
Sli's avatar
Sli committed
240
241
242
243
244
245
246
247
248
249
250
251
                return redirect(
                    reverse(
                        "trombi:moderate_comments",
                        kwargs={"trombi_id": self.object.author.trombi.id},
                    )
                    + "?qn_success"
                )
            elif request.POST["action"] == "reject":
                return super(TrombiModerateCommentView, self).get(
                    request, *args, **kwargs
                )
            elif request.POST["action"] == "delete" and "reason" in request.POST.keys():
Skia's avatar
Skia committed
252
                self.object.author.user.email_user(
Krophil's avatar
Krophil committed
253
                    subject="[%s] %s" % (settings.SITH_NAME, _("Rejected comment")),
Sli's avatar
Sli committed
254
255
256
257
258
259
260
261
262
263
                    message=_(
                        'Your comment to %(target)s on the Trombi "%(trombi)s" was rejected for the following '
                        "reason: %(reason)s\n\n"
                        "Your comment was:\n\n%(content)s"
                    )
                    % {
                        "target": self.object.target.user.get_display_name(),
                        "trombi": self.object.author.trombi,
                        "reason": request.POST["reason"],
                        "content": self.object.content,
Krophil's avatar
Krophil committed
264
265
                    },
                )
Skia's avatar
Skia committed
266
                self.object.delete()
Sli's avatar
Sli committed
267
268
269
270
271
272
273
                return redirect(
                    reverse(
                        "trombi:moderate_comments",
                        kwargs={"trombi_id": self.object.author.trombi.id},
                    )
                    + "?qn_success"
                )
Skia's avatar
Skia committed
274
275
276
277
        raise Http404

    def get_context_data(self, **kwargs):
        kwargs = super(TrombiModerateCommentView, self).get_context_data(**kwargs)
Sli's avatar
Sli committed
278
        kwargs["form"] = TrombiModerateForm()
Skia's avatar
Skia committed
279
280
        return kwargs

Sli's avatar
Sli committed
281

Skia's avatar
Skia committed
282
# User side
Krophil's avatar
Krophil committed
283
284


Skia's avatar
Skia committed
285
286
class TrombiModelChoiceField(forms.ModelChoiceField):
    def label_from_instance(self, obj):
Sli's avatar
Sli committed
287
288
289
290
        return _("%(name)s (deadline: %(date)s)") % {
            "name": str(obj),
            "date": str(obj.subscription_deadline),
        }
Skia's avatar
Skia committed
291

Krophil's avatar
Krophil committed
292

Skia's avatar
Skia committed
293
class UserTrombiForm(forms.Form):
Sli's avatar
Sli committed
294
295
296
297
298
299
300
301
302
303
    trombi = TrombiModelChoiceField(
        Trombi.availables.all(),
        required=False,
        label=_("Select trombi"),
        help_text=_(
            "This allows you to subscribe to a Trombi. "
            "Be aware that you can subscribe only once, so don't play with that, "
            "or you will expose yourself to the admins' wrath!"
        ),
    )
Krophil's avatar
Krophil committed
304

Skia's avatar
Skia committed
305

Sli's avatar
Sli committed
306
307
308
class UserTrombiToolsView(
    QuickNotifMixin, TrombiTabsMixin, UserIsLoggedMixin, TemplateView
):
Skia's avatar
Skia committed
309
    """
Skia's avatar
Skia committed
310
    Display a user's trombi tools
Skia's avatar
Skia committed
311
    """
Sli's avatar
Sli committed
312

Skia's avatar
Skia committed
313
    template_name = "trombi/user_tools.jinja"
Skia's avatar
Skia committed
314
    current_tab = "tools"
Skia's avatar
Skia committed
315
316

    def post(self, request, *args, **kwargs):
Skia's avatar
Skia committed
317
        self.form = UserTrombiForm(request.POST)
Skia's avatar
Skia committed
318
        if self.form.is_valid():
319
320
321
322
323
324
325
            if hasattr(request.user, "trombi_user"):
                trombi_user = request.user.trombi_user
                trombi_user.trombi = self.form.cleaned_data["trombi"]
            else:
                trombi_user = TrombiUser(
                    user=request.user, trombi=self.form.cleaned_data["trombi"]
                )
Skia's avatar
Skia committed
326
            trombi_user.save()
Sli's avatar
Sli committed
327
            self.quick_notif_list += ["qn_success"]
Skia's avatar
Skia committed
328
        return super(UserTrombiToolsView, self).get(request, *args, **kwargs)
Skia's avatar
Skia committed
329
330

    def get_context_data(self, **kwargs):
Skia's avatar
Skia committed
331
        kwargs = super(UserTrombiToolsView, self).get_context_data(**kwargs)
Sli's avatar
Sli committed
332
        kwargs["user"] = self.request.user
333
334
335
336
        if not (
            hasattr(self.request.user, "trombi_user")
            and self.request.user.trombi_user.trombi
        ):
Sli's avatar
Sli committed
337
            kwargs["subscribe_form"] = UserTrombiForm()
Skia's avatar
Skia committed
338
        else:
Sli's avatar
Sli committed
339
340
            kwargs["trombi"] = self.request.user.trombi_user.trombi
            kwargs["date"] = date
Skia's avatar
Skia committed
341
342
        return kwargs

Krophil's avatar
Krophil committed
343

344
class UserTrombiEditPicturesView(TrombiTabsMixin, UserIsInATrombiMixin, UpdateView):
345
    model = TrombiUser
Sli's avatar
Sli committed
346
    fields = ["profile_pict", "scrub_pict"]
347
    template_name = "core/edit.jinja"
Skia's avatar
Skia committed
348
    current_tab = "pictures"
349
350
351
352
353

    def get_object(self):
        return self.request.user.trombi_user

    def get_success_url(self):
Sli's avatar
Sli committed
354
        return reverse("trombi:user_tools") + "?qn_success"
Krophil's avatar
Krophil committed
355

356

357
358
359
class UserTrombiEditProfileView(
    QuickNotifMixin, TrombiTabsMixin, UserIsInATrombiMixin, UpdateView
):
Skia's avatar
Skia committed
360
    model = User
Sli's avatar
Sli committed
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
    form_class = modelform_factory(
        User,
        fields=[
            "second_email",
            "phone",
            "department",
            "dpt_option",
            "quote",
            "parent_address",
        ],
        labels={
            "second_email": _("Personal email (not UTBM)"),
            "phone": _("Phone"),
            "parent_address": _("Native town"),
        },
    )
Skia's avatar
Skia committed
377
    template_name = "trombi/edit_profile.jinja"
Skia's avatar
Skia committed
378
    current_tab = "profile"
Skia's avatar
Skia committed
379
380
381
382

    def get_object(self):
        return self.request.user

383
    def get_success_url(self):
Sli's avatar
Sli committed
384
        return reverse("trombi:user_tools") + "?qn_success"
Krophil's avatar
Krophil committed
385

386

387
class UserTrombiResetClubMembershipsView(UserIsInATrombiMixin, RedirectView):
Skia's avatar
Skia committed
388
389
390
391
392
393
394
395
    permanent = False

    def get(self, request, *args, **kwargs):
        user = self.request.user.trombi_user
        user.make_memberships()
        return redirect(self.get_success_url())

    def get_success_url(self):
Sli's avatar
Sli committed
396
        return reverse("trombi:profile") + "?qn_success"
Krophil's avatar
Krophil committed
397

Skia's avatar
Skia committed
398

Skia's avatar
Skia committed
399
class UserTrombiDeleteMembershipView(TrombiTabsMixin, CanEditMixin, DeleteView):
Skia's avatar
Skia committed
400
401
402
    model = TrombiClubMembership
    pk_url_kwarg = "membership_id"
    template_name = "core/delete_confirm.jinja"
Sli's avatar
Sli committed
403
    success_url = reverse_lazy("trombi:profile")
Skia's avatar
Skia committed
404
405
406
    current_tab = "profile"

    def get_success_url(self):
Sli's avatar
Sli committed
407
408
409
410
        return (
            super(UserTrombiDeleteMembershipView, self).get_success_url()
            + "?qn_success"
        )
Skia's avatar
Skia committed
411

Krophil's avatar
Krophil committed
412

Skia's avatar
Skia committed
413
class UserTrombiEditMembershipView(CanEditMixin, TrombiTabsMixin, UpdateView):
Skia's avatar
Skia committed
414
415
    model = TrombiClubMembership
    pk_url_kwarg = "membership_id"
Sli's avatar
Sli committed
416
    fields = ["role", "start", "end"]
Skia's avatar
Skia committed
417
    template_name = "core/edit.jinja"
Skia's avatar
Skia committed
418
419
420
    current_tab = "profile"

    def get_success_url(self):
Sli's avatar
Sli committed
421
422
423
        return (
            super(UserTrombiEditMembershipView, self).get_success_url() + "?qn_success"
        )
Skia's avatar
Skia committed
424

Skia's avatar
Skia committed
425

Skia's avatar
Skia committed
426
class UserTrombiProfileView(TrombiTabsMixin, DetailView):
Skia's avatar
Skia committed
427
428
429
430
    model = TrombiUser
    pk_url_kwarg = "user_id"
    template_name = "trombi/user_profile.jinja"
    context_object_name = "trombi_user"
Skia's avatar
Skia committed
431
    current_tab = "tools"
Skia's avatar
Skia committed
432
433
434

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
Sli's avatar
Sli committed
435
436
437
438
439
        if (
            self.object.trombi.id != request.user.trombi_user.trombi.id
            or self.object.user.id == request.user.id
            or not self.object.trombi.show_profiles
        ):
Skia's avatar
Skia committed
440
441
442
            raise Http404()
        return super(UserTrombiProfileView, self).get(request, *args, **kwargs)

Krophil's avatar
Krophil committed
443

Sli's avatar
Sli committed
444
class TrombiCommentFormView(UserIsLoggedMixin):
445
    """
Skia's avatar
Skia committed
446
    Create/edit a trombi comment
447
    """
Sli's avatar
Sli committed
448

Skia's avatar
Skia committed
449
    model = TrombiComment
Sli's avatar
Sli committed
450
451
    fields = ["content"]
    template_name = "trombi/comment.jinja"
452
453

    def get_form_class(self):
Skia's avatar
Skia committed
454
        self.trombi = self.request.user.trombi_user.trombi
Skia's avatar
Skia committed
455
        if date.today() <= self.trombi.subscription_deadline:
Sli's avatar
Sli committed
456
457
458
459
460
461
            raise Http404(
                _(
                    "You can not yet write comment, you must wait for "
                    "the subscription deadline to be passed."
                )
            )
Skia's avatar
Skia committed
462
        if self.trombi.comments_deadline < date.today():
Sli's avatar
Sli committed
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
            raise Http404(
                _(
                    "You can not write comment anymore, the deadline is "
                    "already passed."
                )
            )
        return modelform_factory(
            self.model,
            fields=self.fields,
            widgets={
                "content": forms.widgets.Textarea(
                    attrs={"maxlength": self.trombi.max_chars}
                )
            },
            help_texts={
                "content": _("Maximum characters: %(max_length)s")
                % {"max_length": self.trombi.max_chars}
            },
        )
482
483

    def get_success_url(self):
Sli's avatar
Sli committed
484
        return reverse("trombi:user_tools") + "?qn_success"
485

486
487
    def get_context_data(self, **kwargs):
        kwargs = super(TrombiCommentFormView, self).get_context_data(**kwargs)
Sli's avatar
Sli committed
488
489
        if "user_id" in self.kwargs.keys():
            kwargs["target"] = get_object_or_404(TrombiUser, id=self.kwargs["user_id"])
490
        else:
Sli's avatar
Sli committed
491
            kwargs["target"] = self.object.target
492
        return kwargs
493

Krophil's avatar
Krophil committed
494

495
class TrombiCommentCreateView(TrombiCommentFormView, CreateView):
496
    def form_valid(self, form):
Sli's avatar
Sli committed
497
        target = get_object_or_404(TrombiUser, id=self.kwargs["user_id"])
498
499
        author = self.request.user.trombi_user
        form.instance.author = author
500
        form.instance.target = target
501
502
503
504
505
506
        # Check that this combination does not already have a comment
        old = TrombiComment.objects.filter(author=author, target=target).first()
        if old:
            old.content = form.instance.content
            old.save()
            return HttpResponseRedirect(self.get_success_url())
Skia's avatar
Skia committed
507
        return super(TrombiCommentCreateView, self).form_valid(form)
508

Krophil's avatar
Krophil committed
509

Skia's avatar
Skia committed
510
class TrombiCommentEditView(TrombiCommentFormView, CanViewMixin, UpdateView):
511
512
    pk_url_kwarg = "comment_id"

Skia's avatar
Skia committed
513
514
515
    def form_valid(self, form):
        form.instance.is_moderated = False
        return super(TrombiCommentEditView, self).form_valid(form)