codeforlife-portal 5.33.5__py2.py3-none-any.whl → 8.9.9__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.
- cfl_common/common/__init__.py +1 -0
- cfl_common/common/app_settings.py +66 -0
- cfl_common/common/apps.py +6 -0
- cfl_common/common/context_processors.py +9 -0
- cfl_common/common/csp_config.py +85 -0
- cfl_common/common/helpers/__init__.py +0 -0
- cfl_common/common/helpers/data_migration_loader.py +42 -0
- cfl_common/common/helpers/emails.py +393 -0
- cfl_common/common/helpers/generators.py +52 -0
- cfl_common/common/helpers/organisation.py +10 -0
- cfl_common/common/mail.py +201 -0
- cfl_common/common/migrations/0001_initial.py +240 -0
- cfl_common/common/migrations/0002_emailverification.py +55 -0
- cfl_common/common/migrations/0003_aimmocharacter.py +31 -0
- cfl_common/common/migrations/0004_add_aimmocharacters.py +17 -0
- cfl_common/common/migrations/0005_add_worksheets.py +8 -0
- cfl_common/common/migrations/0006_update_aimmo_character_image_path.py +17 -0
- cfl_common/common/migrations/0007_add_pdf_names_to_first_two_worksheets.py +8 -0
- cfl_common/common/migrations/0008_unlock_worksheet_3.py +11 -0
- cfl_common/common/migrations/0009_add_blocked_time_to_teacher_and_student.py +24 -0
- cfl_common/common/migrations/0010_remove_teacher_title.py +18 -0
- cfl_common/common/migrations/0011_student_login_id.py +18 -0
- cfl_common/common/migrations/0012_usersession.py +39 -0
- cfl_common/common/migrations/0013_class_school.py +42 -0
- cfl_common/common/migrations/0014_login_type.py +29 -0
- cfl_common/common/migrations/0015_dailyactivity.py +31 -0
- cfl_common/common/migrations/0016_joinreleasestudent.py +42 -0
- cfl_common/common/migrations/0017_copy_email_to_username.py +18 -0
- cfl_common/common/migrations/0018_update_aimmo_character_image_path.py +15 -0
- cfl_common/common/migrations/0019_aimmocharacter_alt.py +16 -0
- cfl_common/common/migrations/0020_class_is_active_and_null_access_code.py +23 -0
- cfl_common/common/migrations/0021_school_is_active.py +28 -0
- cfl_common/common/migrations/0022_school_cleanup.py +29 -0
- cfl_common/common/migrations/0023_userprofile_aimmo_badges.py +22 -0
- cfl_common/common/migrations/0024_teacher_invited_by.py +25 -0
- cfl_common/common/migrations/0025_schoolteacherinvitation.py +47 -0
- cfl_common/common/migrations/0026_teacher_remove_join_request.py +22 -0
- cfl_common/common/migrations/0027_class_created_by.py +25 -0
- cfl_common/common/migrations/0028_coding_club_downloads.py +23 -0
- cfl_common/common/migrations/0029_dynamicelement.py +22 -0
- cfl_common/common/migrations/0030_add_maintenance_banner.py +25 -0
- cfl_common/common/migrations/0031_improve_admin_panel.py +56 -0
- cfl_common/common/migrations/0032_dailyactivity_level_control_submits.py +18 -0
- cfl_common/common/migrations/0033_password_reset_tracking_fields.py +23 -0
- cfl_common/common/migrations/0034_dailyactivity_daily_school_student_lockout_reset.py +18 -0
- cfl_common/common/migrations/0035_rename_lockout_fields.py +27 -0
- cfl_common/common/migrations/0036_rename_awaiting_email_verification_userprofile_is_verified.py +17 -0
- cfl_common/common/migrations/0037_migrate_email_verification.py +21 -0
- cfl_common/common/migrations/0038_delete_emailverification.py +16 -0
- cfl_common/common/migrations/0039_copy_email_to_username.py +18 -0
- cfl_common/common/migrations/0040_school_county.py +18 -0
- cfl_common/common/migrations/0041_populate_gb_counties.py +27 -0
- cfl_common/common/migrations/0042_totalactivity.py +25 -0
- cfl_common/common/migrations/0043_add_total_activity.py +30 -0
- cfl_common/common/migrations/0044_update_activity_models.py +33 -0
- cfl_common/common/migrations/0045_otp.py +23 -0
- cfl_common/common/migrations/0046_alter_school_country.py +19 -0
- cfl_common/common/migrations/0047_delete_school_postcode.py +16 -0
- cfl_common/common/migrations/0048_unique_school_names.py +42 -0
- cfl_common/common/migrations/0049_anonymise_orphan_users.py +29 -0
- cfl_common/common/migrations/0050_anonymise_orphan_schools.py +30 -0
- cfl_common/common/migrations/0051_verify_returning_users.py +26 -0
- cfl_common/common/migrations/0052_add_cse_fields.py +68 -0
- cfl_common/common/migrations/0053_clean_class_data.py +24 -0
- cfl_common/common/migrations/0054_delete_aimmo_models.py +20 -0
- cfl_common/common/migrations/0055_alter_schoolteacherinvitation_token.py +18 -0
- cfl_common/common/migrations/0056_set_non_school_teachers_as_non_admins.py +25 -0
- cfl_common/common/migrations/0057_teacher_teacher__is_admin.py +19 -0
- cfl_common/common/migrations/0058_userprofile_google_refresh_token_and_more.py +24 -0
- cfl_common/common/migrations/__init__.py +0 -0
- cfl_common/common/models.py +557 -0
- cfl_common/common/permissions.py +84 -0
- cfl_common/common/tests/__init__.py +0 -0
- cfl_common/common/tests/test_migration_anonymise_orphan_schools.py +30 -0
- cfl_common/common/tests/test_migration_anonymise_orphan_users.py +30 -0
- cfl_common/common/tests/test_migration_blocked_time.py +15 -0
- cfl_common/common/tests/test_migration_remove_teacher_title.py +13 -0
- cfl_common/common/tests/test_migration_unique_school_names.py +33 -0
- cfl_common/common/tests/test_migration_verify_returning_users.py +59 -0
- cfl_common/common/tests/test_models.py +87 -0
- cfl_common/common/tests/utils/__init__.py +0 -0
- cfl_common/common/tests/utils/classes.py +38 -0
- cfl_common/common/tests/utils/email.py +67 -0
- cfl_common/common/tests/utils/organisation.py +41 -0
- cfl_common/common/tests/utils/student.py +123 -0
- cfl_common/common/tests/utils/teacher.py +73 -0
- cfl_common/common/tests/utils/user.py +27 -0
- cfl_common/common/utils.py +56 -0
- cfl_common/setup.py +61 -0
- codeforlife_portal-8.9.9.dist-info/METADATA +226 -0
- {codeforlife_portal-5.33.5.dist-info → codeforlife_portal-8.9.9.dist-info}/RECORD +339 -241
- {codeforlife_portal-5.33.5.dist-info → codeforlife_portal-8.9.9.dist-info}/WHEEL +1 -1
- codeforlife_portal-8.9.9.dist-info/licenses/LICENSE.md +3 -0
- {codeforlife_portal-5.33.5.dist-info → codeforlife_portal-8.9.9.dist-info}/top_level.txt +1 -0
- deploy/middleware/maintenance.py +25 -0
- deploy/middleware/screentime_warning.py +29 -0
- deploy/middleware/security.py +5 -6
- deploy/middleware/session_timeout.py +4 -2
- deploy/middleware/tmp_basic_auth.py +41 -0
- example_project/portal_test_settings.py +239 -0
- example_project/settings.py +156 -17
- example_project/urls.py +5 -6
- portal/__init__.py +1 -1
- portal/admin.py +142 -29
- portal/app_settings.py +8 -7
- portal/forms/dotmailer.py +6 -4
- portal/forms/invite_teacher.py +19 -10
- portal/forms/organisation.py +137 -68
- portal/forms/play.py +53 -98
- portal/forms/registration.py +70 -164
- portal/forms/teach.py +147 -121
- portal/handlers.py +1 -2
- portal/helpers/decorators.py +30 -10
- portal/helpers/password.py +86 -47
- portal/helpers/ratelimit.py +32 -15
- portal/helpers/regexes.py +5 -0
- portal/helpers/request_handlers.py +10 -0
- portal/migrations/0044_auto_20150430_0959.py +6 -2
- portal/mixins/__init__.py +1 -0
- portal/mixins/cron_mixin.py +12 -0
- portal/permissions/__init__.py +1 -0
- portal/permissions/is_cron_request_from_google.py +14 -0
- portal/static/portal/img/10_years_anniversary.png +0 -0
- portal/static/portal/img/RR_logo_grass_background.png +0 -0
- portal/static/portal/img/coding_club_hero.jpg +0 -0
- portal/static/portal/img/coding_club_python_pack.png +0 -0
- portal/static/portal/img/facebook.png +0 -0
- portal/static/portal/img/gitbook.png +0 -0
- portal/static/portal/img/howe_dell_1.png +0 -0
- portal/static/portal/img/howe_dell_2.png +0 -0
- portal/static/portal/img/howe_dell_3.png +0 -0
- portal/static/portal/img/logo_cfl.png +0 -0
- portal/static/portal/img/logo_cfl_powered.svg +35 -0
- portal/static/portal/img/logo_cfl_reminder_cards.jpg +0 -0
- portal/static/portal/img/logo_ocado_group.png +0 -0
- portal/static/portal/img/logo_python_den.svg +21 -0
- portal/static/portal/img/long_europe_map.png +0 -0
- portal/static/portal/img/python_den.png +0 -0
- portal/static/portal/img/python_den_banner.svg +26 -0
- portal/static/portal/img/rapid_router_landing_hero.png +0 -0
- portal/static/portal/img/rr_advanced.png +0 -0
- portal/static/portal/img/ten_year_map_pin.svg +1 -0
- portal/static/portal/img/thumbnail_educate_rapid_router.png +0 -0
- portal/static/portal/img/thumbnail_educate_resources.png +0 -0
- portal/static/portal/img/thumbnail_play_rapid_router.png +0 -0
- portal/static/portal/img/thumbnail_python_den.png +0 -0
- portal/static/portal/img/twitter.png +0 -0
- portal/static/portal/js/carouselCards.js +25 -0
- portal/static/portal/js/common.js +96 -1
- portal/static/portal/js/independentLogin.js +16 -0
- portal/static/portal/js/independentRegistration.js +86 -0
- portal/static/portal/js/levelControl.js +77 -0
- portal/static/portal/js/lib/jquery.min.js +2 -0
- portal/static/portal/js/organisation_manage.js +142 -14
- portal/static/portal/js/passwordStrength.js +154 -64
- portal/static/portal/js/resetPassword.js +23 -0
- portal/static/portal/js/riveted.min.js +238 -239
- portal/static/portal/js/school.js +13 -0
- portal/static/portal/js/studentLogin.js +16 -0
- portal/static/portal/js/teacherEditStudent.js +23 -0
- portal/static/portal/js/teacherLogin.js +16 -0
- portal/static/portal/js/tenYearMap.js +14 -0
- portal/static/portal/sass/colorbox.scss +0 -1
- portal/static/portal/sass/modules/_colours.scss +1 -0
- portal/static/portal/sass/modules/_levels.scss +1 -1
- portal/static/portal/sass/modules/_mixins.scss +21 -0
- portal/static/portal/sass/partials/_banners.scss +4 -177
- portal/static/portal/sass/partials/_buttons.scss +12 -15
- portal/static/portal/sass/partials/_carousel.scss +129 -0
- portal/static/portal/sass/partials/_footer.scss +21 -22
- portal/static/portal/sass/partials/_forms.scss +60 -5
- portal/static/portal/sass/partials/_grids.scss +34 -61
- portal/static/portal/sass/partials/_header.scss +28 -20
- portal/static/portal/sass/partials/_images.scss +292 -39
- portal/static/portal/sass/partials/_popup.scss +18 -15
- portal/static/portal/sass/partials/_tables.scss +12 -20
- portal/static/portal/sass/partials/_text.scss +6 -10
- portal/static/portal/sass/styles.scss +0 -1
- portal/static/portal/video/code for life .pdf +0 -0
- portal/strings/about.py +5 -0
- portal/strings/coding_club.py +9 -0
- portal/strings/play.py +6 -5
- portal/strings/teach.py +1 -1
- portal/strings/teacher_resources.py +2 -8
- portal/strings/ten_year_map.py +13 -0
- portal/templates/403.html +2 -2
- portal/templates/404.html +1 -1
- portal/templates/500.html +2 -2
- portal/templates/{captcha → django_recaptcha}/includes/js_v2_invisible.html +3 -3
- portal/templates/{captcha → django_recaptcha}/widget_v2_invisible.html +2 -2
- portal/templates/email.html +4 -2
- portal/templates/maintenance.html +34 -0
- portal/templates/portal/about.html +94 -62
- portal/templates/portal/base.html +176 -152
- portal/templates/portal/coding_club.html +100 -0
- portal/templates/portal/contribute.html +56 -52
- portal/templates/portal/email_invitation_sent.html +1 -1
- portal/templates/portal/email_style_template.html +374 -0
- portal/templates/portal/email_verification_failed.html +1 -1
- portal/templates/portal/email_verification_needed.html +9 -9
- portal/templates/portal/form_shapes.html +20 -8
- portal/templates/portal/getinvolved.html +6 -6
- portal/templates/portal/home.html +35 -10
- portal/templates/portal/home_learning.html +19 -19
- portal/templates/portal/locked_out.html +0 -1
- portal/templates/portal/locked_out_school_student.html +16 -0
- portal/templates/portal/login/independent_student.html +31 -15
- portal/templates/portal/login/student.html +10 -7
- portal/templates/portal/login/student_class_code.html +7 -4
- portal/templates/portal/login/teacher.html +34 -17
- portal/templates/portal/partials/banner.html +18 -4
- portal/templates/portal/partials/benefits.html +1 -1
- portal/templates/portal/partials/card_list.html +34 -24
- portal/templates/portal/partials/character_list.html +5 -5
- portal/templates/portal/partials/cookie_list.html +161 -0
- portal/templates/portal/partials/delete_popup.html +18 -0
- portal/templates/portal/partials/footer.html +57 -26
- portal/templates/portal/partials/header.html +118 -117
- portal/templates/portal/partials/hero_card.html +4 -3
- portal/templates/portal/partials/info_popup.html +3 -3
- portal/templates/portal/partials/invite_admin_teacher.html +23 -0
- portal/templates/portal/partials/popup.html +7 -2
- portal/templates/portal/partials/register_newsletter_tickbox.html +2 -5
- portal/templates/portal/partials/screentime_popup.html +14 -0
- portal/templates/portal/partials/service_unavailable_popup.html +17 -0
- portal/templates/portal/partials/session_popup.html +19 -0
- portal/templates/portal/play/student_dashboard.html +42 -29
- portal/templates/portal/play/student_edit_account.html +64 -9
- portal/templates/portal/play.html +61 -41
- portal/templates/portal/privacy_notice.html +697 -0
- portal/templates/portal/register.html +122 -92
- portal/templates/portal/reset_password.html +20 -40
- portal/templates/portal/reset_password_confirm.html +9 -4
- portal/templates/portal/reset_password_email_sent.html +15 -13
- portal/templates/portal/teach/base_registering.html +1 -1
- portal/templates/portal/teach/class.html +4 -6
- portal/templates/portal/teach/dashboard.html +212 -117
- portal/templates/portal/teach/invited.html +90 -0
- portal/templates/portal/teach/onboarding_classes.html +5 -3
- portal/templates/portal/teach/onboarding_print.html +1 -1
- portal/templates/portal/teach/onboarding_school.html +26 -139
- portal/templates/portal/teach/onboarding_students.html +1 -1
- portal/templates/portal/teach/teacher_dismiss_students.html +73 -55
- portal/templates/portal/teach/teacher_edit_class.html +168 -11
- portal/templates/portal/teach/teacher_edit_student.html +12 -5
- portal/templates/portal/teach/teacher_move_all_classes.html +25 -38
- portal/templates/portal/teach/teacher_move_students_to_class.html +1 -1
- portal/templates/portal/teach.html +61 -42
- portal/templates/portal/ten_year_map.html +147 -0
- portal/templates/portal/terms.html +191 -42
- portal/templates/two_factor/core/login.html +71 -59
- portal/templates/two_factor/core/setup.html +58 -49
- portal/templates/two_factor/profile/disable.html +1 -1
- portal/templates/two_factor/profile/profile.html +35 -17
- portal/templatetags/app_tags.py +59 -84
- portal/templatetags/card_list_tags.py +0 -4
- portal/tests/base_test.py +14 -3
- portal/tests/conftest.py +0 -15
- portal/tests/migrations/test_migration_make_portaladmin_teacher.py +2 -6
- portal/tests/migrations/test_migration_preview_users.py +3 -9
- portal/tests/migrations/test_migration_remove_guardian.py +1 -3
- portal/tests/migrations/test_migration_use_common_models.py +2 -6
- portal/tests/migrations/test_migration_verify_portaladmin.py +1 -3
- portal/tests/pageObjects/portal/admin/admin_base_page.py +0 -21
- portal/tests/pageObjects/portal/base_page.py +16 -26
- portal/tests/pageObjects/portal/email_verification_needed_page.py +3 -2
- portal/tests/pageObjects/portal/game_page.py +12 -19
- portal/tests/pageObjects/portal/home_page.py +13 -15
- portal/tests/pageObjects/portal/independent_login_page.py +13 -17
- portal/tests/pageObjects/portal/password_reset_form_page.py +20 -4
- portal/tests/pageObjects/portal/password_reset_page.py +25 -0
- portal/tests/pageObjects/portal/play/account_page.py +18 -27
- portal/tests/pageObjects/portal/play/dashboard_page.py +4 -4
- portal/tests/pageObjects/portal/play/join_school_or_club_page.py +8 -10
- portal/tests/pageObjects/portal/play/play_base_page.py +5 -3
- portal/tests/pageObjects/portal/signup_page.py +28 -59
- portal/tests/pageObjects/portal/student_login_class_code.py +6 -9
- portal/tests/pageObjects/portal/student_login_page.py +6 -8
- portal/tests/pageObjects/portal/teach/add_independent_student_to_class_page.py +3 -3
- portal/tests/pageObjects/portal/teach/added_independent_student_to_class_page.py +3 -1
- portal/tests/pageObjects/portal/teach/class_page.py +36 -13
- portal/tests/pageObjects/portal/teach/dashboard_page.py +43 -84
- portal/tests/pageObjects/portal/teach/dismiss_students_page.py +7 -5
- portal/tests/pageObjects/portal/teach/edit_student_page.py +10 -8
- portal/tests/pageObjects/portal/teach/move_class_page.py +5 -10
- portal/tests/pageObjects/portal/teach/move_classes_page.py +4 -2
- portal/tests/pageObjects/portal/teach/move_students_disambiguate_page.py +4 -2
- portal/tests/pageObjects/portal/teach/move_students_page.py +6 -13
- portal/tests/pageObjects/portal/teach/onboarding_classes_page.py +5 -3
- portal/tests/pageObjects/portal/teach/onboarding_organisation_page.py +11 -49
- portal/tests/pageObjects/portal/teach/onboarding_student_list_page.py +7 -12
- portal/tests/pageObjects/portal/teach/onboarding_students_page.py +4 -27
- portal/tests/pageObjects/portal/teach/teach_base_page.py +6 -4
- portal/tests/pageObjects/portal/teacher_login_page.py +10 -16
- portal/tests/selenium_test_case.py +3 -43
- portal/tests/snapshots/snap_test_partials.py +11 -165
- portal/tests/test_2FA.py +15 -33
- portal/tests/test_admin.py +15 -97
- portal/tests/test_api.py +212 -91
- portal/tests/test_captcha_forms.py +2 -2
- portal/tests/test_class.py +374 -24
- portal/tests/test_emails.py +83 -20
- portal/tests/{test_newsletter_footer.py → test_global_forms.py} +5 -5
- portal/tests/test_helper_methods.py +30 -0
- portal/tests/test_independent_student.py +255 -144
- portal/tests/test_invite_teacher.py +318 -10
- portal/tests/test_middleware.py +96 -9
- portal/tests/test_organisation.py +78 -262
- portal/tests/test_partials.py +0 -88
- portal/tests/test_ratelimit.py +218 -36
- portal/tests/test_school_student.py +35 -40
- portal/tests/test_security.py +12 -31
- portal/tests/test_teacher.py +425 -325
- portal/tests/test_teacher_student.py +103 -91
- portal/tests/test_views.py +900 -76
- portal/tests/utils/classes.py +2 -2
- portal/tests/utils/messages.py +13 -28
- portal/urls.py +235 -166
- portal/views/admin.py +0 -332
- portal/views/api.py +82 -48
- portal/views/cron/__init__.py +1 -0
- portal/views/cron/user.py +322 -0
- portal/views/dotmailer.py +9 -1
- portal/views/email.py +33 -77
- portal/views/google_analytics.py +28 -0
- portal/views/home.py +126 -97
- portal/views/legal.py +13 -0
- portal/views/login/independent_student.py +5 -5
- portal/views/login/student.py +51 -14
- portal/views/login/teacher.py +2 -6
- portal/views/organisation.py +20 -189
- portal/views/registration.py +97 -17
- portal/views/student/edit_account_details.py +99 -72
- portal/views/student/play.py +81 -62
- portal/views/teacher/dashboard.py +421 -149
- portal/views/teacher/teach.py +226 -177
- portal/views/two_factor/core.py +22 -19
- portal/views/two_factor/profile.py +2 -2
- codeforlife_portal-5.33.5.dist-info/LICENSE.md +0 -577
- codeforlife_portal-5.33.5.dist-info/METADATA +0 -38
- deploy/permissions.py +0 -2
- example_project/manage.py +0 -10
- portal/autoconfig.py +0 -141
- portal/csp_config.py +0 -60
- portal/forms/add_game.py +0 -33
- portal/helpers/location.py +0 -121
- portal/static/portal/img/kurono_hero.jpg +0 -0
- portal/static/portal/img/kurono_landing_hero.png +0 -0
- portal/static/portal/img/kurono_logo.svg +0 -1
- portal/static/portal/img/kurono_logo_grey_background.svg +0 -1
- portal/static/portal/img/kurono_logo_mark.svg +0 -1
- portal/static/portal/img/kurono_resources_hero.jpg +0 -0
- portal/static/portal/img/kurono_story.png +0 -0
- portal/static/portal/img/ocado-swirl.svg +0 -22
- portal/static/portal/img/thumbnail_educate_kurono.png +0 -0
- portal/static/portal/img/thumbnail_educate_resources_and_progress_tracking.png +0 -0
- portal/static/portal/img/thumbnail_kurono_resources.png +0 -0
- portal/static/portal/img/thumbnail_play_kurono.png +0 -0
- portal/static/portal/img/x_close_video.png +0 -0
- portal/static/portal/js/aimmoGame.js +0 -106
- portal/static/portal/js/deleteWorkspaces.js +0 -14
- portal/static/portal/js/fuzzySchoolLookup.js +0 -46
- portal/static/portal/js/lib/jquery-3.5.1.min.js +0 -2
- portal/static/portal/js/lib/jquery-ui-1.12.1.min.js +0 -13
- portal/static/portal/sass/partials/_videos.scss +0 -10
- portal/static/portal/video/aimmo_play_now_background_video.mp4 +0 -0
- portal/strings/student_aimmo_dashboard.py +0 -6
- portal/templates/portal/admin/aggregated_data.html +0 -35
- portal/templates/portal/admin/map.html +0 -70
- portal/templates/portal/mouseflow.html +0 -9
- portal/templates/portal/partials/aimmo_games_table.html +0 -83
- portal/templates/portal/partials/register_over_required_age_tickbox.html +0 -9
- portal/templates/portal/play/independent_student_dashboard.html +0 -64
- portal/templates/portal/play/student_aimmo_dashboard.html +0 -63
- portal/templates/portal/privacy_policy.html +0 -483
- portal/templates/portal/reset_password_email.html +0 -9
- portal/templates/portal/teach/invite.html +0 -25
- portal/templates/portal/teach/teacher_aimmo_dashboard.html +0 -95
- portal/templates/portal/teach/teacher_resources.html +0 -68
- portal/templatetags/character_list_tags.py +0 -16
- portal/tests/pageObjects/portal/kurono_teacher_dashboard_page.py +0 -49
- portal/tests/pageObjects/portal/student_password_reset_form_page.py +0 -23
- portal/tests/pageObjects/portal/teach/onboarding_revoke_request_page.py +0 -20
- portal/tests/pageObjects/portal/teacher_password_reset_form_page.py +0 -23
- portal/tests/test_aimmo_dashboards.py +0 -172
- portal/tests/test_location.py +0 -217
- portal/tests/utils/aimmo_games.py +0 -30
- portal/views/aimmo/dashboard.py +0 -119
- portal/views/privacy_policy.py +0 -9
- portal/views/teacher/teacher_resources.py +0 -42
- {portal/views/aimmo → cfl_common}/__init__.py +0 -0
portal/views/organisation.py
CHANGED
|
@@ -1,220 +1,51 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
from common.models import School, Teacher, Class
|
|
1
|
+
import common.permissions as permissions
|
|
2
|
+
from common.models import Class, School, Teacher
|
|
4
3
|
from django.contrib import messages as messages
|
|
5
4
|
from django.contrib.auth.decorators import login_required, user_passes_test
|
|
5
|
+
from django.http import Http404, HttpResponseRedirect
|
|
6
|
+
from django.shortcuts import get_object_or_404, render
|
|
6
7
|
from django.urls import reverse_lazy
|
|
7
|
-
from django.db.models import Q
|
|
8
|
-
from django.http import Http404, HttpResponse, HttpResponseRedirect
|
|
9
|
-
from django.shortcuts import render, get_object_or_404
|
|
10
|
-
from rest_framework.authentication import SessionAuthentication
|
|
11
|
-
from rest_framework.views import APIView
|
|
12
|
-
|
|
13
|
-
import common.permissions as permissions
|
|
14
|
-
from common import email_messages
|
|
15
|
-
from portal.forms.organisation import OrganisationJoinForm, OrganisationForm
|
|
16
|
-
from common.helpers.emails import send_email, NOTIFICATION_EMAIL
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class OrganisationFuzzyLookup(APIView):
|
|
20
|
-
authentication_classes = (SessionAuthentication,)
|
|
21
|
-
permission_classes = (permissions.LoggedInAsTeacher,)
|
|
22
|
-
|
|
23
|
-
def get(self, request):
|
|
24
|
-
fuzzy_name = request.GET.get("fuzzy_name", None)
|
|
25
|
-
school_data = []
|
|
26
|
-
|
|
27
|
-
# The idea here is to return all schools that satisfy that each
|
|
28
|
-
# part of the fuzzy_name (separated by spaces) occurs in either
|
|
29
|
-
# school.name or school.postcode.
|
|
30
|
-
# So it is an intersection of unions.
|
|
31
|
-
|
|
32
|
-
if fuzzy_name and len(fuzzy_name) > 2:
|
|
33
|
-
schools = School.objects.all()
|
|
34
|
-
for part in fuzzy_name.split():
|
|
35
|
-
schools = schools.filter(
|
|
36
|
-
Q(name__icontains=part) | Q(postcode__icontains=part)
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
self._search_schools(schools, school_data)
|
|
40
8
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def _search_schools(self, schools, school_data):
|
|
44
|
-
for school in schools:
|
|
45
|
-
admins = Teacher.objects.filter(school=school, is_admin=True)
|
|
46
|
-
admin = admins.first()
|
|
47
|
-
if admin:
|
|
48
|
-
email = admin.new_user.email
|
|
49
|
-
admin_domain = "*********" + email[email.find("@") :]
|
|
50
|
-
school_data.append(
|
|
51
|
-
{
|
|
52
|
-
"id": school.id,
|
|
53
|
-
"name": school.name,
|
|
54
|
-
"postcode": school.postcode,
|
|
55
|
-
"admin_domain": admin_domain,
|
|
56
|
-
}
|
|
57
|
-
)
|
|
9
|
+
from portal.forms.organisation import OrganisationForm
|
|
58
10
|
|
|
59
11
|
|
|
60
12
|
@login_required(login_url=reverse_lazy("teacher_login"))
|
|
61
|
-
@user_passes_test(
|
|
62
|
-
permissions.logged_in_as_teacher, login_url=reverse_lazy("teacher_login")
|
|
63
|
-
)
|
|
13
|
+
@user_passes_test(permissions.logged_in_as_teacher, login_url=reverse_lazy("teacher_login"))
|
|
64
14
|
def organisation_create(request):
|
|
65
|
-
|
|
66
15
|
teacher = request.user.new_teacher
|
|
67
16
|
|
|
68
17
|
create_form = OrganisationForm(user=request.user)
|
|
69
|
-
join_form = OrganisationJoinForm()
|
|
70
18
|
|
|
71
19
|
if request.method == "POST":
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
postcode = data.get("postcode", "").upper()
|
|
78
|
-
country = data.get("country", "")
|
|
79
|
-
|
|
80
|
-
error, town, lat, lng = (
|
|
81
|
-
"",
|
|
82
|
-
"0",
|
|
83
|
-
"0",
|
|
84
|
-
"0",
|
|
85
|
-
) # lookup_coord(postcode, country)
|
|
20
|
+
create_form = OrganisationForm(request.POST, user=request.user)
|
|
21
|
+
if create_form.is_valid():
|
|
22
|
+
data = create_form.cleaned_data
|
|
23
|
+
name = data.get("name", "")
|
|
24
|
+
country = data.get("country")
|
|
86
25
|
|
|
87
|
-
|
|
88
|
-
name=name,
|
|
89
|
-
postcode=postcode,
|
|
90
|
-
town=town,
|
|
91
|
-
latitude=lat,
|
|
92
|
-
longitude=lng,
|
|
93
|
-
country=country,
|
|
94
|
-
)
|
|
26
|
+
school = School.objects.create(name=name, country=country)
|
|
95
27
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
28
|
+
teacher.school = school
|
|
29
|
+
teacher.is_admin = True
|
|
30
|
+
teacher.save()
|
|
99
31
|
|
|
100
|
-
|
|
101
|
-
request,
|
|
102
|
-
"The school or club '"
|
|
103
|
-
+ teacher.school.name
|
|
104
|
-
+ "' has been successfully added.",
|
|
105
|
-
)
|
|
32
|
+
messages.success(request, "The school or club '" + teacher.school.name + "' has been successfully added.")
|
|
106
33
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
elif "join_organisation" in request.POST:
|
|
110
|
-
process_join_form(
|
|
111
|
-
request, teacher, OrganisationJoinForm, OrganisationJoinForm
|
|
112
|
-
)
|
|
34
|
+
return HttpResponseRedirect(reverse_lazy("onboarding-classes"))
|
|
113
35
|
|
|
114
|
-
|
|
115
|
-
return process_revoke_request(request, teacher)
|
|
116
|
-
|
|
117
|
-
res = render(
|
|
118
|
-
request,
|
|
119
|
-
"portal/teach/onboarding_school.html",
|
|
120
|
-
{"create_form": create_form, "join_form": join_form, "teacher": teacher},
|
|
121
|
-
)
|
|
36
|
+
res = render(request, "portal/teach/onboarding_school.html", {"create_form": create_form, "teacher": teacher})
|
|
122
37
|
|
|
123
38
|
return res
|
|
124
39
|
|
|
125
40
|
|
|
126
|
-
def compute_input_join_form(
|
|
127
|
-
OrganisationJoinFormWithCaptcha, OrganisationJoinForm, using_captcha
|
|
128
|
-
):
|
|
129
|
-
InputOrganisationJoinForm = (
|
|
130
|
-
OrganisationJoinFormWithCaptcha if using_captcha else OrganisationJoinForm
|
|
131
|
-
)
|
|
132
|
-
return InputOrganisationJoinForm
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
def compute_output_join_form(
|
|
136
|
-
OrganisationJoinFormWithCaptcha, OrganisationJoinForm, should_use_captcha
|
|
137
|
-
):
|
|
138
|
-
OutputOrganisationJoinForm = (
|
|
139
|
-
OrganisationJoinFormWithCaptcha if should_use_captcha else OrganisationJoinForm
|
|
140
|
-
)
|
|
141
|
-
return OutputOrganisationJoinForm
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
def send_pending_requests_emails(school, email_message):
|
|
145
|
-
for admin in Teacher.objects.filter(school=school, is_admin=True):
|
|
146
|
-
send_email(
|
|
147
|
-
NOTIFICATION_EMAIL,
|
|
148
|
-
[admin.new_user.email],
|
|
149
|
-
email_message["subject"],
|
|
150
|
-
email_message["message"],
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
def process_join_form(
|
|
155
|
-
request, teacher, InputOrganisationJoinForm, OutputOrganisationJoinForm
|
|
156
|
-
):
|
|
157
|
-
join_form = InputOrganisationJoinForm(request.POST)
|
|
158
|
-
if join_form.is_valid():
|
|
159
|
-
school = get_object_or_404(School, id=join_form.cleaned_data["chosen_org"])
|
|
160
|
-
|
|
161
|
-
teacher.pending_join_request = school
|
|
162
|
-
teacher.save()
|
|
163
|
-
|
|
164
|
-
email_message = email_messages.joinRequestPendingEmail(
|
|
165
|
-
request, teacher.new_user.email
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
send_pending_requests_emails(school, email_message)
|
|
169
|
-
|
|
170
|
-
email_message = email_messages.joinRequestSentEmail(request, school.name)
|
|
171
|
-
send_email(
|
|
172
|
-
NOTIFICATION_EMAIL,
|
|
173
|
-
[teacher.new_user.email],
|
|
174
|
-
email_message["subject"],
|
|
175
|
-
email_message["message"],
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
messages.success(
|
|
179
|
-
request,
|
|
180
|
-
"Your request to join a school or club has been sent.",
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
return render(
|
|
184
|
-
request,
|
|
185
|
-
"portal/teach/onboarding_school.html",
|
|
186
|
-
{"school": school, "teacher": teacher},
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
else:
|
|
190
|
-
join_form = OutputOrganisationJoinForm(request.POST)
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
def process_revoke_request(request, teacher):
|
|
194
|
-
if "revoke_join_request" in request.POST:
|
|
195
|
-
teacher.pending_join_request = None
|
|
196
|
-
teacher.save()
|
|
197
|
-
|
|
198
|
-
messages.success(
|
|
199
|
-
request,
|
|
200
|
-
"Your request to join the school or club has been revoked successfully.",
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
return HttpResponseRedirect(reverse_lazy("onboarding-organisation"))
|
|
204
|
-
|
|
205
|
-
|
|
206
41
|
@login_required(login_url=reverse_lazy("teacher_login"))
|
|
207
|
-
@user_passes_test(
|
|
208
|
-
permissions.logged_in_as_teacher, login_url=reverse_lazy("teacher_login")
|
|
209
|
-
)
|
|
42
|
+
@user_passes_test(permissions.logged_in_as_teacher, login_url=reverse_lazy("teacher_login"))
|
|
210
43
|
def organisation_manage(request):
|
|
211
44
|
return organisation_create(request)
|
|
212
45
|
|
|
213
46
|
|
|
214
47
|
@login_required(login_url=reverse_lazy("teacher_login"))
|
|
215
|
-
@user_passes_test(
|
|
216
|
-
permissions.logged_in_as_teacher, login_url=reverse_lazy("teacher_login")
|
|
217
|
-
)
|
|
48
|
+
@user_passes_test(permissions.logged_in_as_teacher, login_url=reverse_lazy("teacher_login"))
|
|
218
49
|
def organisation_leave(request):
|
|
219
50
|
teacher = request.user.new_teacher
|
|
220
51
|
|
portal/views/registration.py
CHANGED
|
@@ -1,30 +1,46 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
from
|
|
1
|
+
import ast
|
|
2
|
+
import re
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
from common.helpers.emails import (
|
|
6
|
+
NOTIFICATION_EMAIL,
|
|
7
|
+
PASSWORD_RESET_EMAIL,
|
|
8
|
+
delete_contact,
|
|
9
|
+
send_email,
|
|
10
|
+
)
|
|
11
|
+
from common.mail import campaign_ids, send_dotdigital_email
|
|
12
|
+
from common.models import DailyActivity, Student, Teacher
|
|
13
|
+
from common.permissions import not_fully_logged_in, not_logged_in
|
|
14
|
+
from django.contrib import messages as messages
|
|
4
15
|
from django.contrib.auth import get_user_model
|
|
5
|
-
from django.contrib.auth.decorators import user_passes_test
|
|
16
|
+
from django.contrib.auth.decorators import login_required, user_passes_test
|
|
6
17
|
from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm
|
|
18
|
+
from django.contrib.auth.models import User
|
|
7
19
|
from django.contrib.auth.tokens import default_token_generator
|
|
20
|
+
from django.http import HttpResponseRedirect
|
|
8
21
|
from django.shortcuts import render
|
|
9
22
|
from django.template.response import TemplateResponse
|
|
10
23
|
from django.urls import reverse_lazy
|
|
11
|
-
from django.utils.encoding import
|
|
24
|
+
from django.utils.encoding import force_bytes
|
|
12
25
|
from django.utils.http import urlsafe_base64_decode
|
|
13
|
-
from django.utils.translation import
|
|
26
|
+
from django.utils.translation import gettext as _
|
|
14
27
|
from django.views.decorators.cache import never_cache
|
|
15
28
|
from django.views.decorators.csrf import csrf_protect
|
|
16
29
|
from django.views.decorators.debug import sensitive_post_parameters
|
|
30
|
+
from django.views.decorators.http import require_POST
|
|
17
31
|
|
|
18
32
|
from deploy import captcha
|
|
19
33
|
from portal import app_settings
|
|
20
34
|
from portal.forms.registration import (
|
|
21
|
-
TeacherPasswordResetForm,
|
|
22
|
-
TeacherPasswordResetSetPasswordForm,
|
|
23
35
|
StudentPasswordResetForm,
|
|
24
36
|
StudentPasswordResetSetPasswordForm,
|
|
37
|
+
TeacherPasswordResetForm,
|
|
38
|
+
TeacherPasswordResetSetPasswordForm,
|
|
25
39
|
)
|
|
26
40
|
from portal.helpers.captcha import remove_captcha_from_form
|
|
27
41
|
from portal.helpers.ratelimit import clear_ratelimit_cache_for_user
|
|
42
|
+
from portal.views.api import anonymise
|
|
43
|
+
from portal.views.login import has_user_lockout_expired
|
|
28
44
|
|
|
29
45
|
|
|
30
46
|
@user_passes_test(not_logged_in, login_url=reverse_lazy("home"))
|
|
@@ -39,6 +55,48 @@ def student_password_reset(request):
|
|
|
39
55
|
)
|
|
40
56
|
|
|
41
57
|
|
|
58
|
+
def blocked_and_not_expired(user: Student or Teacher):
|
|
59
|
+
return user.blocked_time and not has_user_lockout_expired(user)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def school_student_reset_password_tracker(request, activity_today):
|
|
63
|
+
if "transfer_students" in request.POST:
|
|
64
|
+
student_list = ast.literal_eval(request.POST.get("transfer_students", []))
|
|
65
|
+
for student_id in student_list:
|
|
66
|
+
current_student = Student.objects.get(id=student_id)
|
|
67
|
+
if blocked_and_not_expired(current_student):
|
|
68
|
+
activity_today.school_student_lockout_resets += 1
|
|
69
|
+
elif "set_password" in request.POST:
|
|
70
|
+
student_id = re.search("/(\d+)/", request.path).group(1)
|
|
71
|
+
current_student = Student.objects.get(id=student_id)
|
|
72
|
+
if blocked_and_not_expired(current_student):
|
|
73
|
+
activity_today.school_student_lockout_resets += 1
|
|
74
|
+
activity_today.save()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def teacher_or_indy_reset_password_tracker(request, activity_today, email):
|
|
78
|
+
get_user = Teacher.objects.filter(new_user__email=email) or Student.objects.filter(new_user__email=email)
|
|
79
|
+
if get_user.exists():
|
|
80
|
+
user = get_user[0]
|
|
81
|
+
if blocked_and_not_expired(user):
|
|
82
|
+
if "teacher" in request.path:
|
|
83
|
+
activity_today.teacher_lockout_resets += 1
|
|
84
|
+
elif "student" in request.path:
|
|
85
|
+
activity_today.indy_lockout_resets += 1
|
|
86
|
+
activity_today.save()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def handle_reset_password_tracking(request, user_type, access_code=None, student_id=None):
|
|
90
|
+
activity_today = DailyActivity.objects.get_or_create(date=datetime.now().date())[0]
|
|
91
|
+
# school student has 2 different ways of resetting password
|
|
92
|
+
# hence the function is extended
|
|
93
|
+
# check for indy student or teacher account
|
|
94
|
+
if user_type == "SCHOOL_STUDENT":
|
|
95
|
+
school_student_reset_password_tracker(request, activity_today)
|
|
96
|
+
elif "email" in request.POST:
|
|
97
|
+
teacher_or_indy_reset_password_tracker(request, activity_today, request.POST.get("email", ""))
|
|
98
|
+
|
|
99
|
+
|
|
42
100
|
@user_passes_test(not_fully_logged_in, login_url=reverse_lazy("teacher_login"))
|
|
43
101
|
def teacher_password_reset(request):
|
|
44
102
|
usertype = "TEACHER"
|
|
@@ -66,6 +124,7 @@ def password_reset(
|
|
|
66
124
|
html_email_template_name=None,
|
|
67
125
|
):
|
|
68
126
|
if request.method == "POST":
|
|
127
|
+
handle_reset_password_tracking(request, usertype)
|
|
69
128
|
form = password_reset_form(request.POST)
|
|
70
129
|
if not captcha.CAPTCHA_ENABLED:
|
|
71
130
|
remove_captcha_from_form(form)
|
|
@@ -81,9 +140,7 @@ def password_reset(
|
|
|
81
140
|
}
|
|
82
141
|
form.save(**opts)
|
|
83
142
|
|
|
84
|
-
return render(
|
|
85
|
-
request, "portal/reset_password_email_sent.html", {"usertype": usertype}
|
|
86
|
-
)
|
|
143
|
+
return render(request, "portal/reset_password_email_sent.html", {"usertype": usertype})
|
|
87
144
|
else:
|
|
88
145
|
form = password_reset_form()
|
|
89
146
|
|
|
@@ -145,7 +202,7 @@ def password_reset_confirm(
|
|
|
145
202
|
check_uidb64(uidb64, token)
|
|
146
203
|
|
|
147
204
|
try:
|
|
148
|
-
uid =
|
|
205
|
+
uid = force_bytes(urlsafe_base64_decode(uidb64))
|
|
149
206
|
user = UserModel._default_manager.get(pk=uid)
|
|
150
207
|
except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist):
|
|
151
208
|
user = None
|
|
@@ -163,9 +220,7 @@ def password_reset_confirm(
|
|
|
163
220
|
|
|
164
221
|
_check_and_unblock_user(user.username, usertype)
|
|
165
222
|
|
|
166
|
-
return render(
|
|
167
|
-
request, "portal/reset_password_done.html", {"usertype": usertype}
|
|
168
|
-
)
|
|
223
|
+
return render(request, "portal/reset_password_done.html", {"usertype": usertype})
|
|
169
224
|
else:
|
|
170
225
|
form = set_password_form(user)
|
|
171
226
|
else:
|
|
@@ -180,11 +235,13 @@ def password_reset_confirm(
|
|
|
180
235
|
return TemplateResponse(request, template_name, context)
|
|
181
236
|
|
|
182
237
|
|
|
183
|
-
def _check_and_unblock_user(username, usertype):
|
|
238
|
+
def _check_and_unblock_user(username, usertype, access_code=None):
|
|
184
239
|
if usertype == "TEACHER":
|
|
185
240
|
user = Teacher.objects.get(new_user__username=username)
|
|
186
|
-
|
|
241
|
+
elif usertype == "INDEP_STUDENT":
|
|
187
242
|
user = Student.objects.get(new_user__username=username)
|
|
243
|
+
elif usertype == "SCHOOL_STUDENT":
|
|
244
|
+
user = Student.objects.get(new_user__first_name=username, class_field__access_code=access_code)
|
|
188
245
|
|
|
189
246
|
if user.blocked_time is not None:
|
|
190
247
|
user.blocked_time = None
|
|
@@ -230,3 +287,26 @@ def password_reset_check_and_confirm(request, uidb64=None, token=None):
|
|
|
230
287
|
token=token,
|
|
231
288
|
extra_context={"usertype": usertype},
|
|
232
289
|
)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
@require_POST
|
|
293
|
+
@login_required(login_url=reverse_lazy("teacher_login"))
|
|
294
|
+
def delete_account(request):
|
|
295
|
+
user = request.user
|
|
296
|
+
password = request.POST.get("password")
|
|
297
|
+
|
|
298
|
+
if not user.check_password(password):
|
|
299
|
+
messages.error(request, "Your account was not deleted due to incorrect password.")
|
|
300
|
+
return HttpResponseRedirect(reverse_lazy("dashboard"))
|
|
301
|
+
|
|
302
|
+
email = user.email
|
|
303
|
+
anonymise(user)
|
|
304
|
+
|
|
305
|
+
# remove from dotmailer
|
|
306
|
+
if bool(request.POST.get("unsubscribe_newsletter")):
|
|
307
|
+
delete_contact(email)
|
|
308
|
+
|
|
309
|
+
# send confirmation email
|
|
310
|
+
send_dotdigital_email(campaign_ids["delete_account"], [email])
|
|
311
|
+
|
|
312
|
+
return HttpResponseRedirect(reverse_lazy("home"))
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
from common.helpers.emails import
|
|
1
|
+
from common.helpers.emails import (
|
|
2
|
+
NOTIFICATION_EMAIL,
|
|
3
|
+
delete_contact,
|
|
4
|
+
send_email,
|
|
5
|
+
update_indy_email,
|
|
6
|
+
)
|
|
2
7
|
from common.models import Student
|
|
3
8
|
from common.permissions import logged_in_as_student
|
|
4
9
|
from django.contrib import messages as messages
|
|
@@ -6,12 +11,15 @@ from django.contrib.auth import logout
|
|
|
6
11
|
from django.contrib.auth.decorators import login_required, user_passes_test
|
|
7
12
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
8
13
|
from django.http import HttpResponseRedirect
|
|
14
|
+
from django.shortcuts import render
|
|
9
15
|
from django.urls import reverse_lazy
|
|
10
16
|
from django.views.generic.edit import FormView
|
|
11
17
|
|
|
12
|
-
from portal.forms.play import
|
|
18
|
+
from portal.forms.play import IndependentStudentEditAccountForm, StudentEditAccountForm
|
|
19
|
+
from portal.forms.registration import DeleteAccountForm
|
|
13
20
|
from portal.helpers.password import check_update_password
|
|
14
21
|
from portal.helpers.ratelimit import clear_ratelimit_cache_for_user
|
|
22
|
+
from portal.views.api import anonymise
|
|
15
23
|
|
|
16
24
|
|
|
17
25
|
def _get_form(self, form_class):
|
|
@@ -68,9 +76,7 @@ class SchoolStudentEditAccountView(LoginRequiredMixin, FormView):
|
|
|
68
76
|
# check not default value for CharField
|
|
69
77
|
changing_password = check_update_password(form, student.new_user, request, data)
|
|
70
78
|
|
|
71
|
-
messages.success(
|
|
72
|
-
request, "Your account details have been changed successfully."
|
|
73
|
-
)
|
|
79
|
+
messages.success(request, "Your account details have been changed successfully.")
|
|
74
80
|
|
|
75
81
|
if changing_password:
|
|
76
82
|
logout(request)
|
|
@@ -84,86 +90,107 @@ class SchoolStudentEditAccountView(LoginRequiredMixin, FormView):
|
|
|
84
90
|
return _get_form(self, form_class)
|
|
85
91
|
|
|
86
92
|
|
|
87
|
-
|
|
93
|
+
def independentStudentEditAccountView(request):
|
|
88
94
|
"""
|
|
89
95
|
A FormView for editing an independent student's account details. This forms enables
|
|
90
96
|
an independent student to change their name, their email and / or their password.
|
|
91
|
-
|
|
97
|
+
Additionally it also deletes the independent students account with its second form
|
|
92
98
|
|
|
93
|
-
|
|
94
|
-
|
|
99
|
+
Django class based views makes it very difficult to have multiple forms in one view,
|
|
100
|
+
hence using the functional based view
|
|
101
|
+
"""
|
|
102
|
+
user = request.user
|
|
103
|
+
change_email_password_form = IndependentStudentEditAccountForm(user, initial={"name": user.first_name})
|
|
104
|
+
delete_account_form = DeleteAccountForm(user)
|
|
95
105
|
template_name = "../templates/portal/play/student_edit_account.html"
|
|
96
|
-
|
|
97
|
-
initial = {"name": "Could not find name"}
|
|
98
|
-
changing_email = False
|
|
99
|
-
changing_password = False
|
|
100
|
-
|
|
101
|
-
def get_form_kwargs(self):
|
|
102
|
-
kwargs = super(IndependentStudentEditAccountView, self).get_form_kwargs()
|
|
103
|
-
kwargs["initial"]["name"] = "{}{}".format(
|
|
104
|
-
self.request.user.first_name, self.request.user.last_name
|
|
105
|
-
)
|
|
106
|
-
return kwargs
|
|
107
|
-
|
|
108
|
-
def get_form(self, form_class=None):
|
|
109
|
-
return _get_form(self, form_class)
|
|
110
|
-
|
|
111
|
-
def get_success_url(self):
|
|
112
|
-
if self.changing_email:
|
|
113
|
-
return reverse_lazy("email_verification")
|
|
114
|
-
elif self.changing_password:
|
|
115
|
-
return reverse_lazy("independent_student_login")
|
|
116
|
-
else:
|
|
117
|
-
return reverse_lazy("independent_student_details")
|
|
118
|
-
|
|
119
|
-
def form_valid(self, form):
|
|
120
|
-
return _process_form(
|
|
121
|
-
self,
|
|
122
|
-
self.process_independent_student_edit_account_form,
|
|
123
|
-
form,
|
|
124
|
-
IndependentStudentEditAccountView,
|
|
125
|
-
)
|
|
106
|
+
delete_account_confirm = False
|
|
126
107
|
|
|
127
|
-
def
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
108
|
+
def process_change_email_password_form(request, student, old_anchor):
|
|
109
|
+
change_email_password_form = IndependentStudentEditAccountForm(request.user, request.POST)
|
|
110
|
+
changing_email = False
|
|
111
|
+
changing_password = False
|
|
112
|
+
new_email = ""
|
|
113
|
+
if change_email_password_form.is_valid():
|
|
114
|
+
data = change_email_password_form.cleaned_data
|
|
115
|
+
# check not default value for CharField
|
|
116
|
+
changing_password = check_update_password(change_email_password_form, student, request, data)
|
|
134
117
|
|
|
135
|
-
|
|
136
|
-
|
|
118
|
+
changing_email, new_email = update_indy_email(student, request, data)
|
|
119
|
+
changing_first_name = False if request.POST["name"] == student.first_name else True
|
|
120
|
+
student.first_name = request.POST["name"]
|
|
137
121
|
|
|
138
|
-
|
|
122
|
+
student.save()
|
|
139
123
|
|
|
140
|
-
|
|
141
|
-
clear_ratelimit_cache_for_user(student.new_user.username)
|
|
124
|
+
anchor = ""
|
|
142
125
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
)
|
|
126
|
+
# Reset ratelimit cache after successful account details update
|
|
127
|
+
clear_ratelimit_cache_for_user(student.username)
|
|
146
128
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
request,
|
|
151
|
-
"Your email will be changed once you have verified it, until then "
|
|
152
|
-
"you can still log in with your old email.",
|
|
153
|
-
)
|
|
129
|
+
messages.success(request, "Your account details have been successfully changed.")
|
|
130
|
+
else:
|
|
131
|
+
anchor = old_anchor
|
|
154
132
|
|
|
155
|
-
|
|
156
|
-
logout(request)
|
|
157
|
-
messages.success(
|
|
158
|
-
request,
|
|
159
|
-
"Please login using your new password.",
|
|
160
|
-
)
|
|
133
|
+
return changing_email, new_email, changing_password, changing_first_name, anchor
|
|
161
134
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
135
|
+
if request.method == "POST":
|
|
136
|
+
if "delete_account" in request.POST:
|
|
137
|
+
delete_account_form = DeleteAccountForm(user, request.POST)
|
|
138
|
+
if not delete_account_form.is_valid():
|
|
139
|
+
messages.warning(request, "Your account was not deleted due to incorrect password.")
|
|
140
|
+
else:
|
|
141
|
+
delete_account_confirm = True
|
|
142
|
+
else:
|
|
143
|
+
change_email_password_form = IndependentStudentEditAccountForm(request.user, request.POST)
|
|
144
|
+
if not change_email_password_form.is_valid():
|
|
145
|
+
messages.warning(request, "Your account was not updated do to incorrect details")
|
|
146
|
+
return render(
|
|
147
|
+
request,
|
|
148
|
+
template_name,
|
|
149
|
+
{
|
|
150
|
+
"form": change_email_password_form,
|
|
151
|
+
"delete_account_form": delete_account_form,
|
|
152
|
+
},
|
|
153
|
+
)
|
|
154
|
+
else:
|
|
155
|
+
(
|
|
156
|
+
changing_email,
|
|
157
|
+
new_email,
|
|
158
|
+
changing_password,
|
|
159
|
+
changing_first_name,
|
|
160
|
+
anchor,
|
|
161
|
+
) = process_change_email_password_form(request, request.user, "")
|
|
162
|
+
if changing_email:
|
|
163
|
+
logout(request)
|
|
164
|
+
messages.success(request, "Your account details have been changed successfully.")
|
|
165
|
+
messages.success(
|
|
166
|
+
request,
|
|
167
|
+
"Your email will be changed once you have verified it, until then "
|
|
168
|
+
"you can still log in with your old email.",
|
|
169
|
+
)
|
|
170
|
+
return render(
|
|
171
|
+
request,
|
|
172
|
+
"portal/email_verification_needed.html",
|
|
173
|
+
{"usertype": "INDEP_STUDENT"},
|
|
174
|
+
)
|
|
175
|
+
if changing_password:
|
|
176
|
+
logout(request)
|
|
177
|
+
messages.success(request, "Your account details have been changed successfully.")
|
|
178
|
+
messages.success(request, "Please login using your new password.")
|
|
179
|
+
return HttpResponseRedirect(reverse_lazy("independent_student_login"))
|
|
180
|
+
|
|
181
|
+
if changing_first_name:
|
|
182
|
+
messages.success(request, "Your account details have been changed successfully.")
|
|
183
|
+
return HttpResponseRedirect(reverse_lazy("independent_student_details"))
|
|
184
|
+
|
|
185
|
+
return render(
|
|
186
|
+
request,
|
|
187
|
+
template_name,
|
|
188
|
+
{
|
|
189
|
+
"form": change_email_password_form,
|
|
190
|
+
"delete_account_form": delete_account_form,
|
|
191
|
+
"delete_account_confirm": delete_account_confirm,
|
|
192
|
+
},
|
|
193
|
+
)
|
|
167
194
|
|
|
168
195
|
|
|
169
196
|
@login_required(login_url=reverse_lazy("home"))
|