From e639971fa9aeac8787f4127d0f3cbf90e21465ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastiaan=20Versteeg?= Date: Mon, 22 Apr 2019 15:13:52 +0200 Subject: [PATCH 1/2] Add benefactors registration form --- .../static/admin/payments/js/payments.js | 4 +- website/registrations/README.rst | 25 +- website/registrations/admin.py | 17 +- website/registrations/emails.py | 102 ++- website/registrations/forms.py | 54 +- .../locale/nl/LC_MESSAGES/django.mo | Bin 22368 -> 24782 bytes .../locale/nl/LC_MESSAGES/django.po | 225 +++++-- .../migrations/0019_auto_20190425_1107.py | 37 ++ website/registrations/models.py | 69 ++- website/registrations/services.py | 37 +- .../static/registrations/css/style.scss | 9 + .../static/registrations/js/main.js | 23 + .../registrations/become_a_member.html | 68 +- .../email/references_information.txt | 20 + .../templates/registrations/reference.html | 42 ++ .../registrations/reference_success.html | 39 ++ .../registrations/register_benefactor.html | 89 +++ .../registrations/register_member.html | 71 +-- .../templates/registrations/renewal.html | 52 +- website/registrations/tests/test_admin.py | 16 +- website/registrations/tests/test_emails.py | 74 ++- website/registrations/tests/test_forms.py | 46 +- website/registrations/tests/test_models.py | 145 +++-- website/registrations/tests/test_services.py | 6 + website/registrations/tests/test_views.py | 581 +++++++++++++++--- website/registrations/urls.py | 12 +- website/registrations/views.py | 124 +++- website/thaliawebsite/settings/settings.py | 4 + website/thaliawebsite/static/css/main.scss | 1 + 29 files changed, 1589 insertions(+), 403 deletions(-) create mode 100644 website/registrations/migrations/0019_auto_20190425_1107.py create mode 100644 website/registrations/static/registrations/js/main.js create mode 100644 website/registrations/templates/registrations/email/references_information.txt create mode 100644 website/registrations/templates/registrations/reference.html create mode 100644 website/registrations/templates/registrations/reference_success.html create mode 100644 website/registrations/templates/registrations/register_benefactor.html diff --git a/website/payments/static/admin/payments/js/payments.js b/website/payments/static/admin/payments/js/payments.js index d0bc80f2..636b087f 100644 --- a/website/payments/static/admin/payments/js/payments.js +++ b/website/payments/static/admin/payments/js/payments.js @@ -1,5 +1,5 @@ -django.jQuery(function () { - var $ = django.jQuery; +(django.jQuery || jQuery)(function () { + var $ = django.jQuery || jQuery; $(".payments-row a.process").click(function(e) { e.preventDefault(); var type = $(e.target).data('type'); diff --git a/website/registrations/README.rst b/website/registrations/README.rst index 07cb3e93..3a8efd02 100644 --- a/website/registrations/README.rst +++ b/website/registrations/README.rst @@ -5,29 +5,35 @@ Registrations This document explains how the registrations module behaviour is defined. The behaviour of upgrading an existing 'year' membership to a 'study' membership (until graduation) is taken from the HR. If the HR ever changes this behaviour should be changed to reflect those changes. -*Note that registrations and renewals for benefactors are implemented in the models, there are simply no views providing this functionality. If we ever want to implement this then it would be best to create a complete new form just for benefactors registrations.* +This module both provides registration for members and for benefactors. The only difference is the form and view used for their registration since the information we ask from them is different. -New member registration -======================= +New member/benefactor registration +================================== Frontend -------- - User enters information + - If the membership type is 'benefactor': + The amount used in the payment is provided during the registration process by the user. - User accepts privacy policy + This step is obligatory. We do not accept people that do not accept the privacy policy. It's currently implemented as a checkbox in the forms. - System validates info - Correct address - Valid and unique email address - Checked against existing users - Privacy policy accepted - - If the selected member type is 'member': + - If the selected membership type is 'member': - valid and unique student number + - Checked against existing users - selected programme - cohort - Registration model created (status: Awaiting email confirmation) - Email address confirmation sent - User confirms email address - Registration model status changed (status: Ready for review) +- If the registration is for a benefactor an email is sent with a link to get references + - Existing members of Thalia add references using the link Backend ------- @@ -37,9 +43,12 @@ Backend - If it's not unique a username can be entered manually - If it's still not unique the registration cannot be accepted - If it's unique the generated username will be added to the registration - - Payment model is created (processed: False) - - Amount is calculated based on the selected length ('study' or 'year') - - Values are located in thaliawebsite.settings + - Payment model is created (unprocessed at first) + - If the membership type is 'member': + - Amount is calculated based on the selected length ('study' or 'year') + - Values are located in thaliawebsite.settings + - If the membership type is 'benefactor': + - Amount is determined by the value entered during registration - Email is sent as acceptance confirmation containg instructions for `Payment processing`_ 2. Admin rejects registration - Email is sent as rejection message @@ -56,6 +65,8 @@ Frontend - If latest membership has ended or ends within 1 month: also allow 'year' length - If latest membership is 'study' and did not end: do not allow renewal - Renewal model created (status: Ready for review) +- If the renewal is for a benefactor an email is sent with a link to get references + - Existing members of Thalia add references using the link Backend ------- diff --git a/website/registrations/admin.py b/website/registrations/admin.py index a5a2686e..479b45fe 100644 --- a/website/registrations/admin.py +++ b/website/registrations/admin.py @@ -7,7 +7,12 @@ from django.utils.translation import ugettext_lazy as _ from payments.widgets import PaymentWidget from . import services -from .models import Entry, Registration, Renewal +from .models import Entry, Registration, Renewal, Reference + + +class ReferenceInline(admin.StackedInline): + model = Reference + extra = 0 def _show_message(admin, request, n, message, error): @@ -29,6 +34,7 @@ class RegistrationAdmin(admin.ModelAdmin): 'created_at', 'payment_status') list_filter = ('status', 'programme', 'payment__type', 'payment__amount') + inlines = (ReferenceInline,) search_fields = ('first_name', 'last_name', 'email', 'phone_number', 'student_number',) date_hierarchy = 'created_at' @@ -38,6 +44,7 @@ class RegistrationAdmin(admin.ModelAdmin): 'updated_at', 'username', 'length', + 'contribution', 'membership_type', 'status', 'payment', @@ -109,8 +116,11 @@ class RegistrationAdmin(admin.ModelAdmin): obj.status == Entry.STATUS_COMPLETED): return ['status', 'created_at', 'updated_at'] else: - return [field.name for field in self.model._meta.get_fields() - if field.editable and not field.name == 'payment'] + return [ + field.name for field in self.model._meta.get_fields() + if not field.name in['payment', 'no_references'] + and field.editable + ] @staticmethod def name(obj): @@ -179,6 +189,7 @@ class RenewalAdmin(RegistrationAdmin): 'created_at', 'updated_at', 'length', + 'contribution', 'membership_type', 'status', 'payment', diff --git a/website/registrations/emails.py b/website/registrations/emails.py index 62333806..1e96595d 100644 --- a/website/registrations/emails.py +++ b/website/registrations/emails.py @@ -1,4 +1,6 @@ """The emails defined by the registrations package""" +from typing import Union + from django.conf import settings from django.core import mail from django.template import loader @@ -7,10 +9,11 @@ from django.urls import reverse from django.utils import translation from django.utils.translation import ugettext_lazy as _ -from . import models +from payments.models import Payment +from registrations.models import Registration, Renewal -def send_registration_email_confirmation(registration): +def send_registration_email_confirmation(registration: Registration) -> None: """ Send the email confirmation message @@ -23,8 +26,8 @@ def send_registration_email_confirmation(registration): 'registrations/email/registration_confirm_mail.txt', { 'name': registration.get_full_name(), - 'confirm_link': '{}{}'.format( - settings.BASE_URL, + 'confirm_link': ( + settings.BASE_URL + reverse('registrations:confirm-email', args=[registration.pk]) ) @@ -32,7 +35,8 @@ def send_registration_email_confirmation(registration): ) -def send_registration_accepted_message(registration, payment): +def send_registration_accepted_message(registration: Registration, + payment: Payment) -> None: """ Send the registration acceptance email @@ -51,7 +55,7 @@ def send_registration_accepted_message(registration, payment): ) -def send_registration_rejected_message(registration): +def send_registration_rejected_message(registration: Registration) -> None: """ Send the registration rejection email @@ -68,29 +72,28 @@ def send_registration_rejected_message(registration): ) -def send_new_registration_board_message(entry): +def send_new_registration_board_message(registration: Registration) -> None: """ Send a notification to the board about a new registration - :param entry: the registration entry + :param registration: the registration entry """ - try: - _send_email( - settings.BOARD_NOTIFICATION_ADDRESS, - 'New registration', - 'registrations/email/registration_board.txt', - { - 'name': entry.registration.get_full_name(), - 'url': settings.BASE_URL - + reverse('admin:registrations_registration_change', - args=[entry.registration.pk]) - } - ) - except models.Registration.DoesNotExist: - pass + _send_email( + settings.BOARD_NOTIFICATION_ADDRESS, + 'New registration', + 'registrations/email/registration_board.txt', + { + 'name': registration.get_full_name(), + 'url': ( + settings.BASE_URL + + reverse('admin:registrations_registration_change', + args=[registration.pk]) + ) + } + ) -def send_renewal_accepted_message(renewal, payment): +def send_renewal_accepted_message(renewal: Renewal, payment: Payment) -> None: """ Send the renewal acceptation email @@ -109,7 +112,7 @@ def send_renewal_accepted_message(renewal, payment): ) -def send_renewal_rejected_message(renewal): +def send_renewal_rejected_message(renewal: Renewal) -> None: """ Send the renewal rejection email @@ -126,7 +129,7 @@ def send_renewal_rejected_message(renewal): ) -def send_renewal_complete_message(renewal): +def send_renewal_complete_message(renewal: Renewal) -> None: """ Send the email completing the renewal @@ -143,7 +146,7 @@ def send_renewal_complete_message(renewal): ) -def send_new_renewal_board_message(renewal): +def send_new_renewal_board_message(renewal: Renewal) -> None: """ Send a notification to the board about a new renewal @@ -155,14 +158,53 @@ def send_new_renewal_board_message(renewal): 'registrations/email/renewal_board.txt', { 'name': renewal.member.get_full_name(), - 'url': settings.BASE_URL - + reverse('admin:registrations_renewal_change', - args=[renewal.pk]) + 'url': ( + settings.BASE_URL + + reverse('admin:registrations_renewal_change', + args=[renewal.pk]) + ) } ) -def _send_email(to, subject, body_template, context): +def send_references_information_message( + entry: Union[Registration, Renewal]) -> None: + """ + Send a notification to the user with information about references + These are required for benefactors who have not been a Thalia member + and do not work for iCIS + + :param entry: the registration or renewal entry + """ + + if type(entry).__name__ == 'Registration': + email = entry.email + name = entry.get_full_name() + language = entry.language + else: + email = entry.member.email + name = entry.member.get_full_name() + language = entry.member.profile.language + + print(language) + + with translation.override(language): + _send_email( + email, + _('Information about references'), + 'registrations/email/references_information.txt', + { + 'name': name, + 'reference_link': ( + settings.BASE_URL + + reverse('registrations:reference', args=[entry.pk]) + ) + } + ) + + +def _send_email(to: str, subject: str, + body_template: str, context: dict) -> None: """ Easily send an email with the right subject and a body template diff --git a/website/registrations/forms.py b/website/registrations/forms.py index 85728508..1c519338 100644 --- a/website/registrations/forms.py +++ b/website/registrations/forms.py @@ -1,15 +1,17 @@ """The forms defined by the registrations package""" from django import forms from django.forms import TypedChoiceField +from django.urls import reverse_lazy from django.utils import timezone +from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ from utils.snippets import datetime_to_lectureyear from .models import Registration, Renewal -class MemberRegistrationForm(forms.ModelForm): - """Form for membership registrations""" +class BaseRegistrationForm(forms.ModelForm): + """Base form for membership registrations""" birthday = forms.DateField( widget=forms.widgets.SelectDateWidget(years=[ @@ -20,9 +22,18 @@ class MemberRegistrationForm(forms.ModelForm): privacy_policy = forms.BooleanField( required=True, - label=_('I accept the privacy policy') ) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['privacy_policy'].label = mark_safe(_( + 'I accept the privacy policy.').format( + reverse_lazy('privacy-policy'))) + + +class MemberRegistrationForm(BaseRegistrationForm): + """Form for member registrations""" + this_year = datetime_to_lectureyear(timezone.now()) years = reversed([(x, "{} - {}".format(x, x + 1)) for x in range(this_year - 20, this_year + 2)]) @@ -30,25 +41,52 @@ class MemberRegistrationForm(forms.ModelForm): starting_year = TypedChoiceField( choices=years, coerce=int, - empty_value=this_year + empty_value=this_year, + required=False ) class Meta: model = Registration fields = '__all__' - exclude = ['created_at', 'updated_at', 'status', 'username', 'remarks', + exclude = ['created_at', 'updated_at', 'status', 'username', 'payment', 'membership'] -class MemberRenewalForm(forms.ModelForm): +class BenefactorRegistrationForm(BaseRegistrationForm): + """Form for benefactor registrations""" + + icis_employee = forms.BooleanField( + required=False, + label=_('I am an employee of iCIS') + ) + + class Meta: + model = Registration + fields = '__all__' + exclude = ['created_at', 'updated_at', 'status', 'username', + 'starting_year', 'programme', 'payment', 'membership'] + + +class RenewalForm(forms.ModelForm): """Form for membership renewals""" privacy_policy = forms.BooleanField( required=True, - label=_('I accept the privacy policy') ) + icis_employee = forms.BooleanField( + required=False, + label=_('I am an employee of iCIS') + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['privacy_policy'].label = mark_safe(_( + 'I accept the privacy policy.').format( + reverse_lazy('privacy-policy'))) + class Meta: model = Renewal fields = '__all__' - exclude = ['created_at', 'updated_at', 'status', 'remarks'] + exclude = ['created_at', 'updated_at', 'status', + 'payment', 'membership'] diff --git a/website/registrations/locale/nl/LC_MESSAGES/django.mo b/website/registrations/locale/nl/LC_MESSAGES/django.mo index a48a920d7c4254fd26f6eb7c9f8a78f590ce608c..f2b7171996f3e500609f60f88513cd73a941353d 100644 GIT binary patch delta 4861 zcmcJRdvF!i9mh`|8oWFtAtJAY6G%u%$O8zX7$XP*fq=*(2nAu2dvfoRdw22Py@^ou z`l^qjcE&B#(bh+OP^*Ksw{(;`YSm6#nW1CR@<&^1+i9oP+OaydW810y{C1Zl)@jFo zdWPTqoU^;gj&2LB1qg{9?69cH~5aJo`?^-6_O ztfk(9li+*NxRgPRaV6}AGvQe<1 z+u`b|N`1LFuhdVcDfJ+cC2)HTFUIIliSiny#nx?d32%YV!1=So_5KKlig2!0sV88*j)2%-HPwgGErOF7cfiGP6KsJ8ph$TPo`Nq! ziTu8XP?4u0>Z)U~7aoVAPz&cGTmdDJ?NE*=3FU}fm>)yuPCBx{p@@eer=XsJsHBcV zS>Sh&z10V>5l-hv-mio*-W`n(K#}-S_&U#9IiC(#ifjwvvWU56&R-UMoClH|uR&4Z zZAiWa;frJAkU<=+f)e2zxCq`3*Rs(Iu#NFpeqM$h5E0N`sf}PR!6(Jt&l=c4?>afQApye@4*=S5fsNy!ZP>|cm^yXyb16uC=O476|e@% zKJBm^UI-=W*X8L*#5w^@vx*lbfi4uR45MA zz)7$XNvaAF#Y zlvhXN15lJX1XseZLy7opSP9>UGhx}qN-c%;P_jM=MTuMBi$!_Li_XVv*u9K%7cN)o zUikV7E)^njS^WW6-i6FO|JK?td5(664!jOiEcg}_2iC0%ld%U@GrkpG3Lk@O;Gf`8 zxOhDk#{TMEI$VY7vCEXYmJMElcf+NZhmoJ$$i}>X6Uq_&2BNO|J8XfAHih5CVHxvW zDBlfj4juXulpMPU_QI#2ysy}T{vvrJopEp}3?hKD823O)x_wXttPaR-zQeK-x?4<(77g`&vwP?GT^91nj5=fQWOC^~IdxXx@SRn-V({*pW$ zsn3g{obje;ycLS118^$54oal=z-D+Diu7+l+2C(b98|kQN5(>#e=d}xZG__BVkqA& zkCQpez+M*qz9lx_#l+$M^kgP zY!xQ^$y@@+2j@MP0aX>;)q_wP$_2?345A-V>LP4&G~OR^IV9)AhuT5^DkOOd$hdQ7AvNrS%yqK33Y2(=nkV?kv=8|%a7W1X1Px}3e_ zPZqlola|CbVB0Y%!zGyPKin=D?uDI(XuNXN^Emx$Fu9WDIL^bQt;1#py)d!luOUeu zv~GSsAN3bTOv4peUK}9VgVr8VYF}D?2&E$y!>chl=Nquy*j1RcW!M^QGFF8dtO=85 zVIA038PMDil8AXVz#xS+V@t71Fll?Rp`aIL@Lu};nB0`7n^eTrce`mwWLV>}}?5t-pbF;HLM;`0&amMm&*U@{egz0FtI{gbL)E4Q& zt_cGrGy2@L(Uu;WqAoGfvvlF5^5=?^m*q0rcKY2+I$Xte;&z|qnJnH|p5Eu?>N7?= zuBRB-oXE^YPonY~`H;;^`%zt(@Kx>C0&UZ*9pk)Zm6 zC{n%%3oGn*Un$)*|KqeAW~GwwcGKkCM#lb#vZ`6WKz!t|=zI$y(W`&9smA$PgZSnNyQQW#J65!9G$fWP26AIylSlLcp1AlC)w`b*?h@_z}w*& z{)y(5lbw-#B84;Ns+^r+_N12nGs7$^CNQq0UiG=wK`IuhVrx>OAoq%Vu_rS z_3Q-EOUa^CR_`&Ia0}-wI$lzyhuln@!H>^BS)A{2Qwc*Mxz2tL=XBy3(Oo7bM5MHd zn<0}KpcDtALXD9I@=A70Q=L*q@!)M(tW@a=j=I-)X23X}j#KVKQj(N*B&ug4VqO-E z>N~|YZ00h@!5@!TxEAYSi)cCyS(cMFskjvGK+bXavezU@HwpOafwcD zc6SLg6`SpqBL|`FrMAR$6u2@xsxr z`NjTU*Um1~cHdT7@#!il9NchsiGRc9E`Q>tGirmif~$kSdsZq)lTlfcFl^$#y!ors z*NjeO&3)tUO_*$ozm$%@b4%CQzTqpy_>XSsKAT$VA9b4kds{A@8r0Xw>T)YNZro19 S{q~-Lvfx(nZ2!KVAN(8Mqva?7 delta 3811 zcmbW3dvH|M9mh`+9s+sr76i&6B(Q-bkn?pb+owo@6h(xr=x2X2I&L zV;S+mFwoSzKI+Z%M1zU7lsBMQ48K$;VtVM@*#y@QP`R%78%K0iBffNN2yZ7 z%9VPIc~hy>hf(p!nB&RyU@j57h8z`mE zKq>uwP&RrJiofSWz6|C4*P$f#JD3*-SLn=!|AKSitlD6~1}NiXIQ|-xl71haWxk!4 zI$%{qsaCi)u~y9 zga|LePFRirHo|Ur1U?6)0&5xq#~a`z#@pZ!n1Q09-B6D90F;EEYM}lio|8-rg{PoI z_7;><{T_GoUzJ1|`#E_&f(iw0mI%d>Jcazy#&SakD!$Dr|>Nto`bUPg@wU;r=hGr z2XBKHp(Get6jY)aa$(eMuv6}TA01KcVJMlu0bj%MX}AwQev49HhvRNV$wYp1F?SrU zU#e6Y^H-KB#Z6RKmj@eV0sei_P9y$@yGKj0`>v6f28 z{hv%n#54~|#2v62u7D$80*XTyikKb^`3*ROaRG{0&%s&nFK`a5>JF}PJB%>i1SNn6 z;9deh1oNkvxM^K*fByuRFrK_#DXOiuLfQC=}sdu=m@4|}Pg9?5CW&M;*)SopfyNONUFW@lPyE)jX5AwNI_d?Oeekc|A3ao%H z!g_cNO64v>dH=6a)_(-$R{ax}!I5_a<4PzMtGWh8JmFJi%FY{?Jo8L;Y#JCaqMaUYs2JbBr$1gvB6?5+<#JW z5Qrl}U(Ton6D_2%47LiBCLb$lGqJ{EkH7IDw}%vslw!@{*oC6_$>CTuBmdjlAp3J0 ziuaP+^SQQ>krmD~g_I8wAH-slKfMmdrMcGD(Bu16{Pc>TrD;q=E;r({b~l{_)@H() zI@phiDm`oqCQZaS9Q&dSXnpci7&Gd0nSZ{rW9Fcpu|4f2jE<%)H?G}udO+KKdQUp5 zbC$!Sv~N{TDD10BmK7$BJ62jab>pu`ASVMHnPjL2d&ti-d*XG4YF>kE>pcYoYlc>$2@EC z=N*R6hXo7#hUzU%^*WmMh=g#$fVJg$#)(S~t~P_l@iL9Nhlq0t%Uj|%&HRb~VC}R1 z)8*Uzf7e7xR{N3K+DIlF9iS4H&bhX{uB}ws>DM{iOK2~b))~9svAnG7TeZ{jdMo9L zS&r_rQz=%abh?is+8J#-I_(7YG>+-BVqV(SeQ9@~p55(OqF7jYlcEsk|5iTI-Q3vJ z*tD=-_q*vVIrOE{IKX?iabuP^Yp!b?sn%K*$h-)`q4Q z-MpZ^W&Yfz=C8I*xi-<-%0wa3);!c-x=jzWQW?F|6i&3iSyp&{@hhc;?aMw9O&Fad zGdrnMc3kJuZrnHsBB}c=Z6%W=5=SPUPGJA9GNLAB?06uIWGFR5`Uz9z=};ZP8vk;4 zQ%S^syL-x_x_UieS*}Euvr{@}Qq+#|&TJ}WBi@*2589sLALn9OPNTk)qj9oJ{11A5 zT=?0#>XN+K-LTq`bF@4=md(hCT%RyWj>_&g^*T=eaT_-~4XQClCoCuKnWS-Sr=RIT zo~I0TDfTuctdwo(4Cm++D`SLdqG1<An^tvBOCQJIRM2!}Ut_X<5fsW(sYcgIo>t>Rzgy`z) z>co%bu(O*+qyH^UwKSISj7xb33Kusvmsa*\n" "Language-Team: \n" "Language: nl\n" @@ -96,13 +96,21 @@ msgstr "Verlenging afgekeurd" msgid "Renewal successful" msgstr "Verlenging succesvol" +#: emails.py +msgid "Information about references" +msgstr "Informatie over referenties" + #: forms.py models.py msgid "birthday" msgstr "verjaardag" #: forms.py -msgid "I accept the privacy policy" -msgstr "Ik accepteer het privacybeleid" +msgid "I accept the privacy policy." +msgstr "Ik accepteer het privacybeleid." + +#: forms.py +msgid "I am an employee of iCIS" +msgstr "Ik ben een medewerker van iCIS" #: models.py msgid "created at" @@ -148,6 +156,14 @@ msgstr "Tot afstuderen" msgid "membership length" msgstr "lengte lidmaatschap" +#: models.py +msgid "contribution" +msgstr "contributie" + +#: models.py +msgid "no references required" +msgstr "geen referenties benodigd" + #: models.py templates/registrations/renewal.html msgid "membership type" msgstr "soort lidmaatschap" @@ -157,8 +173,8 @@ msgid "remarks" msgstr "opmerkingen" #: models.py -msgid "Registration entry" -msgstr "Registratie" +msgid "This field is required for benefactors." +msgstr "Dit veld is verplicht voor begunstigers." #: models.py msgid "entry" @@ -281,6 +297,7 @@ msgid "A user with that username already exists." msgstr "Er bestaat al een gebruiker met deze gebruikersnaam." #: models.py templates/registrations/confirm_email.html +#: templates/registrations/register_benefactor.html #: templates/registrations/register_member.html #: templates/registrations/register_success.html msgid "registration" @@ -357,8 +374,10 @@ msgstr "" "lid te worden!" #: templates/registrations/become_a_member.html -msgid "How do I become a member?" -msgstr "Hoe kan ik lid worden?" +msgid "" +"I'm a Computing Science or Information Sciences student at the Radboud " +"University" +msgstr "Ik studeer Informatica of Informatiekunde aan de Radboud Universiteit" #: templates/registrations/become_a_member.html #, python-format @@ -376,37 +395,37 @@ msgstr "" "informatiekunde studeert aan de Radboud Universiteit." #: templates/registrations/become_a_member.html -msgid "Register now" -msgstr "Nu registreren" +msgid "Become a member" +msgstr "Lid worden" #: templates/registrations/become_a_member.html msgid "" "I'm not a Computing Science and Information Sciences student at the Radboud " -"University, but I do want to attend your events. Now what?" +"University" msgstr "" -"Ik studeer geen Informatica of Informatiekunde (aan de Radboud " -"Universiteit), maar ik wil wel naar jullie activiteiten gaan. Wat nu?" +"Ik studeer geen Informatica of Informatiekunde aan de Radboud Universiteit" #: templates/registrations/become_a_member.html +#: templates/registrations/register_benefactor.html #, python-format msgid "" "It is still possible to be associated with Thalia, even if you do not study " "Computing Science or Information Sciences (anymore): You can become a " "benefactor. For at least € %(year_fees)s per year, you too can enjoy " -"everything Thalia has to offer. If you are not a former Thalia member, ICIS " -"staff member or alumni, you must submit a written along with two signatures " -"of current Thalia members. You can fill all of this in on the benefactor " -"form, which you can get at the board room (M1.0.08, ground floor of Mercator " -"1)." +"everything Thalia has to offer. If you are not a former Thalia member, iCIS " +"staff member or alumni, you must collect two references of current Thalia " +"members." msgstr "" "Mocht je nu geen Informatica of Informatiekunde (meer) studeren, maar toch " "verbonden willen zijn, dan kan dat ook. Hiervoor kun je begunstiger van " "Thalia worden. Voor minimaal € %(year_fees)s per jaar kun je ook genieten " -"van alles wat Thalia je te bieden heeft. Ben je geen oud-Thaliaan, ex-" -"Informaticus of -Informatiekundige, dan dien je twee handtekeningen van " -"leden van Thalia samen met een motivatie waarom je begunstiger wil worden af " -"te geven. Dit kun je allemaal invullen op het begunstigerformulier, dat af " -"te halen is in de bestuurskamer (M1.0.08, begane grond Mercator 1)." +"van alles wat Thalia je te bieden heeft. Ben je geen oud-Thaliaan, iCIS " +"medewerker of alumni, dan dien je twee referenties van leden van Thalia te " +"verzamelen." + +#: templates/registrations/become_a_member.html +msgid "Become a benefactor" +msgstr "Begunstiger worden" #: templates/registrations/become_a_member.html msgid "" @@ -433,6 +452,52 @@ msgstr "" "beoordelen. Mocht je vragen hebben, stuur dan een mailtje naar info@thalia." "nu." +#: templates/registrations/email/references_information.txt +#, python-format +msgid "" +"Dear %(name)s,\n" +"\n" +"Our information indicates that you're not an iCIS employee or\n" +"a former Thalia member.\n" +"\n" +"This means that before we can review your membership registration we\n" +"need to receive two references of current Thalia members.\n" +"\n" +"Share the following link with them to obtain their reference:\n" +"%(reference_link)s\n" +"\n" +"If you have any questions, then don't hesitate and send an email to " +"info@thalia.nu.\n" +"\n" +"With kind regards,\n" +"\n" +"The board of Study Association Thalia\n" +"\n" +"————\n" +"\n" +"This email was automatically generated." +msgstr "" +"Beste %(name)s,\n" +"\n" +"Volgens onze informatie ben je geen medewerker van iCIS\n" +"of een voormalig lid van Thalia.\n" +"\n" +"Dit betekent dat we twee referenties van Thalia leden moeten ontvangen\n" +"Voordat we je aanmelding kunnen behandelen.\n" +"\n" +"Deel de volgende link met een Thalia lid om een referentie te verkrijgen:\n" +"%(reference_link)s\n" +"\n" +"Mocht je vragen hebben, stuur dan vooral een mailtje naar info@thalia.nu.\n" +"\n" +"Met vriendelijke groet,\n" +"\n" +"Het bestuur der Studievereniging Thalia\n" +"\n" +"————\n" +"\n" +"Deze e-mail is automatisch gegenereerd." + #: templates/registrations/email/registration_accepted.txt #, python-format msgid "" @@ -693,29 +758,49 @@ msgstr "" "\n" "Deze e-mail is automatisch gegenereerd." -#: templates/registrations/register_member.html +#: templates/registrations/reference.html +#: templates/registrations/reference_success.html +msgid "reference" +msgstr "referentie" + +#: templates/registrations/reference.html +#: templates/registrations/reference_success.html +msgid "give reference" +msgstr "geef referentie" + +#: templates/registrations/reference.html +#: templates/registrations/reference_success.html #, python-format msgid "" -"A membership costs € %(year_fees)s per year, or € %(study_fees)s for your " -"entire study duration.
Note: Only Computing Science and Information " -"Sciences students at the Radboud University can become a member.

" "It is still possible to be associated with Thalia, even if you do not study " "Computing Science or Information Sciences (anymore): You can become a " -"benefactor. For at least € %(year_fees)s per year, you too can enjoy " -"everything Thalia has to offer.
Note that this form is only for " -"member registration. Please visit the board room if you want to become a " -"benefactor." +"benefactor. If you are not a former Thalia member, iCIS staff member or " +"alumni, you must collect two references of current Thalia members.

%(name)s wants to become a benefactor of Thalia and " +"needs such a reference and has asked you to give it to them." msgstr "" -"Een lidmaatschap kost € %(year_fees)s per jaar, of € %(study_fees)s voor je " -"hele studieperiode.
Let wel op: Je kunt alleen lid worden wanneer je " -"informatica of informatiekunde studeert aan de Radboud Universiteit.

Mocht je nu geen Informatica of Informatiekunde (meer) studeren, maar toch " +"Mocht je nu geen Informatica of Informatiekunde (meer) studeren, maar toch " "verbonden willen zijn, dan kan dat ook. Hiervoor kun je begunstiger van " -"Thalia worden. Voor minimaal € %(year_fees)s per jaar kun je ook genieten " -"van alles wat Thalia je te bieden heeft.
Let op dat dit formulier " -"alleen gebruikt kan worden voor registratie van leden. Neem contact op met " -"het bestuur als je begunstiger wilt worden." +"Thalia worden. Ben je geen oud-Thaliaan, iCIS medewerker of alumni, dan dien " +"je twee referenties van leden van Thalia te verzamelen.

" +"%(name)s wil graag begunstiger van Thalia worden en heeft jouw " +"gevraagd om een deze te geven." + +#: templates/registrations/reference.html +msgid "Your reference has been saved." +msgstr "Je referentie is opgeslagen." +#: templates/registrations/reference.html +#: templates/registrations/reference_success.html +msgid "You've already given a reference for this person." +msgstr "Je hebt deze persoon al een referentie gegeven." + +#: templates/registrations/register_benefactor.html +#: templates/registrations/renewal.html +msgid "Benefactor" +msgstr "Begunstiger" + +#: templates/registrations/register_benefactor.html #: templates/registrations/register_member.html msgid "" "If you've been a member before you should login using your existing account " @@ -726,10 +811,18 @@ msgstr "" "en vernieuw je lidmaatschap in de accountinstellingen. Je kunt je niet " "opnieuw registreren via dit formulier." +#: templates/registrations/register_benefactor.html #: templates/registrations/register_member.html -msgid "Display birthday in calendar" -msgstr "Toon verjaardag in de kalender" +msgid "" +"If you have any other questions about Thalia and/or your membership, feel " +"free to email info@thalia.nu!" +msgstr "" +"Mocht je nog verdere vragen hebben over Thalia en/of je lidmaatschap dan kun " +"je altijd mailen naar info@thalia.nu!" +#: templates/registrations/register_benefactor.html #: templates/registrations/register_member.html msgid "" "Receive emails about (amongst others) job opportunities and in-house days " @@ -738,21 +831,45 @@ msgstr "" "Ik wil e-mails ontvangen over (onder andere) baankansen en in-house dagen " "van partners van Thalia." +#: templates/registrations/register_benefactor.html #: templates/registrations/register_member.html -#: templates/registrations/renewal.html -#, python-format -msgid "" -"I accept the privacy " -"policy" -msgstr "" -"Ik ga akkoord met het privacybeleid" +msgid "Display birthday in calendar" +msgstr "Toon verjaardag in de kalender" +#: templates/registrations/register_benefactor.html #: templates/registrations/register_member.html #: templates/registrations/renewal.html msgid "send" msgstr "verstuur" +#: templates/registrations/register_member.html +msgid "Member" +msgstr "Lid" + +#: templates/registrations/register_member.html +#, python-format +msgid "" +"A membership costs € %(year_fees)s per year, or € %(study_fees)s for your " +"entire study duration.
Note: Only Computing Science and Information " +"Sciences students at the Radboud University can become a member.

" +"It is still possible to be associated with Thalia, even if you do not study " +"Computing Science or Information Sciences (anymore): You can become a " +"benefactor. For at least € %(year_fees)s per year, you too can enjoy " +"everything Thalia has to offer.
Note that this form is only for " +"member registration. Please use the benefactor registration page if you want to become a benefactor. " +msgstr "" +"Een lidmaatschap kost € %(year_fees)s per jaar, of € %(study_fees)s voor je " +"hele studieperiode.
Let wel op: Je kunt alleen lid worden wanneer je " +"informatica of informatiekunde studeert aan de Radboud Universiteit.

Mocht je nu geen Informatica of Informatiekunde (meer) studeren, maar toch " +"verbonden willen zijn, dan kan dat ook. Hiervoor kun je begunstiger van " +"Thalia worden. Voor minimaal € %(year_fees)s per jaar kun je ook genieten " +"van alles wat Thalia je te bieden heeft.
Let op dat dit formulier " +"alleen gebruikt kan worden voor registratie van leden. Gebruik de registratiepagina voor begunstigers als je " +"een begunstiger wilt worden." + #: templates/registrations/register_success.html msgid "registration success" msgstr "registratie gelukt" @@ -845,12 +962,6 @@ msgstr "" "Je bent een erelid. Dat betekent dat je je lidmaatschap nooit hoeft te " "verlengen." -#: templates/registrations/renewal.html -msgid "You're a benefactor. Contact the board to renew your membership." -msgstr "" -"Je bent een begunstiger. Neem contact op met het bestuur om je lidmaatschap " -"te verlengen." - #: templates/registrations/renewal.html msgid "" "You currently have an active membership for your entire study duration. Did " @@ -861,10 +972,6 @@ msgstr "" "afstudeert. Ben je klaar met studeren of ben je om een andere reden gestopt? " "Laat ons dat alsjeblieft weten door een mail te sturen naar info@thalia.nu." -#: templates/registrations/renewal.html -msgid "Benefactor" -msgstr "Begunstiger" - #: templates/registrations/renewal_success.html msgid "success" msgstr "gelukt" diff --git a/website/registrations/migrations/0019_auto_20190425_1107.py b/website/registrations/migrations/0019_auto_20190425_1107.py new file mode 100644 index 00000000..9aa8570b --- /dev/null +++ b/website/registrations/migrations/0019_auto_20190425_1107.py @@ -0,0 +1,37 @@ +# Generated by Django 2.2 on 2019-04-25 09:07 + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0031_benefactor_model_value'), + ('registrations', '0018_benefactor_model_value'), + ] + + operations = [ + migrations.AddField( + model_name='entry', + name='contribution', + field=models.FloatField(blank=True, default=7.5, null=True, validators=[django.core.validators.MinValueValidator(7.5)], verbose_name='contribution'), + ), + migrations.AddField( + model_name='entry', + name='no_references', + field=models.BooleanField(default=False, verbose_name='no references required'), + ), + migrations.CreateModel( + name='Reference', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='registrations.Entry', verbose_name='entry')), + ('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='members.Member', verbose_name='member')), + ], + options={ + 'unique_together': {('member', 'entry')}, + }, + ), + ] diff --git a/website/registrations/models.py b/website/registrations/models.py index e4376879..b09f677e 100644 --- a/website/registrations/models.py +++ b/website/registrations/models.py @@ -5,13 +5,13 @@ from django.conf import settings from django.contrib.auth import get_user_model from django.core import validators from django.core.exceptions import ValidationError +from django.core.validators import MinValueValidator from django.db import models from django.template.defaultfilters import floatformat from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from members.models import Membership, Profile -from registrations import emails from utils import countries @@ -62,6 +62,19 @@ class Entry(models.Model): MEMBERSHIP_TYPES = [m for m in Membership.MEMBERSHIP_TYPES if m[0] != Membership.HONORARY] + contribution = models.FloatField( + verbose_name=_('contribution'), + validators=[MinValueValidator(settings.MEMBERSHIP_PRICES['year'])], + default=settings.MEMBERSHIP_PRICES['year'], + blank=True, + null=True, + ) + + no_references = models.BooleanField( + verbose_name=_('no references required'), + default=False + ) + membership_type = models.CharField( verbose_name=_('membership type'), choices=MEMBERSHIP_TYPES, @@ -96,8 +109,28 @@ class Entry(models.Model): self.status != self.STATUS_REJECTED): self.updated_at = timezone.now() + if (self.contribution is not None and + self.membership_type != Membership.BENEFACTOR): + self.contribution = None + elif self.membership_type == Membership.BENEFACTOR: + self.length = self.MEMBERSHIP_YEAR + super().save(force_insert, force_update, using, update_fields) + def clean(self): + super().clean() + errors = {} + + if (self.contribution is None and + self.membership_type == Membership.BENEFACTOR): + errors.update({ + 'contribution': + _('This field is required for benefactors.') + }) + + if errors: + raise ValidationError(errors) + def __str__(self): try: return self.registration.__str__() @@ -294,12 +327,6 @@ class Registration(Entry): if errors: raise ValidationError(errors) - def save(self, *args, **kwargs): - send_confirm_email = self.pk is None - super().save(*args, **kwargs) - if send_confirm_email: - emails.send_registration_email_confirmation(self) - def __str__(self): return '{} {} ({})'.format(self.first_name, self.last_name, self.email) @@ -329,7 +356,8 @@ class Renewal(Entry): errors = {} if Renewal.objects.filter( - member=self.member, status=Entry.STATUS_REVIEW).exists(): + member=self.member, status=Entry.STATUS_REVIEW + ).exclude(pk=self.pk).exists(): raise ValidationError(_('You already have a renewal ' 'request queued for review.')) @@ -373,3 +401,28 @@ class Renewal(Entry): class Meta: verbose_name = _('renewal') verbose_name_plural = _('renewals') + + +class Reference(models.Model): + """Describes a reference of a member for a potential member""" + member = models.ForeignKey( + 'members.Member', + on_delete=models.CASCADE, + verbose_name=_('member'), + blank=False, + null=False, + ) + + entry = models.ForeignKey( + 'registrations.Entry', + on_delete=models.CASCADE, + verbose_name=_('entry'), + blank=False, + null=False + ) + + def __str__(self): + return f'Reference from {self.member} for {self.entry}' + + class Meta: + unique_together = ('member', 'entry') diff --git a/website/registrations/services.py b/website/registrations/services.py index 05379c69..b42971b1 100644 --- a/website/registrations/services.py +++ b/website/registrations/services.py @@ -1,23 +1,24 @@ """The services defined by the registrations package""" import string import unicodedata +from typing import Union from django.conf import settings from django.contrib.admin.models import LogEntry, CHANGE from django.contrib.admin.options import get_content_type_for_model from django.contrib.auth import get_user_model -from django.db.models import Q +from django.db.models import Q, QuerySet from django.utils import timezone import members -from members.models import Membership, Profile +from members.models import Membership, Profile, Member from payments.models import Payment from registrations import emails from registrations.models import Entry, Registration, Renewal from utils.snippets import datetime_to_lectureyear -def _generate_username(registration): +def _generate_username(registration: Registration) -> str: """ Create username from first and lastname @@ -39,7 +40,7 @@ def _generate_username(registration): return username -def check_unique_user(entry): +def check_unique_user(entry: Entry) -> bool: """ Check that the username and email address of the entry are unique. @@ -62,7 +63,7 @@ def check_unique_user(entry): return True -def confirm_entry(queryset): +def confirm_entry(queryset: QuerySet) -> int: """ Confirm all entries in the queryset @@ -77,10 +78,11 @@ def confirm_entry(queryset): return rows_updated -def reject_entries(user_id, queryset): +def reject_entries(user_id: int, queryset: QuerySet) -> int: """ Reject all entries in the queryset + :param user_id: Id of the user executing this action :param queryset: queryset of entries :type queryset: Queryset[Entry] :return: number of updated rows @@ -117,10 +119,11 @@ def reject_entries(user_id, queryset): return rows_updated -def accept_entries(user_id, queryset): +def accept_entries(user_id: int, queryset: QuerySet) -> int: """ Accept all entries in the queryset + :param user_id: Id of the user executing this action :param queryset: queryset of entries :type queryset: Queryset[Entry] :return: number of updated rows @@ -174,10 +177,11 @@ def accept_entries(user_id, queryset): return len(updated_entries) -def revert_entry(user_id, entry): +def revert_entry(user_id: int, entry: Entry) -> None: """ Revert status of entry to review so that it can be corrected + :param user_id: Id of the user executing this action :param entry: Entry that should be reverted """ if not (entry.status in [Entry.STATUS_ACCEPTED, Entry.STATUS_REJECTED]): @@ -201,8 +205,6 @@ def revert_entry(user_id, entry): except Renewal.DoesNotExist: pass - print(log_obj) - if log_obj: LogEntry.objects.log_action( user_id=user_id, @@ -214,7 +216,7 @@ def revert_entry(user_id, entry): ) -def _create_payment_for_entry(entry): +def _create_payment_for_entry(entry: Entry) -> Payment: """ Create payment model for entry @@ -224,6 +226,8 @@ def _create_payment_for_entry(entry): :rtype: Payment """ amount = settings.MEMBERSHIP_PRICES[entry.length] + if entry.contribution and entry.membership_type == Membership.BENEFACTOR: + amount = entry.contribution notes = f'Membership registration. {entry.get_membership_type_display()}.' try: @@ -242,7 +246,7 @@ def _create_payment_for_entry(entry): # we're checking if that is the case so that these members # still get the discount price if (membership is not None and membership.until is not None and - entry.created_at.date() < membership.until and + entry.created_at.date() < membership.until and renewal.length == Entry.MEMBERSHIP_STUDY): amount = (settings.MEMBERSHIP_PRICES[Entry.MEMBERSHIP_STUDY] - settings.MEMBERSHIP_PRICES[Entry.MEMBERSHIP_YEAR]) @@ -255,7 +259,7 @@ def _create_payment_for_entry(entry): ) -def _create_member_from_registration(registration): +def _create_member_from_registration(registration: Registration) -> Member: """ Create User and Member model from Registration @@ -303,10 +307,11 @@ def _create_member_from_registration(registration): # Send welcome message to new member members.emails.send_welcome_message(user, password, registration.language) - return user + return Member.objects.get(pk=user.pk) -def _create_membership_from_entry(entry, member=None): +def _create_membership_from_entry( + entry: Entry, member: Member = None) -> Union[Membership, None]: """ Create or update Membership model based on Entry model information @@ -371,7 +376,7 @@ def _create_membership_from_entry(entry, member=None): ) -def process_payment(payment): +def process_payment(payment: Payment) -> None: """ Process the payment for the entry and send the right emails diff --git a/website/registrations/static/registrations/css/style.scss b/website/registrations/static/registrations/css/style.scss index e69de29b..c1e02a50 100644 --- a/website/registrations/static/registrations/css/style.scss +++ b/website/registrations/static/registrations/css/style.scss @@ -0,0 +1,9 @@ +#registrations-form { + .required-field { + label::after { + content: '*'; + margin-left: 2px; + color: $dark-grey; + } + } +} diff --git a/website/registrations/static/registrations/js/main.js b/website/registrations/static/registrations/js/main.js new file mode 100644 index 00000000..45775313 --- /dev/null +++ b/website/registrations/static/registrations/js/main.js @@ -0,0 +1,23 @@ +function changeVisiblity(value) { + var bType = $('form').data('benefactor-type'); + if (value === bType) { + $('#id_contribution').parent().removeClass('d-none'); + $('#id_length').parent().addClass('d-none'); + $('#id_length').val('year'); + } else { + $('#id_contribution').parent().addClass('d-none'); + $('#id_length').parent().removeClass('d-none'); + $('#id_length').val(''); + } +} + +$(function() { + var membershipEl = $('select#id_membership_type'); + if (membershipEl.length !== 0) { + changeVisiblity(membershipEl.val()); + + membershipEl.change(function () { + changeVisiblity(this.value); + }); + } +}); diff --git a/website/registrations/templates/registrations/become_a_member.html b/website/registrations/templates/registrations/become_a_member.html index 59414506..d4d417ab 100644 --- a/website/registrations/templates/registrations/become_a_member.html +++ b/website/registrations/templates/registrations/become_a_member.html @@ -9,39 +9,53 @@

{% trans "Become a Member" %}

-

{% trans "Thalia is the study association for Computing Science and Information Sciences students at the Radboud University in Nijmegen. Thalia organises a wide variety of activities, such as bowling events, go cart racing, lunch lectures, drinks and much more! Furthermore, members get access to our tests and summaries database, as well as discounts on books. There's no reason not to become a member!" %}

+

{% trans "Thalia is the study association for Computing Science and Information Sciences students at the Radboud University in Nijmegen. Thalia organises a wide variety of activities, such as bowling events, go cart racing, lunch lectures, drinks and much more! Furthermore, members get access to our tests and summaries database, as well as discounts on books. There's no reason not to become a member!" %}

-

{% trans "How do I become a member?" %}

+
+
+

{% trans "I'm a Computing Science or Information Sciences student at the Radboud University" %}

-

- {% blocktrans trimmed %} - You can become a member of Thalia at any time during the year. A membership costs € - {{ year_fees }} per year, or € {{ study_fees }} for your entire study duration. Click on the button - below to go to the registration form. Note: Only Computing Science and Information Sciences students - at the Radboud University can become a member. - {% endblocktrans %} -

+

+ {% blocktrans trimmed %} + You can become a member of Thalia at any time during the year. A membership costs € + {{ year_fees }} per year, or € {{ study_fees }} for your entire study duration. Click on the button + below to go to the registration form. Note: Only Computing Science and Information Sciences students + at the Radboud University can become a member. + {% endblocktrans %} +

+
-

- - {% trans "Register now" %} - -

+ -

{% trans "I'm not a Computing Science and Information Sciences student at the Radboud University, but I do want to attend your events. Now what?" %}

+
+

{% trans "I'm not a Computing Science and Information Sciences student at the Radboud University" %}

-

- {% blocktrans trimmed %} - It is still possible to be associated with Thalia, even if you do not study Computing - Science or Information Sciences (anymore): You can become a benefactor. For at least € - {{ year_fees }} per year, you too can enjoy everything Thalia has to offer. - If you are not a former Thalia member, ICIS staff member or alumni, you must submit a written along - with two signatures of current Thalia members. You can fill all of this in on the benefactor form, - which you can get at the board room (M1.0.08, ground floor of Mercator 1). - {% endblocktrans %} -

+

+ {% blocktrans trimmed %} + It is still possible to be associated with Thalia, even if you do not study Computing + Science or Information Sciences (anymore): You can become a benefactor. For at least € + {{ year_fees }} per year, you too can enjoy everything Thalia has to offer. + If you are not a former Thalia member, iCIS staff member or alumni, you must collect two references of current Thalia members. + {% endblocktrans %} +

+
+ + +
-

+

{% blocktrans trimmed %} Payment can be made both in cash or by card. If you have any other questions about Thalia and/or your membership, feel free to email diff --git a/website/registrations/templates/registrations/email/references_information.txt b/website/registrations/templates/registrations/email/references_information.txt new file mode 100644 index 00000000..9e28c846 --- /dev/null +++ b/website/registrations/templates/registrations/email/references_information.txt @@ -0,0 +1,20 @@ +{% load i18n %}{% blocktrans %}Dear {{ name }}, + +Our information indicates that you're not an iCIS employee or +a former Thalia member. + +This means that before we can review your membership registration we +need to receive two references of current Thalia members. + +Share the following link with them to obtain their reference: +{{ reference_link }} + +If you have any questions, then don't hesitate and send an email to info@thalia.nu. + +With kind regards, + +The board of Study Association Thalia + +———— + +This email was automatically generated.{% endblocktrans %} diff --git a/website/registrations/templates/registrations/reference.html b/website/registrations/templates/registrations/reference.html new file mode 100644 index 00000000..7d6b08de --- /dev/null +++ b/website/registrations/templates/registrations/reference.html @@ -0,0 +1,42 @@ +{% extends "base.html" %} +{% load i18n bootstrap4 alert %} + +{% block title %}{% trans "reference"|capfirst %} — {{ block.super }}{% endblock %} + +{% block body %} +

+
+

{% trans "give reference" %}

+ +

+ {% blocktrans trimmed %} + It is still possible to be associated with Thalia, even if you do not study Computing + Science or Information Sciences (anymore): You can become a benefactor. + If you are not a former Thalia member, iCIS staff member or alumni, + you must collect two references of current Thalia members. +

+ {{ name }} wants to become a benefactor of Thalia and + needs such a reference and has asked you to give it to them. + {% endblocktrans %} +

+ +
+ + {% if success %} + {% trans "Your reference has been saved." as alert_text %} + {% alert 'success' alert_text extra_classes="mt-3" %} + {% elif form.errors %} + {% trans "You've already given a reference for this person." as alert_text %} + {% alert 'danger' alert_text extra_classes="mt-3" %} + {% else %} +
+ {% csrf_token %} + + +
+ {% endif %} +
+
+{% endblock %} diff --git a/website/registrations/templates/registrations/reference_success.html b/website/registrations/templates/registrations/reference_success.html new file mode 100644 index 00000000..6b810b1d --- /dev/null +++ b/website/registrations/templates/registrations/reference_success.html @@ -0,0 +1,39 @@ +{% extends "base.html" %} +{% load i18n bootstrap4 alert %} + +{% block title %}{% trans "reference"|capfirst %} — {{ block.super }}{% endblock %} + +{% block body %} +
+
+

{% trans "give reference" %}

+ +

+ {% blocktrans trimmed %} + It is still possible to be associated with Thalia, even if you do not study Computing + Science or Information Sciences (anymore): You can become a benefactor. + If you are not a former Thalia member, iCIS staff member or alumni, + you must collect two references of current Thalia members. +

+ {{ name }} wants to become a benefactor of Thalia and + needs such a reference and has asked you to give it to them. + {% endblocktrans %} +

+ +
+ + {% if form.errors %} + {% trans "You've already given a reference for this person." as alert_text %} + {% alert 'danger' alert_text extra_classes="mt-3" %} + {% else %} +
+ {% csrf_token %} + + +
+ {% endif %} +
+
+{% endblock %} diff --git a/website/registrations/templates/registrations/register_benefactor.html b/website/registrations/templates/registrations/register_benefactor.html new file mode 100644 index 00000000..c6708c63 --- /dev/null +++ b/website/registrations/templates/registrations/register_benefactor.html @@ -0,0 +1,89 @@ +{% extends "base.html" %} +{% load i18n static compress bootstrap4 %} + +{% block title %}{% trans "registration"|capfirst %} — + {{ block.super }}{% endblock %} + +{% block body %} +
+
+

{% trans "registration" %} {% trans "Benefactor" %}

+ +

+ {% blocktrans trimmed %} + It is still possible to be associated with Thalia, even if you do not study Computing + Science or Information Sciences (anymore): You can become a benefactor. For at least € + {{ year_fees }} per year, you too can enjoy everything Thalia has to offer. + If you are not a former Thalia member, iCIS staff member or alumni, you must collect two references of current Thalia members. + {% endblocktrans %} +

+ +

+ {% blocktrans trimmed %} + If you've been a member before you should login using your existing account and renew your + membership by visiting the account settings. + You'll be unable to re-register using this form. + {% endblocktrans %} +

+ +

+ {% blocktrans trimmed %} + If you have any other questions about Thalia and/or your membership, feel free to email + info@thalia.nu! + {% endblocktrans %} +

+ +
+ +
+ {% csrf_token %} + +
+ {% bootstrap_field form.first_name %} + {% bootstrap_field form.last_name %} + + {% bootstrap_field form.address_street %} + {% bootstrap_field form.address_street2 %} + {% bootstrap_field form.address_postal_code %} + {% bootstrap_field form.address_city %} + {% bootstrap_field form.address_country %} +
+ +
+ {% bootstrap_field form.email %} + +
+
+ + +
+
+ + {% bootstrap_field form.phone_number %} + + {% bootstrap_field form.birthday %} + +
+
+ + +
+
+ + {% bootstrap_field form.student_number %} + {% bootstrap_field form.icis_employee %} + {% bootstrap_field form.contribution %} + {% bootstrap_field form.privacy_policy %} +
+ + +
+
+
+{% endblock %} diff --git a/website/registrations/templates/registrations/register_member.html b/website/registrations/templates/registrations/register_member.html index 52c5af44..785b9348 100644 --- a/website/registrations/templates/registrations/register_member.html +++ b/website/registrations/templates/registrations/register_member.html @@ -4,11 +4,12 @@ {% block title %}{% trans "registration"|capfirst %} — {{ block.super }}{% endblock %} {% block body %} -
+
-

{% trans "registration" %}

+

{% trans "registration" %} {% trans "Member" %}

+ {% url 'registrations:register-benefactor' as benefactor_register %} {% blocktrans trimmed %} A membership costs € {{ year_fees }} per year, or € {{ study_fees }} for your entire study duration.
@@ -17,8 +18,11 @@ It is still possible to be associated with Thalia, even if you do not study Computing Science or Information Sciences (anymore): You can become a benefactor. For at least € {{ year_fees }} per year, you too can enjoy everything Thalia has to offer.
- Note that this form is only for member registration. Please visit the board room if you want to - become a benefactor. + + Note that this form is only for member registration. + Please use the benefactor registration page + if you want to become a benefactor. + {% endblocktrans %}

@@ -30,29 +34,32 @@ {% endblocktrans %}

+

+ {% blocktrans trimmed %} + If you have any other questions about Thalia and/or your membership, feel free to email + info@thalia.nu! + {% endblocktrans %} +

+
-
+ {% csrf_token %} -
+
{% bootstrap_field form.length %} -
-
{% bootstrap_field form.first_name %} {% bootstrap_field form.last_name %} - {% bootstrap_field form.birthday %} -
-
- - -
-
+ {% bootstrap_field form.address_street %} + {% bootstrap_field form.address_street2 %} + {% bootstrap_field form.address_postal_code %} + {% bootstrap_field form.address_city %} + {% bootstrap_field form.address_country %} +
+
{% bootstrap_field form.email %}
@@ -65,36 +72,26 @@
{% bootstrap_field form.phone_number %} -
- -
- {% bootstrap_field form.address_street %} - {% bootstrap_field form.address_street2 %} - {% bootstrap_field form.address_postal_code %} - {% bootstrap_field form.address_city %} - {% bootstrap_field form.address_country %} -
-
- {% bootstrap_field form.student_number %} - {% bootstrap_field form.programme %} - {% bootstrap_field form.starting_year %} -
+ {% bootstrap_field form.birthday %} -
- + for="id_optin_birthday">{% trans "Display birthday in calendar" %}
+ + {% bootstrap_field form.student_number %} + {% bootstrap_field form.programme %} + {% bootstrap_field form.starting_year %} + + {% bootstrap_field form.privacy_policy %}
- +
diff --git a/website/registrations/templates/registrations/renewal.html b/website/registrations/templates/registrations/renewal.html index 98a1f225..bf9bb820 100644 --- a/website/registrations/templates/registrations/renewal.html +++ b/website/registrations/templates/registrations/renewal.html @@ -1,8 +1,14 @@ {% extends "base.html" %} -{% load i18n bootstrap4 alert %} +{% load i18n bootstrap4 alert compress static %} -{% block title %}{% trans "renewal"|capfirst %} — - {{ block.super }}{% endblock %} +{% block title %}{% trans "renewal"|capfirst %} — {{ block.super }}{% endblock %} + +{% block js_body %} + {{ block.super }} + {% compress js %} + + {% endcompress %} +{% endblock %} {% block body %}
@@ -109,13 +115,6 @@ have to renew your membership. {% endblocktrans %}

- {% elif latest_membership.type == 'benefactor' and not was_member %} -

- {% blocktrans trimmed %} - You're a benefactor. Contact the board to renew your - membership. - {% endblocktrans %} -

{% elif latest_membership.until is None %}

{% blocktrans trimmed %} @@ -132,44 +131,41 @@ {% endfor %} {% endfor %}

+ class="col-lg-6 offset-lg-3" data-benefactor-type="{{ benefactor_type }}"> {% csrf_token %} +
{% if not latest_membership.type == 'benefactor' %} {% bootstrap_field form.membership_type %} {% bootstrap_field form.length %} + {% bootstrap_field form.contribution form_group_class='form-group required-field d-none' %} {% else %}
+ id="id_membership_type" + type="text" class="form-control" />
+ value="{% trans 'One year' %}" + type="text" + id="id_membership_length" + class="form-control" />
+ {% bootstrap_field form.contribution %} {% endif %} +
-
-
- - -
-
+ {% bootstrap_field form.privacy_policy %} + {% if latest_membership.type == 'benefactor' %} + {% bootstrap_field form.icis_employee %} + {% endif %}
John Doe wants to become a benefactor' + ) + + with self.subTest('Renewal'): + response = self.client.get(reverse('registrations:reference', + args=(self.renewal.pk,))) + self.assertEqual(200, response.status_code) + self.assertEqual('Johnny Test', response.context['name']) + self.assertEqual(False, response.context['success']) + self.assertContains( + response, + 'Johnny Test wants to become a benefactor' + ) + + def test_entry_saves_correctly(self): + """ + If a entry is saved it should redirect to the success page + which should show the right content. And the Reference object + should be saved. + """ + response = self.client.post( + reverse('registrations:reference', args=(self.registration.pk,)), + follow=True + ) + self.assertEqual(200, response.status_code) + self.assertEqual( + [(reverse('registrations:reference-success', + args=(self.registration.pk,)), 302)], + response.redirect_chain + ) + self.assertEqual('John Doe', response.context['name']) + self.assertEqual(True, response.context['success']) + self.assertContains( + response, + 'Your reference has been saved.' + ) + + self.assertTrue( + Reference.objects.filter( + member=self.login_user, + entry=self.registration + ).exists() + ) + + def test_entry_reference_exists(self): + """ + If there is already a reference for an entry then the page should + show an error and not redirect. + """ + Reference.objects.create( + member=self.login_user, + entry=self.registration + ) + + response = self.client.post( + reverse('registrations:reference', args=(self.registration.pk,)), + follow=True + ) + self.assertEqual(200, response.status_code) + self.assertEqual( + [], + response.redirect_chain + ) + self.assertEqual({ + '__all__': ['Reference with this Member and Entry already exists.'] + }, response.context['form'].errors) + self.assertEqual(False, response.context['success']) + self.assertContains( + response, + 'You\'ve already given a reference for this person.' + ) diff --git a/website/registrations/urls.py b/website/registrations/urls.py index 252b5dbb..fde62b64 100644 --- a/website/registrations/urls.py +++ b/website/registrations/urls.py @@ -4,13 +4,17 @@ from django.views.generic import TemplateView from .views import (BecomeAMemberView, ConfirmEmailView, EntryAdminView, MemberRegistrationFormView, - RenewalFormView) + RenewalFormView, BenefactorRegistrationFormView, + ReferenceCreateView) app_name = "registrations" urlpatterns = [ path('', BecomeAMemberView.as_view(), name='index'), - path('register/', MemberRegistrationFormView.as_view(), name='register'), + path('register/member/', MemberRegistrationFormView.as_view(), + name='register-member'), + path('register/benefactor/', BenefactorRegistrationFormView.as_view(), + name='register-benefactor'), path('register/success/', TemplateView.as_view( template_name='registrations/register_success.html'), name='register-success'), @@ -22,4 +26,8 @@ urlpatterns = [ name='admin-process'), path('confirm-email//', ConfirmEmailView.as_view(), name='confirm-email'), + path('reference//', + ReferenceCreateView.as_view(), name='reference'), + path('reference//success', + ReferenceCreateView.as_view(success=True), name='reference-success'), ] diff --git a/website/registrations/views.py b/website/registrations/views.py index e4619a7a..ef88846b 100644 --- a/website/registrations/views.py +++ b/website/registrations/views.py @@ -6,19 +6,21 @@ from django.contrib.admin.views.decorators import staff_member_required from django.contrib.auth.decorators import login_required, permission_required from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError -from django.shortcuts import redirect +from django.http import Http404 +from django.shortcuts import redirect, get_object_or_404 from django.template.defaultfilters import floatformat from django.urls import reverse from django.utils import timezone from django.utils.decorators import method_decorator from django.utils.translation import ugettext_lazy as _ from django.views import View -from django.views.generic import FormView +from django.views.generic import FormView, CreateView from django.views.generic.base import TemplateResponseMixin, TemplateView +from members.decorators import membership_required from members.models import Membership from . import emails, forms, services -from .models import Entry, Registration, Renewal +from .models import Entry, Registration, Renewal, Reference class BecomeAMemberView(TemplateView): @@ -31,7 +33,6 @@ class BecomeAMemberView(TemplateView): Entry.MEMBERSHIP_YEAR], 2) context['study_fees'] = floatformat(settings.MEMBERSHIP_PRICES[ Entry.MEMBERSHIP_STUDY], 2) - context['member_form_url'] = reverse('registrations:register') return context @@ -95,25 +96,31 @@ class ConfirmEmailView(View, TemplateResponseMixin): template_name = 'registrations/confirm_email.html' def get(self, request, *args, **kwargs): - entry = Entry.objects.filter(pk=kwargs['pk']) + queryset = Registration.objects.filter(pk=kwargs['pk']) processed = 0 try: - processed = services.confirm_entry(entry) + processed = services.confirm_entry(queryset) except ValidationError: pass if processed == 0: - return redirect('registrations:register') + return redirect('registrations:register-member') - emails.send_new_registration_board_message(entry.get()) + registration = queryset.get() + + if (registration.membership_type == Membership.BENEFACTOR + and not registration.no_references): + emails.send_references_information_message(registration) + + emails.send_new_registration_board_message(registration) return self.render_to_response({}) -class MemberRegistrationFormView(FormView): +class BaseRegistrationFormView(FormView): """ - View that renders the membership registration form + View that renders a membership registration form """ form_class = forms.MemberRegistrationForm template_name = 'registrations/register_member.html' @@ -124,7 +131,6 @@ class MemberRegistrationFormView(FormView): Entry.MEMBERSHIP_YEAR], 2) context['study_fees'] = floatformat(settings.MEMBERSHIP_PRICES[ Entry.MEMBERSHIP_STUDY], 2) - context['privacy_policy_url'] = reverse('privacy-policy') return context def get(self, request, *args, **kwargs): @@ -132,6 +138,18 @@ class MemberRegistrationFormView(FormView): return redirect('registrations:renew') return super().get(request, args, kwargs) + def form_valid(self, form): + form.save() + return redirect('registrations:register-success') + + +class MemberRegistrationFormView(BaseRegistrationFormView): + """ + View that renders the `member` membership registration form + """ + form_class = forms.MemberRegistrationForm + template_name = 'registrations/register_member.html' + def post(self, request, *args, **kwargs): request.POST = request.POST.dict() request.POST['language'] = request.LANGUAGE_CODE @@ -139,8 +157,27 @@ class MemberRegistrationFormView(FormView): return super().post(request, *args, **kwargs) def form_valid(self, form): - form.save() - return redirect('registrations:register-success') + response = super().form_valid(form) + emails.send_registration_email_confirmation(form.instance) + return response + + +class BenefactorRegistrationFormView(BaseRegistrationFormView): + """ + View that renders the `benefactor` membership registration form + """ + form_class = forms.BenefactorRegistrationForm + template_name = 'registrations/register_benefactor.html' + + def post(self, request, *args, **kwargs): + request.POST = request.POST.dict() + request.POST['language'] = request.LANGUAGE_CODE + request.POST['membership_type'] = Membership.BENEFACTOR + request.POST['length'] = Entry.MEMBERSHIP_YEAR + request.POST['remarks'] = ('Registered as iCIS employee' + if 'icis_employee' in request.POST else '') + request.POST['no_references'] = 'icis_employee' in request.POST + return super().post(request, *args, **kwargs) @method_decorator(login_required, name='dispatch') @@ -148,7 +185,7 @@ class RenewalFormView(FormView): """ View that renders the membership renewal form """ - form_class = forms.MemberRenewalForm + form_class = forms.RenewalForm template_name = 'registrations/renewal.html' def get_context_data(self, **kwargs): @@ -160,7 +197,7 @@ class RenewalFormView(FormView): context['latest_membership'] = self.request.member.latest_membership context['was_member'] = Membership.objects.filter( user=self.request.member, type=Membership.MEMBER).exists() - context['privacy_policy_url'] = reverse('privacy-policy') + context['benefactor_type'] = Membership.BENEFACTOR return context def get_form(self, form_class=None): @@ -189,9 +226,66 @@ class RenewalFormView(FormView): request.POST['membership_type'] = Membership.BENEFACTOR request.POST['length'] = Entry.MEMBERSHIP_YEAR request.POST['member'] = request.member.pk + request.POST['remarks'] = '' + request.POST['no_references'] = False + + if request.POST['membership_type'] == Membership.BENEFACTOR: + if Membership.objects.filter(user=request.member, + type=Membership.MEMBER).exists(): + request.POST['remarks'] = 'Was a Thalia member in the past.' + request.POST['no_references'] = True + if 'icis_employee' in request.POST: + request.POST['remarks'] = 'Registered as iCIS employee.' + request.POST['no_references'] = True + return super().post(request, *args, **kwargs) def form_valid(self, form): renewal = form.save() + if not renewal.no_references: + emails.send_references_information_message(renewal) emails.send_new_renewal_board_message(renewal) return redirect('registrations:renew-success') + + +@method_decorator(login_required, name='dispatch') +@method_decorator(membership_required, name='dispatch') +class ReferenceCreateView(CreateView): + """ + View that renders a reference creation form + """ + model = Reference + fields = '__all__' + template_name = 'registrations/reference.html' + entry = None + success = False + + def get_success_url(self): + return reverse('registrations:reference-success', + args=(self.entry.pk,)) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + context['success'] = self.success + try: + context['name'] = self.entry.registration.get_full_name() + except Registration.DoesNotExist: + context['name'] = self.entry.renewal.member.get_full_name() + + return context + + def dispatch(self, request, *args, **kwargs): + self.entry = get_object_or_404(Entry, pk=kwargs.get('pk')) + + if (self.entry.no_references or + self.entry.membership_type != Membership.BENEFACTOR): + raise Http404 + + return super().dispatch(request, *args, **kwargs) + + def post(self, request, *args, **kwargs): + request.POST = request.POST.dict() + request.POST['member'] = request.member.pk + request.POST['entry'] = kwargs['pk'] + return super().post(request, *args, **kwargs) diff --git a/website/thaliawebsite/settings/settings.py b/website/thaliawebsite/settings/settings.py index b7933532..96224442 100644 --- a/website/thaliawebsite/settings/settings.py +++ b/website/thaliawebsite/settings/settings.py @@ -328,6 +328,10 @@ TINYMCE_DEFAULT_CONFIG = { 'remove_script_host': False, } +BOOTSTRAP4 = { + 'required_css_class': 'required-field' +} + DEFAULT_EXCEPTION_REPORTER_FILTER = ( 'utils.exception_filter.ThaliaSafeExceptionReporterFilter') diff --git a/website/thaliawebsite/static/css/main.scss b/website/thaliawebsite/static/css/main.scss index 63c3d646..b5210875 100755 --- a/website/thaliawebsite/static/css/main.scss +++ b/website/thaliawebsite/static/css/main.scss @@ -24,6 +24,7 @@ @import "../../pizzas/static/pizzas/css/style.scss"; @import "../../education/static/education/css/style.scss"; @import "../../payments/static/payments/css/style.scss"; +@import "../../registrations/static/registrations/css/style.scss"; @import "./fontawesome/fontawesome.scss"; @import "./fontawesome/solid.scss"; -- GitLab From e434d2b924f23e39a450fbf98ed2c36cbd485346 Mon Sep 17 00:00:00 2001 From: Thalia WWW Date: Sat, 4 May 2019 23:29:51 +0200 Subject: [PATCH 2/2] Add Google Autocomplete --- .../static/registrations/js/main.js | 28 +++++++++++++++++++ .../registrations/register_benefactor.html | 12 ++++++-- .../registrations/register_member.html | 9 ++++++ website/registrations/tests/test_views.py | 6 ++-- website/registrations/views.py | 1 + website/thaliawebsite/settings/settings.py | 1 + 6 files changed, 53 insertions(+), 4 deletions(-) diff --git a/website/registrations/static/registrations/js/main.js b/website/registrations/static/registrations/js/main.js index 45775313..da0666cf 100644 --- a/website/registrations/static/registrations/js/main.js +++ b/website/registrations/static/registrations/js/main.js @@ -20,4 +20,32 @@ $(function() { changeVisiblity(this.value); }); } + + var input = document.querySelector('#id_address_street'); + var autocomplete = new google.maps.places.Autocomplete(input); + autocomplete.addListener('place_changed', function () { + var place = autocomplete.getPlace(); + + var getAddressItem = function(type, length) { + var address = place.address_components; + var addressItem = address.find(function (item) { + return item.types.includes(type); + }); + var key = length + '_name'; + return addressItem && addressItem[key] ? addressItem[key] : ''; + }; + + $('#id_address_street').val( + getAddressItem('route', 'long') + ' ' + + getAddressItem('street_number', 'long')); + + $('#id_address_city').val( + getAddressItem('locality', 'long')); + + $('#id_address_postal_code').val( + getAddressItem('postal_code', 'long')); + + $('#id_address_country').val( + getAddressItem('country', 'short').toUpperCase()); + }); }); diff --git a/website/registrations/templates/registrations/register_benefactor.html b/website/registrations/templates/registrations/register_benefactor.html index c6708c63..bc186200 100644 --- a/website/registrations/templates/registrations/register_benefactor.html +++ b/website/registrations/templates/registrations/register_benefactor.html @@ -1,8 +1,16 @@ {% extends "base.html" %} {% load i18n static compress bootstrap4 %} -{% block title %}{% trans "registration"|capfirst %} — - {{ block.super }}{% endblock %} +{% block title %}{% trans "registration"|capfirst %} — {{ block.super }}{% endblock %} + +{% block js_body %} + {{ block.super }} + + {% compress js %} + + {% endcompress %} +{% endblock %} {% block body %}
diff --git a/website/registrations/templates/registrations/register_member.html b/website/registrations/templates/registrations/register_member.html index 785b9348..545d802c 100644 --- a/website/registrations/templates/registrations/register_member.html +++ b/website/registrations/templates/registrations/register_member.html @@ -3,6 +3,15 @@ {% block title %}{% trans "registration"|capfirst %} — {{ block.super }}{% endblock %} +{% block js_body %} + {{ block.super }} + + {% compress js %} + + {% endcompress %} +{% endblock %} + {% block body %}
diff --git a/website/registrations/tests/test_views.py b/website/registrations/tests/test_views.py index ceeeb998..e0b29c83 100644 --- a/website/registrations/tests/test_views.py +++ b/website/registrations/tests/test_views.py @@ -10,7 +10,7 @@ from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.http import HttpResponse from django.template.defaultfilters import floatformat -from django.test import Client, TestCase, RequestFactory +from django.test import Client, TestCase, RequestFactory, override_settings from django.urls import reverse from django.utils import timezone from django.utils.translation import ugettext_lazy as _ @@ -463,14 +463,16 @@ class BaseRegistrationFormViewTest(TestCase): self.rf = RequestFactory() self.view = views.BaseRegistrationFormView() + @override_settings(GOOGLE_PLACES_API_KEY='hello') def test_get_context_data(self): self.view.request = self.rf.post('/') context = self.view.get_context_data() - self.assertEqual(len(context), 4) + self.assertEqual(len(context), 5) self.assertEqual(context['year_fees'], floatformat( settings.MEMBERSHIP_PRICES[Entry.MEMBERSHIP_YEAR], 2)) self.assertEqual(context['study_fees'], floatformat( settings.MEMBERSHIP_PRICES[Entry.MEMBERSHIP_STUDY], 2)) + self.assertEqual(context['google_api_key'], 'hello') @mock.patch('django.views.generic.FormView.get') def test_get(self, super_get): diff --git a/website/registrations/views.py b/website/registrations/views.py index ef88846b..7f0fee01 100644 --- a/website/registrations/views.py +++ b/website/registrations/views.py @@ -127,6 +127,7 @@ class BaseRegistrationFormView(FormView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) + context['google_api_key'] = settings.GOOGLE_PLACES_API_KEY context['year_fees'] = floatformat(settings.MEMBERSHIP_PRICES[ Entry.MEMBERSHIP_YEAR], 2) context['study_fees'] = floatformat(settings.MEMBERSHIP_PRICES[ diff --git a/website/thaliawebsite/settings/settings.py b/website/thaliawebsite/settings/settings.py index 96224442..92d7e946 100644 --- a/website/thaliawebsite/settings/settings.py +++ b/website/thaliawebsite/settings/settings.py @@ -296,6 +296,7 @@ ACTIVEMEMBERS_NEXTCLOUD_API_SECRET = os.environ.get( # Google maps API key and secrets GOOGLE_MAPS_API_KEY = os.environ.get('GOOGLE_MAPS_API_KEY', '') GOOGLE_MAPS_API_SECRET = os.environ.get('GOOGLE_MAPS_API_SECRET', '') +GOOGLE_PLACES_API_KEY = os.environ.get('GOOGLE_PLACES_API_KEY', '') # Photos settings PHOTO_UPLOAD_SIZE = 1920, 1080 -- GitLab