Commit 2ad4749a by Calen Pennington

Merge pull request #2119 from cpennington/xblocks-behind-feature-flag

Enable pure XBlocks, hidden by feature flags
parents 4a3cffbb 417fe21d
......@@ -5,6 +5,13 @@ These are notable changes in edx-platform. This is a rolling list of changes,
in roughly chronological order, most recent first. Add your entries at or near
the top. Include a label indicating the component affected.
Common: Add feature flags to allow developer use of pure XBlocks
- ALLOW_ALL_ADVANCED_COMPONENTS disables the hard-coded list of advanced
components in Studio, and allows any xblock to be added as an
advanced component in Studio settings
- XBLOCK_SELECT_FUNCTION allows the insertion of a custom function
to limit loading of XBlocks with (including allowing pure xblocks)
Studio: Add sorting by column to the Files & Uploads page.
See mongo_indexes.md for new indices that should be added.
......
......@@ -9,7 +9,8 @@ from xmodule.modulestore.locator import CourseLocator, BlockUsageLocator, LocalI
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.html_module import HtmlDescriptor
from xmodule.modulestore import inheritance
from xmodule.x_module import XModuleDescriptor
from xmodule.x_module import prefer_xmodules
from xblock.core import XBlock
class TemplateTests(unittest.TestCase):
......@@ -248,9 +249,10 @@ class TemplateTests(unittest.TestCase):
- 'definition':
- '_id' (optional): the usage_id of this. Will generate one if not given one.
"""
class_ = XModuleDescriptor.load_class(
class_ = XBlock.load_class(
json_data.get('category', json_data.get('location', {}).get('category')),
default_class
default_class,
select=prefer_xmodules
)
usage_id = json_data.get('_id', None)
if not '_inherited_settings' in json_data and parent_xblock is not None:
......
......@@ -14,13 +14,14 @@ from xmodule.modulestore.django import modulestore
from xmodule.util.date_utils import get_default_time_display
from xmodule.modulestore.django import loc_mapper
from xmodule.modulestore.locator import BlockUsageLocator
from xmodule.x_module import XModuleDescriptor
from xblock.core import XBlock
from xblock.django.request import webob_to_django_response, django_to_webob_request
from xblock.exceptions import NoSuchHandlerError
from xblock.fields import Scope
from xblock.plugin import PluginMissingError
from xblock.runtime import Mixologist
from xmodule.x_module import prefer_xmodules
from lms.lib.xblock.runtime import unquote_slashes
......@@ -44,12 +45,18 @@ COMPONENT_TYPES = ['discussion', 'html', 'problem', 'video']
OPEN_ENDED_COMPONENT_TYPES = ["combinedopenended", "peergrading"]
NOTE_COMPONENT_TYPES = ['notes']
ADVANCED_COMPONENT_TYPES = [
'annotatable',
'word_cloud',
'graphical_slider_tool',
'lti',
] + OPEN_ENDED_COMPONENT_TYPES + NOTE_COMPONENT_TYPES
if settings.FEATURES.get('ALLOW_ALL_ADVANCED_COMPONENTS'):
ADVANCED_COMPONENT_TYPES = sorted(set(name for name, class_ in XBlock.load_classes()) - set(COMPONENT_TYPES))
else:
ADVANCED_COMPONENT_TYPES = [
'annotatable',
'word_cloud',
'graphical_slider_tool',
'lti',
] + OPEN_ENDED_COMPONENT_TYPES + NOTE_COMPONENT_TYPES
ADVANCED_COMPONENT_CATEGORY = 'advanced'
ADVANCED_COMPONENT_POLICY_KEY = 'advanced_modules'
......@@ -138,7 +145,7 @@ def _load_mixed_class(category):
"""
Load an XBlock by category name, and apply all defined mixins
"""
component_class = XModuleDescriptor.load_class(category)
component_class = XBlock.load_class(category, select=prefer_xmodules)
mixologist = Mixologist(settings.XBLOCK_MIXINS)
return mixologist.mix(component_class)
......
......@@ -9,10 +9,17 @@ from xmodule_modifiers import wrap_xblock
from django.core.exceptions import PermissionDenied
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseBadRequest
from django.views.decorators.http import require_http_methods
from xblock.fields import Scope
from xblock.core import XBlock
from xmodule.modulestore.django import modulestore, loc_mapper
from xmodule.modulestore.inheritance import own_metadata
from xmodule.modulestore.exceptions import ItemNotFoundError, InvalidLocationError
from xmodule.modulestore.inheritance import own_metadata
from xmodule.modulestore.locator import BlockUsageLocator
from xmodule.x_module import prefer_xmodules
from util.json_request import expect_json, JsonResponse
from util.string_utils import str_to_bool
......@@ -23,12 +30,6 @@ from ..utils import get_modulestore
from .access import has_access
from .helpers import _xmodule_recurse
from xmodule.x_module import XModuleDescriptor
from django.views.decorators.http import require_http_methods
from xmodule.modulestore.locator import BlockUsageLocator
from student.models import CourseEnrollment
from django.http import HttpResponseBadRequest
from xblock.fields import Scope
from preview import handler_prefix, get_preview_html
from edxmako.shortcuts import render_to_response, render_to_string
from models.settings.course_grading import CourseGradingModel
......@@ -260,7 +261,7 @@ def _create_item(request):
data = None
template_id = request.json.get('boilerplate')
if template_id is not None:
clz = XModuleDescriptor.load_class(category)
clz = XBlock.load_class(category, select=prefer_xmodules)
if clz is not None:
template = clz.get_template(template_id)
if template is not None:
......
......@@ -31,7 +31,7 @@ from path import path
from lms.lib.xblock.mixin import LmsBlockMixin
from cms.lib.xblock.mixin import CmsBlockMixin
from xmodule.modulestore.inheritance import InheritanceMixin
from xmodule.x_module import XModuleMixin
from xmodule.x_module import XModuleMixin, only_xmodules
from dealer.git import git
############################ FEATURE CONFIGURATION #############################
......@@ -62,6 +62,10 @@ FEATURES = {
# If set to True, new Studio users won't be able to author courses unless
# edX has explicitly added them to the course creator group.
'ENABLE_CREATOR_GROUP': False,
# If set to True, Studio won't restrict the set of advanced components
# to just those pre-approved by edX
'ALLOW_ALL_ADVANCED_COMPONENTS': False,
}
ENABLE_JASMINE = False
......@@ -178,6 +182,15 @@ MIDDLEWARE_CLASSES = (
# once the responsibility of XBlock creation is moved out of modulestore - cpennington
XBLOCK_MIXINS = (LmsBlockMixin, CmsBlockMixin, InheritanceMixin, XModuleMixin)
# Only allow XModules in Studio
XBLOCK_SELECT_FUNCTION = only_xmodules
# Use the following lines to allow any xblock in Studio,
# either by uncommenting them here, or adding them to your private.py
# You should also enable the ALLOW_ALL_ADVANCED_COMPONENTS feature flag, so that
# xblocks can be added via advanced settings
# from xmodule.x_module import prefer_xmodules
# XBLOCK_SELECT_FUNCTION = prefer_xmodules
############################ SIGNAL HANDLERS ################################
# This is imported to register the exception signal handling that logs exceptions
......
......@@ -430,7 +430,7 @@ class ModuleStoreReadBase(ModuleStoreRead):
self,
doc_store_config=None, # ignore if passed up
metadata_inheritance_cache_subsystem=None, request_cache=None,
modulestore_update_signal=None, xblock_mixins=(),
modulestore_update_signal=None, xblock_mixins=(), xblock_select=None,
# temporary parms to enable backward compatibility. remove once all envs migrated
db=None, collection=None, host=None, port=None, tz_aware=True, user=None, password=None
):
......@@ -442,6 +442,7 @@ class ModuleStoreReadBase(ModuleStoreRead):
self.modulestore_update_signal = modulestore_update_signal
self.request_cache = request_cache
self.xblock_mixins = xblock_mixins
self.xblock_select = xblock_select
def _get_errorlog(self, location):
"""
......
......@@ -66,6 +66,7 @@ def create_modulestore_instance(engine, doc_store_config, options):
request_cache=request_cache,
modulestore_update_signal=Signal(providing_args=['modulestore', 'course_id', 'location']),
xblock_mixins=getattr(settings, 'XBLOCK_MIXINS', ()),
xblock_select=getattr(settings, 'XBLOCK_SELECT_FUNCTION', None),
doc_store_config=doc_store_config,
**_options
)
......@@ -83,7 +84,7 @@ def get_default_store_name_for_current_request():
# get mapping information which is defined in configurations
mappings = getattr(settings, 'HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS', None)
# compare hostname against the regex expressions set of mappings
# which will tell us which store name to use
if hostname and mappings:
......
......@@ -25,7 +25,6 @@ from path import path
from importlib import import_module
from xmodule.errortracker import null_error_tracker, exc_info_to_str
from xmodule.mako_module import MakoDescriptorSystem
from xmodule.x_module import XModuleDescriptor
from xmodule.error_module import ErrorDescriptor
from xblock.runtime import DbModel
from xblock.exceptions import InvalidScopeError
......@@ -173,10 +172,8 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
# load the module and apply the inherited metadata
try:
category = json_data['location']['category']
class_ = XModuleDescriptor.load_class(
category,
self.default_class
)
class_ = self.load_block_type(category)
definition = json_data.get('definition', {})
metadata = json_data.get('metadata', {})
for old_name, new_name in getattr(class_, 'metadata_translations', {}).items():
......@@ -506,6 +503,7 @@ class MongoModuleStore(ModuleStoreWriteBase):
render_template=self.render_template,
cached_metadata=cached_metadata,
mixins=self.xblock_mixins,
select=self.xblock_select,
)
return system.load_item(item['location'])
......@@ -627,8 +625,9 @@ class MongoModuleStore(ModuleStoreWriteBase):
render_template=self.render_template,
cached_metadata={},
mixins=self.xblock_mixins,
select=self.xblock_select,
)
xblock_class = XModuleDescriptor.load_class(location.category, self.default_class)
xblock_class = system.load_block_type(location.category)
if definition_data is None:
if hasattr(xblock_class, 'data') and xblock_class.data.default is not None:
definition_data = xblock_class.data.default
......
import sys
import logging
from xmodule.mako_module import MakoDescriptorSystem
from xmodule.x_module import XModuleDescriptor
from xmodule.modulestore.locator import BlockUsageLocator, LocalId
from xmodule.error_module import ErrorDescriptor
from xmodule.errortracker import exc_info_to_str
......@@ -62,10 +61,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
if json_data is None:
raise ItemNotFoundError(block_id)
class_ = XModuleDescriptor.load_class(
json_data.get('category'),
self.default_class
)
class_ = self.load_block_type(json_data.get('category'))
return self.xblock_from_json(class_, block_id, json_data, course_entry_override)
# xblock's runtime does not always pass enough contextual information to figure out
......
......@@ -42,7 +42,7 @@ Representation:
*** 'edited_by': user_id whose edit caused this version of the definition,
*** 'edited_on': datetime of the change causing this version
*** 'previous_version': the definition_id of the previous version of this definition
*** 'original_version': definition_id of the root of the previous version relation on this
*** 'original_version': definition_id of the root of the previous version relation on this
definition. Acts as a pseudo-object identifier.
"""
import threading
......@@ -56,7 +56,7 @@ import copy
from pytz import UTC
from xmodule.errortracker import null_error_tracker
from xmodule.x_module import XModuleDescriptor
from xmodule.x_module import XModuleDescriptor, prefer_xmodules
from xmodule.modulestore.locator import BlockUsageLocator, DefinitionLocator, CourseLocator, VersionTree, LocalId
from xmodule.modulestore.exceptions import InsufficientSpecificationError, VersionConflictError, DuplicateItemError
from xmodule.modulestore import inheritance, ModuleStoreWriteBase, Location, SPLIT_MONGO_MODULESTORE_TYPE
......@@ -68,6 +68,7 @@ from xblock.fields import Scope
from xblock.runtime import Mixologist
from bson.objectid import ObjectId
from xmodule.modulestore.split_mongo.mongo_connection import MongoConnection
from xblock.core import XBlock
log = logging.getLogger(__name__)
#==============================================================================
......@@ -184,7 +185,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
error_tracker=self.error_tracker,
render_template=self.render_template,
resources_fs=None,
mixins=self.xblock_mixins
mixins=self.xblock_mixins,
select=self.xblock_select,
)
self._add_cache(course_entry['structure']['_id'], system)
self.cache_items(system, block_ids, depth, lazy)
......@@ -1471,7 +1473,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
"""
if fields is None:
return {}
cls = self.mixologist.mix(XModuleDescriptor.load_class(category))
cls = self.mixologist.mix(XBlock.load_class(category, select=prefer_xmodules))
result = collections.defaultdict(dict)
for field_name, value in fields.iteritems():
field = getattr(cls, field_name)
......@@ -1581,7 +1583,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
destination_block['edit_info']['edited_by'] = user_id
else:
destination_block = self._new_block(
user_id, new_block['category'],
user_id, new_block['category'],
self._filter_blacklist(copy.copy(new_block['fields']), blacklist),
new_block['definition'],
new_block['edit_info']['update_version']
......
......@@ -6,7 +6,8 @@ from uuid import uuid4
from pytz import UTC
from xmodule.modulestore import Location
from xmodule.x_module import XModuleDescriptor
from xmodule.x_module import prefer_xmodules
from xblock.core import XBlock
class Dummy(object):
......@@ -144,7 +145,7 @@ class ItemFactory(XModuleFactory):
if 'boilerplate' in kwargs:
template_id = kwargs.pop('boilerplate')
clz = XModuleDescriptor.load_class(category)
clz = XBlock.load_class(category, select=prefer_xmodules)
template = clz.get_template(template_id)
assert template is not None
metadata.update(template.get('metadata', {}))
......
......@@ -17,7 +17,7 @@ from xmodule.error_module import ErrorDescriptor
from xmodule.errortracker import make_error_tracker, exc_info_to_str
from xmodule.course_module import CourseDescriptor
from xmodule.mako_module import MakoDescriptorSystem
from xmodule.x_module import XMLParsingSystem, XModuleDescriptor
from xmodule.x_module import XMLParsingSystem, prefer_xmodules
from xmodule.html_module import HtmlDescriptor
from xblock.core import XBlock
......@@ -238,7 +238,7 @@ def create_block_from_xml(xml_data, system, org=None, course=None, default_class
"""
node = etree.fromstring(xml_data)
raw_class = XModuleDescriptor.load_class(node.tag, default_class)
raw_class = XBlock.load_class(node.tag, default_class, select=prefer_xmodules)
xblock_class = system.mixologist.mix(raw_class)
# leave next line commented out - useful for low-level debugging
......@@ -460,6 +460,7 @@ class XMLModuleStore(ModuleStoreReadBase):
load_error_modules=self.load_error_modules,
policy=policy,
mixins=self.xblock_mixins,
select=self.xblock_select,
)
course_descriptor = system.process_xml(etree.tostring(course_data, encoding='unicode'))
......
......@@ -132,6 +132,7 @@ def import_from_xml(
course_dirs=course_dirs,
load_error_modules=load_error_modules,
xblock_mixins=store.xblock_mixins,
xblock_select=store.xblock_select,
)
# NOTE: the XmlModuleStore does not implement get_items()
......
......@@ -12,7 +12,7 @@ samples.
import logging
from collections import defaultdict
from .x_module import XModuleDescriptor
from xblock.core import XBlock
log = logging.getLogger(__name__)
......@@ -23,7 +23,9 @@ def all_templates():
"""
# TODO use memcache to memoize w/ expiration
templates = defaultdict(list)
for category, descriptor in XModuleDescriptor.load_classes():
for category, descriptor in XBlock.load_classes():
if not hasattr(descriptor, 'templates'):
continue
templates[category] = descriptor.templates()
return templates
......@@ -13,7 +13,7 @@ from xmodule.xml_module import is_pointer_tag
from xmodule.modulestore import Location
from xmodule.modulestore.xml import ImportSystem, XMLModuleStore
from xmodule.modulestore.inheritance import compute_inherited_metadata
from xmodule.x_module import XModuleMixin
from xmodule.x_module import XModuleMixin, only_xmodules
from xmodule.fields import Date
from xmodule.tests import DATA_DIR
from xmodule.modulestore.inheritance import InheritanceMixin
......@@ -61,7 +61,12 @@ class BaseCourseTestCase(unittest.TestCase):
"""Get a test course by directory name. If there's more than one, error."""
print("Importing {0}".format(name))
modulestore = XMLModuleStore(DATA_DIR, course_dirs=[name], xblock_mixins=(InheritanceMixin,))
modulestore = XMLModuleStore(
DATA_DIR,
course_dirs=[name],
xblock_mixins=(InheritanceMixin,),
xblock_select=only_xmodules,
)
courses = modulestore.get_courses()
self.assertEquals(len(courses), 1)
return courses[0]
......
......@@ -25,6 +25,7 @@ class InMemorySystem(XMLParsingSystem, MakoDescriptorSystem): # pylint: disable
error_tracker=Mock(),
resources_fs=xml_import_data.filesystem,
mixins=xml_import_data.xblock_mixins,
select=xml_import_data.xblock_select,
render_template=lambda template, context: pprint.pformat((template, context))
)
......
......@@ -9,6 +9,7 @@ from factory import Factory, lazy_attribute, post_generation, Sequence
from lxml import etree
from xmodule.modulestore.inheritance import InheritanceMixin
from xmodule.x_module import only_xmodules
class XmlImportData(object):
......@@ -19,7 +20,7 @@ class XmlImportData(object):
def __init__(self, xml_node, xml=None, org=None, course=None,
default_class=None, policy=None,
filesystem=None, parent=None,
xblock_mixins=()):
xblock_mixins=(), xblock_select=None):
self._xml_node = xml_node
self._xml_string = xml
......@@ -28,6 +29,7 @@ class XmlImportData(object):
self.default_class = default_class
self.filesystem = filesystem
self.xblock_mixins = xblock_mixins
self.xblock_select = xblock_select
self.parent = parent
if policy is None:
......@@ -47,7 +49,8 @@ class XmlImportData(object):
return u"XmlImportData{!r}".format((
self._xml_node, self._xml_string, self.org,
self.course, self.default_class, self.policy,
self.filesystem, self.parent, self.xblock_mixins
self.filesystem, self.parent, self.xblock_mixins,
self.xblock_select,
))
......@@ -65,6 +68,7 @@ class XmlImportFactory(Factory):
filesystem = MemoryFS()
xblock_mixins = (InheritanceMixin,)
xblock_select = only_xmodules
url_name = Sequence(str)
attribs = {}
policy = {}
......
......@@ -15,9 +15,10 @@ from xmodule.modulestore.exceptions import ItemNotFoundError, InsufficientSpecif
from xblock.core import XBlock
from xblock.fields import Scope, Integer, Float, List, XBlockMixin, String
from xmodule.fields import RelativeTime
from xblock.fragment import Fragment
from xblock.plugin import default_select
from xblock.runtime import Runtime
from xmodule.fields import RelativeTime
from xmodule.errortracker import exc_info_to_str
from xmodule.modulestore.locator import BlockUsageLocator
......@@ -564,6 +565,22 @@ class ResourceTemplates(object):
return None
def prefer_xmodules(identifier, entry_points):
"""Prefer entry_points from the xmodule package"""
from_xmodule = [entry_point for entry_point in entry_points if entry_point.dist.key == 'xmodule']
if from_xmodule:
return default_select(identifier, from_xmodule)
else:
return default_select(identifier, entry_points)
def only_xmodules(identifier, entry_points):
"""Only use entry_points that are supplied by the xmodule package"""
from_xmodule = [entry_point for entry_point in entry_points if entry_point.dist.key == 'xmodule']
return default_select(identifier, from_xmodule)
@XBlock.needs("i18n")
class XModuleDescriptor(XModuleMixin, HTMLSnippet, ResourceTemplates, XBlock):
"""
......
......@@ -11,7 +11,9 @@ from django.contrib.auth.models import Group, AnonymousUser
from xmodule.course_module import CourseDescriptor
from xmodule.error_module import ErrorDescriptor
from xmodule.modulestore import Location
from xmodule.x_module import XModule, XModuleDescriptor
from xmodule.x_module import XModule
from xblock.core import XBlock
from student.models import CourseEnrollmentAllowed
from external_auth.models import ExternalAuthMap
......@@ -74,13 +76,13 @@ def has_access(user, obj, action, course_context=None):
if isinstance(obj, ErrorDescriptor):
return _has_access_error_desc(user, obj, action, course_context)
# NOTE: any descriptor access checkers need to go above this
if isinstance(obj, XModuleDescriptor):
return _has_access_descriptor(user, obj, action, course_context)
if isinstance(obj, XModule):
return _has_access_xmodule(user, obj, action, course_context)
# NOTE: any descriptor access checkers need to go above this
if isinstance(obj, XBlock):
return _has_access_descriptor(user, obj, action, course_context)
if isinstance(obj, Location):
return _has_access_location(user, obj, action, course_context)
......@@ -338,7 +340,7 @@ def _dispatch(table, action, user, obj):
debug("%s user %s, object %s, action %s",
'ALLOWED' if result else 'DENIED',
user,
obj.location.url() if isinstance(obj, XModuleDescriptor) else str(obj)[:60],
obj.location.url() if isinstance(obj, XBlock) else str(obj)[:60],
action)
return result
......
......@@ -142,7 +142,7 @@ class FieldDataCache(object):
return self._chunked_query(
StudentModule,
'module_state_key__in',
(descriptor.location.url() for descriptor in self.descriptors),
(str(descriptor.scope_ids.usage_id) for descriptor in self.descriptors),
course_id=self.course_id,
student=self.user.pk,
)
......@@ -150,14 +150,14 @@ class FieldDataCache(object):
return self._chunked_query(
XModuleUserStateSummaryField,
'usage_id__in',
(descriptor.location.url() for descriptor in self.descriptors),
(str(descriptor.scope_ids.usage_id) for descriptor in self.descriptors),
field_name__in=set(field.name for field in fields),
)
elif scope == Scope.preferences:
return self._chunked_query(
XModuleStudentPrefsField,
'module_type__in',
set(descriptor.module_class.__name__ for descriptor in self.descriptors),
set(descriptor.scope_ids.block_type for descriptor in self.descriptors),
student=self.user.pk,
field_name__in=set(field.name for field in fields),
)
......
......@@ -27,6 +27,7 @@ from psychometrics.psychoanalyze import make_psychometrics_data_update_handler
from student.models import anonymous_id_for_user, user_by_anonymous_id
from util.json_request import JsonResponse
from util.sandboxing import can_execute_unsafe_code
from xblock.core import XBlock
from xblock.fields import Scope
from xblock.runtime import DbModel, KeyValueStore
from xblock.exceptions import NoSuchHandlerError
......@@ -38,6 +39,7 @@ from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule_modifiers import replace_course_urls, replace_jump_to_id_urls, replace_static_urls, add_histogram, wrap_xblock
from xmodule.lti_module import LTIModule
from xmodule.x_module import XModuleDescriptor
log = logging.getLogger(__name__)
......@@ -373,7 +375,9 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours
# while giving selected modules a per-course anonymized id.
# As we have the time to manually test more modules, we can add to the list
# of modules that get the per-course anonymized id.
if issubclass(getattr(descriptor, 'module_class', None), LTIModule):
is_pure_xblock = isinstance(descriptor, XBlock) and not isinstance(descriptor, XModuleDescriptor)
is_lti_module = not is_pure_xblock and issubclass(descriptor.module_class, LTIModule)
if is_pure_xblock or is_lti_module:
anonymous_student_id = anonymous_id_for_user(user, course_id)
else:
anonymous_student_id = anonymous_id_for_user(user, '')
......
......@@ -131,7 +131,7 @@ class UserStateSummaryFactory(DjangoModelFactory):
field_name = 'existing_field'
value = json.dumps('old_value')
usage_id = location('def_id').url()
usage_id = location('usage_id').url()
class StudentPrefsFactory(DjangoModelFactory):
......@@ -140,7 +140,7 @@ class StudentPrefsFactory(DjangoModelFactory):
field_name = 'existing_field'
value = json.dumps('old_value')
student = factory.SubFactory(UserFactory)
module_type = 'MockProblemModule'
module_type = 'mock_problem'
class StudentInfoFactory(DjangoModelFactory):
......
......@@ -15,7 +15,7 @@ from courseware.tests.factories import StudentModuleFactory as cmfStudentModuleF
from courseware.tests.factories import UserStateSummaryFactory
from courseware.tests.factories import StudentPrefsFactory, StudentInfoFactory
from xblock.fields import Scope, BlockScope
from xblock.fields import Scope, BlockScope, ScopeIds
from xmodule.modulestore import Location
from django.test import TestCase
from django.db import DatabaseError
......@@ -31,7 +31,7 @@ def mock_field(scope, name):
def mock_descriptor(fields=[]):
descriptor = Mock()
descriptor.location = location('def_id')
descriptor.scope_ids = ScopeIds('user1', 'mock_problem', location('def_id'), location('usage_id'))
descriptor.module_class.fields.values.return_value = fields
descriptor.fields.values.return_value = fields
descriptor.module_class.__name__ = 'MockProblemModule'
......@@ -43,15 +43,15 @@ course_id = 'edX/test_course/test'
# The user ids here are 1 because we make a student in the setUp functions, and
# they get an id of 1. There's an assertion in setUp to ensure that assumption
# is still true.
user_state_summary_key = partial(DjangoKeyValueStore.Key, Scope.user_state_summary, None, location('def_id'))
settings_key = partial(DjangoKeyValueStore.Key, Scope.settings, None, location('def_id'))
user_state_key = partial(DjangoKeyValueStore.Key, Scope.user_state, 1, location('def_id'))
prefs_key = partial(DjangoKeyValueStore.Key, Scope.preferences, 1, 'MockProblemModule')
user_state_summary_key = partial(DjangoKeyValueStore.Key, Scope.user_state_summary, None, location('usage_id'))
settings_key = partial(DjangoKeyValueStore.Key, Scope.settings, None, location('usage_id'))
user_state_key = partial(DjangoKeyValueStore.Key, Scope.user_state, 1, location('usage_id'))
prefs_key = partial(DjangoKeyValueStore.Key, Scope.preferences, 1, 'mock_problem')
user_info_key = partial(DjangoKeyValueStore.Key, Scope.user_info, 1, None)
class StudentModuleFactory(cmfStudentModuleFactory):
module_state_key = location('def_id').url()
module_state_key = location('usage_id').url()
course_id = course_id
......@@ -177,7 +177,7 @@ class TestMissingStudentModule(TestCase):
student_module = StudentModule.objects.all()[0]
self.assertEquals({'a_field': 'a_value'}, json.loads(student_module.state))
self.assertEquals(self.user, student_module.student)
self.assertEquals(location('def_id').url(), student_module.module_state_key)
self.assertEquals(location('usage_id').url(), student_module.module_state_key)
self.assertEquals(course_id, student_module.course_id)
def test_delete_field_from_missing_student_module(self):
......
......@@ -34,6 +34,7 @@ class Command(BaseCommand):
default_class='xmodule.hidden_module.HiddenDescriptor',
load_error_modules=True,
xblock_mixins=settings.XBLOCK_MIXINS,
xblock_select=settings.XBLOCK_SELECT_FUNCTION,
)
export_dir = path(args[0])
......
......@@ -34,7 +34,7 @@ DOC_STORE_CONFIG = {
}
modulestore_options = {
'default_class': 'xmodule.raw_module.RawDescriptor',
'default_class': 'xmodule.hidden_module.HiddenDescriptor',
'fs_root': TEST_ROOT / "data",
'render_template': 'edxmako.shortcuts.render_to_string',
}
......
......@@ -23,7 +23,7 @@ FEATURES['ENABLE_LMS_MIGRATION'] = False
META_UNIVERSITIES = {}
modulestore_options = {
'default_class': 'xmodule.raw_module.RawDescriptor',
'default_class': 'xmodule.hidden_module.HiddenDescriptor',
'fs_root': DATA_DIR,
'render_template': 'edxmako.shortcuts.render_to_string',
}
......
......@@ -31,7 +31,7 @@ MODULESTORE = {
'collection': 'modulestore',
},
'OPTIONS': {
'default_class': 'xmodule.raw_module.RawDescriptor',
'default_class': 'xmodule.hidden_module.HiddenDescriptor',
'fs_root': DATA_DIR,
'render_template': 'edxmako.shortcuts.render_to_string',
}
......
......@@ -32,7 +32,7 @@ from .discussionsettings import *
from lms.lib.xblock.mixin import LmsBlockMixin
from xmodule.modulestore.inheritance import InheritanceMixin
from xmodule.x_module import XModuleMixin
from xmodule.x_module import XModuleMixin, only_xmodules
################################### FEATURES ###################################
# The display name of the platform to be used in templates/emails/etc.
......@@ -406,6 +406,14 @@ INIT_MODULESTORE_ON_STARTUP = True
# once the responsibility of XBlock creation is moved out of modulestore - cpennington
XBLOCK_MIXINS = (LmsBlockMixin, InheritanceMixin, XModuleMixin)
# Only allow XModules in the LMS
XBLOCK_SELECT_FUNCTION = only_xmodules
# Use the following lines to allow any xblock in the LMS,
# either by uncommenting them here, or adding them to your private.py
# from xmodule.x_module import prefer_xmodules
# XBLOCK_SELECT_FUNCTION = prefer_xmodules
#################### Python sandbox ############################################
CODE_JAIL = {
......
......@@ -19,7 +19,7 @@ MODULESTORE = {
'collection': 'modulestore',
},
'OPTIONS': {
'default_class': 'xmodule.raw_module.RawDescriptor',
'default_class': 'xmodule.hidden_module.HiddenDescriptor',
'fs_root': GITHUB_REPO_ROOT,
'render_template': 'edxmako.shortcuts.render_to_string',
}
......
......@@ -15,7 +15,7 @@
-e git+https://github.com/eventbrite/zendesk.git@d53fe0e81b623f084e91776bcf6369f8b7b63879#egg=zendesk
# Our libraries:
-e git+https://github.com/edx/XBlock.git@2a1efc8a413cc140d48f33fa839143ffcbd21d83#egg=XBlock
-e git+https://github.com/edx/XBlock.git@cd77808aadd3ea1c2027ca8c0aa5624d8ccccc52#egg=XBlock
-e git+https://github.com/edx/codejail.git@e3d98f9455#egg=codejail
-e git+https://github.com/edx/diff-cover.git@v0.2.6#egg=diff_cover
-e git+https://github.com/edx/js-test-tool.git@v0.1.5#egg=js_test_tool
......
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