diff --git a/cms/djangoapps/contentstore/tests/test_import.py b/cms/djangoapps/contentstore/tests/test_import.py
index c373be611c..478227d386 100644
--- a/cms/djangoapps/contentstore/tests/test_import.py
+++ b/cms/djangoapps/contentstore/tests/test_import.py
@@ -198,24 +198,45 @@ class ContentStoreImportTest(ModuleStoreTestCase):
peergrading_module.link_to_location
)
- def test_rewrite_reference_value_dict(self):
+ def test_rewrite_reference_value_dict_published(self):
+ """
+ Test rewriting references in ReferenceValueDict, specifically with published content.
+ """
+ self._verify_split_test_import(
+ 'split_test_copy',
+ 'split_test_module',
+ 'split1',
+ {"0": 'sample_0', "2": 'sample_2'},
+ )
+
+ def test_rewrite_reference_value_dict_draft(self):
+ """
+ Test rewriting references in ReferenceValueDict, specifically with draft content.
+ """
+ self._verify_split_test_import(
+ 'split_test_copy_with_draft',
+ 'split_test_module_draft',
+ 'fb34c21fe64941999eaead421a8711b8',
+ {"0": '9f0941d021414798836ef140fb5f6841', "1": '0faf29473cf1497baa33fcc828b179cd'},
+ )
+
+ def _verify_split_test_import(self, target_course_name, source_course_name, split_test_name, groups_to_verticals):
module_store = modulestore()
- target_course_id = SlashSeparatedCourseKey('testX', 'split_test_copy', 'copy_run')
+ target_course_id = SlashSeparatedCourseKey('testX', target_course_name, 'copy_run')
import_from_xml(
module_store,
self.user.id,
'common/test/data/',
- ['split_test_module'],
+ [source_course_name],
target_course_id=target_course_id
)
split_test_module = module_store.get_item(
- target_course_id.make_usage_key('split_test', 'split1')
+ target_course_id.make_usage_key('split_test', split_test_name)
)
self.assertIsNotNone(split_test_module)
- self.assertEqual(
- {
- "0": target_course_id.make_usage_key('vertical', 'sample_0'),
- "2": target_course_id.make_usage_key('vertical', 'sample_2'),
- },
- split_test_module.group_id_to_child,
- )
+
+ remapped_verticals = {
+ key: target_course_id.make_usage_key('vertical', value) for key, value in groups_to_verticals.iteritems()
+ }
+
+ self.assertEqual(remapped_verticals, split_test_module.group_id_to_child)
diff --git a/common/lib/xmodule/xmodule/modulestore/xml_importer.py b/common/lib/xmodule/xmodule/modulestore/xml_importer.py
index 21be802a9d..d3f729ee2e 100644
--- a/common/lib/xmodule/xmodule/modulestore/xml_importer.py
+++ b/common/lib/xmodule/xmodule/modulestore/xml_importer.py
@@ -426,7 +426,7 @@ def _import_course_draft(
draft_course_dir = draft_dir.replace(data_dir, '', 1)
system = ImportSystem(
xmlstore=xml_module_store,
- course_id=target_course_id,
+ course_id=source_course_id,
course_dir=draft_course_dir,
error_tracker=errorlog.tracker,
parent_tracker=ParentTracker(),
@@ -480,21 +480,27 @@ def _import_course_draft(
# Not a 'hidden file', then re-raise exception
raise err
- descriptor = system.process_xml(xml)
+ # process_xml call below recursively processes all descendants. If
+ # we call this on all verticals in a course with verticals nested below
+ # the unit level, we try to import the same content twice, causing naming conflicts.
+ # Therefore only process verticals at the unit level, assuming that any other
+ # verticals must be descendants.
+ if 'index_in_children_list' in xml:
+ descriptor = system.process_xml(xml)
- # HACK: since we are doing partial imports of drafts
- # the vertical doesn't have the 'url-name' set in the
- # attributes (they are normally in the parent object,
- # aka sequential), so we have to replace the location.name
- # with the XML filename that is part of the pack
- fn, fileExtension = os.path.splitext(filename)
- descriptor.location = descriptor.location.replace(name=fn)
+ # HACK: since we are doing partial imports of drafts
+ # the vertical doesn't have the 'url-name' set in the
+ # attributes (they are normally in the parent object,
+ # aka sequential), so we have to replace the location.name
+ # with the XML filename that is part of the pack
+ fn, fileExtension = os.path.splitext(filename)
+ descriptor.location = descriptor.location.replace(name=fn)
- index = int(descriptor.xml_attributes['index_in_children_list'])
- if index in drafts:
- drafts[index].append(descriptor)
- else:
- drafts[index] = [descriptor]
+ index = int(descriptor.xml_attributes['index_in_children_list'])
+ if index in drafts:
+ drafts[index].append(descriptor)
+ else:
+ drafts[index] = [descriptor]
except Exception:
logging.exception('Error while parsing course xml.')
@@ -505,24 +511,27 @@ def _import_course_draft(
course_key = descriptor.location.course_key
try:
def _import_module(module):
+ # IMPORTANT: Be sure to update the module location in the NEW namespace
+ module_location = module.location.map_into_course(target_course_id)
# Update the module's location to DRAFT revision
# We need to call this method (instead of updating the location directly)
# to ensure that pure XBlock field data is updated correctly.
- _update_module_location(module, module.location.replace(revision=MongoRevisionKey.draft))
+ _update_module_location(module, module_location.replace(revision=MongoRevisionKey.draft))
# make sure our parent has us in its list of children
# this is to make sure private only verticals show up
# in the list of children since they would have been
- # filtered out from the non-draft store export
- if module.location.category == 'vertical':
+ # filtered out from the non-draft store export.
+ # Note though that verticals nested below the unit level will not have
+ # a parent_sequential_url and do not need special handling.
+ if module.location.category == 'vertical' and 'parent_sequential_url' in module.xml_attributes:
non_draft_location = module.location.replace(revision=MongoRevisionKey.published)
sequential_url = module.xml_attributes['parent_sequential_url']
index = int(module.xml_attributes['index_in_children_list'])
seq_location = course_key.make_usage_key_from_deprecated_string(sequential_url)
- # IMPORTANT: Be sure to update the sequential
- # in the NEW namespace
+ # IMPORTANT: Be sure to update the sequential in the NEW namespace
seq_location = seq_location.map_into_course(target_course_id)
sequential = store.get_item(seq_location, depth=0)
diff --git a/common/lib/xmodule/xmodule/xml_module.py b/common/lib/xmodule/xmodule/xml_module.py
index 4676b14159..dd710947c4 100644
--- a/common/lib/xmodule/xmodule/xml_module.py
+++ b/common/lib/xmodule/xmodule/xml_module.py
@@ -381,7 +381,7 @@ class XmlDescriptor(XModuleDescriptor):
for key, value in self.xml_attributes.items():
if key not in self.metadata_to_strip:
- xml_object.set(key, value)
+ xml_object.set(key, serialize_field(value))
if self.export_to_file():
# Write the definition to a file
diff --git a/common/test/data/split_test_module_draft/chapter/7ba42782322541d29a4d1be1eae4e4e8.xml b/common/test/data/split_test_module_draft/chapter/7ba42782322541d29a4d1be1eae4e4e8.xml
new file mode 100644
index 0000000000..29148503a9
--- /dev/null
+++ b/common/test/data/split_test_module_draft/chapter/7ba42782322541d29a4d1be1eae4e4e8.xml
@@ -0,0 +1,3 @@
+
Words of encouragement! This is a short note that most students will read.
+ +Unfortunately, the internet throws a lot of text at students, and they + do not read everything that they are given. However, many students do read all that they are + given, and so detailed explainations in this section will benefit the most concientious. + Any essential information should be extremely quickly summarized in the primary section for skimmers.
++ Students appreciate knowing that the course staff is reading what they post, and one of several ways + that you can do this is by acknowledging the star posters in your announcements. +
+