codeforlife-portal 7.3.7__py2.py3-none-any.whl → 7.4.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of codeforlife-portal might be problematic. Click here for more details.

Files changed (30) hide show
  1. cfl_common/common/app_settings.py +0 -1
  2. cfl_common/common/helpers/emails.py +153 -36
  3. cfl_common/common/mail.py +5 -0
  4. {codeforlife_portal-7.3.7.dist-info → codeforlife_portal-7.4.0.dist-info}/METADATA +2 -2
  5. {codeforlife_portal-7.3.7.dist-info → codeforlife_portal-7.4.0.dist-info}/RECORD +30 -29
  6. example_project/portal_test_settings.py +1 -0
  7. example_project/settings.py +1 -0
  8. portal/__init__.py +1 -1
  9. portal/context_processors.py +5 -1
  10. portal/forms/dotmailer.py +39 -4
  11. portal/static/portal/img/rr_advanced.png +0 -0
  12. portal/static/portal/js/common.js +35 -5
  13. portal/static/portal/sass/modules/_colours.scss +3 -0
  14. portal/static/portal/sass/modules/_levels.scss +1 -1
  15. portal/static/portal/sass/modules/_mixins.scss +5 -0
  16. portal/static/portal/sass/partials/_buttons.scss +31 -3
  17. portal/static/portal/sass/partials/_header.scss +7 -6
  18. portal/static/portal/sass/partials/_popup.scss +7 -2
  19. portal/templates/portal/base.html +12 -3
  20. portal/templates/portal/home_learning.html +8 -7
  21. portal/templates/portal/partials/donate_popup.html +46 -0
  22. portal/templates/portal/partials/header.html +34 -30
  23. portal/tests/test_emails.py +17 -22
  24. portal/tests/{test_newsletter_footer.py → test_global_forms.py} +17 -1
  25. portal/urls.py +2 -0
  26. portal/views/dotmailer.py +30 -2
  27. portal/views/teacher/dashboard.py +174 -57
  28. {codeforlife_portal-7.3.7.dist-info → codeforlife_portal-7.4.0.dist-info}/LICENSE.md +0 -0
  29. {codeforlife_portal-7.3.7.dist-info → codeforlife_portal-7.4.0.dist-info}/WHEEL +0 -0
  30. {codeforlife_portal-7.3.7.dist-info → codeforlife_portal-7.4.0.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,6 @@ DOTDIGITAL_AUTH = getattr(settings, "DOTDIGITAL_AUTH", "")
8
8
 
9
9
  # Dotmailer URLs for adding users to the newsletter address book
10
10
  DOTMAILER_CREATE_CONTACT_URL = getattr(settings, "DOTMAILER_CREATE_CONTACT_URL", "")
11
- DOTMAILER_MAIN_ADDRESS_BOOK_URL = getattr(settings, "DOTMAILER_MAIN_ADDRESS_BOOK_URL", "")
12
11
  DOTMAILER_TEACHER_ADDRESS_BOOK_URL = getattr(settings, "DOTMAILER_TEACHER_ADDRESS_BOOK_URL", "")
13
12
  DOTMAILER_STUDENT_ADDRESS_BOOK_URL = getattr(settings, "DOTMAILER_STUDENT_ADDRESS_BOOK_URL", "")
14
13
  DOTMAILER_NO_ACCOUNT_ADDRESS_BOOK_URL = getattr(settings, "DOTMAILER_NO_ACCOUNT_ADDRESS_BOOK_URL", "")
@@ -5,7 +5,12 @@ from uuid import uuid4
5
5
 
6
6
  import jwt
7
7
  from common import app_settings
8
- from common.mail import campaign_ids, django_send_email, send_dotdigital_email
8
+ from common.mail import (
9
+ address_book_ids,
10
+ campaign_ids,
11
+ django_send_email,
12
+ send_dotdigital_email,
13
+ )
9
14
  from common.models import Student, Teacher
10
15
  from django.conf import settings
11
16
  from django.contrib.auth.models import User
@@ -15,9 +20,15 @@ from django.utils import timezone
15
20
  from requests import delete, get, post, put
16
21
  from requests.exceptions import RequestException
17
22
 
18
- NOTIFICATION_EMAIL = "Code For Life Notification <" + app_settings.EMAIL_ADDRESS + ">"
19
- VERIFICATION_EMAIL = "Code For Life Verification <" + app_settings.EMAIL_ADDRESS + ">"
20
- PASSWORD_RESET_EMAIL = "Code For Life Password Reset <" + app_settings.EMAIL_ADDRESS + ">"
23
+ NOTIFICATION_EMAIL = (
24
+ "Code For Life Notification <" + app_settings.EMAIL_ADDRESS + ">"
25
+ )
26
+ VERIFICATION_EMAIL = (
27
+ "Code For Life Verification <" + app_settings.EMAIL_ADDRESS + ">"
28
+ )
29
+ PASSWORD_RESET_EMAIL = (
30
+ "Code For Life Password Reset <" + app_settings.EMAIL_ADDRESS + ">"
31
+ )
21
32
  INVITE_FROM = "Code For Life Invitation <" + app_settings.EMAIL_ADDRESS + ">"
22
33
 
23
34
 
@@ -41,7 +52,9 @@ def generate_token_for_email(email: str, new_email: str = ""):
41
52
  "email": email,
42
53
  "new_email": new_email,
43
54
  "email_verification_token": uuid4().hex[:30],
44
- "expires": (timezone.now() + datetime.timedelta(hours=1)).timestamp(),
55
+ "expires": (
56
+ timezone.now() + datetime.timedelta(hours=1)
57
+ ).timestamp(),
45
58
  },
46
59
  settings.SECRET_KEY,
47
60
  algorithm="HS256",
@@ -62,10 +75,21 @@ def send_email(
62
75
  plaintext_template="email.txt",
63
76
  html_template="email.html",
64
77
  ):
65
- django_send_email(sender, recipients, subject, text_content, title, replace_url, plaintext_template, html_template)
78
+ django_send_email(
79
+ sender,
80
+ recipients,
81
+ subject,
82
+ text_content,
83
+ title,
84
+ replace_url,
85
+ plaintext_template,
86
+ html_template,
87
+ )
66
88
 
67
89
 
68
- def send_verification_email(request, user, data, new_email=None, age=None, school=None):
90
+ def send_verification_email(
91
+ request, user, data, new_email=None, age=None, school=None
92
+ ):
69
93
  """
70
94
  Sends emails relating to email address verification.
71
95
 
@@ -98,18 +122,30 @@ def send_verification_email(request, user, data, new_email=None, age=None, schoo
98
122
  url = f"{request.build_absolute_uri(reverse('verify_email', kwargs={'token': verification}))}"
99
123
 
100
124
  send_dotdigital_email(
101
- campaign_ids["verify_released_student"], [user.email],
102
- personalization_values={"VERIFICATION_LINK": url, "SCHOOL_NAME": school.name}
125
+ campaign_ids["verify_released_student"],
126
+ [user.email],
127
+ personalization_values={
128
+ "VERIFICATION_LINK": url,
129
+ "SCHOOL_NAME": school.name,
130
+ },
103
131
  )
104
132
  else:
105
133
  url = f"{request.build_absolute_uri(reverse('verify_email', kwargs={'token': verification}))}"
106
134
 
107
135
  send_dotdigital_email(
108
- campaign_ids["verify_new_user"], [user.email], personalization_values={"VERIFICATION_LINK": url}
136
+ campaign_ids["verify_new_user"],
137
+ [user.email],
138
+ personalization_values={"VERIFICATION_LINK": url},
109
139
  )
110
140
 
111
141
  if _newsletter_ticked(data):
112
- add_to_dotmailer(user.first_name, user.last_name, user.email, DotmailerUserType.TEACHER)
142
+ add_to_dotmailer(
143
+ user.first_name,
144
+ user.last_name,
145
+ user.email,
146
+ address_book_ids["newsletter"],
147
+ DotmailerUserType.TEACHER,
148
+ )
113
149
  # if the user is an independent student
114
150
  else:
115
151
  if age < 13:
@@ -117,29 +153,50 @@ def send_verification_email(request, user, data, new_email=None, age=None, schoo
117
153
  send_dotdigital_email(
118
154
  campaign_ids["verify_new_user_via_parent"],
119
155
  [user.email],
120
- personalization_values={"FIRST_NAME": user.first_name, "ACTIVATION_LINK": url},
156
+ personalization_values={
157
+ "FIRST_NAME": user.first_name,
158
+ "ACTIVATION_LINK": url,
159
+ },
121
160
  )
122
161
  else:
123
162
  url = f"{request.build_absolute_uri(reverse('verify_email', kwargs={'token': verification}))}"
124
163
  send_dotdigital_email(
125
- campaign_ids["verify_new_user"], [user.email], personalization_values={"VERIFICATION_LINK": url}
164
+ campaign_ids["verify_new_user"],
165
+ [user.email],
166
+ personalization_values={"VERIFICATION_LINK": url},
126
167
  )
127
168
 
128
169
  if _newsletter_ticked(data):
129
- add_to_dotmailer(user.first_name, user.last_name, user.email, DotmailerUserType.STUDENT)
170
+ add_to_dotmailer(
171
+ user.first_name,
172
+ user.last_name,
173
+ user.email,
174
+ address_book_ids["newsletter"],
175
+ DotmailerUserType.STUDENT,
176
+ )
130
177
  # verifying change of email address.
131
178
  else:
132
179
  verification = generate_token(user, new_email)
133
180
  url = f"{request.build_absolute_uri(reverse('verify_email', kwargs={'token': verification}))}"
134
181
  send_dotdigital_email(
135
- campaign_ids["email_change_verification"], [new_email], personalization_values={"VERIFICATION_LINK": url}
182
+ campaign_ids["email_change_verification"],
183
+ [new_email],
184
+ personalization_values={"VERIFICATION_LINK": url},
136
185
  )
137
186
 
138
187
 
139
- def add_to_dotmailer(first_name: str, last_name: str, email: str, user_type: DotmailerUserType):
188
+ def add_to_dotmailer(
189
+ first_name: str,
190
+ last_name: str,
191
+ email: str,
192
+ address_book_id: int,
193
+ user_type: DotmailerUserType = None,
194
+ ):
140
195
  try:
141
196
  create_contact(first_name, last_name, email)
142
- add_contact_to_address_book(first_name, last_name, email, user_type)
197
+ add_contact_to_address_book(
198
+ first_name, last_name, email, address_book_id, user_type
199
+ )
143
200
  except RequestException:
144
201
  return HttpResponse(status=404)
145
202
 
@@ -157,15 +214,34 @@ def create_contact(first_name, last_name, email):
157
214
  {"key": "FULLNAME", "value": f"{first_name} {last_name}"},
158
215
  ],
159
216
  },
160
- "consentFields": [{"fields": [{"key": "DATETIMECONSENTED", "value": datetime.datetime.now().__str__()}]}],
217
+ "consentFields": [
218
+ {
219
+ "fields": [
220
+ {
221
+ "key": "DATETIMECONSENTED",
222
+ "value": datetime.datetime.now().__str__(),
223
+ }
224
+ ]
225
+ }
226
+ ],
161
227
  "preferences": app_settings.DOTMAILER_DEFAULT_PREFERENCES,
162
228
  }
163
229
 
164
- post(url, json=body, auth=(app_settings.DOTMAILER_USER, app_settings.DOTMAILER_PASSWORD))
230
+ post(
231
+ url,
232
+ json=body,
233
+ auth=(app_settings.DOTMAILER_USER, app_settings.DOTMAILER_PASSWORD),
234
+ )
165
235
 
166
236
 
167
- def add_contact_to_address_book(first_name: str, last_name: str, email: str, user_type: DotmailerUserType):
168
- main_address_book_url = app_settings.DOTMAILER_MAIN_ADDRESS_BOOK_URL
237
+ def add_contact_to_address_book(
238
+ first_name: str,
239
+ last_name: str,
240
+ email: str,
241
+ address_book_id: int,
242
+ user_type: DotmailerUserType = None,
243
+ ):
244
+ main_address_book_url = f"https://r1-api.dotmailer.com/v2/address-books/{address_book_id}/contacts"
169
245
 
170
246
  body = {
171
247
  "email": email,
@@ -178,16 +254,31 @@ def add_contact_to_address_book(first_name: str, last_name: str, email: str, use
178
254
  ],
179
255
  }
180
256
 
181
- post(main_address_book_url, json=body, auth=(app_settings.DOTMAILER_USER, app_settings.DOTMAILER_PASSWORD))
182
-
183
- specific_address_book_url = app_settings.DOTMAILER_NO_ACCOUNT_ADDRESS_BOOK_URL
257
+ post(
258
+ main_address_book_url,
259
+ json=body,
260
+ auth=(app_settings.DOTMAILER_USER, app_settings.DOTMAILER_PASSWORD),
261
+ )
184
262
 
185
- if user_type == DotmailerUserType.TEACHER:
186
- specific_address_book_url = app_settings.DOTMAILER_TEACHER_ADDRESS_BOOK_URL
187
- elif user_type == DotmailerUserType.STUDENT:
188
- specific_address_book_url = app_settings.DOTMAILER_STUDENT_ADDRESS_BOOK_URL
263
+ if user_type is not None:
264
+ specific_address_book_url = (
265
+ app_settings.DOTMAILER_NO_ACCOUNT_ADDRESS_BOOK_URL
266
+ )
189
267
 
190
- post(specific_address_book_url, json=body, auth=(app_settings.DOTMAILER_USER, app_settings.DOTMAILER_PASSWORD))
268
+ if user_type == DotmailerUserType.TEACHER:
269
+ specific_address_book_url = (
270
+ app_settings.DOTMAILER_TEACHER_ADDRESS_BOOK_URL
271
+ )
272
+ elif user_type == DotmailerUserType.STUDENT:
273
+ specific_address_book_url = (
274
+ app_settings.DOTMAILER_STUDENT_ADDRESS_BOOK_URL
275
+ )
276
+
277
+ post(
278
+ specific_address_book_url,
279
+ json=body,
280
+ auth=(app_settings.DOTMAILER_USER, app_settings.DOTMAILER_PASSWORD),
281
+ )
191
282
 
192
283
 
193
284
  def delete_contact(email: str):
@@ -195,8 +286,16 @@ def delete_contact(email: str):
195
286
  user = get_dotmailer_user_by_email(email)
196
287
  user_id = user.get("id")
197
288
  if user_id:
198
- url = app_settings.DOTMAILER_DELETE_USER_BY_ID_URL.replace("ID", str(user_id))
199
- delete(url, auth=(app_settings.DOTMAILER_USER, app_settings.DOTMAILER_PASSWORD))
289
+ url = app_settings.DOTMAILER_DELETE_USER_BY_ID_URL.replace(
290
+ "ID", str(user_id)
291
+ )
292
+ delete(
293
+ url,
294
+ auth=(
295
+ app_settings.DOTMAILER_USER,
296
+ app_settings.DOTMAILER_PASSWORD,
297
+ ),
298
+ )
200
299
  except RequestException:
201
300
  return HttpResponse(status=404)
202
301
 
@@ -204,7 +303,9 @@ def delete_contact(email: str):
204
303
  def get_dotmailer_user_by_email(email):
205
304
  url = app_settings.DOTMAILER_GET_USER_BY_EMAIL_URL.replace("EMAIL", email)
206
305
 
207
- response = get(url, auth=(app_settings.DOTMAILER_USER, app_settings.DOTMAILER_PASSWORD))
306
+ response = get(
307
+ url, auth=(app_settings.DOTMAILER_USER, app_settings.DOTMAILER_PASSWORD)
308
+ )
208
309
 
209
310
  return json.loads(response.content)
210
311
 
@@ -212,7 +313,9 @@ def get_dotmailer_user_by_email(email):
212
313
  def add_consent_record_to_dotmailer_user(user):
213
314
  consent_date_time = datetime.datetime.now().__str__()
214
315
 
215
- url = app_settings.DOTMAILER_PUT_CONSENT_DATA_URL.replace("USER_ID", str(user["id"]))
316
+ url = app_settings.DOTMAILER_PUT_CONSENT_DATA_URL.replace(
317
+ "USER_ID", str(user["id"])
318
+ )
216
319
  body = {
217
320
  "contact": {
218
321
  "email": user["email"],
@@ -220,10 +323,20 @@ def add_consent_record_to_dotmailer_user(user):
220
323
  "emailType": user["emailType"],
221
324
  "dataFields": user["dataFields"],
222
325
  },
223
- "consentFields": [{"fields": [{"key": "DATETIMECONSENTED", "value": consent_date_time}]}],
326
+ "consentFields": [
327
+ {
328
+ "fields": [
329
+ {"key": "DATETIMECONSENTED", "value": consent_date_time}
330
+ ]
331
+ }
332
+ ],
224
333
  }
225
334
 
226
- put(url, json=body, auth=(app_settings.DOTMAILER_USER, app_settings.DOTMAILER_PASSWORD))
335
+ put(
336
+ url,
337
+ json=body,
338
+ auth=(app_settings.DOTMAILER_USER, app_settings.DOTMAILER_PASSWORD),
339
+ )
227
340
 
228
341
 
229
342
  def send_dotmailer_consent_confirmation_email_to_user(user):
@@ -231,7 +344,11 @@ def send_dotmailer_consent_confirmation_email_to_user(user):
231
344
  campaign_id = app_settings.DOTMAILER_THANKS_FOR_STAYING_CAMPAIGN_ID
232
345
  body = {"campaignID": campaign_id, "contactIds": [str(user["id"])]}
233
346
 
234
- post(url, json=body, auth=(app_settings.DOTMAILER_USER, app_settings.DOTMAILER_PASSWORD))
347
+ post(
348
+ url,
349
+ json=body,
350
+ auth=(app_settings.DOTMAILER_USER, app_settings.DOTMAILER_PASSWORD),
351
+ )
235
352
 
236
353
 
237
354
  def update_indy_email(user, request, data):
cfl_common/common/mail.py CHANGED
@@ -32,6 +32,11 @@ campaign_ids = {
32
32
  "inactive_users_on_website_final_reminder": 1606215,
33
33
  }
34
34
 
35
+ address_book_ids = {
36
+ "newsletter": 9705772,
37
+ "donors": 37649245,
38
+ }
39
+
35
40
 
36
41
  def add_contact(email: str):
37
42
  """Add a new contact to Dotdigital."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: codeforlife-portal
3
- Version: 7.3.7
3
+ Version: 7.4.0
4
4
  Classifier: Programming Language :: Python
5
5
  Classifier: Programming Language :: Python :: 3.12
6
6
  Classifier: Framework :: Django
@@ -21,7 +21,7 @@ Requires-Dist: django-classy-tags==2.0.0
21
21
  Requires-Dist: libsass==0.23.0
22
22
  Requires-Dist: phonenumbers==8.12.12
23
23
  Requires-Dist: more-itertools==8.7.0
24
- Requires-Dist: cfl-common==7.3.7
24
+ Requires-Dist: cfl-common==7.4.0
25
25
  Requires-Dist: django-ratelimit==3.0.1
26
26
  Requires-Dist: django-preventconcurrentlogins==0.8.2
27
27
  Requires-Dist: django-csp==3.7
@@ -1,17 +1,17 @@
1
1
  cfl_common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  cfl_common/setup.py,sha256=5Rk-FXyWToTujXqGRYqeA0A5nJ4NC5woXxyb6NLLbpo,818
3
3
  cfl_common/common/__init__.py,sha256=XlncBOpKp_gekbKH7Y_i6yu1qy5tJc3Y8sn8cDy-Vgk,48
4
- cfl_common/common/app_settings.py,sha256=x2ROLY5Xl5LgqjxyTiChZvQorZYUXpFzEkaLsjh8UHo,2586
4
+ cfl_common/common/app_settings.py,sha256=Bw1DXkZpNIdwUJ-cIOjZnngH5_NbMXC0koW7NgQ0pKY,2495
5
5
  cfl_common/common/apps.py,sha256=49UXZ3bSkFKvIEOL4zM7y1sAhccQJyRtsoOg5XVd_8Y,129
6
6
  cfl_common/common/context_processors.py,sha256=X0iuX5qu9kMWa7q8osE9CJ2LgM7pPOYQFGdjm8X3rk0,236
7
7
  cfl_common/common/csp_config.py,sha256=9ECOLnp60ENRFAYEEIoYOMhqQzLgfKA-wkWxeUBwDrQ,2824
8
- cfl_common/common/mail.py,sha256=nCY5aRiyiBCudonewpHOQ3GnXhQu4HLJRaqx1vOYhfI,6799
8
+ cfl_common/common/mail.py,sha256=pIRfUMVoJWxdv74UqToj_0_pTVTC51z6QlFVLI3QBOw,6874
9
9
  cfl_common/common/models.py,sha256=yAULJtzuF4loEt6mPrgZjwHGiQH6Hqm82etE7Sofvys,15989
10
10
  cfl_common/common/permissions.py,sha256=gC6RQGZI2QDBbglx-xr_V4Hl2C2nf1V2_uPmEuoEcJo,2416
11
11
  cfl_common/common/utils.py,sha256=Nn2Npao9Uqad5Js_IdHwF-ow6wrPNpBLW4AO1LxoEBc,1727
12
12
  cfl_common/common/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  cfl_common/common/helpers/data_migration_loader.py,sha256=_BhS5lPmhcuVUbryBmJytlWdHyT02KYyxPkHar32mOE,1748
14
- cfl_common/common/helpers/emails.py,sha256=Gu4YAd977k7v_4qezcYFwWYEBUAV3r8pXZYDVxJL3qw,10700
14
+ cfl_common/common/helpers/emails.py,sha256=KyXIudPRgetggOnlNUohArF7cPBuazQI2V6dtMfKnrM,12368
15
15
  cfl_common/common/helpers/generators.py,sha256=kTL5e91I8wgmjJ-mu4jr9vIacjccUZ5pZSAz5cUNhdM,1505
16
16
  cfl_common/common/helpers/organisation.py,sha256=e-JKumKoXrkMTzZPv0H4ViWL8vtCt7oXJjn_zZ1ec00,427
17
17
  cfl_common/common/migrations/0001_initial.py,sha256=Y2kt2xmdCbrmDXCgqmhXeacicNg26Zj7L7SANSsgAAI,9664
@@ -104,23 +104,23 @@ deploy/static/robots.txt,sha256=5cS4RITuQhbpNzvpk4AyDCXdlIBfmfCoBYRvCHY2VT8,24
104
104
  deploy/templates/deploy/csrf_failure.html,sha256=-pBRPn4Y7nUdYHGpTHCokT9Boi-isuwuivF8V2K1SgM,412
105
105
  example_project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
106
  example_project/manage.py,sha256=EUgybZlZ7xk2Zf2KCwBbK_z7gf7Ifqs0_bl4Kijhdgo,242
107
- example_project/portal_test_settings.py,sha256=NfLY72mt1LR2c0_kxF-Yg5pCm2vQ52eceBMn573wW0Y,7247
108
- example_project/settings.py,sha256=Smpr5FHAaQQhXPgh6-Zsy-jnH5_ZV9htPTWJWlGqptI,5575
107
+ example_project/portal_test_settings.py,sha256=_gI7-HMoPJ-cDGO6n5UlEIHKHuTR5SC_Xt-l5Vdbx-0,7312
108
+ example_project/settings.py,sha256=HgGSG0n6Xggd0NieFVoJgn8vKGqyRbCddoB3CRuoT-Y,5640
109
109
  example_project/urls.py,sha256=3SsP5jvPAXV5xmduka4zbSZB5PbXggjsalu1ojY5KIo,434
110
110
  example_project/wsgi.py,sha256=U1W6WzZxZaIdYZ5tks7w9fqp5WS5qvn2iThsVcskrWw,829
111
- portal/__init__.py,sha256=78n5e3iTo_wrUz0JdZ97GiRv_VdAjcNbUcPaPgz5AyI,22
111
+ portal/__init__.py,sha256=Dbq9JbNzDz-GmfE0eozgGmdee4W8D3uVIIQv1bzHZPk,22
112
112
  portal/admin.py,sha256=on1-zNRnZvf2cwBN6GVRVYRhkaksrCgfzX8XPWtkvz8,6062
113
113
  portal/app_settings.py,sha256=DhWLQOwM0zVOXE3O5TNKbMM9K6agfLuCsHOdr1J7xEI,651
114
114
  portal/backends.py,sha256=2Dss6_WoQwPuDzJUF1yEaTQTNG4eUrD12ujJQ5cp5Tc,812
115
115
  portal/beta.py,sha256=0TCC-9_KZoM1nuzJ9FiuKR5n9JITdMYenHGQtRvn9UU,255
116
- portal/context_processors.py,sha256=1TrUZqnMqGa5f7ERph9EpBqojSMJvOrcpnJzTdeCLDI,133
116
+ portal/context_processors.py,sha256=Q68UhmArLPRchS2KmfVR4hKrijllXal3sO5cHYWC2Fc,222
117
117
  portal/handlers.py,sha256=gF99OfQrGcIGDnUyONhvylZNU8sl6XHYEurwu0fuiss,422
118
118
  portal/models.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
119
- portal/urls.py,sha256=lz5qLvtV47Jk2p5kmJtgDnr4PIDVoaFy0OCw3RyOavc,17898
119
+ portal/urls.py,sha256=4bGQQWiQI9zSG1oceVjQctUnDKggzzZsbYAxuBvijpY,18002
120
120
  portal/wsgi.py,sha256=3yRcNxBQG30NhzrVi93bX-DrbXtsIQBc70HiW5wbOyE,401
121
121
  portal/forms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
122
122
  portal/forms/admin.py,sha256=Cdl8-wvasAzvMfgUlFYzQjYeuyC7gIsSiy8V_-jMp7w,2080
123
- portal/forms/dotmailer.py,sha256=McD9_u8yxUfE7PSVG3MPMilRJtx9GTe9QsNzDDV3uuI,761
123
+ portal/forms/dotmailer.py,sha256=j-o9qthQc7xmOsHSQGEEoHvaEyOpzlWrj6bOnV0PpsI,1567
124
124
  portal/forms/error_messages.py,sha256=8d3z_3e2L-5zwj5hFhnUByC5k2CEpIVVuJg2nYkCUQ8,148
125
125
  portal/forms/invite_teacher.py,sha256=jkDNcCfkts4_lXRzhcI3xBam21Zn2yX9wMpMVhDtW1w,880
126
126
  portal/forms/organisation.py,sha256=QcQyd7AiqBmvt4y8uQSQylguUbKOKqo2pjqWIkpWjDg,7433
@@ -243,7 +243,7 @@ portal/static/portal/img/resources_hero.jpg,sha256=7a9UD_Hz8duS4IFfXo_dspgEbJpt2
243
243
  portal/static/portal/img/resources_montage.jpg,sha256=X6ahyduC1qA-Hv0J9cF33ikoyl_SW01djuDz1D-2JQ4,74159
244
244
  portal/static/portal/img/reuben.png,sha256=sJdtrEwtB8TJNIhlzk5uES37oidpODRoZ2F7mgmJCus,93064
245
245
  portal/static/portal/img/rob.png,sha256=7ZtcMzJ_khTflJJIhfU5YQzhgld2ceBB2QBpZX7yVKw,103324
246
- portal/static/portal/img/rr_advanced.png,sha256=jzxGWBjg7ERRcXgY_NmC6pRCpN7JOhedE3NWf_Fx3T0,76220
246
+ portal/static/portal/img/rr_advanced.png,sha256=hpyfd-FWGeuuRoBiMUq2bynpcBcwvRBnO3f04czb4Os,228593
247
247
  portal/static/portal/img/rr_beginner.png,sha256=xnLZ2bpOc5WbS-I9lu8hWcs8W8x3QaYDp21t64auMqU,55473
248
248
  portal/static/portal/img/rr_intermediate.png,sha256=fT8CXKMoMjyX05aEBXHNS4PseuVnqdiEWSr-enNsMeI,90538
249
249
  portal/static/portal/img/sadface.png,sha256=FU9d3XPCfe6sknfAymz8XPl_0nICp0geiqTQoOximvg,2148
@@ -267,7 +267,7 @@ portal/static/portal/img/colorboxImages/loading_background.png,sha256=lZ7Mxrcb7-
267
267
  portal/static/portal/img/colorboxImages/overlay.png,sha256=qy73bf_q55z4kktuaTaMhVrxDzVRCIjgmBQ5cbOmLtE,182
268
268
  portal/static/portal/js/bootstrap.min.js,sha256=nuL8_2cJ5NDSSwnKD8VqreErSWHtnEP9E7AySL-1ev4,39680
269
269
  portal/static/portal/js/carouselCards.js,sha256=IByMjKk37E-TCc3vx9nfF-IJWl9BOFPNVqf4WuD75GI,778
270
- portal/static/portal/js/common.js,sha256=_gNQ7eIgxiw8gviolmShD0aU3TAKDwIVUGGg9wznDAc,6499
270
+ portal/static/portal/js/common.js,sha256=kVUdm0luaqMLElbDq3Mht1k5DfkYHXAmJtce1Rekogg,7554
271
271
  portal/static/portal/js/independentLogin.js,sha256=wjjJBAF5arJ2iwW30lHTraG_f2FJ9UMGnXGpUSXwXIs,538
272
272
  portal/static/portal/js/independentRegistration.js,sha256=YWhIzwJcl0acU9weAKB7pWK76J1dpAuAQkI7DrUm3uA,2622
273
273
  portal/static/portal/js/join_create_game_toggle.js,sha256=eWxhMfzVKu1oft9iUFV4w8CgqMRv9-unb5P5cGi1h_4,393
@@ -394,21 +394,21 @@ portal/static/portal/sass/modules/_all.scss,sha256=k1U_FAKajqT8B3U53UT9B1Hc4jn5P
394
394
  portal/static/portal/sass/modules/_animation.scss,sha256=s75-4xRmPSmqwHUuyVy0OcnbS5jreRnotMXm-CuTLkE,39
395
395
  portal/static/portal/sass/modules/_breakpoints.scss,sha256=Nc7nrvjzQ0eK844ITuqwYcybIJxpYVFU5s30mz3BGOg,498
396
396
  portal/static/portal/sass/modules/_card_constants.scss,sha256=FUojcT5Fs-IYErt-frahD3HnVb06A515k4SQJrqVUfQ,107
397
- portal/static/portal/sass/modules/_colours.scss,sha256=6Wz1ev8T0nDmftQBWzUiCvOcJUrbkqQTCTisfnb-0Go,4250
397
+ portal/static/portal/sass/modules/_colours.scss,sha256=Wen7UlF98ijyDjIEsLeIzLx0fcgXOLjavAVIh8PfqX0,4350
398
398
  portal/static/portal/sass/modules/_homepage_constants.scss,sha256=GfSppJtWNxufu4hKO6vHX7YBQgmU1oB0KBonh-ltgv0,44
399
- portal/static/portal/sass/modules/_levels.scss,sha256=dGSpaNHkyuSY7b1QbFILzirx2X1EKjUgGGqlL2KuEMs,105
400
- portal/static/portal/sass/modules/_mixins.scss,sha256=w04YU6nRK9P4GrSjS0fcy9BsEydko8Jd8ARgJpD1164,1833
399
+ portal/static/portal/sass/modules/_levels.scss,sha256=-PrZ21k_-sQAxYZB0uJd7macz46-VVwsQdmtxYyZYnA,106
400
+ portal/static/portal/sass/modules/_mixins.scss,sha256=8xDWuApUG6B3pjadB4iHpJ87ehXCg0qqpabnVd2cgxU,1987
401
401
  portal/static/portal/sass/modules/_spacing.scss,sha256=WHBcd8ZSl4UHqPAOmW_JLt_MOfww44Hj8W42dWOpTO8,31
402
402
  portal/static/portal/sass/partials/_banners.scss,sha256=Q13Mybarzkq0GWvhStiqIuK_bgKku8wkqC6qCFIruEg,5658
403
403
  portal/static/portal/sass/partials/_base.scss,sha256=cUvskrzNXLA-Xg1EgpL-S0wTWDnRn86AcdMbE2_aLMM,26
404
- portal/static/portal/sass/partials/_buttons.scss,sha256=QwElVoMdDrCK540jFhAEoGR6v-7cldUc0RRd7eHSSGs,8967
404
+ portal/static/portal/sass/partials/_buttons.scss,sha256=UR0a6ne8nZQyyWlWzVDD5RG2vfVe8w9rM5yWLBav3qM,9572
405
405
  portal/static/portal/sass/partials/_carousel.scss,sha256=m8N6jSuHBzeKyMUNGGc48NlNYTmotKlaiy10hwFnyWY,3500
406
406
  portal/static/portal/sass/partials/_footer.scss,sha256=oqt-Qvz7vtUFNnpe4FkQkaZk3J3fojCy9cKCxqGTWoM,2493
407
407
  portal/static/portal/sass/partials/_forms.scss,sha256=cbonSxhtfaHR-4R6i0u65AaIZRG_25C837CgZlLuwCI,7101
408
408
  portal/static/portal/sass/partials/_grids.scss,sha256=pnMQht5lZ65MRB-0_nl8yn655hlKC_oWCwZopillVwc,4968
409
- portal/static/portal/sass/partials/_header.scss,sha256=OYcN1R7LB04BlGkhvfBf4E0CgMjCgBIwop6VZY7s7qw,7372
409
+ portal/static/portal/sass/partials/_header.scss,sha256=NNb8hb66LPTeaGdL-vcsNSDJTxPljsOfpkYMI5EmOWc,7421
410
410
  portal/static/portal/sass/partials/_images.scss,sha256=Fro4a0VfLo6FyrvGk5yjsjzzGKWVdHeMFJiVC96C7vk,6213
411
- portal/static/portal/sass/partials/_popup.scss,sha256=OhBdNto7GEGbGFLzT0ybUKgDmuhSs365fSWy7D8qO1w,2205
411
+ portal/static/portal/sass/partials/_popup.scss,sha256=GQ6Vtmj3vdOP4Hvsf8ZxKdEtuL8gqoClYpDgQ19kvnQ,2280
412
412
  portal/static/portal/sass/partials/_progress-bars.scss,sha256=cTyRa9WLeIE0rETmY58Rcyf-8O6JE3iDTa_jw3mk23g,144
413
413
  portal/static/portal/sass/partials/_subnavs.scss,sha256=6FJV1wBmHchnGBL9Yx56Ua4RR1b5P6ABTextZPTqXSs,5052
414
414
  portal/static/portal/sass/partials/_tables.scss,sha256=XrO-I41hfyFy1Uh-STokSpoG0qifJVNlpWS4sNxDYdQ,2984
@@ -434,7 +434,7 @@ portal/templates/email.txt,sha256=z3bkT2WnkCrVa1RLVbGd-pE81cLEHlZ_QowfbSQAOvg,19
434
434
  portal/templates/captcha/widget_v2_invisible.html,sha256=wPBAvvjm-qhSCrQruwJ2SSBkxFg4oUNFl26Axs1IjZw,306
435
435
  portal/templates/captcha/includes/js_v2_invisible.html,sha256=FUyDf1RDyS7whzW2B_TnD1ovS8an9sDavCJFqAsrnmQ,1409
436
436
  portal/templates/portal/about.html,sha256=_iD0GCP6q3-XuZ2LC-9O0KYY-mKL6c9qk3O-NRRqsRM,10321
437
- portal/templates/portal/base.html,sha256=DhZ1BC9AANRofocQeNtU6wSFMI-5WmQ-4eXemUGAuQo,11689
437
+ portal/templates/portal/base.html,sha256=4xLACNgKmRQTdEsdNNGYhLzMozzYzWIIzk31HrLGBf0,12109
438
438
  portal/templates/portal/base_no_userprofile.html,sha256=PlRufyYmUUGWBZ6CvbYhJWOMTqKqdcee4xnO5--AogA,447
439
439
  portal/templates/portal/coding_club.html,sha256=aaLx9UQ8p_7Vvy3Vlh1rOufeNN0KhJ6fj4oqI6QqJcM,4518
440
440
  portal/templates/portal/contribute.html,sha256=UIC_N3Lun9wB_6jEra0wvlT9fDiiIeMby7_onXYo6k0,4176
@@ -446,7 +446,7 @@ portal/templates/portal/email_verification_needed.html,sha256=lJvwWjBg6y-9V17gyV
446
446
  portal/templates/portal/form_shapes.html,sha256=oiaQ4RKZF8STTQsoQqql184UoYVyFQ60Mwve8lJMKu0,1223
447
447
  portal/templates/portal/getinvolved.html,sha256=jC56RTuLjDISr7eJUPA2TErz1TBcLi9G42BY-jo3Xe8,2872
448
448
  portal/templates/portal/home.html,sha256=J10xWo7opn_BjQrdxpn0wg1bo6w4DwjpTEOJ-pkJfKY,6178
449
- portal/templates/portal/home_learning.html,sha256=AdKDe0dlbWUrL1BOCjJpdQcOsC6SR-0Ciby1dxJ6iXU,9707
449
+ portal/templates/portal/home_learning.html,sha256=U1_AYrTNLqCjla7-vdVkC6SOigGBAmu3kscuiYNH4rw,10070
450
450
  portal/templates/portal/locked_out.html,sha256=JeiFHFyueFX03D54k82swjxvKY78cFG8Vre-bKKv_7Y,894
451
451
  portal/templates/portal/locked_out_school_student.html,sha256=bcpLtu4mkHR-zILLuArtHH2OfUSjHiqntcdbTMlx5Kg,684
452
452
  portal/templates/portal/mouseflow.html,sha256=ji9SWR86nqC59xdmivfdPNG5OzFk8gJEUeOUUh52alk,361
@@ -469,8 +469,9 @@ portal/templates/portal/partials/benefits.html,sha256=TZm4U_XimgivkPMjDXxxkV7PLf
469
469
  portal/templates/portal/partials/card_list.html,sha256=yHcp4oc0WRG9H6Ek871VCHbwYkDRcwQYbZ320gtsFn8,1550
470
470
  portal/templates/portal/partials/character_list.html,sha256=SnNFHzbIkOSaiRPIvSdplEf_B87UYgJ1pOvMltNDxLE,415
471
471
  portal/templates/portal/partials/delete_popup.html,sha256=nEhnwgnmKoFzb6LlCwdX973Em9bVOFh6MON36RPteYs,1275
472
+ portal/templates/portal/partials/donate_popup.html,sha256=iPxXqIwZ-9_n4ssdUEiZ_GY3nbpes2AlcEKphL2Plno,2151
472
473
  portal/templates/portal/partials/footer.html,sha256=m0mKlUenQN4pVCNi8Dkzer5aQ4uUmvfLjyzTMWoBiQ4,4838
473
- portal/templates/portal/partials/header.html,sha256=rfat08RRW0GNhWq074IBR7KZlCV8-EMjKFtZFb97qBk,20100
474
+ portal/templates/portal/partials/header.html,sha256=LCOppTXDr8hEYp2eBJjxw3LvqwnVR-6E9jdYGodlD5U,20373
474
475
  portal/templates/portal/partials/headline.html,sha256=xKb-WtkT0FyQqT0smyNSKkXFvU7KQ5Czad8ca6YKQg4,89
475
476
  portal/templates/portal/partials/hero_card.html,sha256=UN5hyxrw-McuXFsDzPBIECB9yGrv9VjsFf8SEBowwXc,786
476
477
  portal/templates/portal/partials/info_popup.html,sha256=gzffd5MBVb7p2hGQuZMNDudThrRgs847hCu9ziT9uLE,566
@@ -536,12 +537,12 @@ portal/tests/test_api.py,sha256=Yo5s_nEGOoG35jA39yZ6nuDOUZvuCZ8o8o8XhZos61w,1381
536
537
  portal/tests/test_captcha_forms.py,sha256=lirhIli-sHovun8VdrF0he7KRFTAd8DMCpkJ8cQNotg,1015
537
538
  portal/tests/test_class.py,sha256=43g2HslNosbSuSnnMBzM6VWk4LaV0HXkWS3f8VKVRmw,15930
538
539
  portal/tests/test_daily_activities.py,sha256=-siDCMGBD1ijjccHVk7eEmrk4bgTsvbh0B6hDoj2fo0,1803
539
- portal/tests/test_emails.py,sha256=xNgOt592r2nrQu7VeBdc8kvSnw6Z5fPJqMx2-UMcyyk,9482
540
+ portal/tests/test_emails.py,sha256=pLr06j3uMBxP1raoZQWzUTBVFvsEDFtUh85J8OnqCwE,9238
541
+ portal/tests/test_global_forms.py,sha256=A5JpAe4AYK-wpu0o1qU4THmeNv_wr7lhzaMbjz5czpY,1543
540
542
  portal/tests/test_helper_methods.py,sha256=-SQCDZm2XUtyXGEp0CHIb_SSC9CPD-XOSnpnY8QclHk,890
541
543
  portal/tests/test_independent_student.py,sha256=NrRjTEr6V4WXpCE74N8LYNVocvLSvddkjuo3dYpfAZc,27245
542
544
  portal/tests/test_invite_teacher.py,sha256=oeOaoJV1IqJSYPlaPFjnhVXdB2mq8otCTLp_lfjuCfk,12224
543
545
  portal/tests/test_middleware.py,sha256=b6jfNmiRZ2snqLKsyJUG-RivoX5fmrqLlQkG9MeVnqM,8034
544
- portal/tests/test_newsletter_footer.py,sha256=MdVUX53mEoDTa4Krq-jg9LFNo-QyghqvTvhHeNXBGnE,838
545
546
  portal/tests/test_organisation.py,sha256=kCMUNzLN6EzaMUBcFkqXwnqLGgOuQxQWIHHt63nhqBs,7574
546
547
  portal/tests/test_partials.py,sha256=cSLNLjdsriROjPxkZlM3HKD3CSKDuKgpaDIdL3fPyBs,1794
547
548
  portal/tests/test_ratelimit.py,sha256=XWq1A9XgRrlcMHibGoJ0kc4gLc5U_u5UhKHjthxCfYA,19376
@@ -608,7 +609,7 @@ portal/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
608
609
  portal/views/about.py,sha256=-muXy17UhxCSKkjnMAkSLXiCvT_pBPlf2ykTYr794dI,443
609
610
  portal/views/admin.py,sha256=4Xt3zEyQH7sUwQSrwuRtoCodWidjOzd7gJUwWU96pXY,957
610
611
  portal/views/api.py,sha256=lCwiclR98G-yTgK55u8IjkueIH8iremeiZSa3jAvO-M,6990
611
- portal/views/dotmailer.py,sha256=_subSoy5f1j5sAcRrjE_xMejdarjHIY1d_jwSrf7_o0,2299
612
+ portal/views/dotmailer.py,sha256=x49p89TtwA1MLysRLtq5yRRzVtIpzGoU__Xb5hPuHak,3208
612
613
  portal/views/email.py,sha256=V3wXRxIjeZ4OJBVqGCQrPn-GQWKZK1PCXbR1f2Zpa_4,2174
613
614
  portal/views/home.py,sha256=GlNTJqSST_jQi0REuaV2aI2zram-KAhedmATdC6yU58,10247
614
615
  portal/views/legal.py,sha256=nUunsTHnhMcXBcDlg1GmUal86k9Vhinne4A2FWfq78M,342
@@ -626,14 +627,14 @@ portal/views/student/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
626
627
  portal/views/student/edit_account_details.py,sha256=Ba-3D_zzKbX5N01NG5qqBS0ud10B8D5SN8hOpLiGa9U,8468
627
628
  portal/views/student/play.py,sha256=GMxk65bxWOe1Ds2kb6rvuOd1GoAtt5v_9AihLNXoUL0,8768
628
629
  portal/views/teacher/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
629
- portal/views/teacher/dashboard.py,sha256=8WglspwuHF__2LtoX5_XvoW1ulICSupjKv--MtjrvJk,25714
630
+ portal/views/teacher/dashboard.py,sha256=pjzOOCz29d4VHukaCI5QhkDsu-RPvy7SXC9dDcPo50k,27422
630
631
  portal/views/teacher/teach.py,sha256=MxheM_kxt_K1J7jEWlim8yZonnp63iYUTw5dCaLscLI,37056
631
632
  portal/views/two_factor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
632
633
  portal/views/two_factor/core.py,sha256=O_wcBeFqdPYSGNGv-pT_vbs5-Dj1Z-Jfkd6f9-E5yZI,760
633
634
  portal/views/two_factor/form.py,sha256=lnHNKI-BMlpncTuW3zUzjPaJJNuEra2I_nOam0eOKFY,257
634
635
  portal/views/two_factor/profile.py,sha256=tkl_ludo8arMtd5LKNmohM66vpC_YQiP-0nspTSJiJ4,383
635
- codeforlife_portal-7.3.7.dist-info/LICENSE.md,sha256=9AbRlCDqD2D1tPibimysFv3zg3AIc49-eyv9aEsyq9w,115
636
- codeforlife_portal-7.3.7.dist-info/METADATA,sha256=j-1bp9D8W4keNTlFbOjB9f4sjvUKXzPaLgJajTursHU,3317
637
- codeforlife_portal-7.3.7.dist-info/WHEEL,sha256=fS9sRbCBHs7VFcwJLnLXN1MZRR0_TVTxvXKzOnaSFs8,110
638
- codeforlife_portal-7.3.7.dist-info/top_level.txt,sha256=8e5pdsuIoTqEAMqpelHBjGjLbffcBtgOoggmd2q7nMw,41
639
- codeforlife_portal-7.3.7.dist-info/RECORD,,
636
+ codeforlife_portal-7.4.0.dist-info/LICENSE.md,sha256=9AbRlCDqD2D1tPibimysFv3zg3AIc49-eyv9aEsyq9w,115
637
+ codeforlife_portal-7.4.0.dist-info/METADATA,sha256=Nnx8oLJTfx1yuUsKVh7pTcZQDhXxkLSvr3_ehnB9X04,3317
638
+ codeforlife_portal-7.4.0.dist-info/WHEEL,sha256=fS9sRbCBHs7VFcwJLnLXN1MZRR0_TVTxvXKzOnaSFs8,110
639
+ codeforlife_portal-7.4.0.dist-info/top_level.txt,sha256=8e5pdsuIoTqEAMqpelHBjGjLbffcBtgOoggmd2q7nMw,41
640
+ codeforlife_portal-7.4.0.dist-info/RECORD,,
@@ -172,6 +172,7 @@ TEMPLATES = [
172
172
  "common.context_processors.module_name",
173
173
  "common.context_processors.cookie_management_enabled",
174
174
  "portal.context_processors.process_newsletter_form",
175
+ "portal.context_processors.process_donate_form",
175
176
  ]
176
177
  },
177
178
  }
@@ -130,6 +130,7 @@ TEMPLATES = [
130
130
  "common.context_processors.module_name",
131
131
  "common.context_processors.cookie_management_enabled",
132
132
  "portal.context_processors.process_newsletter_form",
133
+ "portal.context_processors.process_donate_form",
133
134
  ]
134
135
  }
135
136
  }
portal/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "7.3.7"
1
+ __version__ = "7.4.0"
@@ -1,5 +1,9 @@
1
- from portal.forms.dotmailer import NewsletterForm
1
+ from portal.forms.dotmailer import DonateForm, NewsletterForm
2
2
 
3
3
 
4
4
  def process_newsletter_form(request):
5
5
  return {"news_form": NewsletterForm()}
6
+
7
+
8
+ def process_donate_form(request):
9
+ return {"donate_form": DonateForm()}
portal/forms/dotmailer.py CHANGED
@@ -5,16 +5,51 @@ class NewsletterForm(forms.Form):
5
5
  email = forms.EmailField(
6
6
  label="Sign up to receive updates about Code for Life games and teaching resources.",
7
7
  label_suffix="",
8
- widget=forms.EmailInput(attrs={"placeholder": "Your email address", "id": "newsletter_email_field"}),
8
+ widget=forms.EmailInput(
9
+ attrs={
10
+ "placeholder": "Your email address",
11
+ "id": "newsletter_email_field",
12
+ }
13
+ ),
9
14
  help_text="Enter email address above",
10
15
  )
11
16
 
12
- age_verification = forms.BooleanField(widget=forms.CheckboxInput(), initial=False, required=True)
17
+ age_verification = forms.BooleanField(
18
+ widget=forms.CheckboxInput(), initial=False, required=True
19
+ )
20
+
21
+
22
+ class DonateForm(forms.Form):
23
+ email = forms.EmailField(
24
+ label="This data will only be used for this purpose and you will be "
25
+ "able to opt out anytime. Please read our privacy notice for "
26
+ "further details.",
27
+ label_suffix="",
28
+ widget=forms.EmailInput(
29
+ attrs={
30
+ "placeholder": "Enter your email address",
31
+ "id": "donate_email_field",
32
+ }
33
+ ),
34
+ help_text="Enter your email address",
35
+ )
36
+
37
+ age_verification = forms.BooleanField(
38
+ widget=forms.CheckboxInput(attrs={"id": "donate_age_verification"}),
39
+ initial=False,
40
+ required=True,
41
+ )
13
42
 
14
43
 
15
44
  class ConsentForm(forms.Form):
16
45
  email = forms.EmailField(
17
- label="Email", label_suffix="", widget=forms.EmailInput(attrs={"placeholder": "your.name@yourdomain.com"})
46
+ label="Email",
47
+ label_suffix="",
48
+ widget=forms.EmailInput(
49
+ attrs={"placeholder": "your.name@yourdomain.com"}
50
+ ),
18
51
  )
19
52
 
20
- consent_ticked = forms.BooleanField(widget=forms.CheckboxInput(), initial=False, required=True)
53
+ consent_ticked = forms.BooleanField(
54
+ widget=forms.CheckboxInput(), initial=False, required=True
55
+ )
Binary file