diff --git a/opentech/apply/activity/templatetags/activity_tags.py b/opentech/apply/activity/templatetags/activity_tags.py index 1f43a872e263ae8585d4b1a0c4cf39cb1626868e..627b45bbe0d42807bba353873d09fd958fdbc2d4 100644 --- a/opentech/apply/activity/templatetags/activity_tags.py +++ b/opentech/apply/activity/templatetags/activity_tags.py @@ -12,7 +12,7 @@ register = template.Library() @register.filter def display_author(activity, user): - if user.is_applicant and isinstance(activity.related_object, Review): + if activity.submission.user == user and isinstance(activity.related_object, Review): return 'Reviewer' return activity.user diff --git a/opentech/apply/dashboard/tests/test_views.py b/opentech/apply/dashboard/tests/test_views.py index 54aba6a63a4a97204163699093cb308ddfde4440..eb3892c58c22ce5d71845e20ee5147eb889a16c7 100644 --- a/opentech/apply/dashboard/tests/test_views.py +++ b/opentech/apply/dashboard/tests/test_views.py @@ -4,12 +4,12 @@ from opentech.apply.funds.tests.factories import ( InvitedToProposalFactory, ) from opentech.apply.review.tests.factories import ReviewFactory, ReviewOpinionFactory -from opentech.apply.users.tests.factories import UserFactory, ReviewerFactory, StaffFactory +from opentech.apply.users.tests.factories import ApplicantFactory, ReviewerFactory, StaffFactory from opentech.apply.utils.testing.tests import BaseViewTestCase class TestApplicantDashboard(BaseViewTestCase): - user_factory = UserFactory + user_factory = ApplicantFactory url_name = 'dashboard:{}' base_view_name = 'dashboard' diff --git a/opentech/apply/funds/templates/funds/applicationsubmission_detail.html b/opentech/apply/funds/templates/funds/applicationsubmission_detail.html index 0326bfbdfdcf20dceb7f9319c19c33d666d53e12..f8914d7bf67b0a7be99be705b423f9d1ddcdc34e 100644 --- a/opentech/apply/funds/templates/funds/applicationsubmission_detail.html +++ b/opentech/apply/funds/templates/funds/applicationsubmission_detail.html @@ -25,7 +25,7 @@ <span>Lead: {{ object.lead }}</span> {% endif %} </h5> - {% status_bar object.workflow object.phase request.user same_stage=True%} + {% status_bar object.workflow object.phase request.user author=object.user same_stage=True %} <div class="tabs js-tabs"> <div class="tabs__container"> diff --git a/opentech/apply/funds/templatetags/statusbar_tags.py b/opentech/apply/funds/templatetags/statusbar_tags.py index 99e4415b3d76af01871bc3fb85da32f257de8b9a..8b48c8b590dd0e298997c78ff1cfd7030d3bbd14 100644 --- a/opentech/apply/funds/templatetags/statusbar_tags.py +++ b/opentech/apply/funds/templatetags/statusbar_tags.py @@ -4,11 +4,12 @@ register = template.Library() @register.inclusion_tag('funds/includes/status_bar.html') -def status_bar(workflow, current_phase, user, css_class='', same_stage=False): +def status_bar(workflow, current_phase, user, author=False, css_class='', same_stage=False): phases = workflow.phases_for(user) + is_applicant = user == author if author else user.is_applicant - if same_stage and not user.is_applicant: + if same_stage and not is_applicant: phases = [ phase for phase in phases if phase.stage == current_phase.stage diff --git a/opentech/apply/funds/tests/factories/models.py b/opentech/apply/funds/tests/factories/models.py index 5aca57fa69bc98e232e65ac850f3368579c60f1e..d31bd9bf205111216535b3bd09abd920281bd70c 100644 --- a/opentech/apply/funds/tests/factories/models.py +++ b/opentech/apply/funds/tests/factories/models.py @@ -28,7 +28,7 @@ from opentech.apply.funds.workflow import ConceptProposal, Request from opentech.apply.home.factories import ApplyHomePageFactory from opentech.apply.stream_forms.testing.factories import FormDataFactory from opentech.apply.users.groups import STAFF_GROUP_NAME, REVIEWER_GROUP_NAME -from opentech.apply.users.tests.factories import StaffFactory, UserFactory, GroupFactory +from opentech.apply.users.tests.factories import StaffFactory, ApplicantFactory, GroupFactory from . import blocks @@ -241,7 +241,7 @@ class ApplicationSubmissionFactory(factory.DjangoModelFactory): workflow_name=factory.SelfAttribute('..workflow_name'), lead=factory.SelfAttribute('..lead'), ) - user = factory.SubFactory(UserFactory) + user = factory.SubFactory(ApplicantFactory) lead = factory.SubFactory(StaffFactory) live_revision = None draft_revision = None diff --git a/opentech/apply/funds/tests/test_views.py b/opentech/apply/funds/tests/test_views.py index 41c17d983419f59cf29ac387d5680ac7e079c1a6..0ffbffe04da14593fbfc5c086703c9ee19fc300a 100644 --- a/opentech/apply/funds/tests/test_views.py +++ b/opentech/apply/funds/tests/test_views.py @@ -23,7 +23,7 @@ from opentech.apply.users.tests.factories import ( ReviewerFactory, StaffFactory, SuperUserFactory, - UserFactory, + ApplicantFactory, ) from opentech.apply.utils.testing import make_request from opentech.apply.utils.testing.tests import BaseViewTestCase @@ -326,7 +326,7 @@ class TestReviewersUpdateView(BaseSubmissionViewTestCase): class TestApplicantSubmissionView(BaseSubmissionViewTestCase): - user_factory = UserFactory + user_factory = ApplicantFactory @classmethod def setUpTestData(cls): @@ -420,7 +420,7 @@ class TestApplicantSubmissionView(BaseSubmissionViewTestCase): class TestRevisionsView(BaseSubmissionViewTestCase): - user_factory = UserFactory + user_factory = ApplicantFactory def test_create_revisions_on_submit(self): submission = ApplicationSubmissionFactory(status='draft_proposal', workflow_stages=2, user=self.user) diff --git a/opentech/apply/funds/views.py b/opentech/apply/funds/views.py index 0b05d75483ffeb19edfad866c72088a80c657f07..4d20af6f23161d026f26d1d76473a1c1d7da49d7 100644 --- a/opentech/apply/funds/views.py +++ b/opentech/apply/funds/views.py @@ -749,10 +749,8 @@ class PartnerSubmissionEditView(ApplicantSubmissionEditView): submission = self.get_object() # If the requesting user submitted the application, return the Applicant view. # Partners may somtimes be appliants as well. - if submission.user == request.user: - return ApplicantSubmissionEditView.as_view()(request, *args, **kwargs) partner_has_access = submission.partners.filter(pk=request.user.pk).exists() - if not partner_has_access: + if not partner_has_access and submission.user != request.user: raise PermissionDenied return super(ApplicantSubmissionEditView, self).dispatch(request, *args, **kwargs) @@ -760,6 +758,7 @@ class PartnerSubmissionEditView(ApplicantSubmissionEditView): class SubmissionEditView(ViewDispatcher): admin_view = AdminSubmissionEditView applicant_view = ApplicantSubmissionEditView + reviewer_view = ApplicantSubmissionEditView partner_view = PartnerSubmissionEditView diff --git a/opentech/apply/users/groups.py b/opentech/apply/users/groups.py index 31887461c21ed6b096231790b4d80c21d0c7aad1..cc9cad3806bf3e4bf2dd4a435da1170dcbb10c0a 100644 --- a/opentech/apply/users/groups.py +++ b/opentech/apply/users/groups.py @@ -1,3 +1,4 @@ +APPLICANT_GROUP_NAME = 'Applicant' STAFF_GROUP_NAME = 'Staff' REVIEWER_GROUP_NAME = 'Reviewer' TEAMADMIN_GROUP_NAME = 'Team Admin' @@ -6,13 +7,17 @@ COMMUNITY_REVIEWER_GROUP_NAME = 'Community reviewer' GROUPS = [ { - 'name': REVIEWER_GROUP_NAME, + 'name': APPLICANT_GROUP_NAME, 'permissions': [], }, { 'name': STAFF_GROUP_NAME, 'permissions': [], }, + { + 'name': REVIEWER_GROUP_NAME, + 'permissions': [], + }, { 'name': TEAMADMIN_GROUP_NAME, 'permissions': [], diff --git a/opentech/apply/users/migrations/0011_add_applicant_group.py b/opentech/apply/users/migrations/0011_add_applicant_group.py new file mode 100644 index 0000000000000000000000000000000000000000..127b739aed4c749ba03ee4a5f181290522964b46 --- /dev/null +++ b/opentech/apply/users/migrations/0011_add_applicant_group.py @@ -0,0 +1,42 @@ + +# Generated by Django 2.0.9 on 2018-12-19 13:21 +from __future__ import unicode_literals + +from django.core.exceptions import ObjectDoesNotExist +from django.core.management.sql import emit_post_migrate_signal +from django.db import migrations + +from opentech.apply.users.groups import GROUPS, APPLICANT_GROUP_NAME + + +def add_groups(apps, schema_editor): + # Workaround for https://code.djangoproject.com/ticket/23422 + db_alias = schema_editor.connection.alias + emit_post_migrate_signal(2, False, db_alias) + + Group = apps.get_model('auth.Group') + Permission = apps.get_model('auth.Permission') + + for group_data in GROUPS: + group, created = Group.objects.get_or_create(name=group_data['name']) + for permission in group_data['permissions']: + try: + group.permissions.add(Permission.objects.get(codename=permission)) + except ObjectDoesNotExist: + print("Could not find the '%s' permission" % permission) + + +def remove_groups(apps, schema_editor): + Group = apps.get_model('auth.Group') + Group.objects.filter(name__in=[APPLICANT_GROUP_NAME]).delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0010_add_community_reviewer_group'), + ] + + operations = [ + migrations.RunPython(add_groups, remove_groups) + ] diff --git a/opentech/apply/users/migrations/0012_set_applicant_group.py b/opentech/apply/users/migrations/0012_set_applicant_group.py new file mode 100644 index 0000000000000000000000000000000000000000..0c215af4c46d66e84c249a354516a915825dd3f2 --- /dev/null +++ b/opentech/apply/users/migrations/0012_set_applicant_group.py @@ -0,0 +1,39 @@ + +# Generated by Django 2.0.9 on 2018-12-19 13:21 +from __future__ import unicode_literals + +from django.contrib.auth import get_user_model +from django.contrib.auth.models import Group +from django.db import migrations + +from opentech.apply.users.groups import APPLICANT_GROUP_NAME + + +def set_group(apps, schema_editor): + User = get_user_model() + applicant_group = Group.objects.get(name=APPLICANT_GROUP_NAME) + applicants = User.objects.exclude(applicationsubmission=None) + for user in applicants: + if not user.is_apply_staff: + user.groups.add(applicant_group) + user.save() + + +def unset_group(apps, schema_editor): + User = get_user_model() + applicant_group = Group.objects.get(name=APPLICANT_GROUP_NAME) + applicants = User.objects.filter(groups__name=APPLICANT_GROUP_NAME).all() + for user in applicants: + user.groups.remove(applicant_group) + user.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0011_add_applicant_group'), + ] + + operations = [ + migrations.RunPython(set_group, unset_group) + ] diff --git a/opentech/apply/users/models.py b/opentech/apply/users/models.py index 458f803ea4cc510d50274daf45c33a7a85863e05..9609eb97e1ebfdf0d7b2b5b866f828088fba333b 100644 --- a/opentech/apply/users/models.py +++ b/opentech/apply/users/models.py @@ -1,12 +1,11 @@ from django.db import models from django.db.models import Q from django.contrib.auth.hashers import make_password -from django.contrib.auth.models import AbstractUser, BaseUserManager +from django.contrib.auth.models import AbstractUser, BaseUserManager, Group from django.urls import reverse from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ - -from .groups import REVIEWER_GROUP_NAME, STAFF_GROUP_NAME, PARTNER_GROUP_NAME, COMMUNITY_REVIEWER_GROUP_NAME +from .groups import APPLICANT_GROUP_NAME, REVIEWER_GROUP_NAME, STAFF_GROUP_NAME, PARTNER_GROUP_NAME, COMMUNITY_REVIEWER_GROUP_NAME from .utils import send_activation_email @@ -25,6 +24,9 @@ class UserQuerySet(models.QuerySet): def community_reviewers(self): return self.filter(groups__name=COMMUNITY_REVIEWER_GROUP_NAME) + def applicants(self): + return self.filter(groups__name=APPLICANT_GROUP_NAME) + class UserManager(BaseUserManager.from_queryset(UserQuerySet)): use_in_migrations = True @@ -65,6 +67,9 @@ class UserManager(BaseUserManager.from_queryset(UserQuerySet)): user, created = self.get_or_create(defaults=defaults, **kwargs) if created: send_activation_email(user, site) + applicant_group = Group.objects.get(name=APPLICANT_GROUP_NAME) + user.groups.add(applicant_group) + user.save() return user, created @@ -125,7 +130,7 @@ class User(AbstractUser): @cached_property def is_applicant(self): - return not self.is_apply_staff and not self.is_reviewer and not self.is_partner + return self.groups.filter(name=APPLICANT_GROUP_NAME).exists() class Meta: ordering = ('full_name', 'email') diff --git a/opentech/apply/users/tests/factories.py b/opentech/apply/users/tests/factories.py index b111108c871b44f89780a9472a3c68b3c3d2dedd..8db74b40f779c61361dc8f2b05e6805015b70cb5 100644 --- a/opentech/apply/users/tests/factories.py +++ b/opentech/apply/users/tests/factories.py @@ -3,7 +3,7 @@ from django.contrib.auth.models import Group import factory -from ..groups import REVIEWER_GROUP_NAME, STAFF_GROUP_NAME +from ..groups import APPLICANT_GROUP_NAME, REVIEWER_GROUP_NAME, STAFF_GROUP_NAME class GroupFactory(factory.DjangoModelFactory): @@ -66,3 +66,10 @@ class ReviewerFactory(UserFactory): def groups(self, create, extracted, **kwargs): if create: self.groups.add(GroupFactory(name=REVIEWER_GROUP_NAME)) + + +class ApplicantFactory(UserFactory): + @factory.post_generation + def groups(self, create, extracted, **kwargs): + if create: + self.groups.add(GroupFactory(name=APPLICANT_GROUP_NAME))