def load_from_json(json_data, system, default_class=None, parent_xblock=None): """ This method instantiates the correct subclass of XModuleDescriptor based on the contents of json_data. It does not persist it and can create one which has no usage id. parent_xblock is used to compute inherited metadata as well as to append the new xblock. json_data: - 'location' : must have this field - 'category': the xmodule category (required or location must be a Location) - 'metadata': a dict of locally set metadata (not inherited) - 'children': a list of children's usage_ids w/in this course - 'definition': - '_id' (optional): the usage_id of this. Will generate one if not given one. """ class_ = XModuleDescriptor.load_class( json_data.get('category', json_data.get('location', {}).get('category')), default_class ) usage_id = json_data.get('_id', None) if not '_inherited_settings' in json_data and parent_xblock is not None: json_data['_inherited_settings'] = parent_xblock.xblock_kvs.inherited_settings.copy() json_fields = json_data.get('fields', {}) for field_name in inheritance.InheritanceMixin.fields: if field_name in json_fields: json_data['_inherited_settings'][field_name] = json_fields[field_name] new_block = system.xblock_from_json(class_, usage_id, json_data) if parent_xblock is not None: parent_xblock.children.append(new_block.scope_ids.usage_id) # decache pending children field settings parent_xblock.save() return new_block
def load_mixed_class(category): """ Load an XBlock by category name, and apply all defined mixins """ component_class = XModuleDescriptor.load_class(category) mixologist = Mixologist(settings.XBLOCK_MIXINS) return mixologist.mix(component_class)
def create_xmodule(self, location, definition_data=None, metadata=None, system=None): """ Create the new xmodule but don't save it. Returns the new module. :param location: a Location--must have a category :param definition_data: can be empty. The initial definition_data for the kvs :param metadata: can be empty, the initial metadata for the kvs :param system: if you already have an xmodule from the course, the xmodule.system value """ if not isinstance(location, Location): location = Location(location) # differs from split mongo in that I believe most of this logic should be above the persistence # layer but added it here to enable quick conversion. I'll need to reconcile these. if metadata is None: metadata = {} if system is None: system = CachingDescriptorSystem( self, {}, self.default_class, None, self.error_tracker, self.render_template, {} ) xblock_class = XModuleDescriptor.load_class(location.category, self.default_class) if definition_data is None: if hasattr(xblock_class, 'data') and getattr(xblock_class, 'data').default is not None: definition_data = getattr(xblock_class, 'data').default else: definition_data = {} dbmodel = self._create_new_model_data(location.category, location, definition_data, metadata) xmodule = xblock_class(system, dbmodel) return xmodule
def create_block_from_xml(xml_data, system, org=None, course=None, default_class=None): """ Create an XBlock instance from XML data. `xml_data' is a string containing valid xml. `system` is an XMLParsingSystem. `org` and `course` are optional strings that will be used in the generated block's url identifiers. `default_class` is the class to instantiate of the XML indicates a class that can't be loaded. Returns the fully instantiated XBlock. """ node = etree.fromstring(xml_data) raw_class = XModuleDescriptor.load_class(node.tag, default_class) xblock_class = system.mixologist.mix(raw_class) # leave next line commented out - useful for low-level debugging # log.debug('[create_block_from_xml] tag=%s, class=%s' % (node.tag, xblock_class)) url_name = node.get('url_name', node.get('slug')) location = Location('i4x', org, course, node.tag, url_name) scope_ids = ScopeIds(None, location.category, location, location) xblock = xblock_class.parse_xml(node, system, scope_ids) return xblock
def create_xmodule(self, location, definition_data=None, metadata=None, system=None): """ Create the new xmodule but don't save it. Returns the new module. :param location: a Location--must have a category :param definition_data: can be empty. The initial definition_data for the kvs :param metadata: can be empty, the initial metadata for the kvs :param system: if you already have an xmodule from the course, the xmodule.system value """ if not isinstance(location, Location): location = Location(location) # differs from split mongo in that I believe most of this logic should be above the persistence # layer but added it here to enable quick conversion. I'll need to reconcile these. if metadata is None: metadata = {} if system is None: system = CachingDescriptorSystem(self, {}, self.default_class, None, self.error_tracker, self.render_template, {}) xblock_class = XModuleDescriptor.load_class(location.category, self.default_class) if definition_data is None: if hasattr(xblock_class, 'data') and getattr( xblock_class, 'data').default is not None: definition_data = getattr(xblock_class, 'data').default else: definition_data = {} dbmodel = self._create_new_model_data(location.category, location, definition_data, metadata) xmodule = xblock_class(system, dbmodel) return xmodule
def _create(cls, target_class, **kwargs): """ Uses ``**kwargs``: :parent_location: (required): the location of the parent module (e.g. the parent course or section) :category: the category of the resulting item. :data: (optional): the data for the item (e.g. XML problem definition for a problem item) :display_name: (optional): the display name of the item :metadata: (optional): dictionary of metadata attributes :boilerplate: (optional) the boilerplate for overriding field values :target_class: is ignored """ DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info'] # catch any old style users before they get into trouble assert not 'template' in kwargs parent_location = Location(kwargs.get('parent_location')) data = kwargs.get('data') category = kwargs.get('category') display_name = kwargs.get('display_name') metadata = kwargs.get('metadata', {}) location = kwargs.get( 'location', XModuleItemFactory.location(parent_location, category, display_name)) assert location != parent_location if kwargs.get('boilerplate') is not None: template_id = kwargs.get('boilerplate') clz = XModuleDescriptor.load_class(category) template = clz.get_template(template_id) assert template is not None metadata.update(template.get('metadata', {})) if not isinstance(data, basestring): data.update(template.get('data')) store = editable_modulestore('direct') # This code was based off that in cms/djangoapps/contentstore/views.py parent = store.get_item(parent_location) # replace the display name with an optional parameter passed in from the caller if display_name is not None: metadata['display_name'] = display_name store.create_and_save_xmodule(location, metadata=metadata, definition_data=data) if location.category not in DETACHED_CATEGORIES: parent.children.append(location.url()) store.update_children(parent_location, parent.children) return store.get_item(location)
def create_item(request): parent_location = Location(request.POST["parent_location"]) category = request.POST["category"] display_name = request.POST.get("display_name") if not has_access(request.user, parent_location): raise PermissionDenied() parent = get_modulestore(category).get_item(parent_location) dest_location = parent_location.replace(category=category, name=uuid4().hex) # get the metadata, display_name, and definition from the request metadata = {} data = None template_id = request.POST.get("boilerplate") if template_id is not None: clz = XModuleDescriptor.load_class(category) if clz is not None: template = clz.get_template(template_id) if template is not None: metadata = template.get("metadata", {}) data = template.get("data") if display_name is not None: metadata["display_name"] = display_name get_modulestore(category).create_and_save_xmodule( dest_location, definition_data=data, metadata=metadata, system=parent.system ) if category not in DETACHED_CATEGORIES: get_modulestore(parent.location).update_children(parent_location, parent.children + [dest_location.url()]) return JsonResponse({"id": dest_location.url()})
def load_item(self, location): """ Return an XModule instance for the specified location """ location = Location(location) json_data = self.module_data.get(location) if json_data is None: module = self.modulestore.get_item(location) if module is not None: # update our own cache after going to the DB to get cache miss self.module_data.update(module.runtime.module_data) return module else: # load the module and apply the inherited metadata try: category = json_data['location']['category'] class_ = XModuleDescriptor.load_class(category, self.default_class) definition = json_data.get('definition', {}) metadata = json_data.get('metadata', {}) for old_name, new_name in getattr(class_, 'metadata_translations', {}).items(): if old_name in metadata: metadata[new_name] = metadata[old_name] del metadata[old_name] kvs = MongoKeyValueStore( definition.get('data', {}), definition.get('children', []), metadata, ) field_data = DbModel(kvs) scope_ids = ScopeIds(None, category, location, location) module = self.construct_xblock_from_class( class_, field_data, scope_ids) if self.cached_metadata is not None: # parent container pointers don't differentiate between draft and non-draft # so when we do the lookup, we should do so with a non-draft location non_draft_loc = location.replace(revision=None) # Convert the serialized fields values in self.cached_metadata # to python values metadata_to_inherit = self.cached_metadata.get( non_draft_loc.url(), {}) inherit_metadata(module, metadata_to_inherit) # decache any computed pending field settings module.save() return module except: log.warning("Failed to load descriptor", exc_info=True) return ErrorDescriptor.from_json(json_data, self, json_data['location'], error_msg=exc_info_to_str( sys.exc_info()))
def _create(cls, target_class, **kwargs): """ Uses ``**kwargs``: :parent_location: (required): the location of the parent module (e.g. the parent course or section) :category: the category of the resulting item. :data: (optional): the data for the item (e.g. XML problem definition for a problem item) :display_name: (optional): the display name of the item :metadata: (optional): dictionary of metadata attributes :boilerplate: (optional) the boilerplate for overriding field values :target_class: is ignored """ DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info'] # catch any old style users before they get into trouble assert not 'template' in kwargs data = kwargs.get('data') category = kwargs.get('category') display_name = kwargs.get('display_name') metadata = kwargs.get('metadata', {}) location = kwargs.get('location') if kwargs.get('boilerplate') is not None: template_id = kwargs.get('boilerplate') clz = XModuleDescriptor.load_class(category) template = clz.get_template(template_id) assert template is not None metadata.update(template.get('metadata', {})) if not isinstance(data, basestring): data.update(template.get('data')) store = kwargs.get('modulestore') # replace the display name with an optional parameter passed in from the caller if display_name is not None: metadata['display_name'] = display_name store.create_and_save_xmodule(location, metadata=metadata, definition_data=data) if location.category not in DETACHED_CATEGORIES: parent_location = Location(kwargs.get('parent_location')) assert location != parent_location # This code was based off that in cms/djangoapps/contentstore/views.py parent = kwargs.get('parent') or store.get_item(parent_location) parent.children.append(location.url()) store.update_children(parent_location, parent.children) return store.get_item(location)
def load_item(self, location): """ Return an XModule instance for the specified location """ location = Location(location) json_data = self.module_data.get(location) if json_data is None: module = self.modulestore.get_item(location) if module is not None: # update our own cache after going to the DB to get cache miss self.module_data.update(module.runtime.module_data) return module else: # load the module and apply the inherited metadata try: category = json_data['location']['category'] class_ = XModuleDescriptor.load_class( category, self.default_class ) definition = json_data.get('definition', {}) metadata = json_data.get('metadata', {}) for old_name, new_name in getattr(class_, 'metadata_translations', {}).items(): if old_name in metadata: metadata[new_name] = metadata[old_name] del metadata[old_name] kvs = MongoKeyValueStore( definition.get('data', {}), definition.get('children', []), metadata, ) field_data = DbModel(kvs) scope_ids = ScopeIds(None, category, location, location) module = self.construct_xblock_from_class(class_, field_data, scope_ids) if self.cached_metadata is not None: # parent container pointers don't differentiate between draft and non-draft # so when we do the lookup, we should do so with a non-draft location non_draft_loc = location.replace(revision=None) # Convert the serialized fields values in self.cached_metadata # to python values metadata_to_inherit = self.cached_metadata.get(non_draft_loc.url(), {}) inherit_metadata(module, metadata_to_inherit) # decache any computed pending field settings module.save() return module except: log.warning("Failed to load descriptor", exc_info=True) return ErrorDescriptor.from_json( json_data, self, json_data['location'], error_msg=exc_info_to_str(sys.exc_info()) )
def _create_new_model_data(self, category, location, definition_data, metadata): """ To instantiate a new xmodule which will be saved latter, set up the dbModel and kvs """ kvs = MongoKeyValueStore(definition_data, [], metadata, location, category) class_ = XModuleDescriptor.load_class(category, self.default_class) model_data = DbModel(kvs, class_, None, MongoUsage(None, location)) model_data["category"] = category model_data["location"] = location return model_data
def load_item(self, location): """ Return an XModule instance for the specified location """ location = Location(location) json_data = self.module_data.get(location) if json_data is None: module = self.modulestore.get_item(location) if module is not None: # update our own cache after going to the DB to get cache miss self.module_data.update(module.system.module_data) return module else: # load the module and apply the inherited metadata try: category = json_data['location']['category'] class_ = XModuleDescriptor.load_class( category, self.default_class ) definition = json_data.get('definition', {}) metadata = json_data.get('metadata', {}) for old_name, new_name in class_.metadata_translations.items(): if old_name in metadata: metadata[new_name] = metadata[old_name] del metadata[old_name] kvs = MongoKeyValueStore( definition.get('data', {}), definition.get('children', []), metadata, location, category ) model_data = DbModel(kvs, class_, None, MongoUsage(self.course_id, location)) model_data['category'] = category model_data['location'] = location module = class_(self, model_data) if self.cached_metadata is not None: # parent container pointers don't differentiate between draft and non-draft # so when we do the lookup, we should do so with a non-draft location non_draft_loc = location.replace(revision=None) metadata_to_inherit = self.cached_metadata.get(non_draft_loc.url(), {}) inherit_metadata(module, metadata_to_inherit) return module except: log.warning("Failed to load descriptor", exc_info=True) return ErrorDescriptor.from_json( json_data, self, json_data['location'], error_msg=exc_info_to_str(sys.exc_info()) )
def _load_item(self, usage_id, course_entry_override=None): # TODO ensure all callers of system.load_item pass just the id json_data = self.module_data.get(usage_id) if json_data is None: # deeper than initial descendant fetch or doesn't exist self.modulestore.cache_items(self, [usage_id], lazy=self.lazy) json_data = self.module_data.get(usage_id) if json_data is None: raise ItemNotFoundError class_ = XModuleDescriptor.load_class(json_data.get("category"), self.default_class) return self.xblock_from_json(class_, usage_id, json_data, course_entry_override)
def _create_new_model_data(self, category, location, definition_data, metadata): """ To instantiate a new xmodule which will be saved latter, set up the dbModel and kvs """ kvs = MongoKeyValueStore(definition_data, [], metadata, location, category) class_ = XModuleDescriptor.load_class(category, self.default_class) model_data = DbModel(kvs, class_, None, MongoUsage(None, location)) model_data['category'] = category model_data['location'] = location return model_data
def create_xmodule(self, location, definition_data=None, metadata=None, system=None): """ Create the new xmodule but don't save it. Returns the new module. :param location: a Location--must have a category :param definition_data: can be empty. The initial definition_data for the kvs :param metadata: can be empty, the initial metadata for the kvs :param system: if you already have an xblock from the course, the xblock.runtime value """ if not isinstance(location, Location): location = Location(location) # differs from split mongo in that I believe most of this logic should be above the persistence # layer but added it here to enable quick conversion. I'll need to reconcile these. if metadata is None: metadata = {} if system is None: system = CachingDescriptorSystem( modulestore=self, module_data={}, default_class=self.default_class, resources_fs=None, error_tracker=self.error_tracker, render_template=self.render_template, cached_metadata={}, mixins=self.xblock_mixins, ) xblock_class = XModuleDescriptor.load_class(location.category, self.default_class) if definition_data is None: if hasattr(xblock_class, 'data') and getattr( xblock_class, 'data').default is not None: definition_data = getattr(xblock_class, 'data').default else: definition_data = {} dbmodel = self._create_new_field_data(location.category, location, definition_data, metadata) xmodule = system.construct_xblock_from_class( xblock_class, dbmodel, # We're loading a descriptor, so student_id is meaningless # We also don't have separate notions of definition and usage ids yet, # so we use the location for both. ScopeIds(None, location.category, location, location)) # decache any pending field settings from init xmodule.save() return xmodule
def load_item(self, location): """ Return an XModule instance for the specified location """ location = Location(location) json_data = self.module_data.get(location) if json_data is None: module = self.modulestore.get_item(location) if module is not None: # update our own cache after going to the DB to get cache miss self.module_data.update(module.system.module_data) return module else: # load the module and apply the inherited metadata try: class_ = XModuleDescriptor.load_class( json_data['location']['category'], self.default_class ) definition = json_data.get('definition', {}) metadata = json_data.get('metadata', {}) for old_name, new_name in class_.metadata_translations.items(): if old_name in metadata: metadata[new_name] = metadata[old_name] del metadata[old_name] kvs = MongoKeyValueStore( definition.get('data', {}), definition.get('children', []), metadata, ) model_data = DbModel(kvs, class_, None, MongoUsage( self.course_id, location)) module = class_(self, location, model_data) if self.cached_metadata is not None: # parent container pointers don't differentiate between draft and non-draft # so when we do the lookup, we should do so with a non- # draft location non_draft_loc = location._replace(revision=None) metadata_to_inherit = self.cached_metadata.get( non_draft_loc.url(), {}) inherit_metadata(module, metadata_to_inherit) return module except: log.warning("Failed to load descriptor", exc_info=True) return ErrorDescriptor.from_json( json_data, self, error_msg=exc_info_to_str(sys.exc_info()) )
def _load_item(self, usage_id, course_entry_override=None): # TODO ensure all callers of system.load_item pass just the id json_data = self.module_data.get(usage_id) if json_data is None: # deeper than initial descendant fetch or doesn't exist self.modulestore.cache_items(self, [usage_id], lazy=self.lazy) json_data = self.module_data.get(usage_id) if json_data is None: raise ItemNotFoundError class_ = XModuleDescriptor.load_class(json_data.get('category'), self.default_class) return self.xblock_from_json(class_, usage_id, json_data, course_entry_override)
def _create_item(request): """View for create items.""" parent_locator = BlockUsageLocator(request.json['parent_locator']) parent_location = loc_mapper().translate_locator_to_location( parent_locator) category = request.json['category'] display_name = request.json.get('display_name') if not has_access(request.user, parent_location): raise PermissionDenied() parent = get_modulestore(category).get_item(parent_location) dest_location = parent_location.replace(category=category, name=uuid4().hex) # get the metadata, display_name, and definition from the request metadata = {} data = None template_id = request.json.get('boilerplate') if template_id is not None: clz = XModuleDescriptor.load_class(category) if clz is not None: template = clz.get_template(template_id) if template is not None: metadata = template.get('metadata', {}) data = template.get('data') if display_name is not None: metadata['display_name'] = display_name get_modulestore(category).create_and_save_xmodule( dest_location, definition_data=data, metadata=metadata, system=parent.system, ) if category not in DETACHED_CATEGORIES: get_modulestore(parent.location).update_children( parent_location, parent.children + [dest_location.url()]) course_location = loc_mapper().translate_locator_to_location( parent_locator, get_course=True) locator = loc_mapper().translate_location(course_location.course_id, dest_location, False, True) return JsonResponse({"locator": unicode(locator)})
def _load_item(self, usage_id, course_entry_override=None): if isinstance(usage_id, BlockUsageLocator) and isinstance(usage_id.usage_id, LocalId): try: return self.local_modules[usage_id] except KeyError: raise ItemNotFoundError json_data = self.module_data.get(usage_id) if json_data is None: # deeper than initial descendant fetch or doesn't exist self.modulestore.cache_items(self, [usage_id], lazy=self.lazy) json_data = self.module_data.get(usage_id) if json_data is None: raise ItemNotFoundError(usage_id) class_ = XModuleDescriptor.load_class(json_data.get("category"), self.default_class) return self.xblock_from_json(class_, usage_id, json_data, course_entry_override)
def _create_item(request): """View for create items.""" parent_locator = BlockUsageLocator(request.json['parent_locator']) parent_location = loc_mapper().translate_locator_to_location(parent_locator) category = request.json['category'] display_name = request.json.get('display_name') if not has_access(request.user, parent_location): raise PermissionDenied() parent = get_modulestore(category).get_item(parent_location) # Necessary to set revision=None or else metadata inheritance does not work # (the ID with @draft will be used as the key in the inherited metadata map, # and that is not expected by the code that later references it). dest_location = parent_location.replace(category=category, name=uuid4().hex, revision=None) # get the metadata, display_name, and definition from the request metadata = {} data = None template_id = request.json.get('boilerplate') if template_id is not None: clz = XModuleDescriptor.load_class(category) if clz is not None: template = clz.get_template(template_id) if template is not None: metadata = template.get('metadata', {}) data = template.get('data') if display_name is not None: metadata['display_name'] = display_name get_modulestore(category).create_and_save_xmodule( dest_location, definition_data=data, metadata=metadata, system=parent.system, ) if category not in DETACHED_CATEGORIES: get_modulestore(parent.location).update_children(parent_location, parent.children + [dest_location.url()]) course_location = loc_mapper().translate_locator_to_location(parent_locator, get_course=True) locator = loc_mapper().translate_location(course_location.course_id, dest_location, False, True) return JsonResponse({'id': dest_location.url(), "locator": unicode(locator)})
def _create_item(request): """View for create items.""" parent_locator = BlockUsageLocator(request.json['parent_locator']) parent_location = loc_mapper().translate_locator_to_location(parent_locator) try: category = request.json['category'] except KeyError: category = 'problem' display_name = request.json.get('display_name') if not has_access(request.user, parent_location): raise PermissionDenied() parent = get_modulestore(category).get_item(parent_location) dest_location = parent_location.replace(category=category, name=uuid4().hex) # get the metadata, display_name, and definition from the request metadata = {} data = None template_id = request.json.get('boilerplate') if template_id is not None: clz = XModuleDescriptor.load_class(category) if clz is not None: template = clz.get_template(template_id) if template is not None: metadata = template.get('metadata', {}) data = template.get('data') if display_name is not None: metadata['display_name'] = display_name get_modulestore(category).create_and_save_xmodule( dest_location, definition_data=data, metadata=metadata, system=parent.system, ) if category not in DETACHED_CATEGORIES: get_modulestore(parent.location).update_children(parent_location, parent.children + [dest_location.url()]) course_location = loc_mapper().translate_locator_to_location(parent_locator, get_course=True) locator = loc_mapper().translate_location(course_location.course_id, dest_location, False, True) return JsonResponse({"locator": unicode(locator)})
def create_xmodule(self, location, definition_data=None, metadata=None, system=None): """ Create the new xmodule but don't save it. Returns the new module. :param location: a Location--must have a category :param definition_data: can be empty. The initial definition_data for the kvs :param metadata: can be empty, the initial metadata for the kvs :param system: if you already have an xblock from the course, the xblock.runtime value """ if not isinstance(location, Location): location = Location(location) # differs from split mongo in that I believe most of this logic should be above the persistence # layer but added it here to enable quick conversion. I'll need to reconcile these. if metadata is None: metadata = {} if system is None: system = CachingDescriptorSystem( modulestore=self, module_data={}, default_class=self.default_class, resources_fs=None, error_tracker=self.error_tracker, render_template=self.render_template, cached_metadata={}, mixins=self.xblock_mixins, ) xblock_class = XModuleDescriptor.load_class(location.category, self.default_class) if definition_data is None: if hasattr(xblock_class, 'data') and xblock_class.data.default is not None: definition_data = xblock_class.data.default else: definition_data = {} dbmodel = self._create_new_field_data(location.category, location, definition_data, metadata) xmodule = system.construct_xblock_from_class( xblock_class, dbmodel, # We're loading a descriptor, so student_id is meaningless # We also don't have separate notions of definition and usage ids yet, # so we use the location for both. ScopeIds(None, location.category, location, location) ) # decache any pending field settings from init xmodule.save() return xmodule
def load_from_json(json_data, system, default_class=None, parent_xblock=None): """ This method instantiates the correct subclass of XModuleDescriptor based on the contents of json_data. It does not persist it and can create one which has no usage id. parent_xblock is used to compute inherited metadata as well as to append the new xblock. json_data: - 'location' : must have this field - 'category': the xmodule category (required or location must be a Location) - 'metadata': a dict of locally set metadata (not inherited) - 'children': a list of children's usage_ids w/in this course - 'definition': - '_id' (optional): the usage_id of this. Will generate one if not given one. """ class_ = XModuleDescriptor.load_class( json_data.get('category', json_data.get('location', {}).get('category')), default_class) usage_id = json_data.get('_id', None) if not '_inherited_settings' in json_data and parent_xblock is not None: json_data[ '_inherited_settings'] = parent_xblock.xblock_kvs.get_inherited_settings( ).copy() json_fields = json_data.get('fields', {}) for field in inheritance.INHERITABLE_METADATA: if field in json_fields: json_data['_inherited_settings'][field] = json_fields[ field] new_block = system.xblock_from_json(class_, usage_id, json_data) if parent_xblock is not None: children = parent_xblock.children children.append(new_block) # trigger setter method by using top level field access parent_xblock.children = children # decache pending children field settings (Note, truly persisting at this point would break b/c # persistence assumes children is a list of ids not actual xblocks) parent_xblock.save() return new_block
def create_item(request): """View for create items.""" parent_location = Location(request.json['parent_location']) category = request.json['category'] display_name = request.json.get('display_name') if not has_access(request.user, parent_location): raise PermissionDenied() parent = get_modulestore(category).get_item(parent_location) dest_location = parent_location.replace(category=category, name=uuid4().hex) # get the metadata, display_name, and definition from the request metadata = {} data = None template_id = request.json.get('boilerplate') if template_id is not None: clz = XModuleDescriptor.load_class(category) if clz is not None: template = clz.get_template(template_id) if template is not None: metadata = template.get('metadata', {}) data = template.get('data') if display_name is not None: metadata['display_name'] = display_name get_modulestore(category).create_and_save_xmodule( dest_location, definition_data=data, metadata=metadata, system=parent.system, ) if category not in DETACHED_CATEGORIES: get_modulestore(parent.location).update_children(parent_location, parent.children + [dest_location.url()]) locator = loc_mapper().translate_location( get_course_for_item(parent_location).location.course_id, dest_location, False, True ) return JsonResponse({'id': dest_location.url(), "update_url": locator.url_reverse("xblock")})
def _load_item(self, usage_id, course_entry_override=None): if isinstance(usage_id, BlockUsageLocator) and isinstance( usage_id.usage_id, LocalId): try: return self.local_modules[usage_id] except KeyError: raise ItemNotFoundError json_data = self.module_data.get(usage_id) if json_data is None: # deeper than initial descendant fetch or doesn't exist self.modulestore.cache_items(self, [usage_id], lazy=self.lazy) json_data = self.module_data.get(usage_id) if json_data is None: raise ItemNotFoundError(usage_id) class_ = XModuleDescriptor.load_class(json_data.get('category'), self.default_class) return self.xblock_from_json(class_, usage_id, json_data, course_entry_override)
def create_item(request): parent_location = Location(request.POST['parent_location']) category = request.POST['category'] display_name = request.POST.get('display_name') if not has_access(request.user, parent_location): raise PermissionDenied() parent = get_modulestore(category).get_item(parent_location) dest_location = parent_location.replace(category=category, name=uuid4().hex) # get the metadata, display_name, and definition from the request metadata = {} data = None template_id = request.POST.get('boilerplate') if template_id is not None: clz = XModuleDescriptor.load_class(category) if clz is not None: template = clz.get_template(template_id) if template is not None: metadata = template.get('metadata', {}) data = template.get('data') if display_name is not None: metadata['display_name'] = display_name get_modulestore(category).create_and_save_xmodule( dest_location, definition_data=data, metadata=metadata, system=parent.system, ) if category not in DETACHED_CATEGORIES: get_modulestore(parent.location).update_children( parent_location, parent.children + [dest_location.url()]) return JsonResponse({'id': dest_location.url()})
def load_from_json(json_data, system, default_class=None, parent_xblock=None): """ This method instantiates the correct subclass of XModuleDescriptor based on the contents of json_data. It does not persist it and can create one which has no usage id. parent_xblock is used to compute inherited metadata as well as to append the new xblock. json_data: - 'location' : must have this field - 'category': the xmodule category (required or location must be a Location) - 'metadata': a dict of locally set metadata (not inherited) - 'children': a list of children's usage_ids w/in this course - 'definition': - '_id' (optional): the usage_id of this. Will generate one if not given one. """ class_ = XModuleDescriptor.load_class( json_data.get('category', json_data.get('location', {}).get('category')), default_class ) usage_id = json_data.get('_id', None) if not '_inherited_settings' in json_data and parent_xblock is not None: json_data['_inherited_settings'] = parent_xblock.xblock_kvs.get_inherited_settings().copy() json_fields = json_data.get('fields', {}) for field in inheritance.INHERITABLE_METADATA: if field in json_fields: json_data['_inherited_settings'][field] = json_fields[field] new_block = system.xblock_from_json(class_, usage_id, json_data) if parent_xblock is not None: children = parent_xblock.children children.append(new_block) # trigger setter method by using top level field access parent_xblock.children = children # decache pending children field settings (Note, truly persisting at this point would break b/c # persistence assumes children is a list of ids not actual xblocks) parent_xblock.save() return new_block
def test_load_class(self): vc = XModuleDescriptor.load_class('video') vc_str = "<class 'xmodule.video_module.VideoDescriptor'>" self.assertEqual(str(vc), vc_str)
def test_load_class(self): vc = XModuleDescriptor.load_class('sequential') vc_str = "<class 'xmodule.seq_module.SequenceDescriptor'>" assert str(vc) == vc_str
def edit_unit(request, location): """ Display an editing page for the specified module. Expects a GET request with the parameter `id`. id: A Location URL """ try: course = get_course_for_item(location) except InvalidLocationError: return HttpResponseBadRequest() if not has_access(request.user, course.location): raise PermissionDenied() try: item = modulestore().get_item(location, depth=1) except ItemNotFoundError: return HttpResponseBadRequest() lms_link = get_lms_link_for_item( item.location, course_id=course.location.course_id ) component_templates = defaultdict(list) for category in COMPONENT_TYPES: component_class = XModuleDescriptor.load_class(category) # add the default template component_templates[category].append(( component_class.display_name.default or 'Blank', category, False, # No defaults have markdown (hardcoded current default) None # no boilerplate for overrides )) # add boilerplates for template in component_class.templates(): component_templates[category].append(( template['metadata'].get('display_name'), category, template['metadata'].get('markdown') is not None, template.get('template_id') )) # Check if there are any advanced modules specified in the course policy. # These modules should be specified as a list of strings, where the strings # are the names of the modules in ADVANCED_COMPONENT_TYPES that should be # enabled for the course. course_advanced_keys = course.advanced_modules # Set component types according to course policy file if isinstance(course_advanced_keys, list): for category in course_advanced_keys: if category in ADVANCED_COMPONENT_TYPES: # Do I need to allow for boilerplates or just defaults on the # class? i.e., can an advanced have more than one entry in the # menu? one for default and others for prefilled boilerplates? try: component_class = XModuleDescriptor.load_class(category) component_templates['advanced'].append(( component_class.display_name.default or category, category, False, None # don't override default data )) except PluginMissingError: # dhm: I got this once but it can happen any time the # course author configures an advanced component which does # not exist on the server. This code here merely # prevents any authors from trying to instantiate the # non-existent component type by not showing it in the menu pass else: log.error( "Improper format for course advanced keys! %", course_advanced_keys ) components = [ component.location.url() for component in item.get_children() ] # TODO (cpennington): If we share units between courses, # this will need to change to check permissions correctly so as # to pick the correct parent subsection containing_subsection_locs = modulestore().get_parent_locations( location, None ) containing_subsection = modulestore().get_item(containing_subsection_locs[0]) containing_section_locs = modulestore().get_parent_locations( containing_subsection.location, None ) containing_section = modulestore().get_item(containing_section_locs[0]) # cdodge hack. We're having trouble previewing drafts via jump_to redirect # so let's generate the link url here # need to figure out where this item is in the list of children as the # preview will need this index = 1 for child in containing_subsection.get_children(): if child.location == item.location: break index = index + 1 preview_lms_base = settings.MITX_FEATURES.get('PREVIEW_LMS_BASE') preview_lms_link = ( '//{preview_lms_base}/courses/{org}/{course}/' '{course_name}/courseware/{section}/{subsection}/{index}' ).format( preview_lms_base=preview_lms_base, lms_base=settings.LMS_BASE, org=course.location.org, course=course.location.course, course_name=course.location.name, section=containing_section.location.name, subsection=containing_subsection.location.name, index=index ) unit_state = compute_unit_state(item) return render_to_response('unit.html', { 'context_course': course, 'unit': item, 'unit_location': location, 'components': components, 'component_templates': component_templates, 'draft_preview_link': preview_lms_link, 'published_preview_link': lms_link, 'subsection': containing_subsection, 'release_date': get_default_time_display(containing_subsection.lms.start) if containing_subsection.lms.start is not None else None, 'section': containing_section, 'new_unit_category': 'vertical', 'unit_state': unit_state, 'published_date': get_default_time_display(item.cms.published_date) if item.cms.published_date is not None else None })
def test_load_class(self): vc = XModuleDescriptor.load_class('sequential') vc_str = "<class 'xmodule.seq_module.SequenceDescriptor'>" self.assertEqual(str(vc), vc_str)
def _create(cls, target_class, **kwargs): """ Uses ``**kwargs``: :parent_location: (required): the location of the parent module (e.g. the parent course or section) :category: the category of the resulting item. :data: (optional): the data for the item (e.g. XML problem definition for a problem item) :display_name: (optional): the display name of the item :metadata: (optional): dictionary of metadata attributes :boilerplate: (optional) the boilerplate for overriding field values :target_class: is ignored """ # All class attributes (from this class and base classes) are # passed in via **kwargs. However, some of those aren't actual field values, # so pop those off for use separately DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info'] # catch any old style users before they get into trouble assert 'template' not in kwargs parent_location = Location(kwargs.pop('parent_location', None)) data = kwargs.pop('data', None) category = kwargs.pop('category', None) display_name = kwargs.pop('display_name', None) metadata = kwargs.pop('metadata', {}) location = kwargs.pop('location') assert location != parent_location store = kwargs.pop('modulestore') # This code was based off that in cms/djangoapps/contentstore/views.py parent = kwargs.pop('parent', None) or store.get_item(parent_location) if 'boilerplate' in kwargs: template_id = kwargs.pop('boilerplate') clz = XModuleDescriptor.load_class(category) template = clz.get_template(template_id) assert template is not None metadata.update(template.get('metadata', {})) if not isinstance(data, basestring): data.update(template.get('data')) # replace the display name with an optional parameter passed in from the caller if display_name is not None: metadata['display_name'] = display_name module = store.create_and_save_xmodule(location, metadata=metadata, definition_data=data) module = store.get_item(location) for attr, val in kwargs.items(): setattr(module, attr, val) module.save() store.save_xmodule(module) if location.category not in DETACHED_CATEGORIES: parent.children.append(location.url()) store.update_children(parent_location, parent.children) return store.get_item(location)
def test_load_class(self): vc = XModuleDescriptor.load_class('video') vc_str = "<class 'xmodule.video_module.video_module.VideoDescriptor'>" self.assertEqual(str(vc), vc_str)
def _create(cls, target_class, **kwargs): """ Uses ``**kwargs``: :parent_location: (required): the location of the parent module (e.g. the parent course or section) :category: the category of the resulting item. :data: (optional): the data for the item (e.g. XML problem definition for a problem item) :display_name: (optional): the display name of the item :metadata: (optional): dictionary of metadata attributes :boilerplate: (optional) the boilerplate for overriding field values :target_class: is ignored """ # All class attributes (from this class and base classes) are # passed in via **kwargs. However, some of those aren't actual field values, # so pop those off for use separately DETACHED_CATEGORIES = ["about", "static_tab", "course_info"] # catch any old style users before they get into trouble assert "template" not in kwargs parent_location = Location(kwargs.pop("parent_location", None)) data = kwargs.pop("data", None) category = kwargs.pop("category", None) display_name = kwargs.pop("display_name", None) metadata = kwargs.pop("metadata", {}) location = kwargs.pop("location") assert location != parent_location store = kwargs.pop("modulestore") # This code was based off that in cms/djangoapps/contentstore/views.py parent = kwargs.pop("parent", None) or store.get_item(parent_location) if "boilerplate" in kwargs: template_id = kwargs.pop("boilerplate") clz = XModuleDescriptor.load_class(category) template = clz.get_template(template_id) assert template is not None metadata.update(template.get("metadata", {})) if not isinstance(data, basestring): data.update(template.get("data")) # replace the display name with an optional parameter passed in from the caller if display_name is not None: metadata["display_name"] = display_name module = store.create_and_save_xmodule(location, metadata=metadata, definition_data=data) module = store.get_item(location) for attr, val in kwargs.items(): setattr(module, attr, val) module.save() store.save_xmodule(module) if location.category not in DETACHED_CATEGORIES: parent.children.append(location.url()) store.update_children(parent_location, parent.children) return store.get_item(location)