From df976db5c9a8ce47689c4813db9111362063da41 Mon Sep 17 00:00:00 2001 From: odurjoseph Date: Tue, 14 Aug 2018 19:34:17 +0300 Subject: [PATCH] [Feature #159687667] Enable manager to delete from a gym and add already existing users to the gym. The following changes have been made: * Modified the UI to enable addition of already existing users to the gym. * Enabled deletion of users from the gym only but not the entire app. * Created form to verify if the user already exists. * added tests. [Delivers #159687667] --- wger/gym/forms.py | 46 +++++++++++++++ wger/gym/templates/gym/member_list.html | 16 +++++- wger/gym/tests/test_user.py | 68 ++++++++++++++++++++++ wger/gym/urls.py | 3 + wger/gym/views/gym.py | 75 +++++++++++++++++++++++-- 5 files changed, 200 insertions(+), 8 deletions(-) diff --git a/wger/gym/forms.py b/wger/gym/forms.py index ce5a90c6..66c1c611 100644 --- a/wger/gym/forms.py +++ b/wger/gym/forms.py @@ -20,6 +20,8 @@ from wger.core.forms import UserPersonalInformationForm from wger.utils.widgets import BootstrapSelectMultiple +from wger.gym.models import GymAdminConfig +from django.db.utils import IntegrityError class GymUserPermisssionForm(forms.ModelForm): @@ -91,3 +93,47 @@ def clean_username(self): return username raise forms.ValidationError( _("A user with that username already exists.")) + + +class GymAddExistingUserForm(GymUserPermisssionForm): + ''' + Form used when adding a user to a gym + ''' + + class Meta: + model = GymAdminConfig + widgets = {'role': BootstrapSelectMultiple()} + fields = ('username', 'role',) + + username = forms.RegexField(label=_("Username"), + max_length=30, + regex=r'^[\w.@+-]+$', + help_text=_("Required. 30 characters or fewer. Letters, digits and " + "@/./+/-/_ only."), + error_messages={ + 'invalid': _("This value may contain only letters, numbers and " + "@/.//-/_ characters.")}) + + def clean_username(self): + ''' + Since User.username is unique, this check is redundant, + but it sets a nicer error message than the ORM. See #13147. + ''' + username = self.cleaned_data["username"] + try: + User._default_manager.get(username=username) + except User.DoesNotExist: + raise forms.ValidationError( + _("Username does not exists.")) + + try: + user = User._default_manager.get(username=username) + + if user.userprofile.gym_id is not None: + raise forms.ValidationError( + _(str(username) + " already belongs to a gym.")) + except (TypeError, IntegrityError): + raise forms.ValidationError( + _(str(username) + " already belongs to a gym.")) + + return username diff --git a/wger/gym/templates/gym/member_list.html b/wger/gym/templates/gym/member_list.html index 4eac16a4..9ea571bf 100644 --- a/wger/gym/templates/gym/member_list.html +++ b/wger/gym/templates/gym/member_list.html @@ -428,8 +428,18 @@

{% trans "Emails" %}

{# #} {% block options %} {% if perms.gym.manage_gym or perms.gym.manage_gyms %} - - {% trans "Add member" %} - +
+ + +
{% endif %} {% endblock %} diff --git a/wger/gym/tests/test_user.py b/wger/gym/tests/test_user.py index 251b5e08..729d5db7 100644 --- a/wger/gym/tests/test_user.py +++ b/wger/gym/tests/test_user.py @@ -142,6 +142,74 @@ def test_new_user_data_export(self): self.new_user_data_export(fail=True) +class GymAddExistingUserTestCase(WorkoutManagerTestCase): + ''' + Tests admin adding users to gyms + ''' + def add_existing_user(self, fail=False, logged_in=True, role='admin'): + ''' + Helper function to add users + ''' + GymAdminConfig.objects.all().delete() + count_after = User.objects.all().count() + + if role == 'admin': + self.client.post(reverse('gym:gym:add-user', kwargs={'gym_pk': 1}), + {'first_name': 'Cletus', + 'last_name': 'Spuckle', + 'username': 'cletus', + 'email': 'cletus@spuckle-megacorp.com', + 'role': 'admin'}) + else: + self.client.post(reverse('gym:gym:add-user', kwargs={'gym_pk': 1}), + {'first_name': 'Cletus', + 'last_name': 'Spuckle', + 'username': 'cletus', + 'email': 'cletus@spuckle-megacorp.com', + 'role': ''}) + + user = GymAdminConfig.objects.first() + + user_pk = user.pk if user else 4 + self.client.post( + reverse('gym:gym:delete-user', kwargs={'user_pk': user_pk})) + + response = self.client.post(reverse('gym:gym:add-user-existing', kwargs={'gym_pk': 1}), + {'username': 'cletus', 'role': 'admin'}) + + count_before = User.objects.all().count() + # self.assertEqual(GymAdminConfig.objects.all().count(), 1) + + if fail: + self.assertEqual(response.status_code, 403) + self.assertEqual(count_before, count_after) + self.assertFalse(self.client.session.get('gym.user')) + else: + self.assertEqual(response.status_code, 200) + self.assertTrue(self.client.session['gym.user']['user_pk'], 3) + self.assertTrue(self.client.session['gym.user']['password']) + + def test_delete_user_authorized(self): + """ + Tests deleting a user an authorized user + """ + self.user_login('admin') + self.add_existing_user() + + def test_delete_user_unauthorized(self): + """ + Tests deleting a user an unauthorized user + """ + self.user_login('test') + self.add_existing_user(fail=True) + + def test_delete_user_not_logged_in(self): + """ + Tests deleting a user an unauthorized user + """ + self.add_existing_user(fail=True, logged_in=False) + + class TrainerLoginTestCase(WorkoutManagerTestCase): ''' Tests the trainer login view (switching to user ID) diff --git a/wger/gym/urls.py b/wger/gym/urls.py index 2eb31c6e..59584d69 100644 --- a/wger/gym/urls.py +++ b/wger/gym/urls.py @@ -51,6 +51,9 @@ url(r'^(?P\d+)/add-member$', gym.GymAddUserView.as_view(), name='add-user'), + url(r'^(?P\d+)/add-member-existing$', + gym.GymAddExistingUserView.as_view(), + name='add-user-existing'), url(r'^add$', gym.GymAddView.as_view(), name='add'), diff --git a/wger/gym/views/gym.py b/wger/gym/views/gym.py index 7807bf25..87f73749 100644 --- a/wger/gym/views/gym.py +++ b/wger/gym/views/gym.py @@ -39,7 +39,11 @@ UpdateView ) -from wger.gym.forms import GymUserAddForm, GymUserPermisssionForm +from wger.gym.forms import ( + GymUserAddForm, + GymAddExistingUserForm, + GymUserPermisssionForm +) from wger.gym.helpers import ( get_user_last_activity, is_any_gym_admin, @@ -179,12 +183,15 @@ def delete_user(request, user_pk): user_matched = GymUserConfig.objects.filter(user_id=member.id).first() or \ GymAdminConfig.objects.filter(user_id=member.id).first() + gym_id = user_matched.gym_id if user_matched: member = User.objects.filter(pk=user_pk).first() - member.delete() + member.userprofile.gym_id = None + member.userprofile.save() + user_matched.delete() - return HttpResponseRedirect(reverse("gym:gym:user-list", kwargs={'pk': user_matched.gym_id})) + return HttpResponseRedirect(reverse("gym:gym:user-list", kwargs={'pk': gym_id})) @login_required() @@ -359,8 +366,8 @@ class GymAddUserView(WgerFormMixin, View to add a user to a new gym ''' - model = User - title = ugettext_lazy('Add user to gym') + model = GymAdminConfig + title = ugettext_lazy('Add new user to gym') success_url = reverse_lazy('gym:gym:new-user-data') permission_required = ('gym.manage_gym', 'gym.manage_gyms') form_class = GymUserAddForm @@ -450,6 +457,64 @@ def get_context_data(self, **kwargs): return context +class GymAddExistingUserView(GymAddUserView): + ''' + View to add a user to a new gym + ''' + + model = GymAdminConfig + title = ugettext_lazy('Add existing user to gym') + success_url = reverse_lazy('gym:gym:new-user-data') + permission_required = ('gym.manage_gym', 'gym.manage_gyms') + form_class = GymAddExistingUserForm + + def form_valid(self, form): + ''' + Create the user, set the user permissions and gym + ''' + gym = Gym.objects.get(pk=self.kwargs['gym_pk']) + user = User.objects.filter(username=form.cleaned_data['username']).first() + form.instance = user + + # Update profile + user.userprofile.gym = gym + user.userprofile.save() + + # Set appropriate permission groups + if 'user' in form.cleaned_data['role']: + user.groups.add(Group.objects.get(name='gym_member')) + if 'trainer' in form.cleaned_data['role']: + user.groups.add(Group.objects.get(name='gym_trainer')) + if 'admin' in form.cleaned_data['role']: + user.groups.add(Group.objects.get(name='gym_manager')) + if 'manager' in form.cleaned_data['role']: + user.groups.add(Group.objects.get(name='general_gym_manager')) + + self.request.session['gym.user'] = {'user_pk': user.pk, + 'password': '-/-'} + + # Create config + if is_any_gym_admin(user): + config = GymAdminConfig() + else: + config = GymUserConfig() + + config.user = user + config.gym = gym + config.save() + + return super(GymAddUserView, self).form_valid(form) + + def get_context_data(self, **kwargs): + ''' + Send some additional data to the template + ''' + context = super(GymAddUserView, self).get_context_data(**kwargs) + context['form_action'] = reverse('gym:gym:add-user-existing', + kwargs={'gym_pk': self.kwargs['gym_pk']}) + return context + + class GymUpdateView(WgerFormMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView): '''