diff --git a/cms/templates/registration/activation_invalid.html b/cms/templates/registration/activation_invalid.html new file mode 100644 index 0000000000..5160b46449 --- /dev/null +++ b/cms/templates/registration/activation_invalid.html @@ -0,0 +1,29 @@ +<%page expression_filter="h"/> +<%inherit file="../base.html" /> +<%namespace name='static' file='../static_content.html'/> +<%! +from django.utils.translation import ugettext as _ +from django.core.urlresolvers import reverse +from openedx.core.djangolib.markup import HTML, Text +%> + +<%block name="content"> +
+ +
+

${_("Activation Invalid")}

+
+ +

${Text(_("Something went wrong. Email programs sometimes split URLs " + "into two lines, so make sure the URL you're using is " "formatted correctly. If you still have issues, send us " + "an email message at " "{email_start}{email}{email_end}.")).format( + email_start=HTML('').format(settings.BUGS_EMAIL), + email=settings.BUGS_EMAIL, + email_end=HTML('') + )}

+ +

${Text(_('Return to the {link_start}home page{link_end}.')).format( + link_start=HTML(''), link_end=HTML(''))}

+
+
+ diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index 3a396c82cb..78d9f1311b 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -90,6 +90,7 @@ from openedx.core.djangoapps.external_auth.login_and_register import ( register as external_auth_register ) from openedx.core.djangoapps import monitoring_utils +from openedx.core.djangolib.markup import HTML, Text import track.views @@ -721,6 +722,11 @@ def dashboard(request): enterprise_message = get_dashboard_consent_notification(request, user, course_enrollments) + # Account activation message + account_activation_messages = [ + message for message in messages.get_messages(request) if 'account-activation' in message.tags + ] + # Global staff can see what courses errored on their dashboard staff_access = False errored_courses = {} @@ -844,6 +850,7 @@ def dashboard(request): 'enterprise_message': enterprise_message, 'enrollment_message': enrollment_message, 'redirect_message': redirect_message, + 'account_activation_messages': account_activation_messages, 'course_enrollments': course_enrollments, 'course_optouts': course_optouts, 'banner_account_activation_message': banner_account_activation_message, @@ -2285,31 +2292,83 @@ def auto_auth(request): @ensure_csrf_cookie def activate_account(request, key): """When link in activation e-mail is clicked""" - regs = Registration.objects.filter(activation_key=key) - if len(regs) == 1: + + # If request is in Studio call the appropriate view + if theming_helpers.get_project_root_name().lower() == u'cms': + return activate_account_studio(request, key) + + try: + registration = Registration.objects.get(activation_key=key) + except (Registration.DoesNotExist, Registration.MultipleObjectsReturned): + messages.error( + request, + Text(_( + '{html_start}Your account could not be activated{html_end}' + 'Something went wrong, please contact support to resolve this issue.' + )).format( + support_url=configuration_helpers.get_value('SUPPORT_SITE_LINK', settings.SUPPORT_SITE_LINK), + html_start=HTML('

'), + html_end=HTML('

'), + ), + extra_tags='account-activation icon' + ) + else: + if not registration.user.is_active: + registration.activate() + # Add account activation success message for display later + messages.success( + request, + Text(_('{html_start}Success{html_end} You have activated your account.')).format( + html_start=HTML('

'), + html_end=HTML('

'), + ), + extra_tags='account-activation icon', + ) + else: + messages.info( + request, + Text(_('{html_start}This account has already been activated.{html_end}')).format( + html_start=HTML('

'), + html_end=HTML('

'), + ), + extra_tags='account-activation icon', + ) + + # Enroll student in any pending courses he/she may have if auto_enroll flag is set + _enroll_user_in_pending_courses(registration.user) + + return redirect('dashboard') + + +@ensure_csrf_cookie +def activate_account_studio(request, key): + """ + When link in activation e-mail is clicked and the link belongs to studio. + """ + try: + registration = Registration.objects.get(activation_key=key) + except (Registration.DoesNotExist, Registration.MultipleObjectsReturned): + return render_to_response( + "registration/activation_invalid.html", + {'csrf': csrf(request)['csrf_token']} + ) + else: user_logged_in = request.user.is_authenticated() already_active = True - if not regs[0].user.is_active: - regs[0].activate() + if not registration.user.is_active: + registration.activate() already_active = False # Enroll student in any pending courses he/she may have if auto_enroll flag is set - _enroll_user_in_pending_courses(regs[0].user) + _enroll_user_in_pending_courses(registration.user) - resp = render_to_response( + return render_to_response( "registration/activation_complete.html", { 'user_logged_in': user_logged_in, 'already_active': already_active } ) - return resp - if len(regs) == 0: - return render_to_response( - "registration/activation_invalid.html", - {'csrf': csrf(request)['csrf_token']} - ) - return HttpResponseServerError(_("Unknown error. Please e-mail us to let us know how it happened.")) @csrf_exempt diff --git a/lms/djangoapps/courseware/tests/helpers.py b/lms/djangoapps/courseware/tests/helpers.py index 6aaf154b99..4c15c6d39b 100644 --- a/lms/djangoapps/courseware/tests/helpers.py +++ b/lms/djangoapps/courseware/tests/helpers.py @@ -3,6 +3,7 @@ Helpers for courseware tests. """ import json +from django.contrib import messages from django.contrib.auth.models import User from django.core.urlresolvers import reverse from django.test import TestCase @@ -53,6 +54,14 @@ class LoginEnrollmentTestCase(TestCase): ) return response + def assert_account_activated(self, url, method="GET", **kwargs): + make_request = getattr(self.client, method.lower()) + response = make_request(url, **kwargs) + message_list = list(messages.get_messages(response.wsgi_request)) + self.assertEqual(len(message_list), 1) + self.assertIn("success", message_list[0].tags) + self.assertTrue("You have activated your account." in message_list[0].message) + # ============ User creation and login ============== def login(self, email, password): @@ -102,7 +111,7 @@ class LoginEnrollmentTestCase(TestCase): activation_key = Registration.objects.get(user__email=email).activation_key # and now we try to activate url = reverse('activate', kwargs={'key': activation_key}) - self.assert_request_status_code(200, url) + self.assert_account_activated(url) # Now make sure that the user is now actually activated user = User.objects.get(email=email) self.assertTrue(user.is_active) diff --git a/lms/djangoapps/courseware/tests/test_course_info.py b/lms/djangoapps/courseware/tests/test_course_info.py index 85b892983e..9de1a6e56b 100644 --- a/lms/djangoapps/courseware/tests/test_course_info.py +++ b/lms/djangoapps/courseware/tests/test_course_info.py @@ -367,7 +367,7 @@ class SelfPacedCourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTest self.assertEqual(resp.status_code, 200) def test_num_queries_instructor_paced(self): - self.fetch_course_info_with_queries(self.instructor_paced_course, 20, 4) + self.fetch_course_info_with_queries(self.instructor_paced_course, 21, 4) def test_num_queries_self_paced(self): - self.fetch_course_info_with_queries(self.self_paced_course, 20, 4) + self.fetch_course_info_with_queries(self.self_paced_course, 21, 4) diff --git a/lms/static/sass/multicourse/_dashboard.scss b/lms/static/sass/multicourse/_dashboard.scss index 1fae8f0982..f002f310c8 100644 --- a/lms/static/sass/multicourse/_dashboard.scss +++ b/lms/static/sass/multicourse/_dashboard.scss @@ -1420,3 +1420,80 @@ a.fade-cover{ } } +// Dashboard alert messages +.activation-message-container { + @include clearfix(); + margin: 0 auto 0; + padding-top: ($baseline/2); + max-width: grid-width(12); + min-width: 760px; + width: flex-grid(12); +} + +.account-activation { + + .message-copy { + position: relative; + left: 2em; + padding: 1em; + } + + .message-title { + margin-bottom: 6px; + font-weight: 600; + } + + &.info { + color: $palette-info-text; + background-color: $palette-info-back; + border: $palette-info-border 1px solid; + padding: 5px; + + .message-title{ + margin-bottom: 0; + } + + &.icon .message-copy:before { + position: absolute; + left: -1em; + content: "\f05a"; // fa-info-circle + font-size: 1.5em; + padding: 0 2px; + font-family: FontAwesome; + } + } + + &.success { + color: $palette-success-text; + background-color: $palette-success-back; + border: $palette-success-border 1px solid; + + &.icon .message-copy:before { + position: absolute; + left: -1em; + content: "\f00c"; // fa-check + font-size: 1.5em; + padding: 0 2px; + font-family: FontAwesome; + } + } + + &.error { + color: $palette-error-text; + background-color: $palette-error-back; + border: $palette-error-border 1px solid; + + &.icon .message-copy:before { + position: absolute; + left: -1em; + content: "\f06a"; // fa-exclamation-circle + font-size: 1.5em; + padding: 0 2px; + font-family: FontAwesome; + } + + a { + text-decoration: underline; + } + } +} diff --git a/lms/static/sass/partials/base/_variables.scss b/lms/static/sass/partials/base/_variables.scss index a17f900f2c..48c5d2be9c 100644 --- a/lms/static/sass/partials/base/_variables.scss +++ b/lms/static/sass/partials/base/_variables.scss @@ -514,6 +514,17 @@ $dashboard-profile-color: rgb(252,252,252) !default; $dot-color: rgb(221, 221, 221) !default; $dashboard-course-cover-border: rgb(221, 221, 221) !default; +// dashboard notification messages +$palette-info-border: #cce3f0; +$palette-info-back: #f2f8fb; +$palette-info-text: #0075b4; +$palette-error-border: #ebccd1; +$palette-error-back: #feeced; +$palette-error-text: #b20610; +$palette-success-border: #b9edb9; +$palette-success-back: #ecfaec; +$palette-success-text: #008100; + // course elements $content-wrapper-bg: $white !default; $course-bg-color: $uxpl-grayscale-x-back !default; diff --git a/lms/templates/dashboard.html b/lms/templates/dashboard.html index 0e57bc3973..72bc6076b5 100644 --- a/lms/templates/dashboard.html +++ b/lms/templates/dashboard.html @@ -80,6 +80,19 @@ from openedx.core.djangolib.markup import HTML, Text ${ enterprise_message | n, decode.utf8 } %endif + + %if account_activation_messages: +
+ % for account_activation_message in account_activation_messages: + + % endfor +
+ %endif +
diff --git a/lms/templates/registration/activation_complete.html b/lms/templates/registration/activation_complete.html deleted file mode 100644 index b49353afbe..0000000000 --- a/lms/templates/registration/activation_complete.html +++ /dev/null @@ -1,36 +0,0 @@ -<%page expression_filter="h"/> -<%inherit file="../main.html" /> -<%namespace name='static' file='../static_content.html'/> -<%! -from django.utils.translation import ugettext as _ -from django.core.urlresolvers import reverse -from openedx.core.djangolib.markup import HTML, Text -%> - -
- -
- %if not already_active: -

${_("Account Activated")}

- %else: -

${_("Account already active")}

- %endif -
- -

- %if not already_active: - ${_("Thanks for activating your account.")} - %else: - ${_("This account has already been activated.")} - %endif - - %if user_logged_in: - ${Text(_("Visit your {link_start}dashboard{link_end} to see your courses.")).format( - link_start=HTML('').format(url=reverse('dashboard')), link_end=HTML(''))} - %else: - ${Text(_("You can now {link_start}sign in{link_end}.")).format( - link_start=HTML('').format(url=reverse('signin_user')), link_end=HTML(''))} - %endif -

-
-
diff --git a/lms/templates/registration/activation_invalid.html b/lms/templates/registration/activation_invalid.html deleted file mode 100644 index 1b54d6c2a3..0000000000 --- a/lms/templates/registration/activation_invalid.html +++ /dev/null @@ -1,27 +0,0 @@ -<%page expression_filter="h"/> -<%inherit file="../main.html" /> -<%namespace name='static' file='../static_content.html'/> -<%! -from django.utils.translation import ugettext as _ -from django.core.urlresolvers import reverse -from openedx.core.djangolib.markup import HTML, Text -%> - -
- -
-

${_("Activation Invalid")}

-
- -

${Text(_("Something went wrong. Email programs sometimes split URLs " - "into two lines, so make sure the URL you're using is " "formatted correctly. If you still have issues, send us " - "an email message at " "{email_start}{email}{email_end}.")).format( - email_start=HTML('').format(settings.BUGS_EMAIL), - email=settings.BUGS_EMAIL, - email_end=HTML('') - )}

- -

${Text(_('Return to the {link_start}home page{link_end}.')).format( - link_start=HTML(''), link_end=HTML(''))}

-
-
diff --git a/themes/edx.org/lms/templates/dashboard.html b/themes/edx.org/lms/templates/dashboard.html index 75cda00e14..2e7733a2be 100644 --- a/themes/edx.org/lms/templates/dashboard.html +++ b/themes/edx.org/lms/templates/dashboard.html @@ -82,6 +82,19 @@ from openedx.core.djangoapps.theming import helpers as theming_helpers ${ enterprise_message | n, decode.utf8 } %endif + + %if account_activation_messages: +
+ % for account_activation_message in account_activation_messages: + + % endfor +
+ %endif +