update footer endpoint in branding api to support inclusion of language selector
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import json
|
||||
import urllib
|
||||
from django.test import TestCase
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
|
||||
@@ -10,6 +11,8 @@ import mock
|
||||
import ddt
|
||||
from config_models.models import cache
|
||||
from branding.models import BrandingApiConfig
|
||||
from openedx.core.djangoapps.dark_lang.models import DarkLangConfig
|
||||
from openedx.core.djangoapps.lang_pref.api import released_languages
|
||||
from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin
|
||||
from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme_context
|
||||
from student.tests.factories import UserFactory
|
||||
@@ -208,6 +211,38 @@ class TestFooter(TestCase):
|
||||
else:
|
||||
self.assertNotIn("vendor", resp.content)
|
||||
|
||||
@ddt.data(
|
||||
# OpenEdX
|
||||
(None, None, '1'),
|
||||
(None, 'eo', '1'),
|
||||
(None, None, ''),
|
||||
|
||||
# EdX.org
|
||||
('edx.org', None, '1'),
|
||||
('edx.org', 'eo', '1'),
|
||||
('edx.org', None, '')
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_include_language_selector(self, theme, language, include_language_selector):
|
||||
self._set_feature_flag(True)
|
||||
DarkLangConfig(released_languages='en,eo,es-419,fr', enabled=True, changed_by=User().save()).save()
|
||||
|
||||
with with_comprehensive_theme_context(theme):
|
||||
params = {
|
||||
key: val for key, val in [
|
||||
('language', language), ('include-language-selector', include_language_selector)
|
||||
] if val
|
||||
}
|
||||
resp = self._get_footer(accepts="text/html", params=params)
|
||||
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
if include_language_selector:
|
||||
selected_language = language if language else 'en'
|
||||
self._verify_language_selector(resp.content, selected_language)
|
||||
else:
|
||||
self.assertNotIn('footer-language-selector', resp.content)
|
||||
|
||||
def test_no_supported_accept_type(self):
|
||||
self._set_feature_flag(True)
|
||||
resp = self._get_footer(accepts="application/x-shockwave-flash")
|
||||
@@ -230,6 +265,20 @@ class TestFooter(TestCase):
|
||||
|
||||
return self.client.get(url, HTTP_ACCEPT=accepts)
|
||||
|
||||
def _verify_language_selector(self, content, selected_language):
|
||||
""" Verify that the language selector is present and correctly configured."""
|
||||
# Verify the selector is included
|
||||
self.assertIn('footer-language-selector', content)
|
||||
|
||||
# Verify the correct language is selected
|
||||
self.assertIn('<option value="{}" selected="selected">'.format(selected_language), content)
|
||||
|
||||
# Verify the language choices
|
||||
for language in released_languages():
|
||||
if language.code == selected_language:
|
||||
continue
|
||||
self.assertIn('<option value="{}">'.format(language.code), content)
|
||||
|
||||
|
||||
class TestIndex(SiteMixin, TestCase):
|
||||
""" Test the index view """
|
||||
|
||||
@@ -20,6 +20,7 @@ from edxmako.shortcuts import marketing_link
|
||||
from util.cache import cache_if_anonymous
|
||||
from util.json_request import JsonResponse
|
||||
import branding.api as branding_api
|
||||
from openedx.core.djangoapps.lang_pref.api import released_languages
|
||||
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -122,12 +123,13 @@ def _footer_css_urls(request, package_name):
|
||||
]
|
||||
|
||||
|
||||
def _render_footer_html(request, show_openedx_logo, include_dependencies):
|
||||
def _render_footer_html(request, show_openedx_logo, include_dependencies, include_language_selector):
|
||||
"""Render the footer as HTML.
|
||||
|
||||
Arguments:
|
||||
show_openedx_logo (bool): If True, include the OpenEdX logo in the rendered HTML.
|
||||
include_dependencies (bool): If True, include JavaScript and CSS dependencies.
|
||||
include_language_selector (bool): If True, include a language selector with all supported languages.
|
||||
|
||||
Returns: unicode
|
||||
|
||||
@@ -141,6 +143,7 @@ def _render_footer_html(request, show_openedx_logo, include_dependencies):
|
||||
'footer_css_urls': _footer_css_urls(request, css_name),
|
||||
'bidi': bidi,
|
||||
'include_dependencies': include_dependencies,
|
||||
'include_language_selector': include_language_selector
|
||||
}
|
||||
|
||||
return render_to_response("footer.html", context)
|
||||
@@ -235,6 +238,13 @@ def footer(request):
|
||||
GET /api/branding/v1/footer?language=en
|
||||
Accepts: text/html
|
||||
|
||||
|
||||
Example: Retrieving the footer with a language selector
|
||||
|
||||
GET /api/branding/v1/footer?include-language-selector=1
|
||||
Accepts: text/html
|
||||
|
||||
|
||||
Example: Retrieving the footer with all JS and CSS dependencies (for testing)
|
||||
|
||||
GET /api/branding/v1/footer?include-dependencies=1
|
||||
@@ -261,19 +271,26 @@ def footer(request):
|
||||
except LookupError:
|
||||
language = settings.LANGUAGE_CODE
|
||||
|
||||
# Include a language selector
|
||||
include_language_selector = request.GET.get('include-language-selector', '') == '1'
|
||||
|
||||
# Render the footer information based on the extension
|
||||
if 'text/html' in accepts or '*/*' in accepts:
|
||||
cache_key = u"branding.footer.{params}.html".format(
|
||||
params=urllib.urlencode({
|
||||
'language': language,
|
||||
'show_openedx_logo': show_openedx_logo,
|
||||
'include_dependencies': include_dependencies,
|
||||
})
|
||||
)
|
||||
cache_params = {
|
||||
'language': language,
|
||||
'show_openedx_logo': show_openedx_logo,
|
||||
'include_dependencies': include_dependencies
|
||||
}
|
||||
if include_language_selector:
|
||||
cache_params['language_selector_options'] = ','.join(sorted([lang.code for lang in released_languages()]))
|
||||
cache_key = u"branding.footer.{params}.html".format(params=urllib.urlencode(cache_params))
|
||||
|
||||
content = cache.get(cache_key)
|
||||
if content is None:
|
||||
with translation.override(language):
|
||||
content = _render_footer_html(request, show_openedx_logo, include_dependencies)
|
||||
content = _render_footer_html(
|
||||
request, show_openedx_logo, include_dependencies, include_language_selector
|
||||
)
|
||||
cache.set(cache_key, content, settings.FOOTER_CACHE_TIMEOUT)
|
||||
return HttpResponse(content, status=200, content_type="text/html; charset=utf-8")
|
||||
|
||||
|
||||
@@ -55,8 +55,8 @@ p {
|
||||
}
|
||||
|
||||
span {
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
/* Fix for CodeMirror: prevent top-level span from affecting deeply-embedded span in CodeMirror */
|
||||
|
||||
@@ -33,6 +33,10 @@ footer#footer-edx-v3 {
|
||||
font-family: $sans-serif;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.site-nav,
|
||||
.legal-notices {
|
||||
li {
|
||||
@@ -68,7 +72,7 @@ footer#footer-edx-v3 {
|
||||
}
|
||||
|
||||
.legal-notices {
|
||||
margin: 20px 0 30px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.openedx-link {
|
||||
@@ -89,7 +93,7 @@ footer#footer-edx-v3 {
|
||||
.social-media-links,
|
||||
.mobile-app-links {
|
||||
@extend %ui-no-list;
|
||||
|
||||
|
||||
.list-item {
|
||||
display: inline-block;
|
||||
}
|
||||
@@ -107,6 +111,12 @@ footer#footer-edx-v3 {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-family: 'FontAwesome';
|
||||
font-style: normal;
|
||||
color: $edx-footer-link-color;
|
||||
}
|
||||
|
||||
a.sm-link {
|
||||
@include float(left);
|
||||
@include margin(0, 0, 10px, 10px);
|
||||
@@ -129,11 +139,6 @@ footer#footer-edx-v3 {
|
||||
opacity: 0.7;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-family: 'FontAwesome';
|
||||
color: $edx-footer-link-color;
|
||||
}
|
||||
}
|
||||
|
||||
.app-link {
|
||||
@@ -208,4 +213,13 @@ footer#footer-edx-v3 {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.footer-language-selector {
|
||||
margin: 20px 0;
|
||||
|
||||
label[for=footer-language-select] {
|
||||
display: inline-block;
|
||||
cursor: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-family: 'FontAwesome';
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
// colophon
|
||||
.colophon {
|
||||
@include span-columns(8);
|
||||
@@ -186,6 +191,13 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer-language-selector {
|
||||
label[for=footer-language-select] {
|
||||
display: inline-block;
|
||||
cursor: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// edx theme overrides
|
||||
|
||||
@@ -28,6 +28,10 @@
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
% if include_language_selector:
|
||||
<%include file="widgets/footer-language-selector.html"/>
|
||||
% endif
|
||||
|
||||
<div class="wrapper-logo">
|
||||
<p>
|
||||
<a href="/">
|
||||
|
||||
72
lms/templates/widgets/footer-language-selector.html
Normal file
72
lms/templates/widgets/footer-language-selector.html
Normal file
@@ -0,0 +1,72 @@
|
||||
## Language-selection widget for the footer.
|
||||
##
|
||||
## Requires settings.LANGUAGE_COOKIE.
|
||||
<%page expression_filter="h"/>
|
||||
<%!
|
||||
from babel import Locale
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from openedx.core.djangoapps.lang_pref import COOKIE_DURATION
|
||||
from openedx.core.djangoapps.lang_pref.api import released_languages
|
||||
from openedx.core.djangolib.js_utils import js_escaped_string
|
||||
|
||||
# Make sure LANGUAGE_COOKIE is present.
|
||||
if not settings.LANGUAGE_COOKIE:
|
||||
raise ValueError('settings.LANGUAGE_COOKIE is required to use footer-language-selector.')
|
||||
%>
|
||||
|
||||
<div class="footer-language-selector">
|
||||
<label for="footer-language-select">
|
||||
<span class="icon fa fa-globe" aria-hidden="true"></span>
|
||||
<span class="sr">${_("Choose Language")}</span>
|
||||
</label>
|
||||
<select id="footer-language-select" name="language" onchange="footerLanguageSelector.handleSelection(this)">
|
||||
% for language in sorted(released_languages(), key=lambda x: x.code):
|
||||
<% language_name = Locale.parse(language.code.replace('_', '-'), sep='-').language_name %>
|
||||
% if language.code == LANGUAGE_CODE:
|
||||
<option value="${language.code}" selected="selected">${language_name}</option>
|
||||
% else:
|
||||
<option value="${language.code}">${language_name}</option>
|
||||
% endif
|
||||
% endfor
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.footerLanguageSelector = {
|
||||
##
|
||||
## Set the language cookie using the same settings as
|
||||
## https://github.com/edx/edx-platform/blob/master/openedx/core/djangoapps/lang_pref/views.py#L26
|
||||
## and
|
||||
## https://github.com/edx/edx-platform/blob/master/openedx/core/djangoapps/lang_pref/middleware.py#63
|
||||
## Then refresh the page so that the language negotiation middleware can read it and re-render everything
|
||||
## in the selected language.
|
||||
##
|
||||
## NOTE: For logged-in users, the LMS language negotiation middleware should persist the selected language
|
||||
## preference to the user's profile. This effect may be delayed on pages that do not use the LMS language
|
||||
## selection middleware.
|
||||
##
|
||||
handleSelection: function($select) {
|
||||
this.setLanguageCookie($select.value, this.refreshPage);
|
||||
},
|
||||
|
||||
setLanguageCookie: function(value, callback) {
|
||||
var cookie = '${settings.LANGUAGE_COOKIE | n, js_escaped_string}=' + value + ';path=/';
|
||||
% if settings.SESSION_COOKIE_DOMAIN:
|
||||
cookie += ';domain=${settings.SESSION_COOKIE_DOMAIN | n, js_escaped_string}';
|
||||
% endif
|
||||
% if COOKIE_DURATION:
|
||||
cookie += ';max-age=${COOKIE_DURATION | n, js_escaped_string}';
|
||||
% endif
|
||||
|
||||
document.cookie = cookie;
|
||||
|
||||
callback();
|
||||
},
|
||||
|
||||
refreshPage: function() {
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -21,7 +21,7 @@
|
||||
<div class="footer-content-wrapper">
|
||||
<div class="footer-logo">
|
||||
<a href="${marketing_link('ROOT')}">
|
||||
<img alt="edX Home Page" src="${footer['logo_image']}">
|
||||
<img alt="${_('edX Home Page')}" src="${footer['logo_image']}">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -44,6 +44,11 @@
|
||||
% endfor
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
% if include_language_selector:
|
||||
<%include file="widgets/footer-language-selector.html"/>
|
||||
% endif
|
||||
|
||||
<p class="copyright">${_(
|
||||
u"\u00A9 2012-{year} edX Inc. All rights reserved except where noted. "
|
||||
u"EdX, Open edX and the edX and Open EdX logos are registered trademarks "
|
||||
|
||||
@@ -43,6 +43,10 @@ from django.utils.translation import ugettext as _
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
% if include_language_selector:
|
||||
<%include file='widgets/footer-language-selector.html'/>
|
||||
% endif
|
||||
|
||||
<div class="wrapper-logo">
|
||||
<p>
|
||||
<a href="/">
|
||||
|
||||
@@ -21,6 +21,11 @@
|
||||
<li><a href="${reverse('tos')}#copyright">${_("Copyright")}</a></li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
% if include_language_selector:
|
||||
<%include file='widgets/footer-language-selector.html'/>
|
||||
% endif
|
||||
|
||||
</div>
|
||||
<div class="references">
|
||||
<span>Built on <a href="http://open.edx.org">OpenEdX</a>.</span>
|
||||
|
||||
Reference in New Issue
Block a user