diff --git a/search/views.py b/search/views.py index fa9a43e8..510837e3 100644 --- a/search/views.py +++ b/search/views.py @@ -74,20 +74,20 @@ def search_result(search, type, request): recherche = {'users_list': None, 'machines_list' : [], 'facture_list' : None, 'ban_list' : None, 'white_list': None, 'port_list': None, 'switch_list': None} if request.user.has_perms(('cableur',)): - query = Q(user__pseudo__icontains = search) | Q(user__name__icontains = search) | Q(user__surname__icontains = search) + query = Q(user__pseudo__icontains = search) | Q(user__adherent__name__icontains = search) | Q(user__surname__icontains = search) else: - query = (Q(user__pseudo__icontains = search) | Q(user__name__icontains = search) | Q(user__surname__icontains = search)) & Q(user = request.user) + query = (Q(user__pseudo__icontains = search) | Q(user__adherent__name__icontains = search) | Q(user__surname__icontains = search)) & Q(user = request.user) for i in aff: if i == '0': - query_user_list = Q(room__name__icontains = search) | Q(pseudo__icontains = search) | Q(name__icontains = search) | Q(surname__icontains = search) & query1 + query_user_list = Q(room__name__icontains = search) | Q(pseudo__icontains = search) | Q(adherent__name__icontains = search) | Q(surname__icontains = search) & query1 if request.user.has_perms(('cableur',)): recherche['users_list'] = User.objects.filter(query_user_list).order_by('state', 'surname').distinct() else : recherche['users_list'] = User.objects.filter(query_user_list & Q(id=request.user.id)).order_by('state', 'surname').distinct() if i == '1': - query_machine_list = Q(machine__user__pseudo__icontains = search) | Q(machine__user__name__icontains = search) | Q(machine__user__surname__icontains = search) | Q(mac_address__icontains = search) | Q(ipv4__ipv4__icontains = search) | Q(domain__name__icontains = search) | Q(domain__related_domain__name__icontains = search) + query_machine_list = Q(machine__user__pseudo__icontains = search) | Q(machine__user__adherent__name__icontains = search) | Q(machine__user__surname__icontains = search) | Q(mac_address__icontains = search) | Q(ipv4__ipv4__icontains = search) | Q(domain__name__icontains = search) | Q(domain__related_domain__name__icontains = search) if request.user.has_perms(('cableur',)): data = Interface.objects.filter(query_machine_list).distinct() else: diff --git a/users/forms.py b/users/forms.py index 9b0b1fb6..f07e3d28 100644 --- a/users/forms.py +++ b/users/forms.py @@ -99,7 +99,7 @@ class UserCreationForm(forms.ModelForm): super(UserCreationForm, self).__init__(*args, prefix=prefix, **kwargs) class Meta: - model = User + model = Adherent fields = ('pseudo', 'surname', 'email') def clean_password2(self): @@ -179,7 +179,7 @@ class UserChangeForm(forms.ModelForm): is_admin = forms.BooleanField(label='is admin', required=False) class Meta: - model = User + model = Adherent fields = ('pseudo', 'password', 'surname', 'email') def __init__(self, *args, **kwargs): @@ -252,13 +252,13 @@ class MassArchiveForm(forms.Form): utilisateurs dont la fin d'accès se situe dans le futur !") -class BaseInfoForm(ModelForm): +class NewUserForm(ModelForm): """Formulaire de base d'edition d'un user. Formulaire de base, utilisé pour l'edition de self par self ou un cableur. On formate les champs avec des label plus jolis""" def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(BaseInfoForm, self).__init__(*args, prefix=prefix, **kwargs) + super(NewUserForm, self).__init__(*args, prefix=prefix, **kwargs) self.fields['name'].label = 'Prénom' self.fields['surname'].label = 'Nom' self.fields['school'].label = 'Établissement' @@ -292,13 +292,89 @@ class BaseInfoForm(ModelForm): return telephone +class NewClubForm(ModelForm): + """Formulaire de base d'edition d'un user. Formulaire de base, utilisé + pour l'edition de self par self ou un cableur. On formate les champs + avec des label plus jolis""" + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(NewClubForm, self).__init__(*args, prefix=prefix, **kwargs) + self.fields['surname'].label = 'Nom' + self.fields['school'].label = 'Établissement' + self.fields['comment'].label = 'Commentaire' + self.fields['room'].label = 'Chambre' + self.fields['room'].empty_label = "Pas de chambre" + self.fields['school'].empty_label = "Séléctionner un établissement" + + class Meta: + model = Club + fields = [ + 'surname', + 'pseudo', + 'email', + 'school', + 'comment', + 'room', + 'telephone', + ] + + def clean_telephone(self): + """Verifie que le tel est présent si 'option est validée + dans preferences""" + telephone = self.cleaned_data['telephone'] + preferences, _created = OptionalUser.objects.get_or_create() + if not telephone and preferences.is_tel_mandatory: + raise forms.ValidationError( + "Un numéro de téléphone valide est requis" + ) + return telephone + + + +class BaseInfoForm(ModelForm): + """Formulaire de base d'edition d'un user. Formulaire de base, utilisé + pour l'edition de self par self ou un cableur. On formate les champs + avec des label plus jolis""" + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(BaseInfoForm, self).__init__(*args, prefix=prefix, **kwargs) + self.fields['surname'].label = 'Nom' + self.fields['school'].label = 'Établissement' + self.fields['comment'].label = 'Commentaire' + self.fields['room'].label = 'Chambre' + self.fields['room'].empty_label = "Pas de chambre" + self.fields['school'].empty_label = "Séléctionner un établissement" + + class Meta: + model = User + fields = [ + 'surname', + 'pseudo', + 'email', + 'school', + 'comment', + 'room', + 'telephone', + ] + + def clean_telephone(self): + """Verifie que le tel est présent si 'option est validée + dans preferences""" + telephone = self.cleaned_data['telephone'] + preferences, _created = OptionalUser.objects.get_or_create() + if not telephone and preferences.is_tel_mandatory: + raise forms.ValidationError( + "Un numéro de téléphone valide est requis" + ) + return telephone + + class EditInfoForm(BaseInfoForm): """Edition complète d'un user. Utilisé par admin, permet d'editer normalement la chambre, ou le shell Herite de la base""" class Meta(BaseInfoForm.Meta): fields = [ - 'name', 'surname', 'pseudo', 'email', diff --git a/users/models.py b/users/models.py index 64b8fe03..777352db 100644 --- a/users/models.py +++ b/users/models.py @@ -149,7 +149,6 @@ class UserManager(BaseUserManager): def _create_user( self, pseudo, - name, surname, email, password=None, @@ -163,7 +162,6 @@ class UserManager(BaseUserManager): user = self.model( pseudo=pseudo, - name=name, surname=surname, email=self.normalize_email(email), ) @@ -174,19 +172,19 @@ class UserManager(BaseUserManager): user.make_admin() return user - def create_user(self, pseudo, name, surname, email, password=None): + def create_user(self, pseudo, surname, email, password=None): """ Creates and saves a User with the given pseudo, name, surname, email, and password. """ - return self._create_user(pseudo, name, surname, email, password, False) + return self._create_user(pseudo, surname, email, password, False) - def create_superuser(self, pseudo, name, surname, email, password): + def create_superuser(self, pseudo, surname, email, password): """ Creates and saves a superuser with the given pseudo, name, surname, email, and password. """ - return self._create_user(pseudo, name, surname, email, password, True) + return self._create_user(pseudo, surname, email, password, True) class User(AbstractBaseUser): @@ -246,10 +244,36 @@ class User(AbstractBaseUser): rezo_rez_uid = models.PositiveIntegerField(unique=True, blank=True, null=True) USERNAME_FIELD = 'pseudo' - REQUIRED_FIELDS = ['name', 'surname', 'email'] + REQUIRED_FIELDS = ['surname', 'email'] objects = UserManager() + @cached_property + def name(self): + """Si il s'agit d'un adhérent, on renvoie le prénom""" + if self.is_class_adherent: + return self.adherent.name + else: + return '' + + @cached_property + def class_name(self): + """Renvoie si il s'agit d'un adhérent ou d'un club""" + if hasattr(self, 'adherent'): + return "Adhérent" + elif hasattr(self, 'club'): + return "Club" + else: + raise NotImplementedError("Type inconnu") + + @cached_property + def is_class_club(self): + return hasattr(self, 'club') + + @cached_property + def is_class_adherent(self): + return hasattr(self, 'adherent') + @property def is_active(self): """ Renvoie si l'user est à l'état actif""" @@ -280,11 +304,15 @@ class User(AbstractBaseUser): def get_full_name(self): """ Renvoie le nom complet de l'user formaté nom/prénom""" - return '%s %s' % (self.name, self.surname) + name = self.name + if name: + return '%s %s' % (name, self.surname) + else: + return self.surname def get_short_name(self): """ Renvoie seulement le nom""" - return self.name + return self.surname def has_perms(self, perms, obj=None): """ Renvoie true si l'user dispose de la permission. @@ -570,7 +598,7 @@ class User(AbstractBaseUser): .objects.get_or_create() general_options, _created = GeneralOption.objects.get_or_create() context = Context({ - 'nom': str(self.name) + ' ' + str(self.surname), + 'nom': self.get_full_name(), 'asso_name': assooptions.name, 'asso_email': assooptions.contact, 'welcome_mail_fr': mailmessageoptions.welcome_mail_fr, @@ -599,7 +627,7 @@ class User(AbstractBaseUser): options, _created = AssoOption.objects.get_or_create() general_options, _created = GeneralOption.objects.get_or_create() context = { - 'name': str(req.user.name) + ' ' + str(req.user.surname), + 'name': req.user.get_full_name(), 'asso': options.name, 'asso_mail': options.contact, 'site_name': general_options.site_name, @@ -929,7 +957,7 @@ class Ban(models.Model): template = loader.get_template('users/email_ban_notif') options, _created = AssoOption.objects.get_or_create() context = Context({ - 'name': str(self.user.name) + ' ' + str(self.user.surname), + 'name': self.user.get_full_name(), 'raison': self.raison, 'date_end': self.date_end, 'asso_name': options.name, diff --git a/users/templates/users/aff_clubs.html b/users/templates/users/aff_clubs.html new file mode 100644 index 00000000..71ac1606 --- /dev/null +++ b/users/templates/users/aff_clubs.html @@ -0,0 +1,57 @@ +{% comment %} +Re2o est un logiciel d'administration développé initiallement au rezometz. Il +se veut agnostique au réseau considéré, de manière à être installable en +quelques clics. + +Copyright © 2017 Gabriel Détraz +Copyright © 2017 Goulven Kermarec +Copyright © 2017 Augustin Lemesle + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +{% endcomment %} + +{% if clubs_list.paginator %} +{% include "pagination.html" with list=clubs_list %} +{% endif %} + + + + + + + + + + + + + {% for club in clubs_list %} + + + + + + + + + {% endfor %} +
{% include "buttons/sort.html" with prefix='club' col="surname" text="Nom" %}{% include "buttons/sort.html" with prefix='club' col="pseudo" text="Pseudo" %}{% include "buttons/sort.html" with prefix='club' col="room" text="Chambre" %}Fin de cotisation leConnexionProfil
{{ club.surname }}{{ club.pseudo }}{{ club.room }}{% if club.is_adherent %}{{ club.end_adhesion }}{% else %}Non adhérent{% endif %}{% if club.has_access == True %} + Active + {% else %} + Désactivée + {% endif %} + +
+ diff --git a/users/templates/users/index_clubs.html b/users/templates/users/index_clubs.html new file mode 100644 index 00000000..4c3dd609 --- /dev/null +++ b/users/templates/users/index_clubs.html @@ -0,0 +1,37 @@ +{% extends "users/sidebar.html" %} +{% comment %} +Re2o est un logiciel d'administration développé initiallement au rezometz. Il +se veut agnostique au réseau considéré, de manière à être installable en +quelques clics. + +Copyright © 2017 Gabriel Détraz +Copyright © 2017 Goulven Kermarec +Copyright © 2017 Augustin Lemesle + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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 Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +{% endcomment %} + +{% load bootstrap3 %} + +{% block title %}Utilisateurs{% endblock %} + +{% block content %} +

Clubs

+ {% include "users/aff_clubs.html" with clubs_list=clubs_list %} +
+
+
+{% endblock %} + diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 267af0be..066248a1 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block title %}Profil{% endblock %} {% block content %} -

Adhérent

+

{{ user.class_name }}

diff --git a/users/templates/users/sidebar.html b/users/templates/users/sidebar.html index ef94c0a9..9a4312ff 100644 --- a/users/templates/users/sidebar.html +++ b/users/templates/users/sidebar.html @@ -30,11 +30,19 @@ with this program; if not, write to the Free Software Foundation, Inc., Créer un adhérent - - - Adhérents + + + Créer un club/association - + + + Adherents + + + + Clubs + + Bannissements diff --git a/users/urls.py b/users/urls.py index 201568cd..e351dcca 100644 --- a/users/urls.py +++ b/users/urls.py @@ -31,6 +31,7 @@ from . import views 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'^state/(?P[0-9]+)$', views.state, name='state'), url(r'^password/(?P[0-9]+)$', views.password, name='password'), @@ -118,5 +119,6 @@ urlpatterns = [ name='history' ), url(r'^$', views.index, name='index'), + url(r'^index_clubs/$', views.index_clubs, name='index-clubs'), url(r'^rest/mailing/$', views.mailing, name='mailing'), ] diff --git a/users/views.py b/users/views.py index bfe2ee59..57ae64df 100644 --- a/users/views.py +++ b/users/views.py @@ -58,7 +58,7 @@ from users.forms import DelRightForm, BanForm, WhitelistForm, DelSchoolForm from users.forms import DelListRightForm, NewListRightForm from users.forms import InfoForm, BaseInfoForm, StateForm from users.forms import RightForm, SchoolForm, EditServiceUserForm -from users.forms import ServiceUserForm, ListRightForm +from users.forms import ServiceUserForm, ListRightForm, NewUserForm, NewClubForm from users.forms import MassArchiveForm, PassForm, ResetPasswordForm from cotisations.models import Facture from machines.models import Machine @@ -85,7 +85,7 @@ def password_change_action(u_form, user, request, req=False): def new_user(request): """ Vue de création d'un nouvel utilisateur, envoie un mail pour le mot de passe""" - user = InfoForm(request.POST or None) + user = NewUserForm(request.POST or None) if user.is_valid(): user = user.save(commit=False) with transaction.atomic(), reversion.create_revision(): @@ -99,6 +99,25 @@ def new_user(request): return form({'userform': user}, 'users/user.html', request) +@login_required +@permission_required('cableur') +def new_club(request): + """ Vue de création d'un nouveau club, + envoie un mail pour le mot de passe""" + club = NewClubForm(request.POST or None) + if club.is_valid(): + club = club.save(commit=False) + with transaction.atomic(), reversion.create_revision(): + club.save() + reversion.set_user(request.user) + reversion.set_comment("Création") + club.reset_passwd_mail(request) + messages.success(request, "L'utilisateur %s a été crée, un mail\ + pour l'initialisation du mot de passe a été envoyé" % club.pseudo) + return redirect("/users/profil/" + str(club.id)) + return form({'userform': club}, 'users/user.html', request) + + @login_required def edit_info(request, userid): """ Edite un utilisateur à partir de son id, @@ -572,10 +591,10 @@ def mass_archive(request): @login_required @permission_required('cableur') def index(request): - """ Affiche l'ensemble des users, need droit cableur """ + """ Affiche l'ensemble des adherents, need droit cableur """ options, _created = GeneralOption.objects.get_or_create() pagination_number = options.pagination_number - users_list = User.objects.select_related('room') + users_list = Adherent.objects.select_related('room') users_list = SortTable.sort( users_list, request.GET.get('col'), @@ -595,6 +614,32 @@ def index(request): return render(request, 'users/index.html', {'users_list': users_list}) +@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') + clubs_list = SortTable.sort( + clubs_list, + request.GET.get('col'), + request.GET.get('order'), + SortTable.USERS_INDEX + ) + paginator = Paginator(clubs_list, pagination_number) + page = request.GET.get('page') + try: + clubs_list = paginator.page(page) + except PageNotAnInteger: + # If page is not an integer, deliver first page. + clubs_list = paginator.page(1) + except EmptyPage: + # If page is out of range (e.g. 9999), deliver last page of results. + clubs_list = paginator.page(paginator.num_pages) + return render(request, 'users/index_clubs.html', {'clubs_list': clubs_list}) + + @login_required @permission_required('cableur') def index_ban(request):