Commit e2f1a71e by Victor Shnayder

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.
parent 96c4af9a
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')
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'],
}
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)
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)}
......
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):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment