views.py 104 KB
Newer Older
1 2 3
#!/usr/bin/env python
# -*- encoding: utf-8 -*-

4
"""Définitions des views.
5

6 7 8
   Une fonction par page servie.
   """

9
### Imports
Vincent Le gallic's avatar
Vincent Le gallic committed
10
import django
11

12 13 14
# Modules standard utiles
import json
import base64
15 16 17
import subprocess
import os
import shutil
baptiste's avatar
baptiste committed
18
import algo1a
raida's avatar
raida committed
19
from django.shortcuts import redirect
20 21
from datetime import datetime
from tempfile import mkdtemp
22

23
# Les objets de réponse HTTP
24
from django.http import HttpResponse, HttpResponseRedirect, Http404
25
# Pour renvoyer facilement un template
26
from django.shortcuts import render, get_object_or_404
27
from django.template import RequestContext, Context, TemplateDoesNotExist
Maxime Bombar's avatar
Maxime Bombar committed
28
from django.template.loader import render_to_string
29 30
# Pour html-escaper, notamment
import django.utils.html
31

32 33 34
# Pour protéger les données sensibles dans un POST en mode DEBUG
from django.views.decorators.debug import sensitive_post_parameters

35 36 37
# Les formset pour avoir plusieurs fields() identiques dans un form()
from django.forms import formset_factory

38 39
#: Import pour la traduction
from django.utils.translation import ugettext_lazy as _
40

41 42
# Les paramètres django
import settings
43

44 45
# Les formulaires
import forms
46 47 48 49
# Les messages
import messages
# La communication avec le backend
import nk
50 51
# Les utilitaires
import utilities
52 53
# Des utilitaires pour lesquels on n'a pas envie de rappeler ``module.`` à chaque fois
from utilities import standard_page, standard_page_withignores
54 55
# Gestion des requêtes AJAJ
import ajaj
56

57 58 59
#: Ce module contient les valeurs qu'on a besoin de conserver
#: d'une requête HTTP du client à une autre
import keep_alive
60

61
import basic
62

63
@sensitive_post_parameters('password')
64
def login_page(request):
Vincent Le gallic's avatar
Vincent Le gallic committed
65
    """Renvoie la page de login ou traite les données du formulaire de login"""
66
    if request.method == "POST":
Vincent Le gallic's avatar
Vincent Le gallic committed
67
        # on récupère le formulaire
Vincent Le gallic's avatar
Vincent Le gallic committed
68
        form = forms.LoginForm(request.POST, label_suffix=" :")
69
        if form.is_valid():
Vincent Le gallic's avatar
Vincent Le gallic committed
70 71
            username = form.cleaned_data["username"]
            password = form.cleaned_data["password"]
72
            masque_droits = form.cleaned_data["droits"]
73
            masque_droits = settings.ACL_MASKS[masque_droits][1]
74
            # On tente un login
75
            return nk.login_NK(request, username, password, form, masque_droits)
76
    else:
Vincent Le gallic's avatar
Vincent Le gallic committed
77
        form = forms.LoginForm(label_suffix=" :")
78
    variables = basic._fundamental_variables()
79
    variables["form"] = form
80
    return render(request, "note/login.html", variables)
81

82 83
def index(request):
    """La page qui ne sert à rien"""
84
    # On peuple les variables, sans demander la socket
85
    success, sock_ou_response, variables = utilities.get_varsock(request)
86
    if success:
87
        return render(request, "note/index.html", variables)
88 89 90 91
    else:
        # Une erreur a eu lieu
        response = sock_ou_response
        return response
92

93 94 95
def logout(request):
    """Fonction de déconnexion"""
    if request.session.get("logged", None) == "ok": # Il est possible qu'en fait on ait déjà timeout, donc pas besoin de vraiment se déloguer.
96
        # On enlève logged du cookie Django
97 98
        request.session["logged"] = "no"
        # Il faut fermer la socket
99
        success, sock_ou_response = nk.socket_still_alive(request)
100 101 102
        if not success:
            return sock_ou_response
        sock = sock_ou_response
103 104
        sock.write(json.dumps(["exit"]))
        sock.close()
105 106
        # On supprime la socket de keep_alive.CONNS
        idbde = request.session["whoami"]["idbde"]
107
        del keep_alive.CONNS[idbde]
108
        # On renvoie sur la page de login
109
        messages.add_success(request, messages.SUCCMSG_LOGOUT)
110 111
    return HttpResponseRedirect(settings.NOTE_LOGIN_URL)

112
@sensitive_post_parameters("password", "password_confirm")
113
def regen_pw(request, token):
Vincent Le gallic's avatar
Vincent Le gallic committed
114 115
    """Page pour demander l'envoie par mail d'un token de changement de mot de passe
       et pour changer le mot de passe quand on vient avec ce token."""
116
    variables = basic._fundamental_variables()
Vincent Le gallic's avatar
Vincent Le gallic committed
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
    if token:
        if request.method == "POST":
            form = forms.PasswordForm(request.POST, label_suffix=" :")
            if form.is_valid():
                try:
                    sock = nk.connect_NK(request)
                except nk.NKError as exc:
                    return nk.gerer_NKError(request, exc)
                new_pass = form.cleaned_data["password"]
                data = [token, new_pass]
                sock.write(json.dumps(["confirm_reset_password", data]))
                out = nk.full_read(sock)
                errors = out["errmsg"]
                if errors:
                    messages.add_error(request, errors)
                else:
                    messages.add_success(request, out["msg"])
        else:
            form = forms.PasswordForm(label_suffix=" :")
        variables["form"] = form
        return render(request, "note/regen_pw.html", variables)
    else :
        if request.method == "POST":
            form = forms.Regen_pwForm(request.POST, label_suffix=" :")
            if form.is_valid():
                try:
                    sock = nk.connect_NK(request)
                except nk.NKError as exc:
                    return nk.gerer_NKError(request, exc)
                nom = form.cleaned_data["nom"]
                prenom = form.cleaned_data["prenom"]
                mail = form.cleaned_data["mail"]
                data = [prenom,nom,mail]
                sock.write(json.dumps(["generate_reset_password", data]))
                out = nk.full_read(sock)
                errors = out["errmsg"]
                if errors:
                    messages.add_error(request, errors)
                else:
                    messages.add_success(request, out["msg"])
        else:
            form = forms.Regen_pwForm(label_suffix=" :")
        variables["form"] = form
        return render(request, "note/ask_regen_pw.html", variables)
161

162 163

@standard_page
164 165
def consos(request, sock, kwargs):
    """La page des consos.
166

167 168 169 170
       Dans ``kwargs`` :
        * ``double="-double"`` si on a demandé le mode conso double
        """
    variables = {"double_stack_mode" : kwargs["double"], "page_consos" : True}
171
    categories = utilities._get_boutons_categories(sock, request, False)
172
    variables["categories"] = categories
173
    boutons = utilities._get_boutons(sock, request)
174 175 176 177 178 179 180
    variables["boutons"] = [(categ, [b for b in boutons if b["categorie"] == categ]) for categ in categories]
    sock.write(json.dumps(["historique_transactions", "last"]))
    out = nk.full_read(sock)
    if nk._is_success_code(out["retcode"]):
        variables["historique"] = out["msg"]
    else:
        messages.add_error(request, out["errmsg"])
Charlie Jacomme's avatar
Charlie Jacomme committed
181
    sock.write(json.dumps(["mayi", "transferts"]))
182
    variables["hastransfert"] = nk.full_read(sock)["msg"]
183
    sock.write(json.dumps(["mayi", "credits"]))
184
    variables["hascredit"] = nk.full_read(sock)["msg"]
185
    sock.write(json.dumps(["mayi", "retraits"]))
186
    variables["hasretrait"] = nk.full_read(sock)["msg"]
187 188 189 190 191 192 193 194 195
    # Le formulaire de Crédit
    variables["credit_form"] = forms.CreditRetraitForm(prefix="credit_form", label_suffix=" :")
    # Le formulaire de Retrait
    variables["retrait_form"] = forms.CreditRetraitForm(prefix="retrait_form", label_suffix=" :")
    # Le formulaire de Transfert
    variables["transfert_form"] = forms.TransfertForm(prefix="transfert_form", label_suffix=" :")
    return (variables, "note/consos.html")

@standard_page
196
def dons(request, sock, kwargs):
197 198 199
    """La page des dons"""
    variables = {}
    # Le formulaire de Don
200
    variables["don_form"] = forms.TransfertForm(prefix='transfert_form', label_suffix=" :")
201 202 203
    return (variables, "note/dons.html")

@standard_page
204
def activites(request, sock, kwargs):
205
    """Affichage des activités
206

207 208
       Dans ``kwargs`` :
        * ``admin`` qui vaut ``"/admin"`` si on a demandé le mode admin
209
        * ̀ `old``   qui vaut ``"/old"``   si on a demandé le mode old
210
        """
211
    admin, old = kwargs["admin"], kwargs["old"]
212
    asked_admin = (admin == "/admin")
213
    asked_old = (old == "/old")
214 215 216 217 218 219
    # on récupère les droits de l'user
    sock.write(json.dumps(["mayi", "full_rights"]))
    rights = nk.full_read(sock)["msg"]
    # on regarde si on certains droits
    hasadmin = ("activites_admin" in rights["droits"])
    hasnote = ("note" in rights["droits"])
220 221
    # on est en mode administration si on en a le droit ET qu'on l'a demandé
    isadmin = asked_admin and hasadmin
222 223
    # on est en mode affichage_old  si on en a le droit ET qu'on a demandé old
    isold = asked_old and hasadmin
224 225 226
    if request.method == "POST":
        return HttpResponse("Bad Request", status=400)
    else:
227
        liste_activites = utilities._get_activites(sock, isadmin, request, isold=isold)
228 229 230
        # On affiche la liste des activités en ajoutant les variables standard
        variables = {"activites": liste_activites,
                     "hasadmin": hasadmin,
231
                     "isadmin": isadmin,
232 233
                     "isold": isold,
                     "hasnote": hasnote}
234 235 236
        return (variables, "note/activites.html")

@standard_page
237
def activite(request, sock, kwargs):
238
    """Affichage d'une activité pour y inviter
239

240 241 242 243 244 245 246 247 248 249 250 251
       Dans ``kwargs`` :
        * ``idact``
        * ``admin`` qui vaut ``"/admin"`` si on a demandé le mode admin
        """
    idact, admin = kwargs["idact"], kwargs["admin"]
    asked_admin = (admin == "/admin")
    # On demande si on le droit d'être admin
    sock.write(json.dumps(["mayi", "activites_admin"]))
    hasadmin = nk.full_read(sock)["msg"]
    # On est en mode administration si on en a le droit ET qu'on l'a demandé
    isadmin = asked_admin and hasadmin
    # On récupère l'activité
252
    activite = utilities._get_activite(sock, idact, request)
253 254 255 256 257 258 259 260 261 262 263 264 265
    if request.method == "POST":
        form = forms.InviteForm(request.POST, label_suffix=" :")
        if form.is_valid():
            nom = form.cleaned_data["nom"]
            prenom = form.cleaned_data["prenom"]
            data = [nom, prenom, idact]
            if isadmin: # un admin doit préciser le reponsable (en cas d'échec ce sera lui le responsable)
                try:
                    data.append(int(request.POST["idrespo"]))
                except:
                    pass
            sock.write(json.dumps(["add_invite", [data, "A" * isadmin]]))
            out = nk.full_read(sock)
266 267
            erreur = out["errmsg"]
            if erreur:
268 269 270
                if out["retcode"] == 110:
                    messages.add_warning(request, erreur)
                else:
271
                    messages.add_error(request, erreur)
272
            else:
273
                messages.add_success(request, messages.SUCCMSG_ADDINV)
274
                return HttpResponseRedirect("{}activites/{}{}/".format(settings.NOTE_ROOT_URL, idact, "/admin" * isadmin))
Charlie Jacomme's avatar
Charlie Jacomme committed
275

276
    else:
277
        form = forms.InviteForm(label_suffix=" :", initial=activite)
278
    liste_invites = utilities._get_invites(sock, idact, isadmin, request)
279 280 281 282 283 284 285
    # on prépare les variables
    variables = {"activite": activite,
                 "form": form,
                 "liste_invites": liste_invites,
                 "hasadmin": hasadmin,
                 "isadmin": isadmin}
    return (variables, "note/invitation.html")
Vincent Le gallic's avatar
Vincent Le gallic committed
286

287
@standard_page
288
def activite_gestion(request, sock, kwargs):
289
    """Page de gestion d'une activité.
290

291 292 293 294 295 296 297 298 299
       Dans ``kwargs`` :
        * ``idact`` : n° de l'activité à gérer
        * ``validation`` : fin de l'url, peut être ``"/validate"``, ``"/invalidate"`` ou ``/delete``
       """
    validation, idact = kwargs["validation"], kwargs["idact"]
    variables_standard = kwargs["variables_standard"]
    if (request.method == "GET") and (validation == "/delete"):
        # suppression de l'activité effectuée par une fonction dédiée
        utilities._del_activite(sock, request, idact)
Maxime Bombar's avatar
Maxime Bombar committed
300
        return HttpResponseRedirect('{}activites/'.format(settings.NOTE_ROOT_URL))
301 302 303 304
    sock.write(json.dumps(["mayi", "activites_admin"]))
    hasadmin = nk.full_read(sock)["msg"]
    if not hasadmin:
        messages.add_error(request, _(u"Tu n'as pas le droit activites_admin !"))
305
        return HttpResponseRedirect("{}activites/".format(settings.NOTE_ROOT_URL))
306
    activite = utilities._get_activite(sock, idact, request, computecandelete=True, whoami=variables_standard["whoami"], isadmin=True)
307
    variables = {}
308
    variables["activite"] = activite
309
    variables["hasadmin"] = hasadmin
310
    if validation == "/validate":
311
        action, data, succmsg = "valider_activite", idact, messages.SUCCMSG_VALIDACT
312
    elif validation == "/invalidate":
313 314 315 316
        action, data, succmsg = "devalider_activite", idact, messages.SUCCMSG_DEVALIDACT
    elif validation in ["/open", "/close"]:
        action, data = "openclose_activite", [(validation == "/open"), idact]
        succmsg = messages.SUCCMSG_OUVRACT if data[0] else messages.SUCCMSG_FERMACT
Vincent Le gallic's avatar
Vincent Le gallic committed
317
    else:
318 319
        action = None
    if action:
320
        sock.write(json.dumps([action, data]))
321 322 323 324 325 326 327 328
        out = nk.full_read(sock)
        if nk._is_success_code(out["retcode"]):
            if out["retcode"] == 0:
                messages.add_success(request, succmsg)
            else:
                messages.add_warning(request, out["errmsg"])
        else:
            messages.add_error(request, out["errmsg"])
Maxime Bombar's avatar
Maxime Bombar committed
329
        return HttpResponseRedirect('{}activites/{}/gestion/'.format(settings.NOTE_ROOT_URL, idact))
330
    return (variables, "note/activite_gestion.html")
331

332
@standard_page
333
def activite_gestion_modifier(request, sock, kwargs):
334
    """Page pour voir/éditer une activité, en tant qu'admin
335

336 337 338
       Dans ``kwargs`` :
        * ``idact`` : n° de l'activité à modifier
        """
339 340 341 342 343

    sock.write(json.dumps(["mayi", "activites_admin"]))
    hasadmin = nk.full_read(sock)["msg"]
    if not hasadmin:
        messages.add_error(request, _(u"Tu n'as pas le droit activites_admin !"))
344
        return HttpResponseRedirect("{}activites/".format(settings.NOTE_ROOT_URL))
345

346 347
    idact = kwargs["idact"]
    variables = {}
348
    activite = utilities._get_activite(sock, idact, request)
349
    variables["activite"] = activite
350
    variables["hasadmin"] = hasadmin
351 352 353 354
    if request.method == "GET":
        form = forms.ActiviteForm(label_suffix=" :", initial=activite)
        variables["form"] = form
        return (variables, "note/activite_modifier.html")
355
    else:
356 357 358 359 360 361 362 363 364 365 366 367 368
        form = forms.ActiviteForm(request.POST, label_suffix=" :")
        variables["form"] = form
        if form.is_valid():
            keysact = activite.keys()
            # on regarde les champs qui sont différents
            actdata = {k: v for (k, v) in form.cleaned_data.items() if k in keysact and (v != activite[k])}
            if actdata != {}:
                # On rajoute l'idact
                actdata["id"] = idact
                # On demande toujours à faire l'update en tant qu'admin
                #  de toutes façon ça ne provoque pas d'erreur si on ne l'est pas
                tosend = [actdata, "A"]
                sock.write(json.dumps(["update_activite", tosend]))
369
                out = nk.full_read(sock)
370
                if nk._is_success_code(out["retcode"]):
371
                    messages.add_success(request, messages.SUCCMSG_CHGACT)
372
                else:
373 374 375
                    messages.add_error(request, out["errmsg"])
                    return (variables, "note/activite_modifier.html")
            # on renvoie sur la page de visualisation de l'activité modifiée
Maxime Bombar's avatar
Maxime Bombar committed
376
            return HttpResponseRedirect("{}activites/{}/gestion/".format(settings.NOTE_ROOT_URL, idact))
377
        else:
378 379 380
            return (variables, "note/activite_modifier.html")

@standard_page_withignores(["idact"])
381
def mes_activites(request, sock, kwargs):
382
    """Page "Mes Activités" (ajout, modification, suppression si non encore validée)
383

384 385 386 387 388 389 390
       Dans ``kwargs`` :
        * ``idact`` si on modifie une activité
        * ``delete="/delete"`` si on supprime une activité
        """
    idact, delete = kwargs["idact"], kwargs["delete"]
    variables_standard = kwargs["variables_standard"]
    variables = {}
391 392 393
    # on demande si on le droit d'être admin
    sock.write(json.dumps(["mayi", "activites_admin"]))
    hasadmin = nk.full_read(sock)["msg"]
394
    variables["hasadmin"] = hasadmin
395
    if idact is None:
396
        mes_activites = utilities._get_activites(sock, False, False, request, computecandelete=True, whoami=variables_standard["whoami"], mine=True)
397 398 399 400 401 402 403
        variables["activites"] = mes_activites
        if request.method == "POST":
            form = forms.ActiviteForm(request.POST, label_suffix=" :", listeimprimee=False)
            if form.is_valid():
                actdata = form.cleaned_data
                del actdata["id"]
                sock.write(json.dumps(["add_activite", actdata]))
404 405
                out = nk.full_read(sock)
                if nk._is_success_code(out["retcode"]):
406
                    messages.add_success(request, messages.SUCCMSG_ADDACT)
Maxime Bombar's avatar
Maxime Bombar committed
407
                    return HttpResponseRedirect('{}mes_activites/'.format(settings.NOTE_ROOT_URL))
408
                else:
409
                    messages.add_error(request, out["errmsg"])
410
        else:
411 412
            form = forms.ActiviteForm(label_suffix=" :", listeimprimee=False)
        variables["form"] = form
413
        return (variables, "note/mes_activites.html")
414
    else:
415 416
        if delete == "/delete":
            succeed = utilities._del_activite(sock, request, idact)
Maxime Bombar's avatar
Maxime Bombar committed
417 418
            return HttpResponseRedirect('{}mes_activites/'.format(settings.NOTE_ROOT_URL))
        activite = utilities._get_activite(sock, idact, request, fallback='{}mes_activites/'.format(settings.NOTE_ROOT_URL))
419
        variables["activite"] = activite
420
        if request.method == "GET":
421
            form = forms.ActiviteForm(label_suffix=" :", initial=activite, listeimprimee=False)
422
        else:
423
            form = forms.ActiviteForm(request.POST, label_suffix=" :", listeimprimee=False)
424
            if form.is_valid():
425 426 427 428
                actdata = {champ: valeur for (champ, valeur) in form.cleaned_data.items() if (champ == "id") or (valeur != activite[champ])}
                sock.write(json.dumps(["update_activite", [actdata, "A"]]))
                out = nk.full_read(sock)
                if nk._is_success_code(out["retcode"]):
429
                    messages.add_success(request, messages.SUCCMSG_CHGACT)
Maxime Bombar's avatar
Maxime Bombar committed
430
                    return HttpResponseRedirect('{}mes_activites/'.format(settings.NOTE_ROOT_URL))
431 432 433
                else:
                    messages.add_error(request, out["errmsg"])
        variables["form"] = form
434
        return (variables, "note/activite_modifier.html")
435

436
@standard_page
437
def del_invite(request, sock, kwargs):
438
    """Suppression d'un invité
439

440 441 442 443
       Dans ``kwargs`` :
        * ``idact`` : l'activité dont on veut retirer l'invité
        * ``idinv`` : l'invité qu'on veut retirer
        * ``admin="/admin"`` si on est mode administration"""
444

445 446 447 448 449 450 451
    variables = {}
    idact, idinv = kwargs["idact"], kwargs["idinv"]
    admin = (kwargs["admin"] == "/admin")
    if idinv != -1:
        # On demande toujours la suppression avec le flag "A",
        # de toutes façons il est ignoré par le serveur si on n'a pas les droits
        sock.write(json.dumps(["del_invite", [idinv, "A"]]))
452
        out = nk.full_read(sock)
453
        if out["retcode"] == 404:
Maxime Bombar's avatar
Maxime Bombar committed
454
            messages.add_error(request, messages.ERRMSG_IDINV_FAIL.format(idinv))
455
        elif nk._is_success_code(out["retcode"]):
456
            messages.add_success(request, messages.SUCCMSG_DELINV)
457
        else:
458
            messages.add_error(request, out["errmsg"])
Maxime Bombar's avatar
Maxime Bombar committed
459
    return HttpResponseRedirect(u"{}activites/{}/{}".format(settings.NOTE_ROOT_URL, idact, "admin/" * admin))
460

461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
@standard_page
def liste_invites_entres(request, sock, kwargs):
    """Affiche la liste des invités qui sont rentrés à l'activité

       Dans ``kwargs`` :
        * ``idact`` : l'activité dont on veut la liste des invités rentrés
    """

    variables = {}
    idact = kwargs["idact"]

    #On récupère l'ensemble des droits dont dispose l'utilisateur
    sock.write(json.dumps(["mayi", "full_rights"]))
    out = nk.full_read(sock)

    if not nk._is_success_code(out["retcode"]):
        messages.add_error(request, out["errmsg"])
        return HttpResponseRedirect("{}activites/".format(settings.NOTE_ROOT_URL))
    else:
        droits =  out["msg"]["droits"]
        hasadmin = "activites_admin" in droits

    if not hasadmin:
       messages.add_error(request, _(u"Tu n'as pas le droit activites_admin !"))
       return HttpResponseRedirect("{}index/".format(settings.NOTE_ROOT_URL))

    # On récupère la liste des invités qui sont rentrés à cette activité
    sock.write(json.dumps(["liste_invites_entres", [idact,]]))
    out = nk.full_read(sock)
    if out["retcode"] == 404:
        messages.add_error(request, messages.ERRMSG_IDACT_FAIL % (idact,))
        return HttpResponseRedirect("{}activites/".format(settings.NOTE_ROOT_URL))
    elif not nk._is_success_code(out["retcode"]):
        messages.add_error(request, out["errmsg"])
        return HttpResponseRedirect("{}activites/".format(settings.NOTE_ROOT_URL))

    else:
        invites_entres = out["msg"]
        # On prépare les variables
        variables = {"invites_entres": invites_entres,
                     "idact" : idact,
                     "hasadmin": hasadmin,
503
                     "nb_invites": len(invites_entres),
504 505 506 507 508 509
                     }


    return (variables, "note/invites_entres.html")


510
@standard_page_withignores(["idbde"])
511
def comptes(request, sock, kwargs):
512
    """La page de recherche d'un compte ou qui l'affiche.
513

514 515 516 517
       Dans ``kwargs`` :
        * ``idbde`` si on affiche un compte
        """
    idbde = kwargs["idbde"]
518
    if request.method == "GET":
519 520
        if idbde is None:
            return ({}, "note/comptes.html")
521
        else:
522
            variables = utilities._prepare_variables(sock, idbde, request)
523
            variables["active"] = "compte"
524
            return (variables, "note/un_compte.html")
525
    else:
526
        return HttpResponse("Méthode invalide", status=400)
Vincent Le gallic's avatar
Vincent Le gallic committed
527

528
@standard_page_withignores(["idbde", "idact"])
529 530
def readhesions(request, sock, kwargs):
    """La page de recherche d'un compte ou qui l'affiche.
531

532 533
       Dans ``kwargs`` :
        * ``idbde`` si on affiche un compte
534
        * ``idact`` si on vient d'une entrée pot
535
        """
536
    idbde, idact = kwargs["idbde"], kwargs["idact"]
537
    tarifs = utilities._get_tarifs_adhesion(sock)
538 539 540 541 542
    if request.method == "GET":
        if idbde is None:
            return ({}, "note/readhesions.html")
        else:
            variables = utilities._prepare_variables(sock, idbde, request)
543
            variables.update(tarifs)
544
            form = forms.ReadhesionForm(label_suffix=" :",initial={"pay_nom" : variables["compte"]["nom"],"pay_prenom" : variables["compte"]["prenom"]})
545 546 547 548 549 550 551 552 553 554 555 556
            variables["form"] = form
            return (variables, "note/une_readhesion.html")
    else:
        form = forms.ReadhesionForm(request.POST, label_suffix=" :")
        if form.is_valid():
            readhdata = form.cleaned_data
            # Il faut formater ça correctement pour l'envoyer au serveur NK
            pay = {"type": readhdata["type_de_paiement"], "montant" : readhdata["on_note"],
                    "nom": readhdata["pay_nom"], "prenom": readhdata["pay_prenom"], "banque": readhdata["pay_banque"]}
            # On vire les champs en question du dico
            for champ in ["on_note", "type_de_paiement", "pay_nom", "pay_prenom", "pay_banque"]:
                del readhdata[champ]
557
            # On cherche à savoir combien lui coûte l'adhésion
558
            tarif = tarifs["prix_adhesion"]
559
            pay["montant"] += tarif
560 561
            if pay["type"] != "none":
                readhdata["pay"] = pay
562 563 564 565
            readhdata["idbde"] = idbde
            sock.write(json.dumps(["readherer", readhdata]))
            out = nk.full_read(sock)
            if nk._is_success_code(out["retcode"]):
566
                messages.add_success(request, messages.SUCCMSG_READHESION)
567 568
            else:
                messages.add_error(request, out["errmsg"])
Maxime Bombar's avatar
Maxime Bombar committed
569
                return HttpResponseRedirect("{}readhesions/{}/".format(settings.NOTE_ROOT_URL, idbde))
570 571
            # si on provient de la page d'entrée pot alors on y retourne
            if idact is not None:
Maxime Bombar's avatar
Maxime Bombar committed
572
                return HttpResponseRedirect("{}activites/{}/entree/".format(settings.NOTE_ROOT_URL, idact))
573 574
            # sinon on renvoie sur la page de réadhésion
            else:
575
                return HttpResponseRedirect("{}readhesions/".format(settings.NOTE_ROOT_URL ))
576

577
        else:
578 579 580
            variables = utilities._prepare_variables(sock, idbde, request)
            variables.update(tarifs)
            variables["form"] = form
581
            # Le formulaire de réadhésion n'est pas valide, on renvoie le formulaire incomplet
582
            return (variables, "note/une_readhesion.html")
583

584
@standard_page
585
def historique_transactions(request, sock, kwargs):
586
    """Page de l'historique des transactions d'un compte.
587

588 589
       Dans ``kwargs`` :
        * ``idbde`` : id du compte dont on veut l'historique des transactions.
590
        * ``num_page`` : numéro de la page de l'historique demandé
591 592
        """
    idbde = kwargs["idbde"]
593 594
    nb = 1000 # Arbitraire, nb de consos par page

595
    if idbde is None:
Maxime Bombar's avatar
Maxime Bombar committed
596
        return HttpResponseRedirect('{}comptes/'.format(settings.NOTE_ROOT_URL))
597 598 599
    elif "num_page" in kwargs:
        num_page = int(kwargs["num_page"])
    else:
Maxime Bombar's avatar
Maxime Bombar committed
600
        return HttpResponseRedirect('{}comptes/{}/historique/1/'.format(settings.NOTE_ROOT_URL, idbde))
601

602
    variables = utilities._prepare_variables(sock, idbde, request)
Vincent Le gallic's avatar
Vincent Le gallic committed
603
    sock.write(json.dumps(["historique_transactions", [idbde, num_page, nb]]))
604 605
    out = nk.full_read(sock)
    if nk._is_success_code(out["retcode"]):
606 607
        variables['historique'] = out["msg"]['historique']
        variables['nb_transactions'] = out["msg"]['nb_transactions']
608 609
        variables['nb_pages'] = out["msg"]['nb_pages']
        variables['num_page'] = out["msg"]['num_page']
610
    else:
611
        messages.add_error(request, out["errmsg"])
Vincent Le gallic's avatar
Vincent Le gallic committed
612 613 614
    if variables['num_page'] != num_page:
        # Au cas où le serveur aurait eu besoin de renuméroter,
        # si l'utilisateur demande un page < 1 ou > max
Maxime Bombar's avatar
Maxime Bombar committed
615
        return HttpResponseRedirect("{}comptes/{}/historique/{}/".format(settings.NOTE_ROOT_URL, idbde, variables["num_page"],))
616
    variables["active"] = "historique"
617
    return (variables, "note/un_compte_historique.html")
618

619
@standard_page
620
def comptes_advanced(request, sock, kwargs):
Vincent Le gallic's avatar
Vincent Le gallic committed
621
    """Page de recherche avancée"""
622 623 624 625 626 627
    variables = {}
    # On cherche si on a le droit full_search
    sock.write(json.dumps(["mayi", "full_search"]))
    mayi = nk.full_read(sock)["msg"]
    variables["acl_full_search"] = mayi
    # certains champs ne seront accessibles qu'aux utilisateurs ayant les droits full_search
628
    variables["full_search_fields"] = ["tel", "adresse", "pbsante"]
629 630 631
    if request.method == "GET":
        form = forms.SearchForm(label_suffix=" :")
        variables["form"] = form
632
        variables["search_flags"] = "cio" # par défaut, la recherche est case-insensitive et sur les comptes non à jour
633
        variables["exactfilter"] = "b" # par défaut, on matche sur le début du mot
Vincent Le gallic's avatar
Vincent Le gallic committed
634
    else:
635 636 637 638 639 640 641 642 643 644
        form = forms.SearchForm(request.POST, label_suffix=" :")
        checked_fields = [champ[4:] for (champ, valeur) in request.POST.items() if (champ[:4] == "box_") and (valeur == "on")]
        variables["checked_fields"] = checked_fields
        exactfilter = request.POST["exactfilter"]
        variables["exactfilter"] = exactfilter
        search_flags_dico = {"search_alias": "A",
                             "give_alias": "a",
                             "search_historique": "H",
                             "give_historique": "h",
                             "case_insensitive": "i",
645 646
                             "old_accounts": "o",
                             "conjunctive_search" : "c"}
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
        search_flags = "".join([search_flags_dico.get(champ, "") for champ in request.POST.keys()])
        if form.is_valid():
            # On récupère toutes les données sur lesquelles on doit chercher
            searching = {champ: form.cleaned_data[champ] for champ in checked_fields}
            searchfields = searching.keys()
            if "alias" in searchfields:
                search_flags += "A"
            if "historique" in searchfields:
                search_flags += "H"
            variables["search_flags"] = search_flags
            flags = exactfilter + search_flags
            tosend = [flags, searching]
            sock.write(json.dumps(["search", tosend]))
            out = nk.full_read(sock)
            if nk._is_success_code(out["retcode"]):
                # les champs alias et historiques ont déjà été gentiments post-processés par le serveur
                # mais quand il n'y en a pas, on récupère null, pythonisé en None, et c'est moche
                liste = out["msg"]
                for i in range(len(liste)):
                    for champ in ["aliases", "historiques"]:
                        if liste[i].get(champ, "") == None:
                            liste[i][champ] = u""
                variables["search_result"] = liste
                variables["give_alias"] = ("a" in search_flags)
                variables["give_historique"] = ("h" in search_flags)
                return (variables, "note/recherche_avancee_resultats.html")
673
            else:
674
                messages.add_error(request, out["errmsg"])
675
                variables["form"] = form
676 677 678
        else:
            variables["form"] = form
    return (variables, "note/recherche_avancee.html")
679

680
@standard_page
681
def modifier_compte(request, sock, kwargs):
682
    """Page de modification de compte.
683

684 685 686 687 688 689
       Dans ``kwargs`` :
        * ``idbde`` : id du compte à modifier
        """
    idbde = kwargs["idbde"]
    variables_standard = kwargs["variables_standard"]
    variables = utilities._prepare_variables(sock, idbde, request, form=True, whoami=variables_standard["whoami"])
690 691 692 693 694
    # On vérifie si l'utilisateur tente de se modifier lui-même
    updating_myself = False
    if variables["whoami"]["idbde"] == idbde and "myself" in variables["whoami"]["full_rights"]:
        updating_myself = True
    variables["updating_myself"] = updating_myself
695 696 697 698
    if request.method == "GET":
        if variables.has_key("compte"):
            form = forms.CompteForm(initial=variables["compte"])
        else:
Maxime Bombar's avatar
Maxime Bombar committed
699
            return HttpResponseRedirect("{}comptes/".format(settings.NOTE_ROOT_URL))
700 701 702 703 704
    elif request.method == "POST":
        # on a envoyé un formulaire de modification du compte
        form = forms.CompteForm(request.POST)
        if form.is_valid():
            fields = form.cleaned_data
705 706
            gotcompte = variables.has_key("compte")
            if not gotcompte:
Maxime Bombar's avatar
Maxime Bombar committed
707
                return HttpResponseRedirect("{}comptes/".format(settings.NOTE_ROOT_URL))
708 709 710 711 712 713 714 715
            compte = variables["compte"]
            keyscompte = compte.keys()
            # on regarde les champs qui sont différents
            tosend = {k: v for (k, v) in form.cleaned_data.items() if k in keyscompte and (v != compte[k])}
            # les droits ça se gère un peu spécialement
            # avec des virgules et des cases à cocher
            for champ in ["droits", "surdroits"]:
                n = len(champ) + 1
Maxime Bombar's avatar
Maxime Bombar committed
716
                droitsousur = [case[n:] for (case, valeur) in request.POST.items() if case.startswith("{}_".format(champ)) and (valeur == "on")]
717
                liste = compte[champ]
718 719 720 721 722 723 724 725 726 727 728
                if set(droitsousur) != set(liste):
                    tosend[champ] = u",".join(droitsousur)
            if tosend != {}: # si il y a vraiment quelque chose à faire
                # on rajoute l'idbde
                tosend["idbde"] = idbde
                # si on n'a pas les droits wei (et que ce n'est pas notre compte)
                #  on ne tient pas compte du contenu de "numsecu" et "pbsante"
                if not variables["has_wei"]:
                    if tosend.has_key("pbsante"):
                        del tosend["pbsante"]
                sock.write(json.dumps(["update_compte", tosend]))
729 730
                out = nk.full_read(sock)
                if nk._is_success_code(out["retcode"]):
731
                    messages.add_success(request, messages.SUCCMSG_ACCOUNT_CHANGED)
732
                else:
733
                    messages.add_error(request, out["errmsg"])
734
                    variables["form"] = form
735
                    return (variables, "note/modifier_compte.html")
736 737
            # si on provient de la page listedroits alors on y retourne
            if request.META["HTTP_REFERER"].endswith("listedroits"):
738
                return HttpResponseRedirect("{}listedroits/".format(settings.NOTE_ROOT_URL ))
739 740
            # sinon renvoie sur la page de visualisation du compte modifié, même si rien n'a été modifié
            else:
Maxime Bombar's avatar
Maxime Bombar committed
741
                return HttpResponseRedirect("{}comptes/{}/".format(settings.NOTE_ROOT_URL, idbde))
742 743 744
        else:
            variables["form"] = form
            return (variables, "note/modifier_compte.html")
745 746 747
    variables["form"] = form
    variables["active"] = "modifier"
    return (variables, "note/modifier_compte.html")
748

749
@standard_page
750
def supprimer_compte(request, sock, kwargs):
751
    """Page de confirmation de suppression de compte.
752

753 754 755 756 757 758 759 760 761 762
       Dans ``kwargs`` :
        * ``idbde`` : id du compte à supprimer
        """
    idbde = kwargs["idbde"]
    variables = {"button_class" : "btn-danger", "button_content" : u"Confirmer la suppression"}
    variables_standard = kwargs["variables_standard"]
    variables.update(utilities._prepare_variables(sock, idbde, request, form=True, whoami=variables_standard["whoami"]))
    if request.method == "GET":
        if variables.has_key("compte"):
            form = forms.DeleteCompteForm(initial=variables["compte"])
763
        else:
Maxime Bombar's avatar
Maxime Bombar committed
764
            return HttpResponseRedirect("{}comptes/".format(settings.NOTE_ROOT_URL))
765 766 767 768 769 770
    elif request.method == "POST":
        # on a envoyé un formulaire de suppression du compte
        form = forms.DeleteCompteForm(request.POST)
        if form.is_valid():
            fields = form.cleaned_data
            if not variables.has_key("compte"):
Maxime Bombar's avatar
Maxime Bombar committed
771
                return HttpResponseRedirect("{}comptes/".format(settings.NOTE_ROOT_URL))
772 773 774 775 776 777
            compte = variables["compte"]
            # on regarde les champs qui sont différents
            tosend = [idbde, fields["anonymiser"]]
            sock.write(json.dumps(["supprimer_compte", tosend]))
            out = nk.full_read(sock)
            if nk._is_success_code(out["retcode"]):
778
                messages.add_success(request, messages.SUCCMSG_ACCOUNT_DELETED)
779
                # on renvoie sur la page de visualisation du compte supprimé
Maxime Bombar's avatar
Maxime Bombar committed
780
                return HttpResponseRedirect("{}comptes/{}/".format(settings.NOTE_ROOT_URL, idbde))
781 782
            else:
                messages.add_error(request, out["errmsg"])
783 784 785
    variables["form"] = form
    variables["active"] = "supprimer"
    return (variables, "note/supprimer_compte.html")
786

787
@standard_page
788
def update_photo(request, sock, kwargs):
789
    """La page de modification des photos.
790

791 792 793 794
       Dans ``kwargs`` :
        * ``idbde`` : id du compte dont on veut modifier la photo
        """
    idbde = kwargs["idbde"]
795
    variables = {}
796
    variables = utilities._prepare_variables(sock, idbde, request)
797 798 799 800 801 802 803 804 805
    if request.method == "POST":
        form = forms.PhotoForm(request.POST, request.FILES, label_suffix=" :")
        if form.is_valid():
            photo = request.FILES["photo"]
            # On envoie la photo au serveur NK
            photodata = photo.read()
            photob64 = base64.b64encode(photodata)
            format = photo.name.rsplit('.',1)[-1]
            sock.write(json.dumps(["update_photo", [idbde, photob64, format]]))
806 807
            answer = nk.full_read(sock)
            if nk._is_success_code(answer["retcode"]):
808
                messages.add_success(request, messages.SUCCMSG_PHOTO_UPDATED)
Maxime Bombar's avatar
Maxime Bombar committed
809
                return HttpResponseRedirect('{}comptes/{}/'.format(settings.NOTE_ROOT_URL, idbde))
810
            else:
811
                messages.add_error(request, answer["errmsg"])
812 813 814
    else:
        form = forms.PhotoForm(label_suffix=" :")
    variables["form"] = form
815
    variables["active"] = "modifier photo"
816
    return (variables, "note/un_compte_photo.html")
817

818
@standard_page
819
def historique_pseudo(request, sock, kwargs):
820
    """La page de visualisation de l'historique des pseudos.
821

822 823 824 825 826 827
       Dans ``kwargs`` :
        * ``idbde`` : id du compte ont on veut l'historique des pseudos
       """
    idbde = kwargs["idbde"]
    variables = {}
    if request.method == "GET":
828 829 830
        compte = utilities._get_historique_pseudo(sock, idbde, request)
        variables["compte"] = compte
        variables["historique_pseudo"] = compte["historique_pseudo"]
831 832
        return (variables, "note/historique_pseudo.html")
    else:
Maxime Bombar's avatar
Maxime Bombar committed
833
        return HttpResponse("Bad request method : {}".format(request.method))
834

835
@standard_page
836
def search_historique_pseudo(request, sock, kwargs):
837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
    """Page de recherche par ancien pseudo (même inactif)."""
    variables = {}
    if request.method == "GET":
        form = forms.SearchHistoriquePseudoForm(label_suffix=" :", initial={"exactfilter": "b"})
        variables["form"] = form
    else:
        form = forms.SearchHistoriquePseudoForm(request.POST, label_suffix=" :")
        if form.is_valid():
            tosend = [form.cleaned_data["historique"], form.cleaned_data["exactfilter"]]
            sock.write(json.dumps(["search_historique_pseudo", tosend]))
            out = nk.full_read(sock)
            if nk._is_success_code(out["retcode"]):
                variables["is_displaying"] = True
                liste = out["msg"]
                variables["search_result"] = liste
852
            else:
853
                messages.add_error(request, out["errmsg"])
854
        else:
855 856 857 858
            variables["form"] = form
    return (variables, "note/search_historique_pseudo.html")

@standard_page
859
def aliases(request, sock, kwargs):
860
    """La page de gestion des aliases.
861

862 863 864 865 866
       Dans ``kwargs`` :
        * ``idbde`` : l'id du compte
        """
    idbde = kwargs["idbde"]
    variables = {}
867 868
    compte = utilities._get_aliases(sock, idbde, request)
    variables["compte"] = compte
869 870 871 872 873 874 875 876 877 878
    if request.method == "GET":
        form = forms.AliasForm(label_suffix=" :")
    else:
        form = forms.AliasForm(request.POST, label_suffix=" :")
        if form.is_valid():
            alias = form.cleaned_data["alias"]
            tosend = [idbde, alias]
            sock.write(json.dumps(["alias", tosend]))
            out = nk.full_read(sock)
            if nk._is_success_code(out["retcode"]):
879
                messages.add_success(request, messages.SUCCMSG_ALIAS_ADDED)
Maxime Bombar's avatar
Maxime Bombar committed
880
                return HttpResponseRedirect("{}comptes/{}/aliases/".format(settings.NOTE_ROOT_URL, idbde))
881 882 883 884
            else:
                messages.add_error(request, out["errmsg"])
    variables["form"] = form
    return (variables, "note/aliases.html")
885

886
@standard_page
887
def unalias(request, sock, kwargs):
888
    """Fonction appelée lorsqu'on veut supprimer un/des alias.
889

890 891 892 893 894 895 896 897 898 899 900
       Dans ``kwargs`` :
        * ``idbde`` : id du compte dont on veut supprimer un/des alias
        * ``delall="_all"`` si on veut supprimer tous les alias du compte (prioritaire)
        * ``idalias`` : id d'un alias à supprimer
        """
    variables = {}
    idbde, delall, idalias = kwargs["idbde"], kwargs["delall"], kwargs["idalias"]
    if request.method == "GET":
        delete_all = (delall == "_all")
        if delete_all:
            sock.write(json.dumps(["unalias", [idbde, True]]))
901
        else:
902 903 904
            try:
                idalias = int(idalias)
            except:
Maxime Bombar's avatar
Maxime Bombar committed
905 906
                messages.add_error(request, messages.ERRMSG_IDALIAS_INVALID.format(idalias))
                return HttpResponseRedirect("{}comptes/{}/aliases/".format(settings.NOTE_ROOT_URL, idbde))
907 908 909 910
            sock.write(json.dumps(["unalias", [idalias, False]]))
        out = nk.full_read(sock)
        if nk._is_success_code(out["retcode"]):
            if delete_all:
911
                msg = messages.SUCCMSG_ALIAS_ALLDELETED
912
            else:
913
                msg = messages.SUCCMSG_ALIAS_DELETED
914 915 916
            messages.add_success(request, msg)
        else:
            messages.add_error(request, out["errmsg"])
Maxime Bombar's avatar
Maxime Bombar committed
917
        return HttpResponseRedirect("{}comptes/{}/aliases/".format(settings.NOTE_ROOT_URL, idbde))
918
    else:
Maxime Bombar's avatar
Maxime Bombar committed
919
        return HttpResponse("Bad request method : {}".format(request.method))
920

921
@sensitive_post_parameters("password")
922
@standard_page
923
def password(request, sock, kwargs):
924
    """Page de changement de mot de passe.
925

926 927 928 929 930 931
       Dans ``kwargs`` :
        * ``idbde`` : id du compte dont on veut changer le mot de passe
        """
    idbde = kwargs["idbde"]
    variables = {}
    if request.method == "GET":
932 933 934 935 936 937
        compte = utilities._get_compte(sock, idbde, request)
        variables["compte"] = compte
        if kwargs["variables_standard"]["whoami"]["idbde"] == idbde:
            # On peut changer son propre mot de passe
            variables["form"] = forms.PasswordForm(label_suffix=" :")
        elif idbde <= 0:
938
            messages.add_error(request, messages.ERRMSG_PASSWORD_NEGATIVE_IDBDE)
939 940 941 942 943
        else:
            # Il faut alors vérifier si il a le droit chgpass
            sock.write(json.dumps(["mayi", "chgpass"]))
            out = nk.full_read(sock)["msg"]
            if out == True:
944 945
                variables["form"] = forms.PasswordForm(label_suffix=" :")
            else:
946
                messages.add_error(request, messages.ERRMSG_NO_ACL_CHGPASS)
947
        return (variables, "note/password.html")
948
    else:
949 950 951 952 953
        form = forms.PasswordForm(request.POST, label_suffix=" :")
        if form.is_valid():
            newpass = form.cleaned_data["password"]
            tosend = [idbde, newpass]
            sock.write(json.dumps(["chgpass", tosend]))
954 955
            out = nk.full_read(sock)
            if nk._is_success_code(out["retcode"]):
956
                messages.add_success(request, messages.SUCCMSG_PASSWORD_CHANGED)
Maxime Bombar's avatar
Maxime Bombar committed
957
                return HttpResponseRedirect("{}comptes/{}/".format(settings.NOTE_ROOT_URL, idbde))
958
            else: