From 2ca1d0ef78825673ff8b956d53c3ae68acafabf7 Mon Sep 17 00:00:00 2001 From: Syed Hassan Raza Date: Fri, 12 Jun 2015 20:29:49 +0500 Subject: [PATCH 1/7] fix the source_version xblock after discard changes formatting changes --- .../xmodule/modulestore/split_mongo/split.py | 12 +++- .../tests/test_mixed_modulestore.py | 69 +++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py index 1dedc2f394..b8e425eb27 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py @@ -1875,6 +1875,11 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): block_data.fields = settings new_id = new_structure['_id'] + + # source_version records which revision a block was copied from. In this method, we're updating + # the block, so it's no longer a direct copy, and we can remove the source_version reference. + block_data.edit_info.source_version = None + self.version_block(block_data, user_id, new_id) self.update_structure(course_key, new_structure) # update the index entry if appropriate @@ -2969,8 +2974,11 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): if getattr(destination_block.edit_info, key) is None: setattr(destination_block.edit_info, key, val) - # introduce new edit info field for tracing where copied/published blocks came - destination_block.edit_info.source_version = new_block.edit_info.update_version + # If the block we are copying from was itself a copy, then just + # reference the original source, rather than the copy. + destination_block.edit_info.source_version = ( + new_block.edit_info.source_version or new_block.edit_info.update_version + ) if blacklist != EXCLUDE_ALL: for child in destination_block.fields.get('children', []): diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py b/common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py index e83c9be516..c490b0f614 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py @@ -523,6 +523,75 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup): component = self.store.publish(component.location, self.user_id) self.assertFalse(self.store.has_changes(component)) + @ddt.data('draft', 'split') + def test_unit_stuck_in_draft_mode(self, default_ms): + """ + After revert_to_published() the has_changes() should return false if draft has no changes + """ + self.initdb(default_ms) + + test_course = self.store.create_course('testx', 'GreekHero', 'test_run', self.user_id) + + # Create a dummy component to test against + xblock = self.store.create_item( + self.user_id, + test_course.id, + 'vertical', + block_id='test_vertical' + ) + + # Not yet published, so changes are present + self.assertTrue(self.store.has_changes(xblock)) + + # Publish and verify that there are no unpublished changes + component = self.store.publish(xblock.location, self.user_id) + self.assertFalse(self.store.has_changes(component)) + + self.store.revert_to_published(component.location, self.user_id) + component = self.store.get_item(component.location) + self.assertFalse(self.store.has_changes(component)) + + # Publish and verify again + component = self.store.publish(component.location, self.user_id) + self.assertFalse(self.store.has_changes(component)) + + @ddt.data('draft', 'split') + def test_unit_stuck_in_published_mode(self, default_ms): + """ + After revert_to_published() the has_changes() should return true if draft has changes + """ + self.initdb(default_ms) + + test_course = self.store.create_course('testx', 'GreekHero', 'test_run', self.user_id) + + # Create a dummy component to test against + xblock = self.store.create_item( + self.user_id, + test_course.id, + 'vertical', + block_id='test_vertical' + ) + + # Not yet published, so changes are present + self.assertTrue(self.store.has_changes(xblock)) + + # Publish and verify that there are no unpublished changes + component = self.store.publish(xblock.location, self.user_id) + self.assertFalse(self.store.has_changes(component)) + + # Discard changes and verify that there are no changes + self.store.revert_to_published(component.location, self.user_id) + component = self.store.get_item(component.location) + self.assertFalse(self.store.has_changes(component)) + + # Change the component, then check that there now are changes + component = self.store.get_item(component.location) + component.display_name = 'Changed Display Name' + self.store.update_item(component, self.user_id) + + # Verify that changes are present + self.assertTrue(self.store.has_changes(component)) + def setup_has_changes(self, default_ms): """ Common set up for has_changes tests below. From 4632a07c0725525310f4f64c92d3d76a8014352a Mon Sep 17 00:00:00 2001 From: Will Daly Date: Mon, 29 Jun 2015 09:23:15 -0700 Subject: [PATCH 2/7] Fix login/logout errors caused by unicode cookie names --- common/djangoapps/student/cookies.py | 15 ++++++++++++--- common/djangoapps/student/tests/test_login.py | 15 +++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/common/djangoapps/student/cookies.py b/common/djangoapps/student/cookies.py index c595157706..694e06f720 100644 --- a/common/djangoapps/student/cookies.py +++ b/common/djangoapps/student/cookies.py @@ -65,7 +65,12 @@ def set_logged_in_cookies(request, response, user): # is logged in. This is just a boolean value, so it's not very useful. # In the future, we should be able to replace this with the "user info" # cookie set below. - response.set_cookie(settings.EDXMKTG_LOGGED_IN_COOKIE_NAME, 'true', secure=None, **cookie_settings) + response.set_cookie( + settings.EDXMKTG_LOGGED_IN_COOKIE_NAME.encode('utf-8'), + 'true', + secure=None, + **cookie_settings + ) # Set a cookie with user info. This can be used by external sites # to customize content based on user information. Currently, @@ -107,7 +112,7 @@ def set_logged_in_cookies(request, response, user): user_info_cookie_is_secure = request.is_secure() response.set_cookie( - settings.EDXMKTG_USER_INFO_COOKIE_NAME, + settings.EDXMKTG_USER_INFO_COOKIE_NAME.encode('utf-8'), json.dumps(user_info), secure=user_info_cookie_is_secure, **cookie_settings @@ -128,7 +133,11 @@ def delete_logged_in_cookies(response): """ for cookie_name in [settings.EDXMKTG_LOGGED_IN_COOKIE_NAME, settings.EDXMKTG_USER_INFO_COOKIE_NAME]: - response.delete_cookie(cookie_name, path='/', domain=settings.SESSION_COOKIE_DOMAIN) + response.delete_cookie( + cookie_name.encode('utf-8'), + path='/', + domain=settings.SESSION_COOKIE_DOMAIN + ) return response diff --git a/common/djangoapps/student/tests/test_login.py b/common/djangoapps/student/tests/test_login.py index 1f4a8bcb06..4a9aa7f58c 100644 --- a/common/djangoapps/student/tests/test_login.py +++ b/common/djangoapps/student/tests/test_login.py @@ -6,6 +6,7 @@ import unittest from django.test import TestCase from django.test.client import Client +from django.test.utils import override_settings from django.conf import settings from django.core.cache import cache from django.core.urlresolvers import reverse, NoReverseMatch @@ -195,6 +196,20 @@ class LoginTest(TestCase): cookie = self.client.cookies[cookie_name] self.assertIn("01-Jan-1970", cookie.get('expires')) + @override_settings( + EDXMKTG_LOGGED_IN_COOKIE_NAME=u"unicode-logged-in", + EDXMKTG_USER_INFO_COOKIE_NAME=u"unicode-user-info", + ) + def test_unicode_mktg_cookie_names(self): + # When logged in cookie names are loaded from JSON files, they may + # have type `unicode` instead of `str`, which can cause errors + # when calling Django cookie manipulation functions. + response, _ = self._login_response('test@edx.org', 'test_password') + self._assert_response(response, success=True) + + response = self.client.post(reverse('logout')) + self.assertRedirects(response, "/") + @patch.dict("django.conf.settings.FEATURES", {'SQUELCH_PII_IN_LOGS': True}) def test_logout_logging_no_pii(self): response, _ = self._login_response('test@edx.org', 'test_password') From 61431015c2d6f5cba6ee9087f5fba3ac0ba077f5 Mon Sep 17 00:00:00 2001 From: Adam Palay Date: Mon, 22 Jun 2015 17:19:53 -0400 Subject: [PATCH 3/7] update xblock only to mark field values as dirty if they've changed (TNL-2475) force save "download_video" field if not set set timezone to UTC explicitly --- .../contentstore/tests/test_utils.py | 2 +- .../xmodule/modulestore/xml_importer.py | 8 ++--- .../tests/test_delay_between_attempts.py | 36 +++++++++---------- .../xmodule/video_module/video_module.py | 3 +- .../tests/test_submitting_problems.py | 28 ++++++++++++++- requirements/edx/github.txt | 2 +- 6 files changed, 52 insertions(+), 27 deletions(-) diff --git a/cms/djangoapps/contentstore/tests/test_utils.py b/cms/djangoapps/contentstore/tests/test_utils.py index c29231b2ea..194e5859d2 100644 --- a/cms/djangoapps/contentstore/tests/test_utils.py +++ b/cms/djangoapps/contentstore/tests/test_utils.py @@ -154,7 +154,7 @@ class XBlockVisibilityTestCase(ModuleStoreTestCase): super(XBlockVisibilityTestCase, self).setUp() self.dummy_user = ModuleStoreEnum.UserID.test - self.past = datetime(1970, 1, 1) + self.past = datetime(1970, 1, 1, tzinfo=UTC) self.future = datetime.now(UTC) + timedelta(days=1) self.course = CourseFactory.create() diff --git a/common/lib/xmodule/xmodule/modulestore/xml_importer.py b/common/lib/xmodule/xmodule/modulestore/xml_importer.py index dd9fb95cf1..9aa0e08432 100644 --- a/common/lib/xmodule/xmodule/modulestore/xml_importer.py +++ b/common/lib/xmodule/xmodule/modulestore/xml_importer.py @@ -1182,9 +1182,7 @@ def _update_module_location(module, new_location): # in which one component of the key is the XBlock's location (equivalent to "scope_ids"). # Since we've changed the XBlock's location, we need to re-save # all the XBlock's fields so they will be stored using the new location in the key. - # However, since XBlocks only save "dirty" fields, we need to first - # explicitly set each field to its current value before triggering the save. + # However, since XBlocks only save "dirty" fields, we need to call + # XBlock's `force_save_fields_method` if len(rekey_fields) > 0: - for rekey_field_name in rekey_fields: - setattr(module, rekey_field_name, getattr(module, rekey_field_name)) - module.save() + module.force_save_fields(rekey_fields) diff --git a/common/lib/xmodule/xmodule/tests/test_delay_between_attempts.py b/common/lib/xmodule/xmodule/tests/test_delay_between_attempts.py index 9a239f0860..7deabf8756 100644 --- a/common/lib/xmodule/xmodule/tests/test_delay_between_attempts.py +++ b/common/lib/xmodule/xmodule/tests/test_delay_between_attempts.py @@ -188,9 +188,9 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase): num_attempts = 1 (module, result) = self.create_and_check( num_attempts=num_attempts, - last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36), + last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), submission_wait_seconds=180, - considered_now=datetime.datetime(2013, 12, 6, 0, 18, 36) + considered_now=datetime.datetime(2013, 12, 6, 0, 18, 36, tzinfo=UTC) ) # You should get a dialog that tells you to wait 2 minutes # Also, the number of attempts should not be incremented @@ -202,9 +202,9 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase): num_attempts = 1 (module, result) = self.create_and_check( num_attempts=num_attempts, - last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36), + last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), submission_wait_seconds=180, - considered_now=datetime.datetime(2013, 12, 6, 0, 20, 35) + considered_now=datetime.datetime(2013, 12, 6, 0, 20, 35, tzinfo=UTC) ) # You should get a dialog that tells you to wait 2 minutes # Also, the number of attempts should not be incremented @@ -216,9 +216,9 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase): num_attempts = 1 (module, result) = self.create_and_check( num_attempts=num_attempts, - last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36), + last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), submission_wait_seconds=180, - considered_now=datetime.datetime(2013, 12, 6, 0, 20, 36) + considered_now=datetime.datetime(2013, 12, 6, 0, 20, 36, tzinfo=UTC) ) # Successfully submitted and answered # Also, the number of attempts should increment by 1 @@ -230,9 +230,9 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase): num_attempts = 1 (module, result) = self.create_and_check( num_attempts=num_attempts, - last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36), + last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), submission_wait_seconds=180, - considered_now=datetime.datetime(2013, 12, 6, 0, 24, 0) + considered_now=datetime.datetime(2013, 12, 6, 0, 24, 0, tzinfo=UTC) ) # Successfully submitted and answered # Also, the number of attempts should increment by 1 @@ -246,17 +246,17 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase): with self.assertRaises(xmodule.exceptions.NotFoundError): (module, unused_result) = self.create_and_check( num_attempts=num_attempts, - last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36), + last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), submission_wait_seconds=180, - considered_now=datetime.datetime(2013, 12, 6, 0, 24, 0) + considered_now=datetime.datetime(2013, 12, 6, 0, 24, 0, tzinfo=UTC) ) # Now try it without the check_problem (module, unused_result) = self.create_and_check( num_attempts=num_attempts, - last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36), + last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), submission_wait_seconds=180, - considered_now=datetime.datetime(2013, 12, 6, 0, 24, 0), + considered_now=datetime.datetime(2013, 12, 6, 0, 24, 0, tzinfo=UTC), skip_check_problem=True ) # Expect that number of attempts NOT incremented @@ -267,9 +267,9 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase): num_attempts = 1 (module, result) = self.create_and_check( num_attempts=num_attempts, - last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36), + last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), submission_wait_seconds=60 * 60 * 2, - considered_now=datetime.datetime(2013, 12, 6, 2, 15, 35) + considered_now=datetime.datetime(2013, 12, 6, 2, 15, 35, tzinfo=UTC) ) # You should get a dialog that tells you to wait 2 minutes # Also, the number of attempts should not be incremented @@ -281,9 +281,9 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase): num_attempts = 1 (module, result) = self.create_and_check( num_attempts=num_attempts, - last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36), + last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), submission_wait_seconds=60 * 60 * 2 + 63, - considered_now=datetime.datetime(2013, 12, 6, 1, 15, 40) + considered_now=datetime.datetime(2013, 12, 6, 1, 15, 40, tzinfo=UTC) ) # You should get a dialog that tells you to wait 2 minutes # Also, the number of attempts should not be incremented @@ -295,9 +295,9 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase): num_attempts = 1 (module, result) = self.create_and_check( num_attempts=num_attempts, - last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36), + last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), submission_wait_seconds=60, - considered_now=datetime.datetime(2013, 12, 6, 0, 17, 36) + considered_now=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC) ) # You should get a dialog that tells you to wait 2 minutes # Also, the number of attempts should not be incremented diff --git a/common/lib/xmodule/xmodule/video_module/video_module.py b/common/lib/xmodule/xmodule/video_module/video_module.py index 72c8c82aba..fc76bb6c0c 100644 --- a/common/lib/xmodule/xmodule/video_module/video_module.py +++ b/common/lib/xmodule/xmodule/video_module/video_module.py @@ -381,9 +381,10 @@ class VideoDescriptor(VideoFields, VideoTranscriptsMixin, VideoStudioViewHandler if not self.fields['download_video'].is_set_on(self): self.download_video = True - # Set download_video field to default value if its not explicitly set for backward compatibility. + # Force download_video field to default value if it's not explicitly set for backward compatibility. if not self.fields['download_video'].is_set_on(self): self.download_video = self.download_video + self.force_save_fields(['download_video']) # for backward compatibility. # If course was existed and was not re-imported by the moment of adding `download_track` field, diff --git a/lms/djangoapps/courseware/tests/test_submitting_problems.py b/lms/djangoapps/courseware/tests/test_submitting_problems.py index 34fa3d8161..dab16c4657 100644 --- a/lms/djangoapps/courseware/tests/test_submitting_problems.py +++ b/lms/djangoapps/courseware/tests/test_submitting_problems.py @@ -18,7 +18,7 @@ from capa.tests.response_xml_factory import ( CodeResponseXMLFactory, ) from courseware import grades -from courseware.models import StudentModule +from courseware.models import StudentModule, StudentModuleHistory from courseware.tests.helpers import LoginEnrollmentTestCase from lms.djangoapps.lms_xblock.runtime import quote_slashes from student.tests.factories import UserFactory @@ -426,6 +426,32 @@ class TestCourseGrader(TestSubmittingProblems): ) self.assertEqual(json.loads(resp.content).get("success"), err_msg) + def test_show_answer_doesnt_write_to_csm(self): + self.basic_setup() + self.submit_question_answer('p1', {'2_1': u'Correct'}) + + # Now fetch the state entry for that problem. + student_module = StudentModule.objects.get( + course_id=self.course.id, + student=self.student_user + ) + # count how many state history entries there are + baseline = StudentModuleHistory.objects.filter( + student_module=student_module + ) + baseline_count = baseline.count() + self.assertEqual(baseline_count, 3) + + # now click "show answer" + self.show_question_answer('p1') + + # check that we don't have more state history entries + csmh = StudentModuleHistory.objects.filter( + student_module=student_module + ) + current_count = csmh.count() + self.assertEqual(current_count, 3) + def test_none_grade(self): """ Check grade is 0 to begin with. diff --git a/requirements/edx/github.txt b/requirements/edx/github.txt index 8926e31496..82f790cdad 100644 --- a/requirements/edx/github.txt +++ b/requirements/edx/github.txt @@ -32,7 +32,7 @@ git+https://github.com/hmarr/django-debug-toolbar-mongo.git@b0686a76f1ce3532088c -e git+https://github.com/jazkarta/ccx-keys.git@e6b03704b1bb97c1d2f31301ecb4e3a687c536ea#egg=ccx-keys # Our libraries: --e git+https://github.com/edx/XBlock.git@e1831fa86bff778ffe1308e00d8ed51b26f7c047#egg=XBlock +-e git+https://github.com/edx/XBlock.git@9fc3367b64a7baa4986f5681ea588d0ffe724a0f#egg=XBlock -e git+https://github.com/edx/codejail.git@6b17c33a89bef0ac510926b1d7fea2748b73aadd#egg=codejail -e git+https://github.com/edx/js-test-tool.git@v0.1.6#egg=js_test_tool -e git+https://github.com/edx/event-tracking.git@0.2.0#egg=event-tracking From 4e9db252815c82343d4aeb827324555d58baa448 Mon Sep 17 00:00:00 2001 From: Braden MacDonald Date: Mon, 29 Jun 2015 13:12:16 -0700 Subject: [PATCH 4/7] Fix: users without course creation permission were not shown new library form --- cms/static/js/index.js | 2 + cms/templates/index.html | 88 ++++++++++++++++++++-------------------- 2 files changed, 46 insertions(+), 44 deletions(-) diff --git a/cms/static/js/index.js b/cms/static/js/index.js index e1c91d03a1..e4470ea22c 100644 --- a/cms/static/js/index.js +++ b/cms/static/js/index.js @@ -141,6 +141,8 @@ define(["domReady", "jquery", "underscore", "js/utils/cancel_on_escape", "js/vie e.preventDefault(); $('.courses-tab').toggleClass('active', tab === 'courses'); $('.libraries-tab').toggleClass('active', tab === 'libraries'); + // Also toggle this course-related notice shown below the course tab, if it is present: + $('.wrapper-creationrights').toggleClass('is-hidden', tab === 'libraries'); }; }; diff --git a/cms/templates/index.html b/cms/templates/index.html index c4ca6e0685..d8fe4389dd 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -103,57 +103,57 @@ - %if libraries_enabled: -
-
-
- + % endif + + %if libraries_enabled and show_new_library_button: +
+ +
+ +
-
-

${_("Create a New Library")}

+
+

${_("Create a New Library")}

-
- ${_("Required Information to Create a New Library")} +
+ ${_("Required Information to Create a New Library")} -
    -
  1. - - ## Translators: This is an example name for a new content library, seen when filling out the form to create a new library. (A library is a collection of content or problems.) - - ${_("The public display name for your library.")} - -
  2. -
  3. - - - ${_("The public organization name for your library.")} ${_("This cannot be changed.")} - -
  4. +
      +
    1. + + ## Translators: This is an example name for a new content library, seen when filling out the form to create a new library. (A library is a collection of content or problems.) + + ${_("The public display name for your library.")} + +
    2. +
    3. + + + ${_("The public organization name for your library.")} ${_("This cannot be changed.")} + +
    4. -
    5. - - ## Translators: This is an example for the "code" used to identify a library, seen when filling out the form to create a new library. This example is short for "Computer Science Problems". The example number may contain letters but must not contain spaces. - - ${_("The unique code that identifies this library.")} ${_("Note: This is part of your library URL, so no spaces or special characters are allowed.")} ${_("This cannot be changed.")} - -
    6. -
    +
  5. + + ## Translators: This is an example for the "code" used to identify a library, seen when filling out the form to create a new library. This example is short for "Computer Science Problems". The example number may contain letters but must not contain spaces. + + ${_("The unique code that identifies this library.")} ${_("Note: This is part of your library URL, so no spaces or special characters are allowed.")} ${_("This cannot be changed.")} + +
  6. +
-
-
- -
- - - -
- -
- % endif + +
+
+ + + +
+ +
% endif From e3080e6d0dde25a8abae31fa4c0d27e605954256 Mon Sep 17 00:00:00 2001 From: Frances Botsford Date: Tue, 30 Jun 2015 16:17:00 -0400 Subject: [PATCH 5/7] give the header the right height in the verify flow ECOM-1808 --- lms/static/sass/views/_verification.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lms/static/sass/views/_verification.scss b/lms/static/sass/views/_verification.scss index d108f5cf20..03d939f3ca 100644 --- a/lms/static/sass/views/_verification.scss +++ b/lms/static/sass/views/_verification.scss @@ -113,6 +113,11 @@ padding-bottom: ($baseline +1); } + // HACK: fix global header height in verification flow ECOM-1808 + header.global { + height: 76px; + } + // HACK: nasty override due to our bad input/button styling button, input[type="submit"], input[type="button"], button[type="submit"] { @include font-size(16); From 6b70785c120ea529311841c20047838cc0f0efcb Mon Sep 17 00:00:00 2001 From: Kyle McCormick Date: Tue, 30 Jun 2015 16:03:43 -0400 Subject: [PATCH 6/7] TNL-2623 Fix bug where status icons were hidden in image response problems --- .../lib/xmodule/xmodule/css/capa/display.scss | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/common/lib/xmodule/xmodule/css/capa/display.scss b/common/lib/xmodule/xmodule/css/capa/display.scss index 02e4b45ebf..e29d186286 100644 --- a/common/lib/xmodule/xmodule/css/capa/display.scss +++ b/common/lib/xmodule/xmodule/css/capa/display.scss @@ -16,6 +16,7 @@ // * +Problem - Rubric // * +Problem - Annotation // * +Problem - Choice Text Group +// * +Problem - Image Input Overrides // +Variables - Capa // ==================== @@ -1350,3 +1351,24 @@ div.problem { } } } + +// +Problem - Image Input Overrides +// ==================== + +// NOTE: temporary override until image inputs use same base html structure as other common capa input types. +div.problem .imageinput.capa_inputtype { + + .status { + display: inline-block; + position: relative; + top: 3px; + width: 25px; + height: 20px; + } + .correct { + background: url('../images/correct-icon.png') center center no-repeat; + } + .incorrect { + background: url('../images/incorrect-icon.png') center center no-repeat; + } +} From 28a234380a2265e6bf6d6b0ffbc86dc0ea750023 Mon Sep 17 00:00:00 2001 From: Sarina Canelake Date: Tue, 30 Jun 2015 11:17:32 -0400 Subject: [PATCH 7/7] Dark lang middleware: Check if user is authenticated Check if the request's user is authenticated before attempting to get their user preferences. Otherwise, log warnings appear indicating the UserNotFound error was caught. --- common/djangoapps/dark_lang/middleware.py | 38 ++++++++++++++--------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/common/djangoapps/dark_lang/middleware.py b/common/djangoapps/dark_lang/middleware.py index 2d37e4e316..868ab05708 100644 --- a/common/djangoapps/dark_lang/middleware.py +++ b/common/djangoapps/dark_lang/middleware.py @@ -17,7 +17,6 @@ from dark_lang.models import DarkLangConfig from openedx.core.djangoapps.user_api.preferences.api import ( delete_user_preference, get_user_preference, set_user_preference ) -from openedx.core.djangoapps.user_api.errors import UserNotFound from lang_pref import LANGUAGE_KEY # TODO re-import this once we're on Django 1.5 or greater. [PLAT-671] @@ -133,27 +132,36 @@ class DarkLangMiddleware(object): and that language doesn't appear in ``self.released_langs``, then set the session LANGUAGE_SESSION_KEY to that language. """ + auth_user = request.user.is_authenticated() if 'clear-lang' in request.GET: - # Reset dark lang - delete_user_preference(request.user, DARK_LANGUAGE_KEY) - # Reset user's language to their language preference, if they have one - user_pref = get_user_preference(request.user, LANGUAGE_KEY) - if user_pref: - request.session[LANGUAGE_SESSION_KEY] = user_pref - elif LANGUAGE_SESSION_KEY in request.session: + # delete the session language key (if one is set) + if LANGUAGE_SESSION_KEY in request.session: del request.session[LANGUAGE_SESSION_KEY] + + if auth_user: + # Reset user's dark lang preference to null + delete_user_preference(request.user, DARK_LANGUAGE_KEY) + # Get & set user's preferred language + user_pref = get_user_preference(request.user, LANGUAGE_KEY) + if user_pref: + request.session[LANGUAGE_SESSION_KEY] = user_pref return + # Get the user's preview lang - this is either going to be set from a query + # param `?preview-lang=xx`, or we may have one already set as a dark lang preference. preview_lang = request.GET.get('preview-lang', None) - if not preview_lang: - try: - # Try to get the request user's preference (might not have a user, though) - preview_lang = get_user_preference(request.user, DARK_LANGUAGE_KEY) - except UserNotFound: - return + if not preview_lang and auth_user: + # Get the request user's dark lang preference + preview_lang = get_user_preference(request.user, DARK_LANGUAGE_KEY) + # User doesn't have a dark lang preference, so just return if not preview_lang: return + # Set the session key to the requested preview lang request.session[LANGUAGE_SESSION_KEY] = preview_lang - set_user_preference(request.user, DARK_LANGUAGE_KEY, preview_lang) + + # Make sure that we set the requested preview lang as the dark lang preference for the + # user, so that the lang_pref middleware doesn't clobber away the dark lang preview. + if auth_user: + set_user_preference(request.user, DARK_LANGUAGE_KEY, preview_lang)