diff --git a/cms/djangoapps/contentstore/views/item.py b/cms/djangoapps/contentstore/views/item.py index 948a5a636b..2959ba1391 100644 --- a/cms/djangoapps/contentstore/views/item.py +++ b/cms/djangoapps/contentstore/views/item.py @@ -24,7 +24,7 @@ from xblock.fragment import Fragment import xmodule from xmodule.tabs import StaticTab, CourseTabList -from xmodule.modulestore import ModuleStoreEnum, PublishState +from xmodule.modulestore import ModuleStoreEnum, PublishState, EdxJSONEncoder from xmodule.modulestore.django import modulestore from xmodule.modulestore.exceptions import ItemNotFoundError, InvalidLocationError from xmodule.modulestore.inheritance import own_metadata @@ -397,7 +397,7 @@ def _save_xblock(user, xblock, data=None, children=None, metadata=None, nullout= modulestore().publish(xblock.location, user.id) # Note that children aren't being returned until we have a use case. - return JsonResponse(result) + return JsonResponse(result, encoder=EdxJSONEncoder) @login_required diff --git a/cms/templates/widgets/metadata-edit.html b/cms/templates/widgets/metadata-edit.html index 3af94c4633..3404bcaf21 100644 --- a/cms/templates/widgets/metadata-edit.html +++ b/cms/templates/widgets/metadata-edit.html @@ -5,6 +5,7 @@ import hashlib import copy import json + from xmodule.modulestore import EdxJSONEncoder hlskey = hashlib.md5(module.location.to_deprecated_string().encode('utf-8')).hexdigest() %> @@ -33,4 +34,4 @@ <%include file="source-edit.html" /> % endif -
+ diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py b/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py index 3cefa70203..2791a7e33b 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py @@ -157,7 +157,7 @@ class TestMongoModuleStore(unittest.TestCase): def test_get_courses(self): '''Make sure the course objects loaded properly''' courses = self.draft_store.get_courses() - assert_equals(len(courses), 5) + assert_equals(len(courses), 6) course_ids = [course.id for course in courses] for course_key in [ @@ -832,6 +832,55 @@ class TestMongoModuleStore(unittest.TestCase): self.assertEqual(component.published_date, published_date) self.assertEqual(component.published_by, published_by) + def test_export_course_with_peer_component(self): + """ + Test export course when link_to_location is given in peer grading interface settings. + """ + + name = "export_peer_component" + + locations = self._create_test_tree(name) + + # Insert the test block directly into the module store + problem_location = Location('edX', 'tree{}'.format(name), name, 'combinedopenended', 'test_peer_problem') + + self.draft_store.create_child( + self.dummy_user, + locations["child"], + problem_location.block_type, + block_id=problem_location.block_id + ) + + interface_location = Location('edX', 'tree{}'.format(name), name, 'peergrading', 'test_peer_interface') + + self.draft_store.create_child( + self.dummy_user, + locations["child"], + interface_location.block_type, + block_id=interface_location.block_id + ) + + self.draft_store._update_single_item( + as_draft(interface_location), + { + 'definition.data': {}, + 'metadata': { + 'link_to_location': unicode(problem_location), + 'use_for_single_location': True, + }, + }, + ) + + component = self.draft_store.get_item(interface_location) + self.assertEqual(unicode(component.link_to_location), unicode(problem_location)) + + root_dir = path(mkdtemp()) + + # export_to_xml should work. + try: + export_to_xml(self.draft_store, self.content_store, interface_location.course_key, root_dir, 'test_export') + finally: + shutil.rmtree(root_dir) class TestMongoKeyValueStore(object): diff --git a/common/test/acceptance/pages/studio/export.py b/common/test/acceptance/pages/studio/export.py index a98b54275f..df65c9e964 100644 --- a/common/test/acceptance/pages/studio/export.py +++ b/common/test/acceptance/pages/studio/export.py @@ -3,6 +3,7 @@ Course Export page. """ from .course_page import CoursePage +from utils import click_css class ExportPage(CoursePage): @@ -14,3 +15,9 @@ class ExportPage(CoursePage): def is_browser_on_page(self): return self.q(css='body.view-export').present + + def click_export_button(self): + """ + Clicks export button. + """ + click_css(self, "a.action-export") diff --git a/common/test/acceptance/tests/test_studio_with_ora_component.py b/common/test/acceptance/tests/test_studio_with_ora_component.py new file mode 100644 index 0000000000..3101468a61 --- /dev/null +++ b/common/test/acceptance/tests/test_studio_with_ora_component.py @@ -0,0 +1,74 @@ +""" +Acceptance tests for Studio related to edit/save peer grading interface. +""" + +from ..fixtures.course import XBlockFixtureDesc +from ..pages.studio.export import ExportPage +from ..pages.studio.component_editor import ComponentEditorView +from ..pages.studio.overview import CourseOutlinePage +from .base_studio_test import StudioCourseTest +from .helpers import load_data_str + + +class ORAComponentTest(StudioCourseTest): + """ + Tests tht edit/save is working correctly when link_to_location + is given in peer grading interface settings. + """ + + def setUp(self): + super(ORAComponentTest, self).setUp() + + self.course_outline_page = CourseOutlinePage( + self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run'] + ) + self.export_page = ExportPage(self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run']) + + def populate_course_fixture(self, course_fixture): + """ + Return a test course fixture containing a discussion component. + """ + + course_fixture.add_children( + XBlockFixtureDesc('chapter', 'Test Section').add_children( + XBlockFixtureDesc('sequential', 'Test Subsection').add_children( + XBlockFixtureDesc('vertical', 'Test Unit').add_children( + XBlockFixtureDesc('combinedopenended', "Peer Problem", + data=load_data_str('ora_peer_problem.xml'), metadata={'graded': True}), + XBlockFixtureDesc('peergrading', 'Peer Module'), + ) + ) + ) + ) + + def _go_to_unit_page(self, section_name='Test Section', subsection_name='Test Subsection', unit_name='Test Unit'): + self.course_outline_page.visit() + subsection = self.course_outline_page.section(section_name).subsection(subsection_name) + return subsection.toggle_expand().unit(unit_name).go_to() + + def test_edit_save_and_export(self): + """ + Ensure that edit/save is working correctly with link_to_location + in peer interface settings. + """ + self.course_outline_page.visit() + unit = self._go_to_unit_page() + peer_problem_location = unit.xblocks[1].locator + + # Problem location should contain "combinedopeneneded". + self.assertIn("combinedopenended", peer_problem_location) + component = unit.xblocks[2] + + # Interface component name should be "Peer Module". + self.assertEqual(component.name, "Peer Module") + component.edit() + component_editor = ComponentEditorView(self.browser, component.locator) + component_editor.set_field_value_and_save('Link to Problem Location', peer_problem_location) + + # Verify that we can edit component again after saving and link_to_location is present. + component.edit() + location_input_element = component_editor.get_setting_element("Link to Problem Location") + self.assertEqual( + location_input_element.get_attribute('value'), + peer_problem_location + )