-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Bug:
- Create a new Drag and Drop v2 component. Do not make any edits to it. Note its display name is "Drag and Drop"
- We heard this is also happening with open response blocks, and probably other XBlock types too.
- Copy it and paste it anywhere.
- The newly pasted Drag and Drop will have the same display_name as the parent unit it gets pasted into.
Reason:
The bug is occurring here:
The problem is with pasting the OLX, not with the copying. The copied OLX is correct (basically just <drag-and-drop-v2 />).
As has bit me before, the XBlock API as used in edx-platform has a chicken-and-egg problem: you need to have an initialized runtime in order to call xblock_class.parse_xml(node, runtime, ...) to create an XBlock, but you need to have an existing XBlock in order to correctly initialize the LMS or Studio runtimes, because they have some internal state specific to the block that's being loaded.
What we're doing to implement "paste from OLX" is loading the vertical block that will become the parent of the newly pasted drag-and-drop-v2 block, then using that parent vertical's runtime to create the child. This should be fine per the XBlock API. However, the problem is that when parse_xml calls runtime.construct_xblock_from_class(xblock_class, keys) to create a new child, the child runtime's field-data service remains bound to the parent block:
xblock_class = runtime.load_block_type("drag-and-drop-v2")
temp_xblock = runtime.construct_xblock_from_class(xblock_class, keys)
temp_xblock.runtime.service(temp_xblock, 'field-data')._authored_data._kvs._definition.definition_locator
# def-v1:6446d555e6d042b1db5d9c81+type@verticalI believe this is because the core/vanilla XBlock API assumes that a runtime can be shared among blocks, and calling temp_xblock.runtime.service(temp_xblock, 'field-data') is always supposed to give a version of the service bound to temp_xblock, not one bound to whatever block the runtime was instantiated with. In fact, even the field data API generally assumes that the KVS is not "bound" to any particular block but used by any active blocks in memory.
So arguably the current PreviewModuleSystem/Runtime has bugs in both construct_xblock_from_class (which should return a fresh XBlock instance with its own separate runtime that's correctly bound to that new XBlock) and .service() (which should never return the 'field-data' service of the parent block when the child block is explicitly passed in as the first arg).
Suggested approaches to fixing it:
- Override
construct_xblock_from_classin the runtime, so that it will create a new runtime instance for the newly constructed block. - Fix
runtime.service()in edx-platform so thatruntime.service(temp_xblock, 'field-data')cannot return the service of another block like it's doing now. - Modify
SplitMongoKVSso that it doesn't take a specific definition locator in its constructor but can work with any definition (risky) - Do a hacky patch to replace or re-bind the
field-dataservice after we callconstruct_xblock_from_class- this is probably not possible unfortunately because that happens withinparse_xmland XBlocks need to be able to overrideparse_xmlto do custom XML parsing. - Possibly use an entirely different runtime for parsing the XML and getting the field data, then use a pure modulestore API to create the new
itemand set its field data.
Also note that for some reason this doesn't seem to affect the XBlocks included with the platform (the ones that have XModule heritage); they seem to be using a different way of getting field data that doesn't involve the field-data service.