diff --git a/cms/envs/bok_choy.env.json b/cms/envs/bok_choy.env.json index 52fc88420259d7afa1991f7aae4dc496638e2ec3..ce0d95e8d3b882632459341718fe5d187103677d 100644 --- a/cms/envs/bok_choy.env.json +++ b/cms/envs/bok_choy.env.json @@ -76,7 +76,8 @@ "PREVIEW_LMS_BASE": "localhost:8003", "ALLOW_ALL_ADVANCED_COMPONENTS": true, "ENABLE_CONTENT_LIBRARIES": true, - "ENABLE_SPECIAL_EXAMS": true + "ENABLE_SPECIAL_EXAMS": true, + "SHOW_LANGUAGE_SELECTOR": true }, "FEEDBACK_SUBMISSION_EMAIL": "", "GITHUB_REPO_ROOT": "** OVERRIDDEN **", diff --git a/cms/envs/common.py b/cms/envs/common.py index 997a8b838116b777542904583ce354995210dd75..03a275ebabdb4d1f5bd648719242f861a75f4949 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -190,6 +190,9 @@ FEATURES = { 'ENABLE_SPECIAL_EXAMS': False, 'ORGANIZATIONS_APP': False, + + # Show Language selector + 'SHOW_LANGUAGE_SELECTOR': False, } ENABLE_JASMINE = False @@ -328,6 +331,9 @@ MIDDLEWARE_CLASSES = ( 'django.contrib.messages.middleware.MessageMiddleware', 'track.middleware.TrackMiddleware', + # This is used to set or update the user language preferences. + 'lang_pref.middleware.LanguagePreferenceMiddleware', + # Allows us to dark-launch particular languages 'dark_lang.middleware.DarkLangMiddleware', @@ -1111,6 +1117,8 @@ OAUTH_OIDC_ISSUER = 'https://www.example.com/oauth2' # 5 minute expiration time for JWT id tokens issued for external API requests. OAUTH_ID_TOKEN_EXPIRATION = 5 * 60 +USERNAME_PATTERN = r'(?P<username>[\w.@+-]+)' + # Partner support link for CMS footer PARTNER_SUPPORT_EMAIL = '' diff --git a/cms/static/cms/js/require-config.js b/cms/static/cms/js/require-config.js index 167ed7617488b0dbb6716c7b54496087f2d5be77..190ae8ebe934e783811db53355ecb8a7609a3808 100644 --- a/cms/static/cms/js/require-config.js +++ b/cms/static/cms/js/require-config.js @@ -87,6 +87,7 @@ "ova": 'js/vendor/ova/ova', "catch": 'js/vendor/ova/catch/js/catch', "handlebars": 'js/vendor/ova/catch/js/handlebars-1.1.2', + "lang_edx": "js/src/lang_edx", // end of Annotation tool files // externally hosted files @@ -196,6 +197,9 @@ "tinymce": { exports: "tinymce" }, + "lang_edx": { + deps: ["jquery"] + }, "mathjax": { exports: "MathJax", init: function() { diff --git a/cms/static/js/factories/base.js b/cms/static/js/factories/base.js index b33a8123faf08f4e72aef1c7f525bbd2191f1b28..305705dea13dd954b08febbcc20b2954a6db314e 100644 --- a/cms/static/js/factories/base.js +++ b/cms/static/js/factories/base.js @@ -1,2 +1,2 @@ define(['js/base', 'coffee/src/main', 'js/src/logger', 'datepair', 'accessibility', -'ieshim', 'tooltip_manager']); +'ieshim', 'tooltip_manager', 'lang_edx']); diff --git a/cms/static/sass/elements/_header.scss b/cms/static/sass/elements/_header.scss index e538dfd9202b76b9ad6e253b2d20964e106638b2..386e30ee148264ccab0ca4336b8c79c7d5a733ab 100644 --- a/cms/static/sass/elements/_header.scss +++ b/cms/static/sass/elements/_header.scss @@ -43,8 +43,19 @@ vertical-align: middle; } + .user-language-selector { + width: 120px; + display: inline-block; + margin: 0 10px 0 5px; + vertical-align: sub; + + .language-selector { + width: 120px; + } + } + .nav-account { - width: 100%; + width: auto; } // basic layout - nav items @@ -208,6 +219,13 @@ } } } + + .settings-language-form { + margin-top: 4px; + .language-selector { + width: 130px; + } + } } // ==================== @@ -229,11 +247,11 @@ .is-signedin { .wrapper-l { - width: flex-grid(9,12); + width: flex-grid(8,12); } .wrapper-r { - width: flex-grid(3,12); + width: flex-grid(4,12); } .branding { @@ -253,11 +271,11 @@ .wrapper-header { .wrapper-l { - width: flex-grid(9,12); + width: flex-grid(8,12); } .wrapper-r { - width: flex-grid(3,12); + width: flex-grid(4,12); } .branding { diff --git a/cms/templates/widgets/header.html b/cms/templates/widgets/header.html index 6f229fe86a60321ab8ba98850ff5e611a62d0339..2cfa82bcc6c94055797f273457ff3149b448166c 100644 --- a/cms/templates/widgets/header.html +++ b/cms/templates/widgets/header.html @@ -6,7 +6,6 @@ from contentstore.context_processors import doc_url %> <%page args="online_help_token"/> - <div class="wrapper-header wrapper" id="view-top"> <header class="primary" role="banner"> @@ -188,6 +187,32 @@ </div> <div class="wrapper wrapper-r"> + % if static.show_language_selector(): + <% languages = static.get_released_languages() %> + % if len(languages) > 1: + <nav class="user-language-selector"> + <form action="/i18n/setlang/" method="post" class="settings-language-form" id="language-settings-form"> + <input type="hidden" id="csrf_token" name="csrfmiddlewaretoken" value="${csrf_token}"> + % if user.is_authenticated(): + <input title="preference api" type="hidden" id="preference-api-url" class="url-endpoint" value="${reverse('preferences_api', kwargs={'username': user.username})}" data-user-is-authenticated="true"> + % else: + <input title="session update url" type="hidden" id="update-session-url" class="url-endpoint" value="${reverse('session_language')}" data-user-is-authenticated="false"> + % endif + <label><span class="sr">${_("Choose Language")}</span> + <select class="input select language-selector" id="settings-language-value" name="language"> + % for language in languages: + % if language[0] == LANGUAGE_CODE: + <option value="${language[0]}" selected="selected">${language[1]}</option> + % else: + <option value="${language[0]}" >${language[1]}</option> + % endif + % endfor + </select> + </label> + </form> + </nav> + % endif + % endif % if user.is_authenticated(): <nav class="nav-account nav-is-signedin nav-dd ui-right" aria-label="${_('Account')}"> <h2 class="sr">${_("Account Navigation")}</h2> @@ -195,7 +220,6 @@ <li class="nav-item nav-account-help"> <h3 class="title"><span class="label"><a href="${get_online_help_info(online_help_token)['doc_url']}" title="${_("Contextual Online Help")}" target="_blank">${_("Help")}</a></span></h3> </li> - <li class="nav-item nav-account-user"> <h3 class="title"><span class="label"><span class="label-prefix sr">${_("Currently signed in as:")}</span><span class="account-username" title="${ user.username }">${ user.username }</span></span> <i class="icon fa fa-caret-down ui-toggle-dd"></i></h3> diff --git a/cms/urls.py b/cms/urls.py index 2b85e924719e5cbd885da5f71cd976271c877c42..cfd79baea75bb48a2bd2d83f4304a7a2f704b280 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -52,6 +52,14 @@ urlpatterns = patterns( url(r'^heartbeat$', include('heartbeat.urls')), url(r'^user_api/', include('openedx.core.djangoapps.user_api.legacy_urls')), + + url(r'^i18n/', include('django.conf.urls.i18n')), + + # User API endpoints + url(r'^api/user/', include('openedx.core.djangoapps.user_api.urls')), + + # Update session view + url(r'^lang_pref/session_language', 'lang_pref.views.update_session_language', name='session_language'), ) # User creation and updating views diff --git a/common/djangoapps/lang_pref/middleware.py b/common/djangoapps/lang_pref/middleware.py index 7fdd29e985e616fab32acd3580af368cf11914f0..ad94534eecaa19097fb6cb4ea60eacb18cfaf41d 100644 --- a/common/djangoapps/lang_pref/middleware.py +++ b/common/djangoapps/lang_pref/middleware.py @@ -2,7 +2,7 @@ Middleware for Language Preferences """ -from openedx.core.djangoapps.user_api.preferences.api import get_user_preference +from openedx.core.djangoapps.user_api.preferences.api import get_user_preference, delete_user_preference from lang_pref import LANGUAGE_KEY from django.utils.translation import LANGUAGE_SESSION_KEY from django.utils.translation.trans_real import parse_accept_lang_header @@ -21,20 +21,26 @@ class LanguagePreferenceMiddleware(object): """ If a user's UserPreference contains a language preference, use the user's preference. """ + languages = released_languages() + system_released_languages = [seq[0] for seq in languages] + # If the user is logged in, check for their language preference if request.user.is_authenticated(): # Get the user's language preference user_pref = get_user_preference(request.user, LANGUAGE_KEY) # Set it to the LANGUAGE_SESSION_KEY (Django-specific session setting governing language pref) if user_pref: - request.session[LANGUAGE_SESSION_KEY] = user_pref - else: - # Setting the session language to the browser language, if it is supported. - preferred_language = request.META.get('HTTP_ACCEPT_LANGUAGE', '') - lang_headers = [seq[0] for seq in parse_accept_lang_header(preferred_language)] - languages = released_languages() - for browser_lang in lang_headers: - if browser_lang in [seq[0] for seq in languages]: - if request.session.get(LANGUAGE_SESSION_KEY, None) != browser_lang: - request.session[LANGUAGE_SESSION_KEY] = unicode(browser_lang) - break + if user_pref in system_released_languages: + request.session[LANGUAGE_SESSION_KEY] = user_pref + else: + delete_user_preference(request.user, LANGUAGE_KEY) + else: + preferred_language = request.META.get('HTTP_ACCEPT_LANGUAGE', '') + lang_headers = [seq[0] for seq in parse_accept_lang_header(preferred_language)] + + # Setting the session language to the browser language, if it is supported. + for browser_lang in lang_headers: + if browser_lang in system_released_languages: + if request.session.get(LANGUAGE_SESSION_KEY, None) is None: + request.session[LANGUAGE_SESSION_KEY] = unicode(browser_lang) + break diff --git a/common/djangoapps/lang_pref/tests/test_middleware.py b/common/djangoapps/lang_pref/tests/test_middleware.py index 47405fecac3ac3661422a3d63d908db096f04f9e..0166c46747e40309b5c64471efbf6771038ee39e 100644 --- a/common/djangoapps/lang_pref/tests/test_middleware.py +++ b/common/djangoapps/lang_pref/tests/test_middleware.py @@ -7,6 +7,7 @@ from lang_pref.middleware import LanguagePreferenceMiddleware from openedx.core.djangoapps.user_api.preferences.api import set_user_preference, get_user_preference from lang_pref import LANGUAGE_KEY from student.tests.factories import UserFactory +from student.tests.factories import AnonymousUserFactory import mock @@ -20,6 +21,7 @@ class TestUserPreferenceMiddleware(TestCase): self.middleware = LanguagePreferenceMiddleware() self.session_middleware = SessionMiddleware() self.user = UserFactory.create() + self.anonymous_user = AnonymousUserFactory() self.request = RequestFactory().get('/somewhere') self.request.user = self.user self.request.META['HTTP_ACCEPT_LANGUAGE'] = 'ar;q=1.0' # pylint: disable=no-member @@ -30,12 +32,16 @@ class TestUserPreferenceMiddleware(TestCase): self.middleware.process_request(self.request) self.assertNotIn(LANGUAGE_SESSION_KEY, self.request.session) + @mock.patch('lang_pref.middleware.released_languages', mock.Mock(return_value=[('eo', 'esperanto')])) def test_language_in_user_prefs(self): # language set in the user preferences and not the session set_user_preference(self.user, LANGUAGE_KEY, 'eo') self.middleware.process_request(self.request) self.assertEquals(self.request.session[LANGUAGE_SESSION_KEY], 'eo') + @mock.patch('lang_pref.middleware.released_languages', mock.Mock( + return_value=[('en', 'english'), ('eo', 'esperanto')] + )) def test_language_in_session(self): # language set in both the user preferences and session, # preference should get precedence. The session will hold the last value, @@ -53,16 +59,29 @@ class TestUserPreferenceMiddleware(TestCase): mock.Mock(return_value=[('eo', 'dummy Esperanto'), ('ar', 'arabic')])) def test_supported_browser_language_in_session(self): """ - test: browser language should be set in user preferences if it is supported by system. + test: browser language should be set in user session if it is supported by system for unauthenticated user. """ - self.assertEquals(get_user_preference(self.request.user, LANGUAGE_KEY), None) + self.request.user = self.anonymous_user self.middleware.process_request(self.request) self.assertEqual(self.request.session[LANGUAGE_SESSION_KEY], 'ar') # pylint: disable=no-member @mock.patch('lang_pref.middleware.released_languages', mock.Mock(return_value=[('en', 'english')])) def test_browser_language_not_be_in_session(self): """ - test: browser language should not be set in user preferences if it is not supported by system. + test: browser language should not be set in user session if it is not supported by system. """ + self.request.user = self.anonymous_user self.middleware.process_request(self.request) self.assertNotEqual(self.request.session.get(LANGUAGE_SESSION_KEY), 'ar') # pylint: disable=no-member + + @mock.patch('lang_pref.middleware.released_languages', mock.Mock( + return_value=[('en', 'english'), ('ar', 'arabic')] + )) + def test_delete_user_lang_preference_not_supported_by_system(self): + """ + test: user preferred language has been removed from user preferences model if it is not supported by system + for authenticated users. + """ + set_user_preference(self.user, LANGUAGE_KEY, 'eo') + self.middleware.process_request(self.request) + self.assertEqual(get_user_preference(self.request.user, LANGUAGE_KEY), None) diff --git a/common/djangoapps/lang_pref/tests/test_views.py b/common/djangoapps/lang_pref/tests/test_views.py new file mode 100644 index 0000000000000000000000000000000000000000..055345c0175f605c2ba62c2a4e53065d033b6854 --- /dev/null +++ b/common/djangoapps/lang_pref/tests/test_views.py @@ -0,0 +1,38 @@ +""" +Tests: lang pref views +""" +import json +from django.test import TestCase +from django.test.client import RequestFactory +from django.core.urlresolvers import reverse +from student.tests.factories import UserFactory +from django.utils.translation import LANGUAGE_SESSION_KEY +from django.contrib.sessions.middleware import SessionMiddleware +from django.utils.translation import get_language + + +class TestLangPrefView(TestCase): + """ + Language preference view tests. + """ + + def setUp(self): + super(TestLangPrefView, self).setUp() + self.session_middleware = SessionMiddleware() + self.user = UserFactory.create() + self.request = RequestFactory().get('/somewhere') + self.request.user = self.user + self.session_middleware.process_request(self.request) + + def test_language_session_update(self): + # test language session updating correctly. + self.request.session[LANGUAGE_SESSION_KEY] = 'ar' # pylint: disable=no-member + response = self.client.patch(reverse("session_language"), json.dumps({'pref-lang': 'eo'})) + self.assertEqual(response.status_code, 200) + self.client.get('/') + self.assertEquals(get_language(), 'eo') + + response = self.client.patch(reverse("session_language"), json.dumps({'pref-lang': 'en'})) + self.assertEqual(response.status_code, 200) + self.client.get('/') + self.assertEquals(get_language(), 'en') diff --git a/common/djangoapps/lang_pref/views.py b/common/djangoapps/lang_pref/views.py new file mode 100644 index 0000000000000000000000000000000000000000..e444724fb9d1cfb6a5b45ce1b428266ad4bfb92d --- /dev/null +++ b/common/djangoapps/lang_pref/views.py @@ -0,0 +1,22 @@ +""" +Language Preference Views +""" +import json +from django.conf import settings +from django.views.decorators.csrf import ensure_csrf_cookie +from django.utils.translation import LANGUAGE_SESSION_KEY +from lang_pref import LANGUAGE_KEY +from django.http import HttpResponse + + +@ensure_csrf_cookie +def update_session_language(request): + """ + Update the language session key. + """ + if request.method == 'PATCH': + data = json.loads(request.body) + language = data.get(LANGUAGE_KEY, settings.LANGUAGE_CODE) + if request.session.get(LANGUAGE_SESSION_KEY, None) != language: + request.session[LANGUAGE_SESSION_KEY] = unicode(language) + return HttpResponse(200) diff --git a/common/djangoapps/pipeline_mako/templates/static_content.html b/common/djangoapps/pipeline_mako/templates/static_content.html index 68c0263dca84801753c863f63b1fd50b0251c76a..20e527f5c98d8c55e3dd774be3c055f5932eb01d 100644 --- a/common/djangoapps/pipeline_mako/templates/static_content.html +++ b/common/djangoapps/pipeline_mako/templates/static_content.html @@ -6,6 +6,7 @@ from mako.exceptions import TemplateLookupException from openedx.core.djangoapps.theming.helpers import get_page_title_breadcrumbs, get_value, get_template_path, get_themed_template_path, is_request_in_themed_site from certificates.api import get_asset_url_by_slug +from lang_pref.api import released_languages %> <%def name='url(file, raw=False)'><% @@ -132,3 +133,7 @@ else: <%def name="show_language_selector()"><% return settings.FEATURES.get('SHOW_LANGUAGE_SELECTOR', False) %></%def> + +<%def name="get_released_languages()"><% + return released_languages() +%></%def> diff --git a/common/static/js/fixtures/lang-edx-fixture.html b/common/static/js/fixtures/lang-edx-fixture.html index aaf95bed5f3d9fc8da3bbc192cadecb6989417fb..fe7cb1acc2807babc5ef90be37df5089b8f9f52d 100644 --- a/common/static/js/fixtures/lang-edx-fixture.html +++ b/common/static/js/fixtures/lang-edx-fixture.html @@ -1,5 +1,5 @@ <form action="/i18n/setlang/" method="post" class="settings-language-form" id="language-settings-form"> - <input title="preference api" type="hidden" id="preference-api-url" value="/api/user/v1/preferences/test1/"> + <input title="preference api" class="url-endpoint" type="hidden" id="preference-api-url" value="/api/user/v1/preferences/test1/" data-user-is-authenticated="true"> <label><span class="sr">Choose Language</span> <select class="input select" id="settings-language-value" name="language"> <option value="en" selected="selected">English</option> diff --git a/common/static/js/src/lang_edx.js b/common/static/js/src/lang_edx.js index 65eb686c6538a24bae82886f5a659980aedb1489..263ad3a560d13092089948a016b66e121fdc23ec 100644 --- a/common/static/js/src/lang_edx.js +++ b/common/static/js/src/lang_edx.js @@ -1,12 +1,9 @@ -var edx = edx || {}, - Language = (function() { +var Language = (function() { 'use strict'; - var preference_api_url, - settings_language_selector, + var settings_language_selector, self = null; return { init: function() { - preference_api_url = $('#preference-api-url').val(); settings_language_selector = $('#settings-language-value'); self = this; this.listenForLanguagePreferenceChange(); @@ -18,32 +15,49 @@ var edx = edx || {}, */ listenForLanguagePreferenceChange: function() { settings_language_selector.change(function(event) { - var language = this.value; + var language = this.value, + url = $('.url-endpoint').val(), + is_user_authenticated = JSON.parse($('.url-endpoint').data('user-is-authenticated')); event.preventDefault(); - $.ajax({ - type: 'PATCH', - data: JSON.stringify({'pref-lang': language}) , - url: preference_api_url, - dataType: 'json', - contentType: "application/merge-patch+json", - beforeSend: function (xhr) { - xhr.setRequestHeader("X-CSRFToken", $('#csrf_token').val()); + self.submitAjaxRequest(language, url, function() { + if (is_user_authenticated) { + // User language preference has been set successfully + // Now submit the form in success callback. + $('#language-settings-form').submit(); + } else { + self.refresh(); } - }).done(function () { - // User language preference has been set successfully - // Now submit the form in success callback. - $("#language-settings-form").submit(); - }).fail(function() { - self.refresh(); }); }); }, + /** + * Send an ajax request to set user language preferences. + */ + submitAjaxRequest: function(language, url, callback) { + + $.ajax({ + type: 'PATCH', + data: JSON.stringify({'pref-lang': language}) , + url: url, + dataType: 'json', + contentType: 'application/merge-patch+json', + notifyOnError: false, + beforeSend: function (xhr) { + xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken')); + } + }).done(function () { + callback(); + }).fail(function() { + self.refresh(); + }); + }, + /** * refresh the page. */ refresh: function () { - // reloading the page so we can get the latest state of realsesd languages from model + // reloading the page so we can get the latest state of released languages from model location.reload(); } diff --git a/common/test/acceptance/pages/studio/index.py b/common/test/acceptance/pages/studio/index.py index 3d2a3fdf9f3d025771a4623c116bcfeb085906cd..f6f122e5813e70a55eb0c96605d1f87a4bedb269 100644 --- a/common/test/acceptance/pages/studio/index.py +++ b/common/test/acceptance/pages/studio/index.py @@ -225,6 +225,17 @@ class DashboardPage(PageObject): return True return False + @property + def language_selector(self): + """ + return language selector + """ + self.wait_for_element_visibility( + '#settings-language-value', + 'Language selector element is available' + ) + return self.q(css='#settings-language-value') + class DashboardPageWithPrograms(DashboardPage): """ diff --git a/common/test/acceptance/tests/studio/test_studio_home.py b/common/test/acceptance/tests/studio/test_studio_home.py index d5309c16c90a0da927fcccaeefa8442961fd3a99..3142d6b78a8b42f16aaf718531a6312e6be7c276 100644 --- a/common/test/acceptance/tests/studio/test_studio_home.py +++ b/common/test/acceptance/tests/studio/test_studio_home.py @@ -10,6 +10,11 @@ from ...fixtures.programs import ProgramsFixture from ...pages.studio.auto_auth import AutoAuthPage from ...pages.studio.library import LibraryEditPage from ...pages.studio.index import DashboardPage, DashboardPageWithPrograms +from ...pages.lms.account_settings import AccountSettingsPage +from ..helpers import ( + select_option_by_text, + get_selected_option_text +) class CreateLibraryTest(WebAppTest): @@ -139,3 +144,37 @@ class DashboardProgramsTabTest(WebAppTest): self.dashboard_page.visit() self.assertFalse(self.dashboard_page.is_programs_tab_present()) self.assertFalse(self.dashboard_page.is_new_program_button_present()) + + +class StudioLanguageTest(WebAppTest): + """ Test suite for the Studio Language """ + def setUp(self): + super(StudioLanguageTest, self).setUp() + self.dashboard_page = DashboardPage(self.browser) + self.account_settings = AccountSettingsPage(self.browser) + AutoAuthPage(self.browser).visit() + + def test_studio_language_change(self): + """ + Scenario: Ensure that language selection is working fine. + First I go to the user dashboard page in studio. I can see 'English' is selected by default. + Then I choose 'Dummy Language' from drop down (at top of the page). + Then I visit the student account settings page and I can see the language has been updated to 'Dummy Language' + in both drop downs. + """ + dummy_language = u'Dummy Language (Esperanto)' + self.dashboard_page.visit() + language_selector = self.dashboard_page.language_selector + self.assertEqual( + get_selected_option_text(language_selector), + u'English' + ) + + select_option_by_text(language_selector, dummy_language) + self.dashboard_page.wait_for_ajax() + self.account_settings.visit() + self.assertEqual(self.account_settings.value_for_dropdown_field('pref-lang'), dummy_language) + self.assertEqual( + get_selected_option_text(language_selector), + u'Dummy Language (Esperanto)' + ) diff --git a/lms/envs/common.py b/lms/envs/common.py index be786340a2b40326d8d2198c9715b4cc88cb70d8..f874a9a7ef17e1dc1c2cab0e32c21451d9fcbfe3 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -365,7 +365,7 @@ FEATURES = { # Enable LTI Provider feature. 'ENABLE_LTI_PROVIDER': False, - # Show LMS Language selector + # Show Language selector. 'SHOW_LANGUAGE_SELECTOR': False, } diff --git a/lms/static/sass/shared/_header.scss b/lms/static/sass/shared/_header.scss index d1dc31e846cb92d68c17c345b4d6e1d709d64daf..670077a258c5088f8b31cdc82147f35444bc919b 100644 --- a/lms/static/sass/shared/_header.scss +++ b/lms/static/sass/shared/_header.scss @@ -119,7 +119,10 @@ header.global { margin: 0; .settings-language-form { - margin-top: 4px; + margin: 4px; + .language-selector { + width: 120px; + } } > .primary { diff --git a/lms/templates/navigation.html b/lms/templates/navigation.html index 962153e5d100ee6f488070ec6822af941cabe0e3..1405b9c7bd3b7c275584b72ed67818e6231bc604 100644 --- a/lms/templates/navigation.html +++ b/lms/templates/navigation.html @@ -14,7 +14,6 @@ from openedx.core.djangoapps.user_api.accounts.image_helpers import get_profile_ from branding import api as branding_api # app that handles site status messages from status.status import get_site_status_msg -from lang_pref.api import released_languages %> ## Provide a hook for themes to inject branding on top. @@ -38,7 +37,6 @@ site_status_msg = get_site_status_msg(course_id) </div> % endif </%block> - <header id="global-navigation" class="global ${"slim" if course else ""}" > <nav class="nav-wrapper" aria-label="${_('Global')}"> <h1 class="logo"> @@ -79,28 +77,6 @@ site_status_msg = get_site_status_msg(course_id) </%block> </ol> <ol class="user"> - % if static.show_language_selector(): - <% languages = released_languages() %> - % if len(languages) > 1: - <li class="primary"> - <form action="/i18n/setlang/" method="post" class="settings-language-form" id="language-settings-form"> - <input type="hidden" id="csrf_token" name="csrfmiddlewaretoken" value="${csrf_token}"> - <input title="preference api" type="hidden" id="preference-api-url" value="${reverse('preferences_api', kwargs={'username': user.username})}"> - <label><span class="sr">${_("Choose Language")}</span> - <select class="input select" id="settings-language-value" name="language"> - % for language in languages: - % if language[0] == LANGUAGE_CODE: - <option value="${language[0]}" selected="selected">${language[1]}</option> - % else: - <option value="${language[0]}" >${language[1]}</option> - % endif - % endfor - </select> - </label> - </form> - </li> - % endif - % endif <li class="primary"> <a href="${reverse('dashboard')}" class="user-link"> <span class="sr">${_("Dashboard for:")}</span> @@ -186,7 +162,35 @@ site_status_msg = get_site_status_msg(course_id) </%block> </ol> % endif - </nav> + % if static.show_language_selector(): + <% languages = static.get_released_languages() %> + % if len(languages) > 1: + <ol class="user"> + <li class="primary"> + <form action="/i18n/setlang/" method="post" class="settings-language-form" id="language-settings-form"> + <input type="hidden" id="csrf_token" name="csrfmiddlewaretoken" value="${csrf_token}"> + % if user.is_authenticated(): + <input title="preference api" type="hidden" class="url-endpoint" value="${reverse('preferences_api', kwargs={'username': user.username})}" data-user-is-authenticated="true"> + % else: + <input title="session update url" type="hidden" class="url-endpoint" value="${reverse('session_language')}" data-user-is-authenticated="false"> + % endif + <label><span class="sr">${_("Choose Language")}</span> + <select class="input select language-selector" id="settings-language-value" name="language"> + % for language in languages: + % if language[0] == LANGUAGE_CODE: + <option value="${language[0]}" selected="selected">${language[1]}</option> + % else: + <option value="${language[0]}" >${language[1]}</option> + % endif + % endfor + </select> + </label> + </form> + </li> + </ol> + % endif + % endif + </nav> </header> % if course: <!--[if lte IE 9]> diff --git a/lms/urls.py b/lms/urls.py index dc201de5d0312fdee334ec4b093953d1dbe59d66..078797bf3a8aab2229621f44d3a4dc6bf23f8236 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -103,6 +103,9 @@ urlpatterns = ( url(r'^rss_proxy/', include('rss_proxy.urls', namespace='rss_proxy')), url(r'^api/organizations/', include('organizations.urls', namespace='organizations')), + # Update session view + url(r'^lang_pref/session_language', 'lang_pref.views.update_session_language', name='session_language'), + # Multiple course modes and identity verification # TODO Namespace these! url(r'^course_modes/', include('course_modes.urls')),