Commit eebc524c by Calen Pennington

Merge pull request #909 from cpennington/xmodule-mixin

Pull XModule attributes out into a mixin that can be applied to xblocks
parents f27af194 d77491e4
...@@ -31,6 +31,7 @@ from path import path ...@@ -31,6 +31,7 @@ from path import path
from lms.xblock.mixin import LmsBlockMixin from lms.xblock.mixin import LmsBlockMixin
from cms.xmodule_namespace import CmsBlockMixin from cms.xmodule_namespace import CmsBlockMixin
from xmodule.modulestore.inheritance import InheritanceMixin from xmodule.modulestore.inheritance import InheritanceMixin
from xmodule.x_module import XModuleMixin
############################ FEATURE CONFIGURATION ############################# ############################ FEATURE CONFIGURATION #############################
...@@ -168,7 +169,7 @@ MIDDLEWARE_CLASSES = ( ...@@ -168,7 +169,7 @@ MIDDLEWARE_CLASSES = (
# This should be moved into an XBlock Runtime/Application object # This should be moved into an XBlock Runtime/Application object
# once the responsibility of XBlock creation is moved out of modulestore - cpennington # once the responsibility of XBlock creation is moved out of modulestore - cpennington
XBLOCK_MIXINS = (LmsBlockMixin, CmsBlockMixin, InheritanceMixin) XBLOCK_MIXINS = (LmsBlockMixin, CmsBlockMixin, InheritanceMixin, XModuleMixin)
############################ SIGNAL HANDLERS ################################ ############################ SIGNAL HANDLERS ################################
......
...@@ -43,8 +43,6 @@ log = logging.getLogger(__name__) ...@@ -43,8 +43,6 @@ log = logging.getLogger(__name__)
#============================================================================== #==============================================================================
class SplitMongoModuleStore(ModuleStoreBase): class SplitMongoModuleStore(ModuleStoreBase):
""" """
A Mongodb backed ModuleStore supporting versions, inheritance, A Mongodb backed ModuleStore supporting versions, inheritance,
......
...@@ -15,6 +15,7 @@ from xmodule.modulestore.exceptions import InsufficientSpecificationError, ItemN ...@@ -15,6 +15,7 @@ from xmodule.modulestore.exceptions import InsufficientSpecificationError, ItemN
DuplicateItemError DuplicateItemError
from xmodule.modulestore.locator import CourseLocator, BlockUsageLocator, VersionTree, DescriptionLocator from xmodule.modulestore.locator import CourseLocator, BlockUsageLocator, VersionTree, DescriptionLocator
from xmodule.modulestore.inheritance import InheritanceMixin from xmodule.modulestore.inheritance import InheritanceMixin
from xmodule.x_module import XModuleMixin
from pytz import UTC from pytz import UTC
from path import path from path import path
import re import re
...@@ -34,7 +35,7 @@ class SplitModuleTest(unittest.TestCase): ...@@ -34,7 +35,7 @@ class SplitModuleTest(unittest.TestCase):
'db': 'test_xmodule', 'db': 'test_xmodule',
'collection': 'modulestore{0}'.format(uuid.uuid4().hex), 'collection': 'modulestore{0}'.format(uuid.uuid4().hex),
'fs_root': '', 'fs_root': '',
'xblock_mixins': (InheritanceMixin,) 'xblock_mixins': (InheritanceMixin, XModuleMixin)
} }
MODULESTORE = { MODULESTORE = {
......
...@@ -11,15 +11,11 @@ import json ...@@ -11,15 +11,11 @@ import json
import os import os
import unittest import unittest
import fs
import fs.osfs
import numpy
from mock import Mock from mock import Mock
from path import path from path import path
import calc
from xblock.field_data import DictFieldData from xblock.field_data import DictFieldData
from xmodule.x_module import ModuleSystem, XModuleDescriptor, DescriptorSystem from xmodule.x_module import ModuleSystem, XModuleDescriptor, XModuleMixin
from xmodule.modulestore.inheritance import InheritanceMixin from xmodule.modulestore.inheritance import InheritanceMixin
from xmodule.mako_module import MakoDescriptorSystem from xmodule.mako_module import MakoDescriptorSystem
...@@ -81,7 +77,7 @@ def get_test_descriptor_system(): ...@@ -81,7 +77,7 @@ def get_test_descriptor_system():
resources_fs=Mock(), resources_fs=Mock(),
error_tracker=Mock(), error_tracker=Mock(),
render_template=lambda template, context: repr(context), render_template=lambda template, context: repr(context),
mixins=(InheritanceMixin,), mixins=(InheritanceMixin, XModuleMixin),
) )
......
...@@ -13,6 +13,7 @@ from xmodule.xml_module import is_pointer_tag ...@@ -13,6 +13,7 @@ from xmodule.xml_module import is_pointer_tag
from xmodule.modulestore import Location from xmodule.modulestore import Location
from xmodule.modulestore.xml import ImportSystem, XMLModuleStore from xmodule.modulestore.xml import ImportSystem, XMLModuleStore
from xmodule.modulestore.inheritance import compute_inherited_metadata from xmodule.modulestore.inheritance import compute_inherited_metadata
from xmodule.x_module import XModuleMixin
from xmodule.fields import Date from xmodule.fields import Date
from xmodule.tests import DATA_DIR from xmodule.tests import DATA_DIR
from xmodule.modulestore.inheritance import InheritanceMixin from xmodule.modulestore.inheritance import InheritanceMixin
...@@ -42,7 +43,7 @@ class DummySystem(ImportSystem): ...@@ -42,7 +43,7 @@ class DummySystem(ImportSystem):
error_tracker=error_tracker, error_tracker=error_tracker,
parent_tracker=parent_tracker, parent_tracker=parent_tracker,
load_error_modules=load_error_modules, load_error_modules=load_error_modules,
mixins=(InheritanceMixin,) mixins=(InheritanceMixin, XModuleMixin)
) )
def render_template(self, _template, _context): def render_template(self, _template, _context):
......
...@@ -12,10 +12,10 @@ from xblock.runtime import DbModel ...@@ -12,10 +12,10 @@ from xblock.runtime import DbModel
from xmodule.fields import Date, Timedelta from xmodule.fields import Date, Timedelta
from xmodule.modulestore.inheritance import InheritanceKeyValueStore, InheritanceMixin from xmodule.modulestore.inheritance import InheritanceKeyValueStore, InheritanceMixin
from xmodule.x_module import XModuleFields
from xmodule.xml_module import XmlDescriptor, serialize_field, deserialize_field from xmodule.xml_module import XmlDescriptor, serialize_field, deserialize_field
from xmodule.course_module import CourseDescriptor from xmodule.course_module import CourseDescriptor
from xmodule.seq_module import SequenceDescriptor from xmodule.seq_module import SequenceDescriptor
from xmodule.x_module import XModuleMixin
from xmodule.tests import get_test_descriptor_system from xmodule.tests import get_test_descriptor_system
from xmodule.tests.xml import XModuleXmlImportTest from xmodule.tests.xml import XModuleXmlImportTest
...@@ -65,7 +65,7 @@ class EditableMetadataFieldsTest(unittest.TestCase): ...@@ -65,7 +65,7 @@ class EditableMetadataFieldsTest(unittest.TestCase):
# Also tests that xml_attributes is filtered out of XmlDescriptor. # Also tests that xml_attributes is filtered out of XmlDescriptor.
self.assertEqual(1, len(editable_fields), editable_fields) self.assertEqual(1, len(editable_fields), editable_fields)
self.assert_field_values( self.assert_field_values(
editable_fields, 'display_name', XModuleFields.display_name, editable_fields, 'display_name', XModuleMixin.display_name,
explicitly_set=False, value=None, default_value=None explicitly_set=False, value=None, default_value=None
) )
...@@ -73,7 +73,7 @@ class EditableMetadataFieldsTest(unittest.TestCase): ...@@ -73,7 +73,7 @@ class EditableMetadataFieldsTest(unittest.TestCase):
# Tests that explicitly_set is correct when a value overrides the default (not inheritable). # Tests that explicitly_set is correct when a value overrides the default (not inheritable).
editable_fields = self.get_xml_editable_fields(DictFieldData({'display_name': 'foo'})) editable_fields = self.get_xml_editable_fields(DictFieldData({'display_name': 'foo'}))
self.assert_field_values( self.assert_field_values(
editable_fields, 'display_name', XModuleFields.display_name, editable_fields, 'display_name', XModuleMixin.display_name,
explicitly_set=True, value='foo', default_value=None explicitly_set=True, value='foo', default_value=None
) )
......
import logging import logging
import copy
import yaml import yaml
import os import os
...@@ -11,7 +10,7 @@ from xmodule.modulestore import Location ...@@ -11,7 +10,7 @@ from xmodule.modulestore import Location
from xmodule.modulestore.exceptions import ItemNotFoundError, InsufficientSpecificationError, InvalidLocationError from xmodule.modulestore.exceptions import ItemNotFoundError, InsufficientSpecificationError, InvalidLocationError
from xblock.core import XBlock from xblock.core import XBlock
from xblock.fields import Scope, String, Integer, Float, List from xblock.fields import Scope, Integer, Float, List, XBlockMixin, String
from xblock.fragment import Fragment from xblock.fragment import Fragment
from xblock.runtime import Runtime from xblock.runtime import Runtime
from xmodule.modulestore.locator import BlockUsageLocator from xmodule.modulestore.locator import BlockUsageLocator
...@@ -83,7 +82,13 @@ class HTMLSnippet(object): ...@@ -83,7 +82,13 @@ class HTMLSnippet(object):
.format(self.__class__)) .format(self.__class__))
class XModuleFields(object): class XModuleMixin(XBlockMixin):
"""
Fields and methods used by XModules internally.
Adding this Mixin to an :class:`XBlock` allows it to cooperate with old-style :class:`XModules`
"""
display_name = String( display_name = String(
display_name="Display Name", display_name="Display Name",
help="This name appears in the horizontal navigation at the top of the page.", help="This name appears in the horizontal navigation at the top of the page.",
...@@ -93,41 +98,6 @@ class XModuleFields(object): ...@@ -93,41 +98,6 @@ class XModuleFields(object):
default=None default=None
) )
class XModule(XModuleFields, HTMLSnippet, XBlock):
''' Implements a generic learning module.
Subclasses must at a minimum provide a definition for get_html in order
to be displayed to users.
See the HTML module for a simple example.
'''
# The default implementation of get_icon_class returns the icon_class
# attribute of the class
#
# This attribute can be overridden by subclasses, and
# the function can also be overridden if the icon class depends on the data
# in the module
icon_class = 'other'
def __init__(self, descriptor, *args, **kwargs):
'''
Construct a new xmodule
runtime: An XBlock runtime allowing access to external resources
descriptor: the XModuleDescriptor that this module is an instance of.
field_data: A dictionary-like object that maps field names to values
for those fields.
'''
super(XModule, self).__init__(*args, **kwargs)
self.system = self.runtime
self.descriptor = descriptor
self._loaded_children = None
@property @property
def id(self): def id(self):
return self.location.url() return self.location.url()
...@@ -155,31 +125,133 @@ class XModule(XModuleFields, HTMLSnippet, XBlock): ...@@ -155,31 +125,133 @@ class XModule(XModuleFields, HTMLSnippet, XBlock):
@property @property
def url_name(self): def url_name(self):
if self.descriptor: if isinstance(self.location, Location):
return self.descriptor.url_name
elif isinstance(self.location, Location):
return self.location.name return self.location.name
elif isinstance(self.location, BlockUsageLocator): elif isinstance(self.location, BlockUsageLocator):
return self.location.usage_id return self.location.usage_id
else: else:
raise InsufficientSpecificationError() raise InsufficientSpecificationError()
@property @property
def display_name_with_default(self): def display_name_with_default(self):
''' """
Return a display name for the module: use display_name if defined in Return a display name for the module: use display_name if defined in
metadata, otherwise convert the url name. metadata, otherwise convert the url name.
''' """
name = self.display_name name = self.display_name
if name is None: if name is None:
name = self.url_name.replace('_', ' ') name = self.url_name.replace('_', ' ')
return name return name
def get_explicitly_set_fields_by_scope(self, scope=Scope.content):
"""
Get a dictionary of the fields for the given scope which are set explicitly on this xblock. (Including
any set to None.)
"""
result = {}
for field in self.fields.values():
if (field.scope == scope and field.is_set_on(self)):
result[field.name] = field.read_json(self)
return result
@property
def xblock_kvs(self):
"""
Use w/ caution. Really intended for use by the persistence layer.
"""
# if caller wants kvs, caller's assuming it's up to date; so, decache it
self.save()
return self._field_data._kvs # pylint: disable=protected-access
def get_children(self): def get_children(self):
''' """Returns a list of XBlock instances for the children of
this module"""
if not self.has_children:
return []
if getattr(self, '_child_instances', None) is None:
self._child_instances = [] # pylint: disable=attribute-defined-outside-init
for child_loc in self.children:
try:
child = self.runtime.get_block(child_loc)
except ItemNotFoundError:
log.exception('Unable to load item {loc}, skipping'.format(loc=child_loc))
continue
self._child_instances.append(child)
return self._child_instances
def get_required_module_descriptors(self):
"""Returns a list of XModuleDescriptor instances upon which this module depends, but are
not children of this module"""
return []
def get_display_items(self):
"""
Returns a list of descendent module instances that will display
immediately inside this module.
"""
items = []
for child in self.get_children():
items.extend(child.displayable_items())
return items
def displayable_items(self):
"""
Returns list of displayable modules contained by this module. If this
module is visible, should return [self].
"""
return [self]
def get_child_by(self, selector):
"""
Return a child XBlock that matches the specified selector
"""
for child in self.get_children():
if selector(child):
return child
return None
class XModule(XModuleMixin, HTMLSnippet, XBlock): # pylint: disable=abstract-method
""" Implements a generic learning module.
Subclasses must at a minimum provide a definition for get_html in order
to be displayed to users.
See the HTML module for a simple example.
"""
# The default implementation of get_icon_class returns the icon_class
# attribute of the class
#
# This attribute can be overridden by subclasses, and
# the function can also be overridden if the icon class depends on the data
# in the module
icon_class = 'other'
def __init__(self, descriptor, *args, **kwargs):
"""
Construct a new xmodule
runtime: An XBlock runtime allowing access to external resources
descriptor: the XModuleDescriptor that this module is an instance of.
field_data: A dictionary-like object that maps field names to values
for those fields.
"""
super(XModule, self).__init__(*args, **kwargs)
self.system = self.runtime
self.descriptor = descriptor
self._loaded_children = None
def get_children(self):
"""
Return module instances for all the children of this module. Return module instances for all the children of this module.
''' """
if self._loaded_children is None: if self._loaded_children is None:
child_descriptors = self.get_child_descriptors() child_descriptors = self.get_child_descriptors()
...@@ -200,7 +272,7 @@ class XModule(XModuleFields, HTMLSnippet, XBlock): ...@@ -200,7 +272,7 @@ class XModule(XModuleFields, HTMLSnippet, XBlock):
return '<x_module(id={0})>'.format(self.id) return '<x_module(id={0})>'.format(self.id)
def get_child_descriptors(self): def get_child_descriptors(self):
''' """
Returns the descriptors of the child modules Returns the descriptors of the child modules
Overriding this changes the behavior of get_children and Overriding this changes the behavior of get_children and
...@@ -211,40 +283,13 @@ class XModule(XModuleFields, HTMLSnippet, XBlock): ...@@ -211,40 +283,13 @@ class XModule(XModuleFields, HTMLSnippet, XBlock):
These children will be the same children returned by the These children will be the same children returned by the
descriptor unless descriptor.has_dynamic_children() is true. descriptor unless descriptor.has_dynamic_children() is true.
'''
return self.descriptor.get_children()
def get_child_by(self, selector):
""" """
Return a child XModuleDescriptor with the specified url_name, if it exists, and None otherwise. return self.descriptor.get_children()
"""
for child in self.get_children():
if selector(child):
return child
return None
def get_display_items(self):
'''
Returns a list of descendent module instances that will display
immediately inside this module.
'''
items = []
for child in self.get_children():
items.extend(child.displayable_items())
return items
def displayable_items(self):
'''
Returns list of displayable modules contained by this module. If this
module is visible, should return [self].
'''
return [self]
def get_icon_class(self): def get_icon_class(self):
''' """
Return a css class identifying this module in the context of an icon Return a css class identifying this module in the context of an icon
''' """
return self.icon_class return self.icon_class
# Functions used in the LMS # Functions used in the LMS
...@@ -267,7 +312,7 @@ class XModule(XModuleFields, HTMLSnippet, XBlock): ...@@ -267,7 +312,7 @@ class XModule(XModuleFields, HTMLSnippet, XBlock):
return None return None
def max_score(self): def max_score(self):
''' Maximum score. Two notes: """ Maximum score. Two notes:
* This is generic; in abstract, a problem could be 3/5 points on one * This is generic; in abstract, a problem could be 3/5 points on one
randomization, and 5/7 on another randomization, and 5/7 on another
...@@ -275,22 +320,22 @@ class XModule(XModuleFields, HTMLSnippet, XBlock): ...@@ -275,22 +320,22 @@ class XModule(XModuleFields, HTMLSnippet, XBlock):
* In practice, this is a Very Bad Idea, and (a) will break some code * In practice, this is a Very Bad Idea, and (a) will break some code
in place (although that code should get fixed), and (b) break some in place (although that code should get fixed), and (b) break some
analytics we plan to put in place. analytics we plan to put in place.
''' """
return None return None
def get_progress(self): def get_progress(self):
''' Return a progress.Progress object that represents how far the """ Return a progress.Progress object that represents how far the
student has gone in this module. Must be implemented to get correct student has gone in this module. Must be implemented to get correct
progress tracking behavior in nesting modules like sequence and progress tracking behavior in nesting modules like sequence and
vertical. vertical.
If this module has no notion of progress, return None. If this module has no notion of progress, return None.
''' """
return None return None
def handle_ajax(self, _dispatch, _data): def handle_ajax(self, _dispatch, _data):
''' dispatch is last part of the URL. """ dispatch is last part of the URL.
data is a dictionary-like object with the content of the request''' data is a dictionary-like object with the content of the request"""
return "" return ""
...@@ -381,7 +426,7 @@ class ResourceTemplates(object): ...@@ -381,7 +426,7 @@ class ResourceTemplates(object):
return None return None
class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock): class XModuleDescriptor(XModuleMixin, HTMLSnippet, ResourceTemplates, XBlock):
""" """
An XModuleDescriptor is a specification for an element of a course. This An XModuleDescriptor is a specification for an element of a course. This
could be a problem, an organizational element (a group of content), or a could be a problem, an organizational element (a group of content), or a
...@@ -446,86 +491,6 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock): ...@@ -446,86 +491,6 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
self.edited_by = self.edited_on = self.previous_version = self.update_version = self.definition_locator = None self.edited_by = self.edited_on = self.previous_version = self.update_version = self.definition_locator = None
self._child_instances = None self._child_instances = None
@property
def id(self):
return self.location.url()
@property
def category(self):
return self.scope_ids.block_type
@property
def location(self):
try:
return Location(self.scope_ids.usage_id)
except InvalidLocationError:
if isinstance(self.scope_ids.usage_id, BlockUsageLocator):
return self.scope_ids.usage_id
else:
return BlockUsageLocator(self.scope_ids.usage_id)
@location.setter
def location(self, value):
self.scope_ids = self.scope_ids._replace(
def_id=value,
usage_id=value,
)
@property
def url_name(self):
if isinstance(self.location, Location):
return self.location.name
elif isinstance(self.location, BlockUsageLocator):
return self.location.usage_id
else:
raise InsufficientSpecificationError()
@property
def display_name_with_default(self):
'''
Return a display name for the module: use display_name if defined in
metadata, otherwise convert the url name.
'''
name = self.display_name
if name is None:
name = self.url_name.replace('_', ' ')
return name
def get_required_module_descriptors(self):
"""Returns a list of XModuleDescritpor instances upon which this module depends, but are
not children of this module"""
return []
def get_children(self):
"""Returns a list of XModuleDescriptor instances for the children of
this module"""
if not self.has_children:
return []
if self._child_instances is None:
self._child_instances = []
for child_loc in self.children:
if isinstance(child_loc, XModuleDescriptor):
child = child_loc
else:
try:
child = self.runtime.get_block(child_loc)
except ItemNotFoundError:
log.exception('Unable to load item {loc}, skipping'.format(loc=child_loc))
continue
self._child_instances.append(child)
return self._child_instances
def get_child_by(self, selector):
"""
Return a child XModuleDescriptor with the specified url_name, if it exists, and None otherwise.
"""
for child in self.get_children():
if selector(child):
return child
return None
def xmodule(self, system): def xmodule(self, system):
""" """
...@@ -619,15 +584,6 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock): ...@@ -619,15 +584,6 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
raise NotImplementedError( raise NotImplementedError(
'Modules must implement export_to_xml to enable xml export') 'Modules must implement export_to_xml to enable xml export')
@property
def xblock_kvs(self):
"""
Use w/ caution. Really intended for use by the persistence layer.
"""
# if caller wants kvs, caller's assuming it's up to date; so, decache it
self.save()
return self._field_data._kvs
# =============================== BUILTIN METHODS ========================== # =============================== BUILTIN METHODS ==========================
def __eq__(self, other): def __eq__(self, other):
return (self.scope_ids == other.scope_ids and return (self.scope_ids == other.scope_ids and
...@@ -655,17 +611,6 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock): ...@@ -655,17 +611,6 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
return [XBlock.tags, XBlock.name] return [XBlock.tags, XBlock.name]
def get_explicitly_set_fields_by_scope(self, scope=Scope.content):
"""
Get a dictionary of the fields for the given scope which are set explicitly on this xblock. (Including
any set to None.)
"""
result = {}
for field in self.fields.values():
if (field.scope == scope and field.is_set_on(self)):
result[field.name] = field.read_json(self)
return result
@property @property
def editable_metadata_fields(self): def editable_metadata_fields(self):
""" """
...@@ -825,7 +770,7 @@ class XMLParsingSystem(DescriptorSystem): ...@@ -825,7 +770,7 @@ class XMLParsingSystem(DescriptorSystem):
class ModuleSystem(Runtime): class ModuleSystem(Runtime):
''' """
This is an abstraction such that x_modules can function independent This is an abstraction such that x_modules can function independent
of the courseware (e.g. import into other types of courseware, LMS, of the courseware (e.g. import into other types of courseware, LMS,
or if we want to have a sandbox server for user-contributed content) or if we want to have a sandbox server for user-contributed content)
...@@ -835,7 +780,7 @@ class ModuleSystem(Runtime): ...@@ -835,7 +780,7 @@ class ModuleSystem(Runtime):
Note that these functions can be closures over e.g. a django request Note that these functions can be closures over e.g. a django request
and user, or other environment-specific info. and user, or other environment-specific info.
''' """
def __init__( def __init__(
self, ajax_url, track_function, get_module, render_template, self, ajax_url, track_function, get_module, render_template,
replace_urls, xmodule_field_data, user=None, filestore=None, replace_urls, xmodule_field_data, user=None, filestore=None,
...@@ -844,7 +789,7 @@ class ModuleSystem(Runtime): ...@@ -844,7 +789,7 @@ class ModuleSystem(Runtime):
open_ended_grading_interface=None, s3_interface=None, open_ended_grading_interface=None, s3_interface=None,
cache=None, can_execute_unsafe_code=None, replace_course_urls=None, cache=None, can_execute_unsafe_code=None, replace_course_urls=None,
replace_jump_to_id_urls=None, **kwargs): replace_jump_to_id_urls=None, **kwargs):
''' """
Create a closure around the system environment. Create a closure around the system environment.
ajax_url - the url where ajax calls to the encapsulating module go. ajax_url - the url where ajax calls to the encapsulating module go.
...@@ -893,7 +838,7 @@ class ModuleSystem(Runtime): ...@@ -893,7 +838,7 @@ class ModuleSystem(Runtime):
can_execute_unsafe_code - A function returning a boolean, whether or can_execute_unsafe_code - A function returning a boolean, whether or
not to allow the execution of unsafe, unsandboxed code. not to allow the execution of unsafe, unsandboxed code.
''' """
super(ModuleSystem, self).__init__(**kwargs) super(ModuleSystem, self).__init__(**kwargs)
self.ajax_url = ajax_url self.ajax_url = ajax_url
...@@ -926,11 +871,11 @@ class ModuleSystem(Runtime): ...@@ -926,11 +871,11 @@ class ModuleSystem(Runtime):
self.replace_jump_to_id_urls = replace_jump_to_id_urls self.replace_jump_to_id_urls = replace_jump_to_id_urls
def get(self, attr): def get(self, attr):
''' provide uniform access to attributes (like etree).''' """ provide uniform access to attributes (like etree)."""
return self.__dict__.get(attr) return self.__dict__.get(attr)
def set(self, attr, val): def set(self, attr, val):
'''provide uniform access to attributes (like etree)''' """provide uniform access to attributes (like etree)"""
self.__dict__[attr] = val self.__dict__[attr] = val
def __repr__(self): def __repr__(self):
......
...@@ -32,6 +32,7 @@ from .discussionsettings import * ...@@ -32,6 +32,7 @@ from .discussionsettings import *
from lms.xblock.mixin import LmsBlockMixin from lms.xblock.mixin import LmsBlockMixin
from xmodule.modulestore.inheritance import InheritanceMixin from xmodule.modulestore.inheritance import InheritanceMixin
from xmodule.x_module import XModuleMixin
################################### FEATURES ################################### ################################### FEATURES ###################################
# The display name of the platform to be used in templates/emails/etc. # The display name of the platform to be used in templates/emails/etc.
...@@ -363,7 +364,7 @@ CONTENTSTORE = None ...@@ -363,7 +364,7 @@ CONTENTSTORE = None
# This should be moved into an XBlock Runtime/Application object # This should be moved into an XBlock Runtime/Application object
# once the responsibility of XBlock creation is moved out of modulestore - cpennington # once the responsibility of XBlock creation is moved out of modulestore - cpennington
XBLOCK_MIXINS = (LmsBlockMixin, InheritanceMixin) XBLOCK_MIXINS = (LmsBlockMixin, InheritanceMixin, XModuleMixin)
#################### Python sandbox ############################################ #################### Python sandbox ############################################
......
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