Add a fallback MalformedDescriptor
* when things don't load normally, use this * separate raw editing functionality into EditingDescriptor * raw descriptor just enforces that xml is valid * add a MalformedDescriptor that just saves a string * Fallback to it on import.
This commit is contained in:
38
common/lib/xmodule/tests/test_import.py
Normal file
38
common/lib/xmodule/tests/test_import.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from path import path
|
||||
|
||||
import unittest
|
||||
|
||||
from xmodule.x_module import XMLParsingSystem, XModuleDescriptor
|
||||
from xmodule.errorhandlers import ignore_errors_handler
|
||||
from xmodule.modulestore import Location
|
||||
|
||||
class ImportTestCase(unittest.TestCase):
|
||||
'''Make sure module imports work properly, including for malformed inputs'''
|
||||
|
||||
def test_fallback(self):
|
||||
'''Make sure that malformed xml loads as a MalformedDescriptorb.'''
|
||||
|
||||
bad_xml = '''<sequential display_name="oops"><video url="hi"></sequential>'''
|
||||
|
||||
# Shouldn't need any system params, because the initial parse should fail
|
||||
def load_item(loc):
|
||||
raise Exception("Shouldn't be called")
|
||||
|
||||
resources_fs = None
|
||||
|
||||
def process_xml(xml):
|
||||
raise Exception("Shouldn't be called")
|
||||
|
||||
|
||||
def render_template(template, context):
|
||||
raise Exception("Shouldn't be called")
|
||||
|
||||
system = XMLParsingSystem(load_item, resources_fs,
|
||||
ignore_errors_handler, process_xml)
|
||||
system.render_template = render_template
|
||||
|
||||
descriptor = XModuleDescriptor.load_from_xml(bad_xml, system, 'org', 'course',
|
||||
None)
|
||||
|
||||
self.assertEqual(descriptor.__class__.__name__,
|
||||
'MalformedDescriptor')
|
||||
24
common/lib/xmodule/xmodule/editing_module.py
Normal file
24
common/lib/xmodule/xmodule/editing_module.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from pkg_resources import resource_string
|
||||
from lxml import etree
|
||||
from xmodule.mako_module import MakoModuleDescriptor
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class EditingDescriptor(MakoModuleDescriptor):
|
||||
"""
|
||||
Module that provides a raw editing view of its data and children. It does not
|
||||
perform any validation on its definition---just passes it along to the browser.
|
||||
|
||||
This class is intended to be used as a mixin.
|
||||
"""
|
||||
mako_template = "widgets/raw-edit.html"
|
||||
|
||||
js = {'coffee': [resource_string(__name__, 'js/src/raw/edit.coffee')]}
|
||||
js_module_name = "RawDescriptor"
|
||||
|
||||
def get_context(self):
|
||||
return {
|
||||
'module': self,
|
||||
'data': self.definition['data'],
|
||||
}
|
||||
41
common/lib/xmodule/xmodule/malformed_module.py
Normal file
41
common/lib/xmodule/xmodule/malformed_module.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from pkg_resources import resource_string
|
||||
from lxml import etree
|
||||
from xmodule.mako_module import MakoModuleDescriptor
|
||||
from xmodule.xml_module import XmlDescriptor
|
||||
from xmodule.editing_module import EditingDescriptor
|
||||
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class MalformedDescriptor(EditingDescriptor):
|
||||
"""
|
||||
Module that provides a raw editing view of broken xml.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def from_xml(cls, xml_data, system, org=None, course=None):
|
||||
'''Create an instance of this descriptor from the supplied data.
|
||||
|
||||
Does not try to parse the data--just stores it.
|
||||
'''
|
||||
|
||||
# TODO (vshnayder): how does one get back from this to a valid descriptor?
|
||||
# try to parse and if successfull, send back to x_module?
|
||||
|
||||
definition = { 'data' : xml_data }
|
||||
# TODO (vshnayder): Do we need a valid slug here? Just pick a random
|
||||
# 64-bit num?
|
||||
location = ['i4x', org, course, 'malformed', 'slug']
|
||||
metadata = {} # stays in the xml_data
|
||||
|
||||
return cls(system, definition, location=location, metadata=metadata)
|
||||
|
||||
def export_to_xml(self, resource_fs):
|
||||
'''
|
||||
Export as a string wrapped in xml
|
||||
'''
|
||||
root = etree.Element('malformed')
|
||||
root.text = self.definition['data']
|
||||
return etree.tostring(root)
|
||||
|
||||
@@ -1,27 +1,17 @@
|
||||
from pkg_resources import resource_string
|
||||
from lxml import etree
|
||||
from xmodule.editing_module import EditingDescriptor
|
||||
from xmodule.mako_module import MakoModuleDescriptor
|
||||
from xmodule.xml_module import XmlDescriptor
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RawDescriptor(MakoModuleDescriptor, XmlDescriptor):
|
||||
class RawDescriptor(XmlDescriptor, EditingDescriptor):
|
||||
"""
|
||||
Module that provides a raw editing view of its data and children
|
||||
Module that provides a raw editing view of its data and children. It
|
||||
requires that the definition xml is valid.
|
||||
"""
|
||||
mako_template = "widgets/raw-edit.html"
|
||||
|
||||
js = {'coffee': [resource_string(__name__, 'js/src/raw/edit.coffee')]}
|
||||
js_module_name = "RawDescriptor"
|
||||
|
||||
def get_context(self):
|
||||
return {
|
||||
'module': self,
|
||||
'data': self.definition['data'],
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def definition_from_xml(cls, xml_object, system):
|
||||
return {'data': etree.tostring(xml_object)}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
from lxml import etree
|
||||
from lxml.etree import XMLSyntaxError
|
||||
import pkg_resources
|
||||
import logging
|
||||
from fs.errors import ResourceNotFoundError
|
||||
from functools import partial
|
||||
|
||||
from xmodule.modulestore import Location
|
||||
|
||||
from functools import partial
|
||||
|
||||
log = logging.getLogger('mitx.' + __name__)
|
||||
|
||||
@@ -443,16 +445,28 @@ class XModuleDescriptor(Plugin, HTMLSnippet):
|
||||
system is an XMLParsingSystem
|
||||
|
||||
org and course are optional strings that will be used in the generated
|
||||
modules url identifiers
|
||||
module's url identifiers
|
||||
"""
|
||||
class_ = XModuleDescriptor.load_class(
|
||||
etree.fromstring(xml_data).tag,
|
||||
default_class
|
||||
)
|
||||
# leave next line, commented out - useful for low-level debugging
|
||||
# log.debug('[XModuleDescriptor.load_from_xml] tag=%s, class_=%s' % (
|
||||
# etree.fromstring(xml_data).tag,class_))
|
||||
return class_.from_xml(xml_data, system, org, course)
|
||||
try:
|
||||
class_ = XModuleDescriptor.load_class(
|
||||
etree.fromstring(xml_data).tag,
|
||||
default_class
|
||||
)
|
||||
# leave next line, commented out - useful for low-level debugging
|
||||
# log.debug('[XModuleDescriptor.load_from_xml] tag=%s, class_=%s' % (
|
||||
# etree.fromstring(xml_data).tag,class_))
|
||||
|
||||
descriptor = class_.from_xml(xml_data, system, org, course)
|
||||
except (ResourceNotFoundError, XMLSyntaxError) as err:
|
||||
# Didn't load properly. Fall back on loading as a malformed
|
||||
# descriptor. This should never error due to formatting.
|
||||
|
||||
# Put import here to avoid circular import errors
|
||||
from xmodule.malformed_module import MalformedDescriptor
|
||||
|
||||
descriptor = MalformedDescriptor.from_xml(xml_data, system, org, course)
|
||||
|
||||
return descriptor
|
||||
|
||||
@classmethod
|
||||
def from_xml(cls, xml_data, system, org=None, course=None):
|
||||
|
||||
Reference in New Issue
Block a user