Commit 52fbe6f1 by Douglas Hall Committed by GitHub

Merge pull request #15144 from edx/saleem-latif/ENT-329

ENT-329: Success Message for Account Activation for logged out users.
parents 95964a0a 727dd9f9
...@@ -6,6 +6,8 @@ from django.conf import settings ...@@ -6,6 +6,8 @@ from django.conf import settings
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from uuid import uuid4
from edxmako.shortcuts import render_to_string from edxmako.shortcuts import render_to_string
from student.models import Registration from student.models import Registration
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
...@@ -159,3 +161,28 @@ class TestActivateAccount(TestCase): ...@@ -159,3 +161,28 @@ class TestActivateAccount(TestCase):
) )
response = self.client.get(reverse('dashboard')) response = self.client.get(reverse('dashboard'))
self.assertNotContains(response, expected_message, html=True) self.assertNotContains(response, expected_message, html=True)
def test_account_activation_notification_on_logistration(self):
"""
Verify that logistration page displays success/error/info messages
about account activation.
"""
login_page_url = "{login_url}?next={redirect_url}".format(
login_url=reverse('signin_user'),
redirect_url=reverse('dashboard'),
)
# Access activation link, message should say that account has been activated.
response = self.client.get(reverse('activate', args=[self.registration.activation_key]), follow=True)
self.assertRedirects(response, login_page_url)
self.assertContains(response, 'You have activated your account.')
# Access activation link again, message should say that account is already active.
response = self.client.get(reverse('activate', args=[self.registration.activation_key]), follow=True)
self.assertRedirects(response, login_page_url)
self.assertContains(response, 'This account has already been activated.')
# Open account activation page with an invalid activation link,
# there should be an error message displayed.
response = self.client.get(reverse('activate', args=[uuid4().hex]), follow=True)
self.assertRedirects(response, login_page_url)
self.assertContains(response, 'Your account could not be activated')
...@@ -90,7 +90,7 @@ from openedx.core.djangoapps.external_auth.login_and_register import ( ...@@ -90,7 +90,7 @@ from openedx.core.djangoapps.external_auth.login_and_register import (
register as external_auth_register register as external_auth_register
) )
from openedx.core.djangoapps import monitoring_utils from openedx.core.djangoapps import monitoring_utils
from openedx.core.djangolib.markup import HTML, Text from openedx.core.djangolib.markup import HTML
import track.views import track.views
...@@ -2302,7 +2302,7 @@ def activate_account(request, key): ...@@ -2302,7 +2302,7 @@ def activate_account(request, key):
except (Registration.DoesNotExist, Registration.MultipleObjectsReturned): except (Registration.DoesNotExist, Registration.MultipleObjectsReturned):
messages.error( messages.error(
request, request,
Text(_( HTML(_(
'{html_start}Your account could not be activated{html_end}' '{html_start}Your account could not be activated{html_end}'
'Something went wrong, please <a href="{support_url}">contact support</a> to resolve this issue.' 'Something went wrong, please <a href="{support_url}">contact support</a> to resolve this issue.'
)).format( )).format(
...@@ -2310,7 +2310,7 @@ def activate_account(request, key): ...@@ -2310,7 +2310,7 @@ def activate_account(request, key):
html_start=HTML('<p class="message-title">'), html_start=HTML('<p class="message-title">'),
html_end=HTML('</p>'), html_end=HTML('</p>'),
), ),
extra_tags='account-activation icon' extra_tags='account-activation aa-icon'
) )
else: else:
if not registration.user.is_active: if not registration.user.is_active:
...@@ -2318,20 +2318,20 @@ def activate_account(request, key): ...@@ -2318,20 +2318,20 @@ def activate_account(request, key):
# Add account activation success message for display later # Add account activation success message for display later
messages.success( messages.success(
request, request,
Text(_('{html_start}Success{html_end} You have activated your account.')).format( HTML(_('{html_start}Success{html_end} You have activated your account.')).format(
html_start=HTML('<p class="message-title">'), html_start=HTML('<p class="message-title">'),
html_end=HTML('</p>'), html_end=HTML('</p>'),
), ),
extra_tags='account-activation icon', extra_tags='account-activation aa-icon',
) )
else: else:
messages.info( messages.info(
request, request,
Text(_('{html_start}This account has already been activated.{html_end}')).format( HTML(_('{html_start}This account has already been activated.{html_end}')).format(
html_start=HTML('<p class="message-title">'), html_start=HTML('<p class="message-title">'),
html_end=HTML('</p>'), html_end=HTML('</p>'),
), ),
extra_tags='account-activation icon', extra_tags='account-activation aa-icon',
) )
# Enroll student in any pending courses he/she may have if auto_enroll flag is set # Enroll student in any pending courses he/she may have if auto_enroll flag is set
......
...@@ -353,7 +353,7 @@ class CombinedLoginAndRegisterPage(PageObject): ...@@ -353,7 +353,7 @@ class CombinedLoginAndRegisterPage(PageObject):
"""Wait for a status message to be visible following third_party registration, then return it.""" """Wait for a status message to be visible following third_party registration, then return it."""
def _check_func(): def _check_func():
"""Return third party auth status notice message.""" """Return third party auth status notice message."""
selector = '.js-auth-warning p' selector = '.js-auth-warning div'
msg_element = self.q(css=selector) msg_element = self.q(css=selector)
if msg_element.visible: if msg_element.visible:
return (True, msg_element.text[0]) return (True, msg_element.text[0])
......
...@@ -117,6 +117,13 @@ def login_and_registration_form(request, initial_mode="login"): ...@@ -117,6 +117,13 @@ def login_and_registration_form(request, initial_mode="login"):
if ext_auth_response is not None: if ext_auth_response is not None:
return ext_auth_response return ext_auth_response
# Account activation message
account_activation_messages = [
{
'message': message.message, 'tags': message.tags
} for message in messages.get_messages(request) if 'account-activation' in message.tags
]
# Otherwise, render the combined login/registration page # Otherwise, render the combined login/registration page
context = { context = {
'data': { 'data': {
...@@ -126,6 +133,7 @@ def login_and_registration_form(request, initial_mode="login"): ...@@ -126,6 +133,7 @@ def login_and_registration_form(request, initial_mode="login"):
'third_party_auth_hint': third_party_auth_hint or '', 'third_party_auth_hint': third_party_auth_hint or '',
'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME), 'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME),
'support_link': configuration_helpers.get_value('SUPPORT_SITE_LINK', settings.SUPPORT_SITE_LINK), 'support_link': configuration_helpers.get_value('SUPPORT_SITE_LINK', settings.SUPPORT_SITE_LINK),
'account_activation_messages': account_activation_messages,
# Include form descriptions retrieved from the user API. # Include form descriptions retrieved from the user API.
# We could have the JS client make these requests directly, # We could have the JS client make these requests directly,
......
...@@ -52,6 +52,9 @@ ...@@ -52,6 +52,9 @@
this.thirdPartyAuthHint = options.third_party_auth_hint || null; this.thirdPartyAuthHint = options.third_party_auth_hint || null;
// Account activation messages
this.accountActivationMessages = options.account_activation_messages || [];
if (options.login_redirect_url) { if (options.login_redirect_url) {
// Ensure that the next URL is internal for security reasons // Ensure that the next URL is internal for security reasons
if (! window.isExternal(options.login_redirect_url)) { if (! window.isExternal(options.login_redirect_url)) {
...@@ -82,6 +85,10 @@ ...@@ -82,6 +85,10 @@
// Once the third party error message has been shown once, // Once the third party error message has been shown once,
// there is no need to show it again, if the user changes mode: // there is no need to show it again, if the user changes mode:
this.thirdPartyAuth.errorMessage = null; this.thirdPartyAuth.errorMessage = null;
// Once the account activation messages have been shown once,
// there is no need to show it again, if the user changes mode:
this.accountActivationMessages = [];
}, },
render: function() { render: function() {
...@@ -119,6 +126,7 @@ ...@@ -119,6 +126,7 @@
model: model, model: model,
resetModel: this.resetModel, resetModel: this.resetModel,
thirdPartyAuth: this.thirdPartyAuth, thirdPartyAuth: this.thirdPartyAuth,
accountActivationMessages: this.accountActivationMessages,
platformName: this.platformName, platformName: this.platformName,
supportURL: this.supportURL, supportURL: this.supportURL,
createAccountOption: this.createAccountOption createAccountOption: this.createAccountOption
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
this.resetModel = data.resetModel; this.resetModel = data.resetModel;
this.supportURL = data.supportURL; this.supportURL = data.supportURL;
this.createAccountOption = data.createAccountOption; this.createAccountOption = data.createAccountOption;
this.accountActivationMessages = data.accountActivationMessages;
this.listenTo(this.model, 'sync', this.saveSuccess); this.listenTo(this.model, 'sync', this.saveSuccess);
this.listenTo(this.resetModel, 'sync', this.resetEmail); this.listenTo(this.resetModel, 'sync', this.resetEmail);
...@@ -85,6 +86,20 @@ ...@@ -85,6 +86,20 @@
*/ */
this.model.save(); this.model.save();
} }
// Display account activation success or error messages.
this.renderAccountActivationMessages();
},
renderAccountActivationMessages: function() {
_.each(this.accountActivationMessages, this.renderAccountActivationMessage, this);
},
renderAccountActivationMessage: function(message) {
this.renderFormFeedback(this.formStatusTpl, {
jsHook: message.tags,
message: HtmlUtils.HTML(message.message)
});
}, },
forgotPassword: function(event) { forgotPassword: function(event) {
......
...@@ -1453,7 +1453,7 @@ a.fade-cover{ ...@@ -1453,7 +1453,7 @@ a.fade-cover{
margin-bottom: 0; margin-bottom: 0;
} }
&.icon .message-copy:before { &.aa-icon .message-copy:before {
position: absolute; position: absolute;
left: -1em; left: -1em;
content: "\f05a"; // fa-info-circle content: "\f05a"; // fa-info-circle
...@@ -1468,7 +1468,7 @@ a.fade-cover{ ...@@ -1468,7 +1468,7 @@ a.fade-cover{
background-color: $palette-success-back; background-color: $palette-success-back;
border: $palette-success-border 1px solid; border: $palette-success-border 1px solid;
&.icon .message-copy:before { &.aa-icon .message-copy:before {
position: absolute; position: absolute;
left: -1em; left: -1em;
content: "\f00c"; // fa-check content: "\f00c"; // fa-check
...@@ -1483,7 +1483,7 @@ a.fade-cover{ ...@@ -1483,7 +1483,7 @@ a.fade-cover{
background-color: $palette-error-back; background-color: $palette-error-back;
border: $palette-error-border 1px solid; border: $palette-error-border 1px solid;
&.icon .message-copy:before { &.aa-icon .message-copy:before {
position: absolute; position: absolute;
left: -1em; left: -1em;
content: "\f06a"; // fa-exclamation-circle content: "\f06a"; // fa-exclamation-circle
......
...@@ -560,6 +560,30 @@ ...@@ -560,6 +560,30 @@
margin: 0 0 ($baseline/4) $baseline; margin: 0 0 ($baseline/4) $baseline;
} }
} }
&.account-activation {
.message-copy {
padding: 0 1em !important;
}
&.info {
background-color: $palette-info-back;
border: $palette-info-border 1px solid;
color: $palette-info-text;
}
&.success {
background-color: $palette-success-back;
border: $palette-success-border 1px solid;
color: $palette-success-text;
}
&.error {
background-color: $palette-error-back;
border: $palette-error-border 1px solid;
color: $palette-error-text 1px solid;
}
}
} }
.submission-error, .system-error { .submission-error, .system-error {
......
<div class="<%- jsHook %> status"> <div class="<%- jsHook %> status">
<p class="message-copy"> <div class="message-copy">
<%- message %> <%= HtmlUtils.ensureHtml(message) %>
</p> </p>
</div> </div>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment