Commit 15dab29b by Chris Committed by GitHub

Merge pull request #13308 from edx/hotfix/2016-08-24_clrux

Hotfix: edx-opaque-keys 0.3.3, OpaqueKeyField newline usage log, fix inline JS for CCX navigation
parents 9af50a75 43834839
...@@ -2,11 +2,14 @@ ...@@ -2,11 +2,14 @@
Useful django models for implementing XBlock infrastructure in django. Useful django models for implementing XBlock infrastructure in django.
""" """
import warnings import warnings
import logging
from django.db import models from django.db import models
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from opaque_keys.edx.keys import CourseKey, UsageKey, BlockTypeKey from opaque_keys.edx.keys import CourseKey, UsageKey, BlockTypeKey
log = logging.getLogger(__name__)
class NoneToEmptyManager(models.Manager): class NoneToEmptyManager(models.Manager):
""" """
...@@ -104,6 +107,16 @@ class OpaqueKeyField(models.CharField): ...@@ -104,6 +107,16 @@ class OpaqueKeyField(models.CharField):
return None return None
if isinstance(value, basestring): if isinstance(value, basestring):
if value.endswith('\n'):
# An opaque key with a trailing newline has leaked into the DB.
# Log and strip the value.
log.warning(u'{}:{}:{}:to_python: Invalid key: {}. Removing trailing newline.'.format(
self.model._meta.db_table, # pylint: disable=protected-access
self.name,
self.KEY_CLASS.__name__,
repr(value)
))
value = value.rstrip()
return self.KEY_CLASS.from_string(value) return self.KEY_CLASS.from_string(value)
else: else:
return value return value
...@@ -123,7 +136,17 @@ class OpaqueKeyField(models.CharField): ...@@ -123,7 +136,17 @@ class OpaqueKeyField(models.CharField):
return '' # CharFields should use '' as their empty value, rather than None return '' # CharFields should use '' as their empty value, rather than None
assert isinstance(value, self.KEY_CLASS), "%s is not an instance of %s" % (value, self.KEY_CLASS) assert isinstance(value, self.KEY_CLASS), "%s is not an instance of %s" % (value, self.KEY_CLASS)
return unicode(_strip_value(value)) serialized_key = unicode(_strip_value(value))
if serialized_key.endswith('\n'):
# An opaque key object serialized to a string with a trailing newline.
# Log the value - but do not modify it.
log.warning(u'{}:{}:{}:get_prep_value: Invalid key: {}.'.format(
self.model._meta.db_table, # pylint: disable=protected-access
self.name,
self.KEY_CLASS.__name__,
repr(serialized_key)
))
return serialized_key
def validate(self, value, model_instance): def validate(self, value, model_instance):
"""Validate Empty values, otherwise defer to the parent""" """Validate Empty values, otherwise defer to the parent"""
......
...@@ -4,10 +4,11 @@ Feature: LMS.Navigate Course ...@@ -4,10 +4,11 @@ Feature: LMS.Navigate Course
In order to access courseware In order to access courseware
I want to be able to navigate through the content I want to be able to navigate through the content
Scenario: I can navigate to a section # This scenario is flaky: See TNL-5315
Given I am viewing a course with multiple sections # Scenario: I can navigate to a section
When I navigate to a section # Given I am viewing a course with multiple sections
Then I see the content of the section # When I navigate to a section
# Then I see the content of the section
Scenario: I can navigate to subsections Scenario: I can navigate to subsections
Given I am viewing a section with multiple subsections Given I am viewing a section with multiple subsections
......
...@@ -89,9 +89,9 @@ from openedx.core.djangolib.js_utils import ( ...@@ -89,9 +89,9 @@ from openedx.core.djangolib.js_utils import (
<script> <script>
function setup_tabs() { function setup_tabs() {
$(".instructor-nav a").on("click", function(event) { $(".instructor-nav .btn-link").on("click", function(event) {
event.preventDefault(); event.preventDefault();
$(".instructor-nav a").removeClass("active-section"); $(".instructor-nav .btn-link").removeClass("active-section");
var section_sel = "#" + $(this).attr("data-section"); var section_sel = "#" + $(this).attr("data-section");
$("section.idash-section").hide(); $("section.idash-section").hide();
$(section_sel).show(); $(section_sel).show();
...@@ -101,12 +101,12 @@ from openedx.core.djangolib.js_utils import ( ...@@ -101,12 +101,12 @@ from openedx.core.djangolib.js_utils import (
var url = document.URL, var url = document.URL,
hashbang = url.indexOf('#!'); hashbang = url.indexOf('#!');
if (hashbang != -1) { if (hashbang != -1) {
var selector = '.instructor-nav a[data-section=' + var selector = '.instructor-nav [data-section=' +
url.substr(hashbang + 2) + ']'; url.substr(hashbang + 2) + ']';
$(selector).click(); $(selector).click();
} }
else { else {
$(".instructor-nav a").first().click(); $(".instructor-nav .btn-link").first().click();
} }
} }
......
...@@ -44,7 +44,7 @@ edx-lint==0.4.3 ...@@ -44,7 +44,7 @@ edx-lint==0.4.3
edx-django-oauth2-provider==1.1.1 edx-django-oauth2-provider==1.1.1
edx-django-sites-extensions==2.0.1 edx-django-sites-extensions==2.0.1
edx-oauth2-provider==1.1.3 edx-oauth2-provider==1.1.3
edx-opaque-keys==0.2.1 edx-opaque-keys==0.3.3
edx-organizations==0.4.1 edx-organizations==0.4.1
edx-rest-api-client==1.2.1 edx-rest-api-client==1.2.1
edx-search==0.1.2 edx-search==0.1.2
......
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