diff --git a/pydotorg/settings/base.py b/pydotorg/settings/base.py index cc48d6b12..3f067eda5 100644 --- a/pydotorg/settings/base.py +++ b/pydotorg/settings/base.py @@ -144,6 +144,7 @@ 'django.contrib.staticfiles', 'django.contrib.admin', 'django.contrib.admindocs', + 'django.contrib.humanize', 'pipeline', 'sitetree', diff --git a/sponsors/admin.py b/sponsors/admin.py index 93b07da77..094423dc7 100644 --- a/sponsors/admin.py +++ b/sponsors/admin.py @@ -75,6 +75,7 @@ class SponsorshipPackageAdmin(OrderedModelAdmin): class SponsorContactInline(admin.TabularInline): model = SponsorContact + raw_id_fields = ["user"] extra = 0 diff --git a/sponsors/tests/test_views.py b/sponsors/tests/test_views.py index 5d8a68529..797ccb467 100644 --- a/sponsors/tests/test_views.py +++ b/sponsors/tests/test_views.py @@ -3,6 +3,7 @@ from itertools import chain from django.contrib import messages +from django.contrib.auth.models import Group from django.conf import settings from django.urls import reverse, reverse_lazy from django.test import TestCase @@ -43,6 +44,9 @@ def setUp(self): self.user = baker.make(settings.AUTH_USER_MODEL, is_staff=True, is_active=True) self.client.force_login(self.user) + self.group = Group(name="Sponsorship Preview") + self.group.save() + def test_display_template_with_form_and_context(self): psf_package = baker.make("sponsors.SponsorshipPackage") extra_package = baker.make("sponsors.SponsorshipPackage") @@ -73,7 +77,7 @@ def test_login_required(self): self.assertRedirects(r, redirect_url) - def test_staff_required(self): + def test_not_staff_no_group_not_allowed(self): redirect_url = f"{settings.LOGIN_URL}?next={self.url}" self.user.is_staff = False self.user.save() @@ -83,6 +87,16 @@ def test_staff_required(self): self.assertRedirects(r, redirect_url, fetch_redirect_response=False) + def test_group_allowed(self): + redirect_url = f"{settings.LOGIN_URL}?next={self.url}" + self.user.groups.add(self.group) + self.user.save() + self.client.force_login(self.user) + + r = self.client.get(self.url) + + self.assertEqual(r.status_code, 200, "user in group should have access") + def test_valid_post_redirect_user_to_next_form_step_and_save_info_in_cookies(self): package = baker.make("sponsors.SponsorshipPackage") for benefit in self.program_1_benefits: @@ -112,6 +126,20 @@ def test_populate_form_initial_with_values_from_cookie(self): self.assertEqual(initial, r.context["form"].initial) + def test_capacity_flag(self): + psf_package = baker.make("sponsors.SponsorshipPackage") + r = self.client.get(self.url) + self.assertEqual(False, r.context["capacities_met"]) + + def test_capacity_flag_when_needed(self): + at_capacity_benefit = baker.make( + SponsorshipBenefit, program=self.psf, capacity=0, soft_capacity=False + ) + psf_package = baker.make("sponsors.SponsorshipPackage") + + r = self.client.get(self.url) + self.assertEqual(True, r.context["capacities_met"]) + class NewSponsorshipApplicationViewTests(TestCase): url = reverse_lazy("new_sponsorship_application") diff --git a/sponsors/views.py b/sponsors/views.py index 071aefc08..1d4438492 100644 --- a/sponsors/views.py +++ b/sponsors/views.py @@ -3,7 +3,7 @@ from django.conf import settings from django.contrib import messages from django.contrib.auth.decorators import login_required -from django.contrib.admin.views.decorators import staff_member_required +from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.db import transaction from django.utils.decorators import method_decorator from django.http import JsonResponse @@ -11,22 +11,51 @@ from django.urls import reverse_lazy, reverse from django.shortcuts import redirect -from .models import Sponsor, SponsorshipBenefit, SponsorshipPackage, Sponsorship +from .models import ( + Sponsor, + SponsorshipBenefit, + SponsorshipPackage, + SponsorshipProgram, + Sponsorship, +) from sponsors.forms import SponsorshiptBenefitsForm, SponsorshipApplicationForm from sponsors import cookies -@method_decorator(staff_member_required(login_url=settings.LOGIN_URL), name="dispatch") -class SelectSponsorshipApplicationBenefitsView(FormView): +class SelectSponsorshipApplicationBenefitsView(UserPassesTestMixin, FormView): form_class = SponsorshiptBenefitsForm template_name = "sponsors/sponsorship_benefits_form.html" + # TODO: Remove UserPassesTestMixin when launched, also remove following methods + def test_func(self): + return ( + self.request.user.is_staff + or self.request.user.groups.filter(name="Sponsorship Preview").exists() + ) + + def permission_denied_message(self): + msg = "New Sponsorship Application is not yet generally available, check back soon!" + messages.add_message(self.request, messages.INFO, msg) + return msg + + # END TODO + def get_context_data(self, *args, **kwargs): + programs = SponsorshipProgram.objects.all() + packages = SponsorshipPackage.objects.all() + benefits_qs = SponsorshipBenefit.objects.select_related("program") + capacities_met = any( + [ + any([not b.has_capacity for b in benefits_qs.filter(program=p)]) + for p in programs + ] + ) kwargs.update( { "benefit_model": SponsorshipBenefit, - "sponsorship_packages": SponsorshipPackage.objects.all(), + "sponsorship_packages": packages, + "capacities_met": capacities_met, } ) return super().get_context_data(*args, **kwargs) @@ -35,7 +64,7 @@ def get_success_url(self): if self.request.user.is_authenticated: return reverse_lazy("new_sponsorship_application") else: - # TODO unit test this scenario after removing staff_member_required decortor + # TODO unit test this scenario after removing UserPassesTestMixin return settings.LOGIN_URL def get_initial(self): diff --git a/static/js/sponsors/applicationForm.js b/static/js/sponsors/applicationForm.js index cadc43ada..1944e2d68 100644 --- a/static/js/sponsors/applicationForm.js +++ b/static/js/sponsors/applicationForm.js @@ -6,7 +6,11 @@ $(document).ready(function(){ $("#clear_form_btn").click(function(){ $("#application_form").trigger("reset"); $("#application_form [class=active]").removeClass("active"); - $("#cost_label").html("Select a package or customize the benefits"); + $("input[name=package]").prop("checked", false); + checkboxesContainer.find(':checkbox').each(function(){ + $(this).prop('checked', false); + }); + $("#cost_label").html(""); }); $("input[name=package]").change(function(){ @@ -34,19 +38,21 @@ $(document).ready(function(){ let data = $("form").serialize(); let cost = packageInfo.attr("data-cost"); - costLabel.html('Sponsorship cost is $' + cost + '.00') + costLabel.html('Sponsorship cost is $' + cost.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + ' USD') }); $("input[id^=id_benefits_]").change(function(){ let benefit = this.value; if (benefit.length == 0) return; - if (costLabel.html() != "Updating cost...") costLabel.html("Submit your application and we'll get in touch..."); + if (costLabel.html() != "Updating cost...") costLabel.html("Please submit your customized sponsorship package application and we'll contact you within 2 business days."); let active = checkboxesContainer.find('[value=' + benefit + ']').prop("checked"); if (!active) { let packageOnlyBenefit = $(this).attr("package_only"); if (packageOnlyBenefit) $(this).attr("disabled", true); return; + } else { + $('label[benefit_id=' + benefit + ']').addClass("active"); } diff --git a/templates/sponsors/new_sponsorship_application_form.html b/templates/sponsors/new_sponsorship_application_form.html index 438a32356..5c174020a 100644 --- a/templates/sponsors/new_sponsorship_application_form.html +++ b/templates/sponsors/new_sponsorship_application_form.html @@ -1,5 +1,6 @@ {% extends "psf/full-width.html" %} {% load boxes widget_tweaks %} +{% load humanize %} {% block page_title %}Submit Sponsorship Information{% endblock %} @@ -9,18 +10,19 @@
We need more information to proceed with your sponsorship application. +
{% if sponsorship_package %} - You selected the {{ sponsorship_package.name }} package {% if sponsorship_price %}costing ${{ sponsorship_price }}.00 {% endif %}and the following benefits: + You selected the {{ sponsorship_package.name }} package {% if sponsorship_price %}costing ${{ sponsorship_price|intcomma }} USD {% endif %}and the following benefits: {% else %} You selected the following benefits: {% endif %}
{{ benefit.name }}
+Please complete the form below.
| Back to select benefits