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.
- cfl_common/common/app_settings.py +0 -1
- cfl_common/common/helpers/emails.py +153 -36
- cfl_common/common/mail.py +5 -0
- {codeforlife_portal-7.3.7.dist-info → codeforlife_portal-7.4.0.dist-info}/METADATA +2 -2
- {codeforlife_portal-7.3.7.dist-info → codeforlife_portal-7.4.0.dist-info}/RECORD +30 -29
- example_project/portal_test_settings.py +1 -0
- example_project/settings.py +1 -0
- portal/__init__.py +1 -1
- portal/context_processors.py +5 -1
- portal/forms/dotmailer.py +39 -4
- portal/static/portal/img/rr_advanced.png +0 -0
- portal/static/portal/js/common.js +35 -5
- portal/static/portal/sass/modules/_colours.scss +3 -0
- portal/static/portal/sass/modules/_levels.scss +1 -1
- portal/static/portal/sass/modules/_mixins.scss +5 -0
- portal/static/portal/sass/partials/_buttons.scss +31 -3
- portal/static/portal/sass/partials/_header.scss +7 -6
- portal/static/portal/sass/partials/_popup.scss +7 -2
- portal/templates/portal/base.html +12 -3
- portal/templates/portal/home_learning.html +8 -7
- portal/templates/portal/partials/donate_popup.html +46 -0
- portal/templates/portal/partials/header.html +34 -30
- portal/tests/test_emails.py +17 -22
- portal/tests/{test_newsletter_footer.py → test_global_forms.py} +17 -1
- portal/urls.py +2 -0
- portal/views/dotmailer.py +30 -2
- portal/views/teacher/dashboard.py +174 -57
- {codeforlife_portal-7.3.7.dist-info → codeforlife_portal-7.4.0.dist-info}/LICENSE.md +0 -0
- {codeforlife_portal-7.3.7.dist-info → codeforlife_portal-7.4.0.dist-info}/WHEEL +0 -0
- {codeforlife_portal-7.3.7.dist-info → codeforlife_portal-7.4.0.dist-info}/top_level.txt +0 -0
|
@@ -38,13 +38,42 @@ function hidePopupConfirmation() {
|
|
|
38
38
|
$("#popup").find(".popup-text").remove();
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
function showDonatePopup(title, text) {
|
|
42
|
+
let popup = $("#donate-popup");
|
|
43
|
+
popup.find(".popup-box__title").text(title);
|
|
44
|
+
popup.find(".popup-box__msg").append(text);
|
|
41
45
|
|
|
42
|
-
|
|
46
|
+
popup.addClass("popup--fade");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function hideDonatePopup() {
|
|
50
|
+
$("#donate-popup").removeClass("popup--fade");
|
|
51
|
+
$("#donate-popup").find(".popup-text").remove();
|
|
52
|
+
$("#donate_email_field").val("");
|
|
53
|
+
$('#donate_age_verification').prop("checked", false);
|
|
54
|
+
const donateSubmitButton = $('#confirm_donate_button');
|
|
55
|
+
donateSubmitButton.addClass("disabled");
|
|
56
|
+
donateSubmitButton.prop("disabled", true);
|
|
57
|
+
}
|
|
43
58
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
59
|
+
function showDonateConfirmation(path) {
|
|
60
|
+
let title = "We would love to have you support our non-profit mission! ❤️";
|
|
61
|
+
let text =
|
|
62
|
+
"<div class='popup-text'>" +
|
|
63
|
+
"<h6>If you would like to be added to a mailing list and find out" +
|
|
64
|
+
" more about how you could support our mission, please add your" +
|
|
65
|
+
" email below.</h6>" +
|
|
66
|
+
"</div>";
|
|
67
|
+
let confirm_handler = "postWithCsrf('" + path + "')";
|
|
68
|
+
|
|
69
|
+
showDonatePopup(title, text, confirm_handler);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Show a service unavailable popup with a close button in the top-right corner.
|
|
74
|
+
* @param {String} title The title of the popup.
|
|
75
|
+
* @param {String} text The message of the popup.
|
|
76
|
+
*/
|
|
48
77
|
|
|
49
78
|
function showServiceUnavailable(title, text) {
|
|
50
79
|
let popup = $("#service-unavailable-popup");
|
|
@@ -57,6 +86,7 @@ function showServiceUnavailable(title, text) {
|
|
|
57
86
|
function hideServiceUnavailable() {
|
|
58
87
|
$("#service-unavailable-popup").removeClass("popup--fade");
|
|
59
88
|
}
|
|
89
|
+
|
|
60
90
|
/**
|
|
61
91
|
* Show an info popup with a close button in the top-right corner.
|
|
62
92
|
* @param {String} title The title of the popup.
|
|
@@ -63,6 +63,9 @@ $color-button-contained-disabled: $color-secondary-100;
|
|
|
63
63
|
$color-button-outlined-border: $color-secondary-600;
|
|
64
64
|
$color-button-outlined-border-active: $color-secondary-600;
|
|
65
65
|
$color-button-subnav-border: white;
|
|
66
|
+
$color-button-pill: #86ae18;
|
|
67
|
+
$color-button-pill-hover: #82a210;
|
|
68
|
+
$color-button-pill-active: #82a210;
|
|
66
69
|
// Old buttons
|
|
67
70
|
$color-button-primary-action-navigation: $color-secondary;
|
|
68
71
|
$color-button-primary-action-navigation-hover: $color-secondary;
|
|
@@ -55,6 +55,11 @@
|
|
|
55
55
|
calculate-rem($radius-bottom-left);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
@mixin _border-radius-all($radius) {
|
|
59
|
+
border-radius: calculate-rem($radius) calculate-rem($radius)
|
|
60
|
+
calculate-rem($radius) calculate-rem($radius);
|
|
61
|
+
}
|
|
62
|
+
|
|
58
63
|
@mixin _box-shadow($h-offset, $v-offset, $blur, $spread, $color) {
|
|
59
64
|
box-shadow: calculate-rem($h-offset) calculate-rem($v-offset)
|
|
60
65
|
calculate-rem($blur) calculate-rem($spread) $color;
|
|
@@ -89,7 +89,6 @@ table {
|
|
|
89
89
|
color: $color-text-primary;
|
|
90
90
|
box-shadow: none;
|
|
91
91
|
}
|
|
92
|
-
|
|
93
92
|
}
|
|
94
93
|
|
|
95
94
|
/* Outlined button */
|
|
@@ -139,6 +138,30 @@ table {
|
|
|
139
138
|
}
|
|
140
139
|
}
|
|
141
140
|
|
|
141
|
+
/* Green button */
|
|
142
|
+
.button--tertiary {
|
|
143
|
+
@include _padding(10px, 15px, 10px, 15px);
|
|
144
|
+
background: $color-button-pill;
|
|
145
|
+
color: $color-text-secondary;
|
|
146
|
+
|
|
147
|
+
&:hover:not(.disabled){
|
|
148
|
+
@include material-shadow();
|
|
149
|
+
background: $color-button-pill-hover;
|
|
150
|
+
color: $color-text-secondary;
|
|
151
|
+
text-decoration: none;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
&:active:not(.disabled) {
|
|
155
|
+
background: $color-button-pill-active;
|
|
156
|
+
color: $color-text-secondary;
|
|
157
|
+
box-shadow: none;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.button--pill {
|
|
162
|
+
@include _border-radius-all(20px);
|
|
163
|
+
}
|
|
164
|
+
|
|
142
165
|
.button-right-arrow {
|
|
143
166
|
&:after {
|
|
144
167
|
content: "\e5e1";
|
|
@@ -172,8 +195,6 @@ table {
|
|
|
172
195
|
}
|
|
173
196
|
|
|
174
197
|
.button--menu {
|
|
175
|
-
background-color: transparent;
|
|
176
|
-
border-radius: 0;
|
|
177
198
|
text-decoration: none;
|
|
178
199
|
}
|
|
179
200
|
|
|
@@ -416,6 +437,13 @@ td .button--primary {
|
|
|
416
437
|
justify-content: center;
|
|
417
438
|
}
|
|
418
439
|
|
|
440
|
+
&.button--donate {
|
|
441
|
+
background: $color-button-pill;
|
|
442
|
+
border: 0;
|
|
443
|
+
color: $color-text-secondary;
|
|
444
|
+
justify-content: center;
|
|
445
|
+
}
|
|
446
|
+
|
|
419
447
|
&.button--header--login {
|
|
420
448
|
border: 2px solid $color-secondary-600;
|
|
421
449
|
margin: 0;
|
|
@@ -260,13 +260,14 @@
|
|
|
260
260
|
width: 100%;
|
|
261
261
|
z-index: $nav-bar-level;
|
|
262
262
|
|
|
263
|
-
.button--menu
|
|
264
|
-
|
|
263
|
+
.button--menu:hover,
|
|
264
|
+
.button--menu:focus, {
|
|
265
|
+
font-weight: normal;
|
|
266
|
+
}
|
|
265
267
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
268
|
+
.button--pill:hover,
|
|
269
|
+
.button--pill:focus, {
|
|
270
|
+
font-weight: 600;
|
|
270
271
|
}
|
|
271
272
|
|
|
272
273
|
.button--menu--secondary {
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
.popup-box {
|
|
21
21
|
background-color: $color-background-secondary;
|
|
22
22
|
box-shadow: 0 0 4 * $spacing $color-background-box-shadow;
|
|
23
|
-
margin:
|
|
23
|
+
margin: 10% auto;
|
|
24
24
|
overflow: hidden;
|
|
25
|
-
width:
|
|
25
|
+
width: 520px;
|
|
26
26
|
|
|
27
27
|
.popup-box__msg {
|
|
28
28
|
@include _padding(0px, 5 * $spacing, 5 * $spacing, 5 * $spacing);
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
.popup-box__title {
|
|
48
48
|
@include _padding(5 * $spacing, 5 * $spacing, 0px, 5 * $spacing);
|
|
49
49
|
background-color: $color-background-secondary;
|
|
50
|
+
color: $color-text-primary;
|
|
50
51
|
display: flex;
|
|
51
52
|
justify-content: flex-start;
|
|
52
53
|
font-family: "Space Grotesk", sans-serif;
|
|
@@ -57,6 +58,10 @@
|
|
|
57
58
|
min-width:0;
|
|
58
59
|
@include _margin(8px, 8px, 8px, 8px);
|
|
59
60
|
}
|
|
61
|
+
|
|
62
|
+
label {
|
|
63
|
+
text-align: left;
|
|
64
|
+
}
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
&#screentime-popup,
|
|
@@ -32,7 +32,6 @@
|
|
|
32
32
|
type="image/x-icon">
|
|
33
33
|
{% endblock css %}
|
|
34
34
|
{% include "portal/tag_manager/tag_manager_head.html" %}
|
|
35
|
-
|
|
36
35
|
<script type="text/javascript" src="{% static 'portal/js/lib/modernizr-build.js' %}"></script>
|
|
37
36
|
<script type="text/javascript" src="{% static 'portal/js/common.js' %}"></script>
|
|
38
37
|
<script type="text/javascript" src="{% static 'portal/js/jquery.placeholder.js' %}"></script>
|
|
@@ -82,6 +81,7 @@
|
|
|
82
81
|
{% include "portal/tag_manager/tag_manager_body.html" %}
|
|
83
82
|
{% render_block "js" %}
|
|
84
83
|
{% include 'portal/mouseflow.html' %}
|
|
84
|
+
{% include "portal/partials/donate_popup.html" %}
|
|
85
85
|
<div class="content-footer-wrapper">
|
|
86
86
|
{% block contentWrapper %}
|
|
87
87
|
<div {% block pageID %}id="contentWrapper"{% endblock %}>
|
|
@@ -240,16 +240,25 @@
|
|
|
240
240
|
});
|
|
241
241
|
</script>
|
|
242
242
|
<script>
|
|
243
|
-
// disable newsletter signup
|
|
243
|
+
// disable newsletter signup and donate buttons by default
|
|
244
244
|
const newsletterSubmitButton = $('#submit_newsletter');
|
|
245
245
|
newsletterSubmitButton.addClass("disabled");
|
|
246
246
|
newsletterSubmitButton.prop("disabled", true);
|
|
247
247
|
|
|
248
|
-
|
|
248
|
+
const donateSubmitButton = $('#confirm_donate_button');
|
|
249
|
+
donateSubmitButton.addClass("disabled");
|
|
250
|
+
donateSubmitButton.prop("disabled", true);
|
|
251
|
+
|
|
252
|
+
// enable them or disable them depending on age verification checkbox
|
|
249
253
|
$('#id_age_verification').on("click", function() {
|
|
250
254
|
newsletterSubmitButton.prop("disabled", !this.checked);
|
|
251
255
|
newsletterSubmitButton.toggleClass("disabled", !this.checked);
|
|
252
256
|
});
|
|
257
|
+
|
|
258
|
+
$('#donate_age_verification').on("click", function() {
|
|
259
|
+
donateSubmitButton.prop("disabled", !this.checked);
|
|
260
|
+
donateSubmitButton.toggleClass("disabled", !this.checked);
|
|
261
|
+
})
|
|
253
262
|
</script>
|
|
254
263
|
{% endblock scripts %}
|
|
255
264
|
</body>
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
<div class="row">
|
|
16
16
|
<div class="col-sm-6">
|
|
17
17
|
<p>Rapid Router is our shopping delivery game that teaches children aged 5–14 to learn how to code using
|
|
18
|
-
Blockly
|
|
19
|
-
<p>The game and lessons support the English National Curriculum Computing strand, and
|
|
18
|
+
Blockly. The pupils can then progress to Python Den to continue to build up their skills.</p>
|
|
19
|
+
<p>The game and lessons support the English National Curriculum Computing strand, and teachers across the
|
|
20
20
|
world love them.</p>
|
|
21
21
|
<p>Now, we’ve made lessons available for parents and caregivers to teach at home, so we can #KeepKidsCoding.
|
|
22
22
|
They're free and easy, but most of all, they’re fun!</p>
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
algorithms, and learn how to create and debug simple programs. Designed for children aged 5–7,
|
|
56
56
|
but start here if you’ve never played Rapid Router.
|
|
57
57
|
</div>
|
|
58
|
-
<a class="button" href="https://code-for-life.gitbook.io/independent-student-resources/beginner
|
|
58
|
+
<a class="button" href="https://code-for-life.gitbook.io/independent-student-resources/rapid-router-resources/beginner" target="_blank">
|
|
59
59
|
<button class="button--primary button--icon">
|
|
60
60
|
Go to lessons<span class="iconify" data-icon="mdi:open-in-new"></span>
|
|
61
61
|
</button>
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
programming constructs. Designed for children aged 8–11, but anyone can progress here if
|
|
122
122
|
ready.
|
|
123
123
|
</div>
|
|
124
|
-
<a class="button" href="https://code-for-life.gitbook.io/independent-student-resources/intermediate
|
|
124
|
+
<a class="button" href="https://code-for-life.gitbook.io/independent-student-resources/rapid-router-resources/intermediate" target="_blank">
|
|
125
125
|
<button class="button--primary button--icon">
|
|
126
126
|
Go to lessons<span class="iconify" data-icon="mdi:open-in-new"></span>
|
|
127
127
|
</button>
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
<div>Let’s get advanced! Learn about repeat loops and selection, variables, and how to create
|
|
151
151
|
efficient code. Designed for children aged 12–14, but open to all.
|
|
152
152
|
</div>
|
|
153
|
-
<a class="button" href="https://code-for-life.gitbook.io/independent-student-resources/advanced
|
|
153
|
+
<a class="button" href="https://code-for-life.gitbook.io/independent-student-resources/rapid-router-resources/advanced" target="_blank">
|
|
154
154
|
<button class="button--primary button--icon">
|
|
155
155
|
Go to lessons<span class="iconify" data-icon="mdi:open-in-new"></span>
|
|
156
156
|
</button>
|
|
@@ -171,10 +171,11 @@
|
|
|
171
171
|
<h6>Session 3 & 4</h6>
|
|
172
172
|
<p>Extra tasks for children who want a challenge! Watch the if...do video to learn about selection
|
|
173
173
|
statements. Ask your child to explain how their finished program works!</p>
|
|
174
|
+
<h6>Session 5</h6>
|
|
175
|
+
<p>Learn more about if...else through traffic lights. In the Traffic Lights levels in Rapid Router, traffic light is a variable that either contains red or green.</p>
|
|
174
176
|
<h6>Extended</h6>
|
|
175
177
|
<p>Build on everything learned so far with traffic lights, limited blocks, procedures and brain teasers.
|
|
176
|
-
Older children might even like to start learning to program using the Python language using
|
|
177
|
-
80 onwards.</p>
|
|
178
|
+
Older children might even like to start learning to program using the Python language using <a href="{% url 'python_levels' %}">Python Den</a>.</p>
|
|
178
179
|
</div>
|
|
179
180
|
</div>
|
|
180
181
|
</div>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{% load static %}
|
|
2
|
+
{% load app_tags %}
|
|
3
|
+
{# A popup with a form input for email addresses. #}
|
|
4
|
+
<section id="donate-popup" class="popup-wrapper">
|
|
5
|
+
<div class="popup-box">
|
|
6
|
+
<div>
|
|
7
|
+
<button type="button" onclick="hideDonatePopup()" class="close popup-box__close" aria-label="Close">
|
|
8
|
+
<span class="iconify" data-icon="mdi:close"></span>
|
|
9
|
+
</button>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="popup-box__title"></div>
|
|
12
|
+
<div class="popup-box__msg"></div>
|
|
13
|
+
<form id="donate_form" action="{% url 'process_donate_form' %}"
|
|
14
|
+
method="post">
|
|
15
|
+
<div class="col-sm-12">
|
|
16
|
+
{{ donate_form.non_field_errors }}
|
|
17
|
+
<div class="form--row">
|
|
18
|
+
<div class="form--row__input">
|
|
19
|
+
<label><small>{{ donate_form.email.label }}</small></label>
|
|
20
|
+
<div class="input--icon">
|
|
21
|
+
{{ donate_form.email }}
|
|
22
|
+
<span class="iconify" data-icon="mdi:email-outline"></span>
|
|
23
|
+
</div>
|
|
24
|
+
{{ donate_form.email.errors }}
|
|
25
|
+
<small>{{ donate_form.email.help_text }}</small>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="form--row justify-content-between">
|
|
29
|
+
<div class="form__checkbox">
|
|
30
|
+
<div class="form__checkbox-input">
|
|
31
|
+
{{ donate_form.age_verification }}
|
|
32
|
+
</div>
|
|
33
|
+
<p class="p-0">Please confirm that you are over 18.</p>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
<div class="popup-box__buttons">
|
|
38
|
+
<a class="button button--small button--secondary button--secondary--dark"
|
|
39
|
+
onclick="hideDonatePopup()" aria-labelledby="cancel_button">Cancel</a>
|
|
40
|
+
<button type="submit" id="confirm_donate_button"
|
|
41
|
+
class='button button--small button--primary'
|
|
42
|
+
aria-labelledby="confirm_button">Confirm</button>
|
|
43
|
+
</div>
|
|
44
|
+
</form>
|
|
45
|
+
</div>
|
|
46
|
+
</section>
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
<a class="button--menu button--menu--secondary button--menu--disabled">Games</a>
|
|
58
58
|
<a class="button--menu button--menu--secondary button--menu--disabled">Teaching Resources</a>
|
|
59
59
|
{% endif %}
|
|
60
|
+
<button onclick="showDonateConfirmation()" class="button--menu button--pill button--tertiary">Donate</button>
|
|
60
61
|
{% else %}
|
|
61
62
|
{% if user|is_independent_student %}
|
|
62
63
|
<div class="menu--title">Independent</div>
|
|
@@ -103,6 +104,7 @@
|
|
|
103
104
|
<div class="menu__left-side col-md-5">
|
|
104
105
|
<a href="{% url 'teach' %}" class="button--menu button--menu--primary button--menu--enabled">Teachers</a>
|
|
105
106
|
<a href="{% url 'play' %}" class="button--menu button--menu--primary button--menu--enabled">Students</a>
|
|
107
|
+
<button id="donate" onclick="showDonateConfirmation()" class="button--menu button--pill button--tertiary">Donate</button>
|
|
106
108
|
{% endif %}
|
|
107
109
|
</div>
|
|
108
110
|
{% if user|is_logged_in %}
|
|
@@ -145,36 +147,36 @@
|
|
|
145
147
|
{% endif %}
|
|
146
148
|
</ul>
|
|
147
149
|
</div>
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
150
|
+
{% else %}
|
|
151
|
+
<div class="menu__right-side col-md-5">
|
|
152
|
+
<a href="{% url 'register' %}" id="signup_button" class="button button--primary button--register register">Register</a>
|
|
153
|
+
<div class="dropdown">
|
|
154
|
+
<button id="login_dropdown" class="button--regular button--secondary button--header--login button--dropdown"
|
|
155
|
+
data-toggle="dropdown">
|
|
156
|
+
<div class="dropdown__text">Log in</div>
|
|
157
|
+
</button>
|
|
158
|
+
<ul class="dropdown-menu header__login-options-dropdown-menu">
|
|
159
|
+
<li class="dropdown-menu__option">
|
|
160
|
+
<a id="teacher_login_button" class="button--regular" aria-labelledby="Teacher" href="{% url 'teacher_login' %}">
|
|
161
|
+
<div class="dropdown-menu__option__text">Teacher</div>
|
|
162
|
+
<span data-nosnippet class="material-icons-outlined">chevron_right</span>
|
|
163
|
+
</a>
|
|
164
|
+
</li>
|
|
165
|
+
<li class="dropdown-menu__option">
|
|
166
|
+
<a id="student_login_button" class="button--regular" aria-labelledby="Student" href="{% url 'student_login_access_code' %}">
|
|
167
|
+
<div class="dropdown-menu__option__text">Student</div>
|
|
168
|
+
<span data-nosnippet class="material-icons-outlined">chevron_right</span>
|
|
169
|
+
</a>
|
|
170
|
+
</li>
|
|
171
|
+
<li class="dropdown-menu__option">
|
|
172
|
+
<a id="independent_login_button" class="button--regular" aria-labelledby="Independent" href="{% url 'independent_student_login' %}">
|
|
173
|
+
<div class="dropdown-menu__option__text">Independent</div>
|
|
174
|
+
<span data-nosnippet class="material-icons-outlined">chevron_right</span>
|
|
175
|
+
</a>
|
|
176
|
+
</li>
|
|
177
|
+
</ul>
|
|
178
|
+
</div>
|
|
179
|
+
{% endif %}
|
|
178
180
|
</div>
|
|
179
181
|
</div>
|
|
180
182
|
<div id="menu-items" class="collapse">
|
|
@@ -239,6 +241,8 @@
|
|
|
239
241
|
{% else %}
|
|
240
242
|
<a class="button button--menu__item button--register"
|
|
241
243
|
href="{% url 'register' %}">Register now</a>
|
|
244
|
+
<button onclick="showDonateConfirmation()"
|
|
245
|
+
class="button--menu__item button--donate">Donate</button>
|
|
242
246
|
<button class="button--menu__item button--menu__item--sub-header login"
|
|
243
247
|
data-toggle="collapse" data-target="#login-tabs">Log in</button>
|
|
244
248
|
<div id="login-tabs" class="collapse">
|
portal/tests/test_emails.py
CHANGED
|
@@ -10,6 +10,7 @@ from common.helpers.emails import (
|
|
|
10
10
|
send_dotmailer_consent_confirmation_email_to_user,
|
|
11
11
|
DotmailerUserType,
|
|
12
12
|
)
|
|
13
|
+
from common.mail import address_book_ids
|
|
13
14
|
from django.test import Client
|
|
14
15
|
from django.urls import reverse
|
|
15
16
|
|
|
@@ -21,7 +22,6 @@ from example_project.portal_test_settings import (
|
|
|
21
22
|
DOTMAILER_THANKS_FOR_STAYING_CAMPAIGN_ID,
|
|
22
23
|
DOTMAILER_CREATE_CONTACT_URL,
|
|
23
24
|
DOTMAILER_DELETE_USER_BY_ID_URL,
|
|
24
|
-
DOTMAILER_MAIN_ADDRESS_BOOK_URL,
|
|
25
25
|
DOTMAILER_TEACHER_ADDRESS_BOOK_URL,
|
|
26
26
|
DOTMAILER_STUDENT_ADDRESS_BOOK_URL,
|
|
27
27
|
DOTMAILER_NO_ACCOUNT_ADDRESS_BOOK_URL,
|
|
@@ -47,7 +47,11 @@ def test_newsletter_calls_correct_requests(mocker, monkeypatch):
|
|
|
47
47
|
)
|
|
48
48
|
|
|
49
49
|
add_to_dotmailer(
|
|
50
|
-
"Ray",
|
|
50
|
+
"Ray",
|
|
51
|
+
"Charles",
|
|
52
|
+
"ray.charles@example.com",
|
|
53
|
+
address_book_ids["newsletter"],
|
|
54
|
+
DotmailerUserType.TEACHER,
|
|
51
55
|
)
|
|
52
56
|
|
|
53
57
|
mocked_create_contact.assert_called_once()
|
|
@@ -121,17 +125,15 @@ def test_newsletter_sends_correct_request_data(
|
|
|
121
125
|
)
|
|
122
126
|
|
|
123
127
|
add_contact_to_address_book(
|
|
124
|
-
"Ray",
|
|
128
|
+
"Ray",
|
|
129
|
+
"Charles",
|
|
130
|
+
"ray.charles@example.com",
|
|
131
|
+
address_book_ids["newsletter"],
|
|
132
|
+
DotmailerUserType.TEACHER,
|
|
125
133
|
)
|
|
126
134
|
|
|
127
135
|
assert mocked_post.call_count == 3
|
|
128
136
|
|
|
129
|
-
mocked_post.assert_any_call(
|
|
130
|
-
DOTMAILER_MAIN_ADDRESS_BOOK_URL,
|
|
131
|
-
auth=(DOTMAILER_USER, DOTMAILER_PASSWORD),
|
|
132
|
-
json=expected_body2,
|
|
133
|
-
)
|
|
134
|
-
|
|
135
137
|
mocked_post.assert_any_call(
|
|
136
138
|
DOTMAILER_TEACHER_ADDRESS_BOOK_URL,
|
|
137
139
|
auth=(DOTMAILER_USER, DOTMAILER_PASSWORD),
|
|
@@ -141,17 +143,15 @@ def test_newsletter_sends_correct_request_data(
|
|
|
141
143
|
mocked_post.reset_mock()
|
|
142
144
|
|
|
143
145
|
add_contact_to_address_book(
|
|
144
|
-
"Ray",
|
|
146
|
+
"Ray",
|
|
147
|
+
"Charles",
|
|
148
|
+
"ray.charles@example.com",
|
|
149
|
+
address_book_ids["newsletter"],
|
|
150
|
+
DotmailerUserType.STUDENT,
|
|
145
151
|
)
|
|
146
152
|
|
|
147
153
|
assert mocked_post.call_count == 2
|
|
148
154
|
|
|
149
|
-
mocked_post.assert_any_call(
|
|
150
|
-
DOTMAILER_MAIN_ADDRESS_BOOK_URL,
|
|
151
|
-
auth=(DOTMAILER_USER, DOTMAILER_PASSWORD),
|
|
152
|
-
json=expected_body2,
|
|
153
|
-
)
|
|
154
|
-
|
|
155
155
|
mocked_post.assert_any_call(
|
|
156
156
|
DOTMAILER_STUDENT_ADDRESS_BOOK_URL,
|
|
157
157
|
auth=(DOTMAILER_USER, DOTMAILER_PASSWORD),
|
|
@@ -164,17 +164,12 @@ def test_newsletter_sends_correct_request_data(
|
|
|
164
164
|
"Ray",
|
|
165
165
|
"Charles",
|
|
166
166
|
"ray.charles@example.com",
|
|
167
|
+
address_book_ids["newsletter"],
|
|
167
168
|
DotmailerUserType.NO_ACCOUNT,
|
|
168
169
|
)
|
|
169
170
|
|
|
170
171
|
assert mocked_post.call_count == 2
|
|
171
172
|
|
|
172
|
-
mocked_post.assert_any_call(
|
|
173
|
-
DOTMAILER_MAIN_ADDRESS_BOOK_URL,
|
|
174
|
-
auth=(DOTMAILER_USER, DOTMAILER_PASSWORD),
|
|
175
|
-
json=expected_body2,
|
|
176
|
-
)
|
|
177
|
-
|
|
178
173
|
mocked_post.assert_any_call(
|
|
179
174
|
DOTMAILER_NO_ACCOUNT_ADDRESS_BOOK_URL,
|
|
180
175
|
auth=(DOTMAILER_USER, DOTMAILER_PASSWORD),
|
|
@@ -2,7 +2,7 @@ from django.urls import reverse
|
|
|
2
2
|
from django.test import TestCase, Client
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
class
|
|
5
|
+
class TestGlobalForms(TestCase):
|
|
6
6
|
def test_newsletter_signup_successful(self):
|
|
7
7
|
url = reverse("process_newsletter_form")
|
|
8
8
|
client = Client()
|
|
@@ -18,3 +18,19 @@ class TestNewsletterFooter(TestCase):
|
|
|
18
18
|
response = client.post(url, data)
|
|
19
19
|
messages = list(response.wsgi_request._messages)
|
|
20
20
|
assert len([m for m in messages if "error" in m.tags]) == 1
|
|
21
|
+
|
|
22
|
+
def test_donate_signup_successful(self):
|
|
23
|
+
url = reverse("process_donate_form")
|
|
24
|
+
client = Client()
|
|
25
|
+
data = {"email": "valid_email@example.com", "age_verification": "on"}
|
|
26
|
+
response = client.post(url, data)
|
|
27
|
+
messages = list(response.wsgi_request._messages)
|
|
28
|
+
assert len([m for m in messages if m.tags == "success"]) == 1
|
|
29
|
+
|
|
30
|
+
def test_donate_signup_fail(self):
|
|
31
|
+
url = reverse("process_donate_form")
|
|
32
|
+
client = Client()
|
|
33
|
+
data = {"email": "invalid_email", "age_verification": "on"}
|
|
34
|
+
response = client.post(url, data)
|
|
35
|
+
messages = list(response.wsgi_request._messages)
|
|
36
|
+
assert len([m for m in messages if "error" in m.tags]) == 1
|
portal/urls.py
CHANGED
|
@@ -38,6 +38,7 @@ from portal.views.api import (
|
|
|
38
38
|
)
|
|
39
39
|
from portal.views.dotmailer import (
|
|
40
40
|
dotmailer_consent_form,
|
|
41
|
+
process_donate_form,
|
|
41
42
|
process_newsletter_form,
|
|
42
43
|
)
|
|
43
44
|
from portal.views.email import verify_email
|
|
@@ -278,6 +279,7 @@ urlpatterns = [
|
|
|
278
279
|
process_newsletter_form,
|
|
279
280
|
name="process_newsletter_form",
|
|
280
281
|
),
|
|
282
|
+
url(r"^donate_signup/$", process_donate_form, name="process_donate_form"),
|
|
281
283
|
url(r"^consent_form/$", dotmailer_consent_form, name="consent_form"),
|
|
282
284
|
url(
|
|
283
285
|
r"^verify_email/$",
|
portal/views/dotmailer.py
CHANGED
|
@@ -5,13 +5,14 @@ from common.helpers.emails import (
|
|
|
5
5
|
add_consent_record_to_dotmailer_user,
|
|
6
6
|
DotmailerUserType,
|
|
7
7
|
)
|
|
8
|
+
from common.mail import address_book_ids
|
|
8
9
|
from django.contrib import messages as messages
|
|
9
10
|
from django.http import HttpResponseRedirect, HttpResponse
|
|
10
11
|
from django.shortcuts import render
|
|
11
12
|
from django.urls import reverse_lazy
|
|
12
13
|
from django.views.decorators.csrf import csrf_exempt
|
|
13
14
|
|
|
14
|
-
from portal.forms.dotmailer import NewsletterForm, ConsentForm
|
|
15
|
+
from portal.forms.dotmailer import NewsletterForm, DonateForm, ConsentForm
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
@csrf_exempt
|
|
@@ -20,7 +21,13 @@ def process_newsletter_form(request):
|
|
|
20
21
|
newsletter_form = NewsletterForm(data=request.POST)
|
|
21
22
|
if newsletter_form.is_valid():
|
|
22
23
|
user_email = newsletter_form.cleaned_data["email"]
|
|
23
|
-
add_to_dotmailer(
|
|
24
|
+
add_to_dotmailer(
|
|
25
|
+
"",
|
|
26
|
+
"",
|
|
27
|
+
user_email,
|
|
28
|
+
address_book_ids["newsletter"],
|
|
29
|
+
DotmailerUserType.NO_ACCOUNT,
|
|
30
|
+
)
|
|
24
31
|
messages.success(request, "Thank you for signing up! 🎉")
|
|
25
32
|
return HttpResponseRedirect(reverse_lazy("home"))
|
|
26
33
|
messages.error(
|
|
@@ -33,6 +40,27 @@ def process_newsletter_form(request):
|
|
|
33
40
|
return HttpResponse(status=405)
|
|
34
41
|
|
|
35
42
|
|
|
43
|
+
@csrf_exempt
|
|
44
|
+
def process_donate_form(request):
|
|
45
|
+
if request.method == "POST":
|
|
46
|
+
donate_form = DonateForm(data=request.POST)
|
|
47
|
+
if donate_form.is_valid():
|
|
48
|
+
user_email = request.POST.get("email", "")
|
|
49
|
+
add_to_dotmailer("", "", user_email, address_book_ids["donors"])
|
|
50
|
+
messages.success(
|
|
51
|
+
request, "Thank you for registering your interest! 🎉"
|
|
52
|
+
)
|
|
53
|
+
return HttpResponseRedirect(reverse_lazy("home"))
|
|
54
|
+
messages.error(
|
|
55
|
+
request,
|
|
56
|
+
"Invalid email address. Please try again.",
|
|
57
|
+
extra_tags="sub-nav--warning",
|
|
58
|
+
)
|
|
59
|
+
return HttpResponseRedirect(reverse_lazy("home"))
|
|
60
|
+
|
|
61
|
+
return HttpResponse(status=405)
|
|
62
|
+
|
|
63
|
+
|
|
36
64
|
def dotmailer_consent_form(request):
|
|
37
65
|
if request.method == "POST":
|
|
38
66
|
consent_form = ConsentForm(data=request.POST)
|