diff --git a/preferences/migrations/0024_optionaluser_all_can_create.py b/preferences/migrations/0024_optionaluser_all_can_create.py new file mode 100644 index 00000000..3f7cf56e --- /dev/null +++ b/preferences/migrations/0024_optionaluser_all_can_create.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-11-21 04:42 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0023_auto_20171015_2033'), + ] + + operations = [ + migrations.AddField( + model_name='optionaluser', + name='all_can_create', + field=models.BooleanField(default=False, help_text="Tous les users peuvent en créer d'autres"), + ), + ] diff --git a/preferences/models.py b/preferences/models.py index dc1412e7..6a793a24 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -42,6 +42,10 @@ class OptionalUser(models.Model): default=0 ) gpg_fingerprint = models.BooleanField(default=True) + all_can_create = models.BooleanField( + default=False, + help_text="Tous les users peuvent en créer d'autres", + ) def clean(self): """Creation du mode de paiement par solde""" diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 9a2abe08..4858e414 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -52,6 +52,10 @@ with this program; if not, write to the Free Software Foundation, Inc., {{ useroptions.solde_negatif }} {% endif %} + + Creations d'users par tous + {{ useroptions.all_can_create }} +

Préférences machines

{% if is_bureau %} diff --git a/users/forms.py b/users/forms.py index 8aa2ffe7..a47c1436 100644 --- a/users/forms.py +++ b/users/forms.py @@ -379,6 +379,18 @@ class FullClubForm(ClubForm): ] +class ClubAdminandMembersForm(ModelForm): + """Permet d'éditer la liste des membres et des administrateurs + d'un club""" + class Meta: + model = Club + fields = ['administrators', 'members'] + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(ClubAdminandMembersForm, self).__init__(*args, prefix=prefix, **kwargs) + + class PasswordForm(ModelForm): """ Formulaire de changement brut de mot de passe. Ne pas utiliser sans traitement""" diff --git a/users/migrations/0060_auto_20171120_0317.py b/users/migrations/0060_auto_20171120_0317.py new file mode 100644 index 00000000..a77b24db --- /dev/null +++ b/users/migrations/0060_auto_20171120_0317.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-11-20 02:17 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0059_auto_20171025_1854'), + ] + + operations = [ + migrations.AddField( + model_name='club', + name='administrators', + field=models.ManyToManyField(blank=True, related_name='club_administrator', to='users.Adherent'), + ), + migrations.AddField( + model_name='club', + name='members', + field=models.ManyToManyField(blank=True, related_name='club_members', to='users.Adherent'), + ), + ] diff --git a/users/models.py b/users/models.py index 54500f4e..ac767bd3 100644 --- a/users/models.py +++ b/users/models.py @@ -60,7 +60,10 @@ from django.core.mail import send_mail from django.core.urlresolvers import reverse from django.db import transaction from django.utils import timezone -from django.contrib.auth.models import AbstractBaseUser, BaseUserManager +from django.contrib.auth.models import ( + AbstractBaseUser, + BaseUserManager +) from django.core.validators import RegexValidator from reversion import revisions as reversion @@ -755,6 +758,28 @@ class User(AbstractBaseUser): num += 1 return composed_pseudo(num) + def can_create(user): + options, _created = OptionalUser.objects.get_or_create() + if options.all_can_create: + return True + else: + return user.has_perms(('cableur',)) + + def can_edit(self, user): + if self.is_class_club and user.is_class_adherent: + return self == user or user.has_perms(('cableur',)) or\ + user.adherent in self.club.administrators.all() + else: + return self == user or user.has_perms(('cableur',)) + + def can_view(self, user): + if self.is_class_club and user.is_class_adherent: + return self == user or user.has_perms(('cableur',)) or\ + user.adherent in self.club.administrators.all() or\ + user.adherent in self.club.members.all() + else: + return self == user or user.has_perms(('cableur',)) + def __str__(self): return self.pseudo @@ -771,6 +796,7 @@ class Adherent(User): pass + class Club(User): PRETTY_NAME = "Clubs" room = models.ForeignKey( @@ -779,6 +805,17 @@ class Club(User): blank=True, null=True ) + administrators = models.ManyToManyField( + blank=True, + to='users.Adherent', + related_name='club_administrator' + ) + members = models.ManyToManyField( + blank=True, + to='users.Adherent', + related_name='club_members' + ) + pass diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 253ab86d..fb9e37d9 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -141,21 +141,61 @@ with this program; if not, write to the Free Software Foundation, Inc., {{ user.shell }} {% endif %} -

Machines :

+ {% if user.is_class_club %} + + + Gérer admin et membres + +

Administrateurs du club

+ + + + + + + + + {% for admin in user.club.administrators.all %} + + + + + + {% endfor %} +
NomPrenomPseudo
{{ admin.surname }}{{ admin.name }}{{ admin.pseudo }}
+

Membres

+ + + + + + + + + {% for admin in user.club.members.all %} + + + + + + {% endfor %} +
NomPrenomPseudo
{{ admin.surname }}{{ admin.name }}{{ admin.pseudo }}
+ {% endif %} +

Machines

Ajouter une machine

{% if machines_list %} {% include "machines/aff_machines.html" with machines_list=machines_list %} {% else %}

Aucune machine

{% endif %} -

Cotisations :

+

Cotisations

{% if is_cableur %}

Ajouter une cotisation {% if user_solde %} Modifier le solde{% endif%}

{% endif%} {% if facture_list %} {% include "cotisations/aff_cotisations.html" with facture_list=facture_list %} {% else %}

Aucune facture

{% endif %} -

Bannissements :

+

Bannissements

{% if is_bofh %}

Ajouter un bannissement

{% endif %} {% if ban_list %} {% include "users/aff_bans.html" with ban_list=ban_list %} diff --git a/users/templates/users/sidebar.html b/users/templates/users/sidebar.html index 9a4312ff..d6b31acd 100644 --- a/users/templates/users/sidebar.html +++ b/users/templates/users/sidebar.html @@ -25,22 +25,24 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block sidebar %} - {% if is_cableur %} - - - Créer un adhérent - + {% if is_cableur %} Créer un club/association - - - Adherents + {% endif %} + + + Créer un adhérent - Clubs + Clubs et assos + + {% if is_cableur %} + + + Adherents diff --git a/users/templates/users/user.html b/users/templates/users/user.html index ad6ddb64..bf1bef0f 100644 --- a/users/templates/users/user.html +++ b/users/templates/users/user.html @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% csrf_token %} - {% massive_bootstrap_form userform 'room,school' %} + {% massive_bootstrap_form userform 'room,school,administrators,members' %} {% bootstrap_button "Créer ou modifier" button_type="submit" icon="star" %}

diff --git a/users/urls.py b/users/urls.py index e351dcca..e669a0d9 100644 --- a/users/urls.py +++ b/users/urls.py @@ -33,6 +33,11 @@ urlpatterns = [ url(r'^new_user/$', views.new_user, name='new-user'), url(r'^new_club/$', views.new_club, name='new-club'), url(r'^edit_info/(?P[0-9]+)$', views.edit_info, name='edit-info'), + url( + r'^edit_club_admin_members/(?P[0-9]+)$', + views.edit_club_admin_members, + name='edit-club-admin-members' + ), url(r'^state/(?P[0-9]+)$', views.state, name='state'), url(r'^password/(?P[0-9]+)$', views.password, name='password'), url(r'^new_serviceuser/$', views.new_serviceuser, name='new-serviceuser'), diff --git a/users/views.py b/users/views.py index 5cbe462e..e0c71157 100644 --- a/users/views.py +++ b/users/views.py @@ -40,7 +40,7 @@ from django.shortcuts import get_object_or_404, render, redirect from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.contrib import messages from django.contrib.auth.decorators import login_required, permission_required -from django.db.models import ProtectedError +from django.db.models import ProtectedError, Q from django.db import IntegrityError from django.utils import timezone from django.db import transaction @@ -53,14 +53,40 @@ from rest_framework.renderers import JSONRenderer from reversion.models import Version from reversion import revisions as reversion from users.serializers import MailSerializer -from users.models import User, Right, Ban, Whitelist, School, ListRight -from users.models import Request, ServiceUser, Adherent, Club -from users.forms import DelRightForm, BanForm, WhitelistForm, DelSchoolForm -from users.forms import DelListRightForm, NewListRightForm, FullAdherentForm -from users.forms import StateForm, FullClubForm -from users.forms import RightForm, SchoolForm, EditServiceUserForm -from users.forms import ServiceUserForm, ListRightForm, AdherentForm, ClubForm -from users.forms import MassArchiveForm, PassForm, ResetPasswordForm +from users.models import ( + User, + Right, + Ban, + Whitelist, + School, + ListRight, + Request, + ServiceUser, + Adherent, + Club +) +from users.forms import ( + DelRightForm, + BanForm, + WhitelistForm, + DelSchoolForm, + DelListRightForm, + NewListRightForm, + FullAdherentForm, + StateForm, + FullClubForm, + RightForm, + SchoolForm, + EditServiceUserForm, + ServiceUserForm, + ListRightForm, + AdherentForm, + ClubForm, + MassArchiveForm, + PassForm, + ResetPasswordForm, + ClubAdminandMembersForm +) from cotisations.models import Facture from machines.models import Machine from preferences.models import OptionalUser, GeneralOption @@ -85,10 +111,15 @@ def password_change_action(u_form, user, request, req=False): @login_required -@permission_required('cableur') def new_user(request): """ Vue de création d'un nouvel utilisateur, envoie un mail pour le mot de passe""" + if not User.can_create(request.user): + messages.error(request, "Vous ne pouvez pas accéder à ce menu") + return redirect(reverse( + 'users:profil', + kwargs={'userid':str(request.user.id)} + )) user = AdherentForm(request.POST or None) if user.is_valid(): user = user.save(commit=False) @@ -128,6 +159,37 @@ def new_club(request): return form({'userform': club}, 'users/user.html', request) +@login_required +def edit_club_admin_members(request, clubid): + """Vue d'edition de la liste des users administrateurs et + membres d'un club""" + try: + club_instance = Club.objects.get(pk=clubid) + except Club.DoesNotExist: + messages.error(request, "Club inexistant") + return redirect(reverse('users:index')) + if not club_instance.can_edit(request.user): + messages.error(request, "Vous ne pouvez pas accéder à ce menu") + return redirect(reverse( + 'users:profil', + kwargs={'userid':str(request.user.id)} + )) + club = ClubAdminandMembersForm(request.POST or None, instance=club_instance) + if club.is_valid(): + with transaction.atomic(), reversion.create_revision(): + club.save() + reversion.set_user(request.user) + reversion.set_comment("Champs modifié(s) : %s" % ', '.join( + field for field in club.changed_data + )) + messages.success(request, "Le club a bien été modifié") + return redirect(reverse( + 'users:profil', + kwargs={'userid':str(club_instance.id)} + )) + return form({'userform': club}, 'users/user.html', request) + + def select_user_edit_form(request, user): """Fonction de choix du bon formulaire, en fonction de: - droit @@ -156,9 +218,8 @@ def edit_info(request, userid): except User.DoesNotExist: messages.error(request, "Utilisateur inexistant") return redirect(reverse('users:index')) - if not request.user.has_perms(('cableur',)) and user != request.user: - messages.error(request, "Vous ne pouvez pas modifier un autre\ - user que vous sans droit cableur") + if not user.can_edit(request.user): + messages.error(request, "Vous ne pouvez pas accéder à ce menu") return redirect(reverse( 'users:profil', kwargs={'userid':str(request.user.id)} @@ -221,9 +282,8 @@ def password(request, userid): except User.DoesNotExist: messages.error(request, "Utilisateur inexistant") return redirect(reverse('users')) - if not request.user.has_perms(('cableur',)) and user != request.user: - messages.error(request, "Vous ne pouvez pas modifier un\ - autre user que vous sans droit cableur") + if not user.can_edit(request.user): + messages.error(request, "Vous ne pouvez pas accéder à ce menu") return redirect(reverse( 'users:profil', kwargs={'userid':str(request.user.id)} @@ -664,12 +724,16 @@ def index(request): @login_required -@permission_required('cableur') def index_clubs(request): """ Affiche l'ensemble des clubs, need droit cableur """ options, _created = GeneralOption.objects.get_or_create() pagination_number = options.pagination_number - clubs_list = Club.objects.select_related('room') + if not request.user.has_perms(('cableur',)): + clubs_list = Club.objects.filter( + Q(administrators=request.user.adherent) | Q(members=request.user.adherent) + ).distinct().select_related('room') + else: + clubs_list = Club.objects.select_related('room') clubs_list = SortTable.sort( clubs_list, request.GET.get('col'), @@ -795,10 +859,8 @@ def history(request, object_name, object_id): except User.DoesNotExist: messages.error(request, "Utilisateur inexistant") return redirect(reverse('users:index')) - if not request.user.has_perms(('cableur',)) and\ - object_instance != request.user: - messages.error(request, "Vous ne pouvez pas afficher\ - l'historique d'un autre user que vous sans droit cableur") + if not object_instance.can_view(request.user): + messages.error(request, "Vous ne pouvez pas afficher ce menu") return redirect(reverse( 'users:profil', kwargs={'userid':str(request.user.id)} @@ -889,9 +951,8 @@ def profil(request, userid): except User.DoesNotExist: messages.error(request, "Utilisateur inexistant") return redirect(reverse('users:index')) - if not request.user.has_perms(('cableur',)) and users != request.user: - messages.error(request, "Vous ne pouvez pas afficher un autre user\ - que vous sans droit cableur") + if not users.can_view(request.user): + messages.error(request, "Vous ne pouvez pas accéder à ce menu") return redirect(reverse( 'users:profil', kwargs={'userid':str(request.user.id)}