diff --git a/CHANGELOG.rst b/CHANGELOG.rst index bc81f59fbe..b98865e301 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -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. diff --git a/cms/djangoapps/contentstore/tests/test_crud.py b/cms/djangoapps/contentstore/tests/test_crud.py index 8fd54ac831..6094c7cf3b 100644 --- a/cms/djangoapps/contentstore/tests/test_crud.py +++ b/cms/djangoapps/contentstore/tests/test_crud.py @@ -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: diff --git a/cms/djangoapps/contentstore/views/component.py b/cms/djangoapps/contentstore/views/component.py index 0273ccc716..6dd5b6c293 100644 --- a/cms/djangoapps/contentstore/views/component.py +++ b/cms/djangoapps/contentstore/views/component.py @@ -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) diff --git a/cms/djangoapps/contentstore/views/item.py b/cms/djangoapps/contentstore/views/item.py index 9fa2a56f7f..d6a8ff3abb 100644 --- a/cms/djangoapps/contentstore/views/item.py +++ b/cms/djangoapps/contentstore/views/item.py @@ -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: diff --git a/cms/envs/common.py b/cms/envs/common.py index d8f187223c..eb7d600c20 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -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 diff --git a/common/lib/xmodule/xmodule/modulestore/__init__.py b/common/lib/xmodule/xmodule/modulestore/__init__.py index ebec6d6ee8..e3386608dc 100644 --- a/common/lib/xmodule/xmodule/modulestore/__init__.py +++ b/common/lib/xmodule/xmodule/modulestore/__init__.py @@ -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): """ diff --git a/common/lib/xmodule/xmodule/modulestore/django.py b/common/lib/xmodule/xmodule/modulestore/django.py index ef4b9df40a..13e0a2878f 100644 --- a/common/lib/xmodule/xmodule/modulestore/django.py +++ b/common/lib/xmodule/xmodule/modulestore/django.py @@ -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: diff --git a/common/lib/xmodule/xmodule/modulestore/mongo/base.py b/common/lib/xmodule/xmodule/modulestore/mongo/base.py index e13c609ab3..c655800609 100644 --- a/common/lib/xmodule/xmodule/modulestore/mongo/base.py +++ b/common/lib/xmodule/xmodule/modulestore/mongo/base.py @@ -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 diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py index f209ceddf7..b755ba691f 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py @@ -1,7 +1,6 @@ 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 diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py index 4c1d8b116b..1a92f961ec 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py @@ -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'] diff --git a/common/lib/xmodule/xmodule/modulestore/tests/factories.py b/common/lib/xmodule/xmodule/modulestore/tests/factories.py index 2cbe7e6e22..70b580b69d 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/factories.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/factories.py @@ -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', {})) diff --git a/common/lib/xmodule/xmodule/modulestore/xml.py b/common/lib/xmodule/xmodule/modulestore/xml.py index 9d055356fc..02df55569a 100644 --- a/common/lib/xmodule/xmodule/modulestore/xml.py +++ b/common/lib/xmodule/xmodule/modulestore/xml.py @@ -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')) diff --git a/common/lib/xmodule/xmodule/modulestore/xml_importer.py b/common/lib/xmodule/xmodule/modulestore/xml_importer.py index 744a771e1a..176710bca2 100644 --- a/common/lib/xmodule/xmodule/modulestore/xml_importer.py +++ b/common/lib/xmodule/xmodule/modulestore/xml_importer.py @@ -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() diff --git a/common/lib/xmodule/xmodule/templates.py b/common/lib/xmodule/xmodule/templates.py index 8e350bb618..d5d45619f1 100644 --- a/common/lib/xmodule/xmodule/templates.py +++ b/common/lib/xmodule/xmodule/templates.py @@ -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 diff --git a/common/lib/xmodule/xmodule/tests/test_import.py b/common/lib/xmodule/xmodule/tests/test_import.py index 969886345d..3a192318a6 100644 --- a/common/lib/xmodule/xmodule/tests/test_import.py +++ b/common/lib/xmodule/xmodule/tests/test_import.py @@ -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] diff --git a/common/lib/xmodule/xmodule/tests/xml/__init__.py b/common/lib/xmodule/xmodule/tests/xml/__init__.py index 93aa582cde..624a61d13f 100644 --- a/common/lib/xmodule/xmodule/tests/xml/__init__.py +++ b/common/lib/xmodule/xmodule/tests/xml/__init__.py @@ -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)) ) diff --git a/common/lib/xmodule/xmodule/tests/xml/factories.py b/common/lib/xmodule/xmodule/tests/xml/factories.py index 168dcd579b..29857f2571 100644 --- a/common/lib/xmodule/xmodule/tests/xml/factories.py +++ b/common/lib/xmodule/xmodule/tests/xml/factories.py @@ -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 = {} diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py index bf5fb18d66..ffc6862115 100644 --- a/common/lib/xmodule/xmodule/x_module.py +++ b/common/lib/xmodule/xmodule/x_module.py @@ -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): """ diff --git a/lms/djangoapps/courseware/access.py b/lms/djangoapps/courseware/access.py index acb850ff56..fa67a920d3 100644 --- a/lms/djangoapps/courseware/access.py +++ b/lms/djangoapps/courseware/access.py @@ -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 diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index 5b8168d204..377a238ee6 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -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, '') diff --git a/lms/djangoapps/debug/management/commands/dump_xml_courses.py b/lms/djangoapps/debug/management/commands/dump_xml_courses.py index f0834282b7..571ba59aa9 100644 --- a/lms/djangoapps/debug/management/commands/dump_xml_courses.py +++ b/lms/djangoapps/debug/management/commands/dump_xml_courses.py @@ -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]) diff --git a/lms/envs/acceptance.py b/lms/envs/acceptance.py index a10ff9a164..b1d49b5237 100644 --- a/lms/envs/acceptance.py +++ b/lms/envs/acceptance.py @@ -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', } diff --git a/lms/envs/cms/dev.py b/lms/envs/cms/dev.py index 0dca42f343..6fbda95ba6 100644 --- a/lms/envs/cms/dev.py +++ b/lms/envs/cms/dev.py @@ -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', } diff --git a/lms/envs/cms/mixed_dev.py b/lms/envs/cms/mixed_dev.py index f636566558..0454c25409 100644 --- a/lms/envs/cms/mixed_dev.py +++ b/lms/envs/cms/mixed_dev.py @@ -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', } diff --git a/lms/envs/common.py b/lms/envs/common.py index 42d9589401..150b9ab8b8 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -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 = { diff --git a/lms/envs/dev_mongo.py b/lms/envs/dev_mongo.py index 3a99f44655..4d304028d7 100644 --- a/lms/envs/dev_mongo.py +++ b/lms/envs/dev_mongo.py @@ -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', } diff --git a/requirements/edx/github.txt b/requirements/edx/github.txt index d9fca2300e..19ed7b283c 100644 --- a/requirements/edx/github.txt +++ b/requirements/edx/github.txt @@ -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