From fddbba99600bc14c0ea691dd76a0bb96f95fe8dd 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 | 40 ++++++++++++ wger/gym/templates/gym/member_list.html | 16 ++++- wger/gym/tests/test_add_existing_user.py | 56 +++++++++++++++++ wger/gym/tests/test_user.py | 5 +- wger/gym/urls.py | 3 + wger/gym/views/gym.py | 80 ++++++++++++++++++++++-- 6 files changed, 191 insertions(+), 9 deletions(-) create mode 100644 wger/gym/tests/test_add_existing_user.py diff --git a/wger/gym/forms.py b/wger/gym/forms.py index ce5a90c6..17d91f08 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,41 @@ 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 = User._default_manager.get(username=username) + except User.DoesNotExist: + raise forms.ValidationError( + _("Username does not exists.")) + + if user.userprofile.gym_id is not None: + 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_add_existing_user.py b/wger/gym/tests/test_add_existing_user.py new file mode 100644 index 00000000..885aef92 --- /dev/null +++ b/wger/gym/tests/test_add_existing_user.py @@ -0,0 +1,56 @@ +from django.core.urlresolvers import reverse +from wger.core.tests.base_testcase import WorkoutManagerTestCase +from wger.gym.models import GymAdminConfig +from django.contrib.auth.models import User + + +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() + + 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': str(role)}) + + user = GymAdminConfig.objects.first() + if not fail: + user_pk = user.user_id 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': str(role)}) + + if fail: + self.assertEqual(response.status_code, 403) + else: + self.assertEqual(response.status_code, 302) + + 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) diff --git a/wger/gym/tests/test_user.py b/wger/gym/tests/test_user.py index 251b5e08..8fa991e7 100644 --- a/wger/gym/tests/test_user.py +++ b/wger/gym/tests/test_user.py @@ -22,7 +22,10 @@ from wger.core.models import UserProfile from wger.core.tests.base_testcase import WorkoutManagerTestCase from wger.gym.models import Gym -from wger.gym.models import GymAdminConfig +from wger.gym.models import ( + GymAdminConfig, + GymUserConfig +) class GymAddUserTestCase(WorkoutManagerTestCase): 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..2ed820eb 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,69 @@ 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 + ''' + permissions = ['gym_member', 'gym_trainer', 'gym_manager', 'general_gym_manager'] + 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() + + # Remove all previously set permissions + for perm in permissions: + user.groups.remove(Group.objects.get(name=str(perm))) + + # 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(GymAddExistingUserView, 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): '''