codeforlife-portal 7.3.8__py2.py3-none-any.whl → 7.4.1__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 (32) 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.8.dist-info → codeforlife_portal-7.4.1.dist-info}/METADATA +2 -2
  5. {codeforlife_portal-7.3.8.dist-info → codeforlife_portal-7.4.1.dist-info}/RECORD +31 -31
  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/js/common.js +35 -5
  12. portal/static/portal/sass/colorbox.scss +0 -1
  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/partials/donate_popup.html +46 -0
  21. portal/templates/portal/partials/header.html +34 -30
  22. portal/templates/portal/teach.html +1 -1
  23. portal/templates/portal/terms.html +11 -9
  24. portal/tests/test_emails.py +17 -22
  25. portal/tests/{test_newsletter_footer.py → test_global_forms.py} +17 -1
  26. portal/urls.py +2 -0
  27. portal/views/dotmailer.py +30 -2
  28. portal/views/teacher/dashboard.py +174 -57
  29. portal/static/portal/img/x_close_video.png +0 -0
  30. {codeforlife_portal-7.3.8.dist-info → codeforlife_portal-7.4.1.dist-info}/LICENSE.md +0 -0
  31. {codeforlife_portal-7.3.8.dist-info → codeforlife_portal-7.4.1.dist-info}/WHEEL +0 -0
  32. {codeforlife_portal-7.3.8.dist-info → codeforlife_portal-7.4.1.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.8
3
+ Version: 7.4.1
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.8
24
+ Requires-Dist: cfl-common==7.4.1
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=FCiEuSldZMXdn_ZeI9NoBRTbEYvq1MVvlsuGRArXhNg,22
111
+ portal/__init__.py,sha256=p8eq7jnQadKhC-wWoeEv-nRdPB1JN2Jan_o-R14Xz9w,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
@@ -259,7 +259,6 @@ portal/static/portal/img/thumbnail_python_den.png,sha256=0if8hLRK9tR-oII6IEuRRq2
259
259
  portal/static/portal/img/twitter.png,sha256=QD2ckrIWgV6WpuV0R7p717_T9a-DibzIxXsqHbvDR48,15969
260
260
  portal/static/portal/img/universities.png,sha256=y015DeiJzJyIAEC-qIGY5HgsXJDr_DhUXOx8_2xbxf0,145961
261
261
  portal/static/portal/img/wes.png,sha256=z2Z-vqqzIPkHY0PN7pgb7Im8CU0w9p2bc9VNRXGxtd0,54619
262
- portal/static/portal/img/x_close_video.png,sha256=R6YR7j0axvkIdfB2u17hvbgTWA1sCk_0GREBRfFu3aY,1051
263
262
  portal/static/portal/img/colorboxImages/border.png,sha256=Eb2D9kRqG0Gw2I3bLicfzJkSshDXf0DjTl4x4amvF0o,112
264
263
  portal/static/portal/img/colorboxImages/controls.png,sha256=zQowXWoW0otiA3-wj5sGLdGgpqO5cNj5Xs69VvdAZ-k,2893
265
264
  portal/static/portal/img/colorboxImages/loading.gif,sha256=NO9VJC_CTJTweQkCwJYB0ijpB0v3ofiMTeajm0DOOPo,9427
@@ -267,7 +266,7 @@ portal/static/portal/img/colorboxImages/loading_background.png,sha256=lZ7Mxrcb7-
267
266
  portal/static/portal/img/colorboxImages/overlay.png,sha256=qy73bf_q55z4kktuaTaMhVrxDzVRCIjgmBQ5cbOmLtE,182
268
267
  portal/static/portal/js/bootstrap.min.js,sha256=nuL8_2cJ5NDSSwnKD8VqreErSWHtnEP9E7AySL-1ev4,39680
269
268
  portal/static/portal/js/carouselCards.js,sha256=IByMjKk37E-TCc3vx9nfF-IJWl9BOFPNVqf4WuD75GI,778
270
- portal/static/portal/js/common.js,sha256=_gNQ7eIgxiw8gviolmShD0aU3TAKDwIVUGGg9wznDAc,6499
269
+ portal/static/portal/js/common.js,sha256=kVUdm0luaqMLElbDq3Mht1k5DfkYHXAmJtce1Rekogg,7554
271
270
  portal/static/portal/js/independentLogin.js,sha256=wjjJBAF5arJ2iwW30lHTraG_f2FJ9UMGnXGpUSXwXIs,538
272
271
  portal/static/portal/js/independentRegistration.js,sha256=YWhIzwJcl0acU9weAKB7pWK76J1dpAuAQkI7DrUm3uA,2622
273
272
  portal/static/portal/js/join_create_game_toggle.js,sha256=eWxhMfzVKu1oft9iUFV4w8CgqMRv9-unb5P5cGi1h_4,393
@@ -293,7 +292,7 @@ portal/static/portal/js/lib/jquery.min.js,sha256=2Pmvv0kuTBOenSvLm6bvfBSSHrUJ-3A
293
292
  portal/static/portal/js/lib/modernizr-build.js,sha256=pGqgo-dxjy43hd1yL75ivaH9xWF9qfVKLlD4EYlTzJ4,36698
294
293
  portal/static/portal/js/lib/papaparse.min.js,sha256=tDf9H6Pr0oXtuyG0hGdMQ5OU5EuM5IK8WMK2iTbdvd0,18829
295
294
  portal/static/portal/sass/bootstrap.scss,sha256=q_gMkiqFVJBrMKNr0kXaP7y4caychGNGkyEyEUUFN3k,2144
296
- portal/static/portal/sass/colorbox.scss,sha256=ZSilgJLSC_ZIMp2Wb5zJvwR-cyRbmcDPy93Kt78VbIM,3790
295
+ portal/static/portal/sass/colorbox.scss,sha256=DqEMSAPBfBwNGY1acoh9fCW9PPY-SchjKW7sNMxd9UM,3637
297
296
  portal/static/portal/sass/styles.scss,sha256=gTv8yjfzrN1acywPBkS9b49648LUGN4xvkwqAK-gAvc,780
298
297
  portal/static/portal/sass/bootstrap/_alerts.scss,sha256=C5kv4Wt7RFE3ESibknOeFzbE79it0qfwV59y_yaeImE,1546
299
298
  portal/static/portal/sass/bootstrap/_badges.scss,sha256=lHaAb3wWe4sh5kwNTsni1f0PCkNXBTfcVbakJTzoAYY,1228
@@ -394,21 +393,21 @@ portal/static/portal/sass/modules/_all.scss,sha256=k1U_FAKajqT8B3U53UT9B1Hc4jn5P
394
393
  portal/static/portal/sass/modules/_animation.scss,sha256=s75-4xRmPSmqwHUuyVy0OcnbS5jreRnotMXm-CuTLkE,39
395
394
  portal/static/portal/sass/modules/_breakpoints.scss,sha256=Nc7nrvjzQ0eK844ITuqwYcybIJxpYVFU5s30mz3BGOg,498
396
395
  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
396
+ portal/static/portal/sass/modules/_colours.scss,sha256=Wen7UlF98ijyDjIEsLeIzLx0fcgXOLjavAVIh8PfqX0,4350
398
397
  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
398
+ portal/static/portal/sass/modules/_levels.scss,sha256=-PrZ21k_-sQAxYZB0uJd7macz46-VVwsQdmtxYyZYnA,106
399
+ portal/static/portal/sass/modules/_mixins.scss,sha256=8xDWuApUG6B3pjadB4iHpJ87ehXCg0qqpabnVd2cgxU,1987
401
400
  portal/static/portal/sass/modules/_spacing.scss,sha256=WHBcd8ZSl4UHqPAOmW_JLt_MOfww44Hj8W42dWOpTO8,31
402
401
  portal/static/portal/sass/partials/_banners.scss,sha256=Q13Mybarzkq0GWvhStiqIuK_bgKku8wkqC6qCFIruEg,5658
403
402
  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
403
+ portal/static/portal/sass/partials/_buttons.scss,sha256=UR0a6ne8nZQyyWlWzVDD5RG2vfVe8w9rM5yWLBav3qM,9572
405
404
  portal/static/portal/sass/partials/_carousel.scss,sha256=m8N6jSuHBzeKyMUNGGc48NlNYTmotKlaiy10hwFnyWY,3500
406
405
  portal/static/portal/sass/partials/_footer.scss,sha256=oqt-Qvz7vtUFNnpe4FkQkaZk3J3fojCy9cKCxqGTWoM,2493
407
406
  portal/static/portal/sass/partials/_forms.scss,sha256=cbonSxhtfaHR-4R6i0u65AaIZRG_25C837CgZlLuwCI,7101
408
407
  portal/static/portal/sass/partials/_grids.scss,sha256=pnMQht5lZ65MRB-0_nl8yn655hlKC_oWCwZopillVwc,4968
409
- portal/static/portal/sass/partials/_header.scss,sha256=OYcN1R7LB04BlGkhvfBf4E0CgMjCgBIwop6VZY7s7qw,7372
408
+ portal/static/portal/sass/partials/_header.scss,sha256=NNb8hb66LPTeaGdL-vcsNSDJTxPljsOfpkYMI5EmOWc,7421
410
409
  portal/static/portal/sass/partials/_images.scss,sha256=Fro4a0VfLo6FyrvGk5yjsjzzGKWVdHeMFJiVC96C7vk,6213
411
- portal/static/portal/sass/partials/_popup.scss,sha256=OhBdNto7GEGbGFLzT0ybUKgDmuhSs365fSWy7D8qO1w,2205
410
+ portal/static/portal/sass/partials/_popup.scss,sha256=GQ6Vtmj3vdOP4Hvsf8ZxKdEtuL8gqoClYpDgQ19kvnQ,2280
412
411
  portal/static/portal/sass/partials/_progress-bars.scss,sha256=cTyRa9WLeIE0rETmY58Rcyf-8O6JE3iDTa_jw3mk23g,144
413
412
  portal/static/portal/sass/partials/_subnavs.scss,sha256=6FJV1wBmHchnGBL9Yx56Ua4RR1b5P6ABTextZPTqXSs,5052
414
413
  portal/static/portal/sass/partials/_tables.scss,sha256=XrO-I41hfyFy1Uh-STokSpoG0qifJVNlpWS4sNxDYdQ,2984
@@ -434,7 +433,7 @@ portal/templates/email.txt,sha256=z3bkT2WnkCrVa1RLVbGd-pE81cLEHlZ_QowfbSQAOvg,19
434
433
  portal/templates/captcha/widget_v2_invisible.html,sha256=wPBAvvjm-qhSCrQruwJ2SSBkxFg4oUNFl26Axs1IjZw,306
435
434
  portal/templates/captcha/includes/js_v2_invisible.html,sha256=FUyDf1RDyS7whzW2B_TnD1ovS8an9sDavCJFqAsrnmQ,1409
436
435
  portal/templates/portal/about.html,sha256=_iD0GCP6q3-XuZ2LC-9O0KYY-mKL6c9qk3O-NRRqsRM,10321
437
- portal/templates/portal/base.html,sha256=DhZ1BC9AANRofocQeNtU6wSFMI-5WmQ-4eXemUGAuQo,11689
436
+ portal/templates/portal/base.html,sha256=4xLACNgKmRQTdEsdNNGYhLzMozzYzWIIzk31HrLGBf0,12109
438
437
  portal/templates/portal/base_no_userprofile.html,sha256=PlRufyYmUUGWBZ6CvbYhJWOMTqKqdcee4xnO5--AogA,447
439
438
  portal/templates/portal/coding_club.html,sha256=aaLx9UQ8p_7Vvy3Vlh1rOufeNN0KhJ6fj4oqI6QqJcM,4518
440
439
  portal/templates/portal/contribute.html,sha256=UIC_N3Lun9wB_6jEra0wvlT9fDiiIeMby7_onXYo6k0,4176
@@ -457,9 +456,9 @@ portal/templates/portal/reset_password.html,sha256=YzsREz5D2OwhicMLahVOVDXiNDxoH
457
456
  portal/templates/portal/reset_password_confirm.html,sha256=jPHSDatezRXzCG4zH_5BQPWAxLblidqro0hzvsH54ho,3499
458
457
  portal/templates/portal/reset_password_done.html,sha256=rpzN3svZne5H2FS3TJaGnHypRj2KX-SRS6DbFQkgLf0,667
459
458
  portal/templates/portal/reset_password_email_sent.html,sha256=kwNvoH_M-Qd5s-g0HiULwfQt_SD12WEMgPRSnLmldTQ,914
460
- portal/templates/portal/teach.html,sha256=bRzwfmrZr53G5zr_QF7l5peT9r5e9z7dhgIFL3POzrQ,9006
459
+ portal/templates/portal/teach.html,sha256=2qOWzA00evHYlwSvXDOz8VQo1yXB-95Db7KgqpOItm0,9006
461
460
  portal/templates/portal/ten_year_map.html,sha256=_NxEXDJMGdtguuZ3vYl00yBgYo12wv1w8QJ-LMhw8fg,8390
462
- portal/templates/portal/terms.html,sha256=SO8ruLF2kdYpOinxlVx1CHfPNgphemX4wvWJ9-W6Nj4,29155
461
+ portal/templates/portal/terms.html,sha256=ekhBnhJVHcr5BE5W4PZ-YNzGsmhvRTrj1gdpdNdSYRk,29314
463
462
  portal/templates/portal/login/independent_student.html,sha256=G4u82m0lko4yQvqRDMger5p63A6KtFI_bsEOsQWlyJw,1777
464
463
  portal/templates/portal/login/student.html,sha256=s5rUyT-u7gei2xWQ-Ba35ePC5WbtwReEzUZAKwUoZVY,1261
465
464
  portal/templates/portal/login/student_class_code.html,sha256=rJr6NB4Oz7XjZXDOUHpwzT6r6yxh7nZ5joca-z2bK-s,966
@@ -469,8 +468,9 @@ portal/templates/portal/partials/benefits.html,sha256=TZm4U_XimgivkPMjDXxxkV7PLf
469
468
  portal/templates/portal/partials/card_list.html,sha256=yHcp4oc0WRG9H6Ek871VCHbwYkDRcwQYbZ320gtsFn8,1550
470
469
  portal/templates/portal/partials/character_list.html,sha256=SnNFHzbIkOSaiRPIvSdplEf_B87UYgJ1pOvMltNDxLE,415
471
470
  portal/templates/portal/partials/delete_popup.html,sha256=nEhnwgnmKoFzb6LlCwdX973Em9bVOFh6MON36RPteYs,1275
471
+ portal/templates/portal/partials/donate_popup.html,sha256=iPxXqIwZ-9_n4ssdUEiZ_GY3nbpes2AlcEKphL2Plno,2151
472
472
  portal/templates/portal/partials/footer.html,sha256=m0mKlUenQN4pVCNi8Dkzer5aQ4uUmvfLjyzTMWoBiQ4,4838
473
- portal/templates/portal/partials/header.html,sha256=rfat08RRW0GNhWq074IBR7KZlCV8-EMjKFtZFb97qBk,20100
473
+ portal/templates/portal/partials/header.html,sha256=LCOppTXDr8hEYp2eBJjxw3LvqwnVR-6E9jdYGodlD5U,20373
474
474
  portal/templates/portal/partials/headline.html,sha256=xKb-WtkT0FyQqT0smyNSKkXFvU7KQ5Czad8ca6YKQg4,89
475
475
  portal/templates/portal/partials/hero_card.html,sha256=UN5hyxrw-McuXFsDzPBIECB9yGrv9VjsFf8SEBowwXc,786
476
476
  portal/templates/portal/partials/info_popup.html,sha256=gzffd5MBVb7p2hGQuZMNDudThrRgs847hCu9ziT9uLE,566
@@ -536,12 +536,12 @@ portal/tests/test_api.py,sha256=Yo5s_nEGOoG35jA39yZ6nuDOUZvuCZ8o8o8XhZos61w,1381
536
536
  portal/tests/test_captcha_forms.py,sha256=lirhIli-sHovun8VdrF0he7KRFTAd8DMCpkJ8cQNotg,1015
537
537
  portal/tests/test_class.py,sha256=43g2HslNosbSuSnnMBzM6VWk4LaV0HXkWS3f8VKVRmw,15930
538
538
  portal/tests/test_daily_activities.py,sha256=-siDCMGBD1ijjccHVk7eEmrk4bgTsvbh0B6hDoj2fo0,1803
539
- portal/tests/test_emails.py,sha256=xNgOt592r2nrQu7VeBdc8kvSnw6Z5fPJqMx2-UMcyyk,9482
539
+ portal/tests/test_emails.py,sha256=pLr06j3uMBxP1raoZQWzUTBVFvsEDFtUh85J8OnqCwE,9238
540
+ portal/tests/test_global_forms.py,sha256=A5JpAe4AYK-wpu0o1qU4THmeNv_wr7lhzaMbjz5czpY,1543
540
541
  portal/tests/test_helper_methods.py,sha256=-SQCDZm2XUtyXGEp0CHIb_SSC9CPD-XOSnpnY8QclHk,890
541
542
  portal/tests/test_independent_student.py,sha256=NrRjTEr6V4WXpCE74N8LYNVocvLSvddkjuo3dYpfAZc,27245
542
543
  portal/tests/test_invite_teacher.py,sha256=oeOaoJV1IqJSYPlaPFjnhVXdB2mq8otCTLp_lfjuCfk,12224
543
544
  portal/tests/test_middleware.py,sha256=b6jfNmiRZ2snqLKsyJUG-RivoX5fmrqLlQkG9MeVnqM,8034
544
- portal/tests/test_newsletter_footer.py,sha256=MdVUX53mEoDTa4Krq-jg9LFNo-QyghqvTvhHeNXBGnE,838
545
545
  portal/tests/test_organisation.py,sha256=kCMUNzLN6EzaMUBcFkqXwnqLGgOuQxQWIHHt63nhqBs,7574
546
546
  portal/tests/test_partials.py,sha256=cSLNLjdsriROjPxkZlM3HKD3CSKDuKgpaDIdL3fPyBs,1794
547
547
  portal/tests/test_ratelimit.py,sha256=XWq1A9XgRrlcMHibGoJ0kc4gLc5U_u5UhKHjthxCfYA,19376
@@ -608,7 +608,7 @@ portal/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
608
608
  portal/views/about.py,sha256=-muXy17UhxCSKkjnMAkSLXiCvT_pBPlf2ykTYr794dI,443
609
609
  portal/views/admin.py,sha256=4Xt3zEyQH7sUwQSrwuRtoCodWidjOzd7gJUwWU96pXY,957
610
610
  portal/views/api.py,sha256=lCwiclR98G-yTgK55u8IjkueIH8iremeiZSa3jAvO-M,6990
611
- portal/views/dotmailer.py,sha256=_subSoy5f1j5sAcRrjE_xMejdarjHIY1d_jwSrf7_o0,2299
611
+ portal/views/dotmailer.py,sha256=x49p89TtwA1MLysRLtq5yRRzVtIpzGoU__Xb5hPuHak,3208
612
612
  portal/views/email.py,sha256=V3wXRxIjeZ4OJBVqGCQrPn-GQWKZK1PCXbR1f2Zpa_4,2174
613
613
  portal/views/home.py,sha256=GlNTJqSST_jQi0REuaV2aI2zram-KAhedmATdC6yU58,10247
614
614
  portal/views/legal.py,sha256=nUunsTHnhMcXBcDlg1GmUal86k9Vhinne4A2FWfq78M,342
@@ -626,14 +626,14 @@ portal/views/student/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
626
626
  portal/views/student/edit_account_details.py,sha256=Ba-3D_zzKbX5N01NG5qqBS0ud10B8D5SN8hOpLiGa9U,8468
627
627
  portal/views/student/play.py,sha256=GMxk65bxWOe1Ds2kb6rvuOd1GoAtt5v_9AihLNXoUL0,8768
628
628
  portal/views/teacher/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
629
- portal/views/teacher/dashboard.py,sha256=8WglspwuHF__2LtoX5_XvoW1ulICSupjKv--MtjrvJk,25714
629
+ portal/views/teacher/dashboard.py,sha256=pjzOOCz29d4VHukaCI5QhkDsu-RPvy7SXC9dDcPo50k,27422
630
630
  portal/views/teacher/teach.py,sha256=MxheM_kxt_K1J7jEWlim8yZonnp63iYUTw5dCaLscLI,37056
631
631
  portal/views/two_factor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
632
632
  portal/views/two_factor/core.py,sha256=O_wcBeFqdPYSGNGv-pT_vbs5-Dj1Z-Jfkd6f9-E5yZI,760
633
633
  portal/views/two_factor/form.py,sha256=lnHNKI-BMlpncTuW3zUzjPaJJNuEra2I_nOam0eOKFY,257
634
634
  portal/views/two_factor/profile.py,sha256=tkl_ludo8arMtd5LKNmohM66vpC_YQiP-0nspTSJiJ4,383
635
- codeforlife_portal-7.3.8.dist-info/LICENSE.md,sha256=9AbRlCDqD2D1tPibimysFv3zg3AIc49-eyv9aEsyq9w,115
636
- codeforlife_portal-7.3.8.dist-info/METADATA,sha256=E1jfV74HV6_-p8e25vU4HTIVNFe5SBgml5zGxDpnyGU,3317
637
- codeforlife_portal-7.3.8.dist-info/WHEEL,sha256=fS9sRbCBHs7VFcwJLnLXN1MZRR0_TVTxvXKzOnaSFs8,110
638
- codeforlife_portal-7.3.8.dist-info/top_level.txt,sha256=8e5pdsuIoTqEAMqpelHBjGjLbffcBtgOoggmd2q7nMw,41
639
- codeforlife_portal-7.3.8.dist-info/RECORD,,
635
+ codeforlife_portal-7.4.1.dist-info/LICENSE.md,sha256=9AbRlCDqD2D1tPibimysFv3zg3AIc49-eyv9aEsyq9w,115
636
+ codeforlife_portal-7.4.1.dist-info/METADATA,sha256=6kN9QQduDOTlZTEwl5YTS2LiqxZijcWzKsBWa0qUYFQ,3317
637
+ codeforlife_portal-7.4.1.dist-info/WHEEL,sha256=fS9sRbCBHs7VFcwJLnLXN1MZRR0_TVTxvXKzOnaSFs8,110
638
+ codeforlife_portal-7.4.1.dist-info/top_level.txt,sha256=8e5pdsuIoTqEAMqpelHBjGjLbffcBtgOoggmd2q7nMw,41
639
+ codeforlife_portal-7.4.1.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.8"
1
+ __version__ = "7.4.1"
@@ -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
+ )