Add support for page banner status messages
LEARNER-1890
This commit is contained in:
@@ -4,9 +4,15 @@ These views will NOT be shown on production: trying to access them will result
|
||||
in a 404 error.
|
||||
"""
|
||||
from django.http import HttpResponseNotFound
|
||||
from mako.exceptions import TopLevelLookupException
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from edxmako.shortcuts import render_to_response
|
||||
from mako.exceptions import TopLevelLookupException
|
||||
from openedx.core.djangoapps.util.user_messages import (
|
||||
register_error_message,
|
||||
register_info_message,
|
||||
register_success_message,
|
||||
register_warning_message,
|
||||
)
|
||||
|
||||
|
||||
def show_reference_template(request, template):
|
||||
@@ -23,13 +29,22 @@ def show_reference_template(request, template):
|
||||
"""
|
||||
try:
|
||||
uses_bootstrap = u'/bootstrap/' in request.path
|
||||
uses_pattern_library = not uses_bootstrap
|
||||
uses_pattern_library = u'/pattern-library/' in request.path
|
||||
is_v1 = u'/v1/' in request.path
|
||||
context = {
|
||||
"disable_courseware_js": True,
|
||||
"disable_courseware_js": not is_v1,
|
||||
"uses_pattern_library": uses_pattern_library,
|
||||
"uses_bootstrap": uses_bootstrap,
|
||||
}
|
||||
context.update(request.GET.dict())
|
||||
|
||||
# Add some messages to the course skeleton pages
|
||||
if u'course-skeleton.html' in request.path:
|
||||
register_info_message(request, _('This is a test message'))
|
||||
register_success_message(request, _('This is a success message'))
|
||||
register_warning_message(request, _('This is a test warning'))
|
||||
register_error_message(request, _('This is a test error'))
|
||||
|
||||
return render_to_response(template, context)
|
||||
except TopLevelLookupException:
|
||||
return HttpResponseNotFound("Couldn't find template {template}".format(template=template))
|
||||
|
||||
85
openedx/core/djangoapps/util/tests/test_user_messages.py
Normal file
85
openedx/core/djangoapps/util/tests/test_user_messages.py
Normal file
@@ -0,0 +1,85 @@
|
||||
"""
|
||||
Unit tests for user messages.
|
||||
"""
|
||||
|
||||
import ddt
|
||||
from unittest import TestCase
|
||||
|
||||
from django.contrib.messages.middleware import MessageMiddleware
|
||||
from django.test import RequestFactory
|
||||
from openedx.core.djangolib.markup import HTML, Text
|
||||
from student.tests.factories import UserFactory
|
||||
|
||||
from ..user_messages import (
|
||||
register_error_message,
|
||||
register_info_message,
|
||||
register_success_message,
|
||||
register_user_message,
|
||||
register_warning_message,
|
||||
user_messages,
|
||||
UserMessageType,
|
||||
)
|
||||
|
||||
TEST_MESSAGE = 'Test message'
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class UserMessagesTestCase(TestCase):
|
||||
"""
|
||||
Unit tests for user messages.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(UserMessagesTestCase, self).setUp()
|
||||
self.student = UserFactory.create()
|
||||
self.request = RequestFactory().request()
|
||||
self.request.session = {}
|
||||
self.request.user = self.student
|
||||
MessageMiddleware().process_request(self.request)
|
||||
|
||||
@ddt.data(
|
||||
('Rock & Roll', 'Rock & Roll'),
|
||||
(Text('Rock & Roll'), 'Rock & Roll'),
|
||||
(HTML('<p>Hello, world!</p>'), '<p>Hello, world!</p>')
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_message_escaping(self, message, expected_message_html):
|
||||
"""
|
||||
Verifies that a user message is escaped correctly.
|
||||
"""
|
||||
register_user_message(self.request, UserMessageType.INFO, message)
|
||||
messages = list(user_messages(self.request))
|
||||
self.assertEqual(len(messages), 1)
|
||||
self.assertEquals(messages[0].message_html, expected_message_html)
|
||||
|
||||
@ddt.data(
|
||||
(UserMessageType.ERROR, 'alert-danger', 'fa fa-warning'),
|
||||
(UserMessageType.INFO, 'alert-info', 'fa fa-bullhorn'),
|
||||
(UserMessageType.SUCCESS, 'alert-success', 'fa fa-check-circle'),
|
||||
(UserMessageType.WARNING, 'alert-warning', 'fa fa-warning'),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_message_icon(self, message_type, expected_css_class, expected_icon_class):
|
||||
"""
|
||||
Verifies that a user message returns the correct CSS and icon classes.
|
||||
"""
|
||||
register_user_message(self.request, message_type, TEST_MESSAGE)
|
||||
messages = list(user_messages(self.request))
|
||||
self.assertEqual(len(messages), 1)
|
||||
self.assertEquals(messages[0].css_class, expected_css_class)
|
||||
self.assertEquals(messages[0].icon_class, expected_icon_class)
|
||||
|
||||
@ddt.data(
|
||||
(register_error_message, UserMessageType.ERROR),
|
||||
(register_info_message, UserMessageType.INFO),
|
||||
(register_success_message, UserMessageType.SUCCESS),
|
||||
(register_warning_message, UserMessageType.WARNING),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_message_type(self, register_message_function, expected_message_type):
|
||||
"""
|
||||
Verifies that each user message function returns the correct type.
|
||||
"""
|
||||
register_message_function(self.request, TEST_MESSAGE)
|
||||
messages = list(user_messages(self.request))
|
||||
self.assertEqual(len(messages), 1)
|
||||
self.assertEquals(messages[0].type, expected_message_type)
|
||||
137
openedx/core/djangoapps/util/user_messages.py
Normal file
137
openedx/core/djangoapps/util/user_messages.py
Normal file
@@ -0,0 +1,137 @@
|
||||
"""
|
||||
Support for per-request messages to be shown to the user.
|
||||
|
||||
These utilities are based upon the Django message framework, and allow
|
||||
code to register messages to be shown to the user on their next page
|
||||
view. These messages are shown in a page banner which is supported on
|
||||
all pages that utilize the main.html template.
|
||||
|
||||
There are two common use cases:
|
||||
- register a message before rendering a view, in which case the message
|
||||
will be shown on the resulting page
|
||||
- register a message before posting or redirecting. In these situations
|
||||
the message will be shown on the subsequent page. This is typically
|
||||
used to show a success message to the use.
|
||||
"""
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from django.contrib import messages
|
||||
from openedx.core.djangolib.markup import Text
|
||||
|
||||
EDX_USER_MESSAGE_TAG = 'edx-user-message'
|
||||
|
||||
|
||||
class UserMessageType(Enum):
|
||||
"""
|
||||
An enumeration of the types of user messages.
|
||||
"""
|
||||
INFO = messages.constants.INFO
|
||||
SUCCESS = messages.constants.SUCCESS
|
||||
WARNING = messages.constants.WARNING
|
||||
ERROR = messages.constants.ERROR
|
||||
|
||||
|
||||
CSS_CLASSES = {
|
||||
UserMessageType.INFO: 'alert-info',
|
||||
UserMessageType.SUCCESS: 'alert-success',
|
||||
UserMessageType.WARNING: 'alert-warning',
|
||||
UserMessageType.ERROR: 'alert-danger',
|
||||
}
|
||||
|
||||
ICON_CLASSES = {
|
||||
UserMessageType.INFO: 'fa fa-bullhorn',
|
||||
UserMessageType.SUCCESS: 'fa fa-check-circle',
|
||||
UserMessageType.WARNING: 'fa fa-warning',
|
||||
UserMessageType.ERROR: 'fa fa-warning',
|
||||
}
|
||||
|
||||
|
||||
class UserMessage():
|
||||
"""
|
||||
Representation of a message to be shown to a user
|
||||
"""
|
||||
def __init__(self, type, message_html):
|
||||
assert isinstance(type, UserMessageType)
|
||||
self.type = type
|
||||
self.message_html = message_html
|
||||
|
||||
@property
|
||||
def css_class(self):
|
||||
"""
|
||||
Returns the CSS class to be used on the message element.
|
||||
"""
|
||||
return CSS_CLASSES[self.type]
|
||||
|
||||
@property
|
||||
def icon_class(self):
|
||||
"""
|
||||
Returns the CSS icon class representing the message type.
|
||||
Returns:
|
||||
"""
|
||||
return ICON_CLASSES[self.type]
|
||||
|
||||
|
||||
def register_user_message(request, message_type, message, title=None):
|
||||
"""
|
||||
Register a message to be shown to the user in the next page.
|
||||
"""
|
||||
assert isinstance(message_type, UserMessageType)
|
||||
messages.add_message(request, message_type.value, Text(message), extra_tags=EDX_USER_MESSAGE_TAG)
|
||||
|
||||
|
||||
def register_info_message(request, message, **kwargs):
|
||||
"""
|
||||
Registers an information message to be shown to the user.
|
||||
"""
|
||||
register_user_message(request, UserMessageType.INFO, message, **kwargs)
|
||||
|
||||
|
||||
def register_success_message(request, message, **kwargs):
|
||||
"""
|
||||
Registers a success message to be shown to the user.
|
||||
"""
|
||||
register_user_message(request, UserMessageType.SUCCESS, message, **kwargs)
|
||||
|
||||
|
||||
def register_warning_message(request, message, **kwargs):
|
||||
"""
|
||||
Registers a warning message to be shown to the user.
|
||||
"""
|
||||
register_user_message(request, UserMessageType.WARNING, message, **kwargs)
|
||||
|
||||
|
||||
def register_error_message(request, message, **kwargs):
|
||||
"""
|
||||
Registers an error message to be shown to the user.
|
||||
"""
|
||||
register_user_message(request, UserMessageType.ERROR, message, **kwargs)
|
||||
|
||||
|
||||
def user_messages(request):
|
||||
"""
|
||||
Returns any outstanding user messages.
|
||||
|
||||
Note: this function also marks these messages as being complete
|
||||
so they won't be returned in the next request.
|
||||
"""
|
||||
def _get_message_type_for_level(level):
|
||||
"""
|
||||
Returns the user message type associated with a level.
|
||||
"""
|
||||
for __, type in UserMessageType.__members__.items():
|
||||
if type.value is level:
|
||||
return type
|
||||
raise 'Unable to find UserMessageType for level {level}'.format(level=level)
|
||||
|
||||
def _create_user_message(message):
|
||||
"""
|
||||
Creates a user message from a Django message.
|
||||
"""
|
||||
return UserMessage(
|
||||
type=_get_message_type_for_level(message.level),
|
||||
message_html=unicode(message.message),
|
||||
)
|
||||
|
||||
django_messages = messages.get_messages(request)
|
||||
return (_create_user_message(message) for message in django_messages if EDX_USER_MESSAGE_TAG in message.tags)
|
||||
Reference in New Issue
Block a user