class Blocklike(...):
@classmethod
def parse_xml(cls, node, runtime, keys):
"""
Use `node` to construct a new block.
Arguments:
node (:class:`~xml.etree.ElementTree.Element`): The xml node to parse into an xblock.
runtime (:class:`.Runtime`): The runtime to use while parsing.
keys (:class:`.ScopeIds`): The keys identifying where this block
will store its data.
"""
block = runtime.construct_xblock_from_class(cls, keys)
# The base implementation: child nodes become child blocks.
# Or fields, if they belong to the right namespace.
for child in node:
if child.tag is etree.Comment:
continue
qname = etree.QName(child)
tag = qname.localname
namespace = qname.namespace
if namespace == XML_NAMESPACES["option"]:
cls._set_field_if_present(block, tag, child.text, child.attrib)
else:
block.runtime.add_node_as_child(block, child)
# Attributes become fields.
for name, value in list(node.items()): # lxml has no iteritems
cls._set_field_if_present(block, name, value, {})
# Text content becomes "content", if such a field exists.
if "content" in block.fields and block.fields["content"].scope == Scope.content:
text = node.text
if text:
text = text.strip()
if text:
block.content = text
return block
...
def add_xml_to_node(self, node):
"""
For exporting, set data on `node` from ourselves.
"""
# pylint: disable=E1101
# Set node.tag based on our class name.
node.tag = self.xml_element_name()
node.set('xblock-family', self.entry_point)
# Set node attributes based on our fields.
for field_name, field in list(self.fields.items()):
if field_name in ('children', 'parent', 'content'):
continue
if field.is_set_on(self) or field.force_export:
self._add_field(node, field_name, field)
# A content field becomes text content.
text = self.xml_text_content()
if text is not None:
node.text = text
def xml_element_name(self):
"""
What XML element name should be used for this block?
"""
return self.scope_ids.block_type
def xml_text_content(self):
"""
What is the text content for this block's XML node?
"""
if 'content' in self.fields and self.content: # pylint: disable=unsupported-membership-test
return self.content
else:
return None
def _add_field(self, node, field_name, field):
"""
Add xml representation of field to node.
Depending on settings, it either stores the value of field
as an xml attribute or creates a separate child node.
"""
value = field.to_string(field.read_from(self))
text_value = "" if value is None else value
# Is the field type supposed to serialize the fact that the value is None to XML?
save_none_as_xml_attr = field.none_to_xml and value is None
field_attrs = {"none": "true"} if save_none_as_xml_attr else {}
if save_none_as_xml_attr or field.xml_node:
# Field will be output to XML as an separate element.
tag = etree.QName(XML_NAMESPACES["option"], field_name)
elem = etree.SubElement(node, tag, field_attrs)
if field.xml_node:
# Only set the value if forced via xml_node;
# in all other cases, the value is None.
# Avoids an unnecessary XML end tag.
elem.text = text_value
else:
# Field will be output to XML as an attribute on the node.
node.set(field_name, text_value)
...
class XBlock(Blocklike, ...):
... |