Skip to content

Commit d61fe48

Browse files
committed
chore: add parse_xml method to import block properly with pointer tags
- extracted necessary implementation from `XmlMixin` here
1 parent 0db139a commit d61fe48

File tree

2 files changed

+398
-0
lines changed

2 files changed

+398
-0
lines changed

xblocks_contrib/lti/lti.py

+80
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import base64
5959
import datetime
6060
import hashlib
61+
import json
6162
import logging
6263
import markupsafe
6364
import textwrap
@@ -77,6 +78,8 @@
7778
from web_fragments.fragment import Fragment
7879
from xblock.core import List, Scope, String, XBlock
7980
from xblock.fields import Boolean, Float, UserScope
81+
from xblock.runtime import KvsFieldData
82+
8083
try:
8184
from xblock.utils.resources import ResourceLoader
8285
from xblock.utils.studio_editable import StudioEditableXBlockMixin
@@ -85,6 +88,7 @@
8588
from xblockutils.studio_editable import StudioEditableXBlockMixin
8689

8790
from .lti_2_util import LTI20BlockMixin, LTIError
91+
from .xml_mixin import InheritanceKeyValueStore, XmlMixin, is_pointer_tag
8892

8993
# The anonymous user ID for the user in the course.
9094
ATTR_KEY_ANONYMOUS_USER_ID = 'edx-platform.anonymous_user_id'
@@ -296,6 +300,7 @@ class LTIBlock(
296300
LTIFields,
297301
LTI20BlockMixin,
298302
StudioEditableXBlockMixin,
303+
XmlMixin,
299304
XBlock,
300305
): # pylint: disable=abstract-method
301306
"""
@@ -1055,3 +1060,78 @@ def bind_for_student(self, user_id, wrappers=None):
10551060

10561061
# Optionally save the state if needed
10571062
self.save()
1063+
1064+
@classmethod
1065+
def parse_xml(cls, node, runtime, keys): # pylint: disable=too-many-statements
1066+
"""
1067+
Use `node` to construct a new block.
1068+
1069+
Arguments:
1070+
node (etree.Element): The xml node to parse into an xblock.
1071+
1072+
runtime (:class:`.Runtime`): The runtime to use while parsing.
1073+
1074+
keys (:class:`.ScopeIds`): The keys identifying where this block
1075+
will store its data.
1076+
1077+
Returns (XBlock): The newly parsed XBlock
1078+
1079+
"""
1080+
1081+
# VS[compat]
1082+
# In 2012, when the platform didn't have CMS, and all courses were handwritten XML files, problem tags
1083+
# contained XML problem descriptions withing themselves. Later, when Studio has been created, and "pointer" tags
1084+
# became the preferred problem format, edX has to add this compatibility code to 1) support both pre- and
1085+
# post-Studio course formats simulteneously, and 2) be able to migrate 2012-fall courses to Studio. Old style
1086+
# support supposed to be removed, but the deprecation process have never been initiated, so this
1087+
# compatibility must stay, probably forever.
1088+
if is_pointer_tag(node):
1089+
# new style:
1090+
# read the actual definition file--named using url_name.replace(':','/')
1091+
definition_xml, filepath = cls.load_definition_xml(node, runtime, keys.def_id)
1092+
aside_children = runtime.parse_asides(definition_xml, keys.def_id, keys.usage_id, runtime.id_generator)
1093+
else:
1094+
filepath = None
1095+
definition_xml = node
1096+
1097+
# Note: removes metadata.
1098+
definition, children = cls.load_definition(definition_xml, runtime, keys.def_id, runtime.id_generator)
1099+
1100+
# VS[compat]
1101+
# Make Ike's github preview links work in both old and new file layouts.
1102+
if is_pointer_tag(node):
1103+
# new style -- contents actually at filepath
1104+
definition['filename'] = [filepath, filepath]
1105+
1106+
metadata = cls.load_metadata(definition_xml)
1107+
1108+
# move definition metadata into dict
1109+
dmdata = definition.get('definition_metadata', '')
1110+
if dmdata:
1111+
metadata['definition_metadata_raw'] = dmdata
1112+
try:
1113+
metadata.update(json.loads(dmdata))
1114+
except Exception as err: # lint-amnesty, pylint: disable=broad-except
1115+
log.debug('Error in loading metadata %r', dmdata, exc_info=True)
1116+
metadata['definition_metadata_err'] = str(err)
1117+
1118+
definition_aside_children = definition.pop('aside_children', None)
1119+
if definition_aside_children:
1120+
aside_children.extend(definition_aside_children)
1121+
1122+
# Set/override any metadata specified by policy
1123+
cls.apply_policy(metadata, runtime.get_policy(keys.usage_id))
1124+
1125+
field_data = {**metadata, **definition, "children": children}
1126+
field_data['xml_attributes']['filename'] = definition.get('filename', ['', None]) # for git link
1127+
if "filename" in field_data:
1128+
del field_data["filename"] # filename should only be in xml_attributes.
1129+
1130+
# we shouldn't be instantiating our own field data instance here, but there are complex inter-depenencies
1131+
# between this mixin and ImportSystem that currently seem to require it for proper metadata inheritance.
1132+
kvs = InheritanceKeyValueStore(initial_values=field_data)
1133+
field_data = KvsFieldData(kvs)
1134+
1135+
# super().parse_xml(cls, keys, field_data)
1136+
xblock = runtime.construct_xblock_from_class(cls, keys, field_data)
1137+
return xblock

0 commit comments

Comments
 (0)