* feat: add xblock endpoint for updating an xblock fix: remove debugger feat: make function call more generic refactor: just use request.json for request data as before refactor: extract method fix: revert wrong method change fix: refactor correct method feat: use handle_xblock method so that we can do more than update xblocks fix: usage_key_string defaults to None add all CRUD operations fix usage key parameter refactor: create /views folder refactor: move xblock view functions to xblock_services fix: tests fix: tests refactor: move xblock API endpoint to contentstore * docs: add explanatory comment to new xblock_service * feat: add feature flag for enabling content editing api * feat: raise 404 if studio content api is disabled * tests: test xblock endpoint * test: make all post tests work * test: check that xblock_handler receives correct args * refactor: create util mixin for course factories with staff * refactor: extract course staff authorization tests * refactor: extract tests to api view testcase class * test: add get tests * test: fix tests * test: fix tests * test: fix tests * test: add all crud tests * fix: refactor to fix tests * fix: merge conflict * fix: merge conflict * fix: tests after merge * fix: json request decorator * fix: lint * fix: lint * fix: lint * fix: lint * fix: new test files * fix: lint * fix: lint and apply PR suggestions * fix: lint * fix: lint * fix: lint * fix: lint * fix: lint * fix: lint
112 lines
4.4 KiB
Python
112 lines
4.4 KiB
Python
"""
|
|
Xblock services for creating xblocks.
|
|
"""
|
|
|
|
from uuid import uuid4
|
|
|
|
from django.utils.translation import gettext as _
|
|
from xmodule.modulestore.django import modulestore
|
|
from xmodule.tabs import StaticTab
|
|
|
|
from cms.djangoapps.models.settings.course_grading import CourseGradingModel
|
|
from openedx.core.toggles import ENTRANCE_EXAMS
|
|
|
|
from .xblock_helpers import usage_key_with_run
|
|
from ..helpers import GRADER_TYPES, remove_entrance_exam_graders
|
|
|
|
|
|
def create_xblock(parent_locator, user, category, display_name, boilerplate=None, is_entrance_exam=False):
|
|
"""
|
|
Performs the actual grunt work of creating items/xblocks -- knows nothing about requests, views, etc.
|
|
"""
|
|
store = modulestore()
|
|
usage_key = usage_key_with_run(parent_locator)
|
|
with store.bulk_operations(usage_key.course_key):
|
|
parent = store.get_item(usage_key)
|
|
dest_usage_key = usage_key.replace(category=category, name=uuid4().hex)
|
|
|
|
# get the metadata, display_name, and definition from the caller
|
|
metadata = {}
|
|
data = None
|
|
template_id = boilerplate
|
|
if template_id:
|
|
clz = parent.runtime.load_block_type(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
|
|
|
|
# We should use the 'fields' kwarg for newer block settings/values (vs. metadata or data)
|
|
fields = {}
|
|
|
|
# Entrance Exams: Chapter module positioning
|
|
child_position = None
|
|
if ENTRANCE_EXAMS.is_enabled():
|
|
if category == 'chapter' and is_entrance_exam:
|
|
fields['is_entrance_exam'] = is_entrance_exam
|
|
fields['in_entrance_exam'] = True # Inherited metadata, all children will have it
|
|
child_position = 0
|
|
|
|
# TODO need to fix components that are sending definition_data as strings, instead of as dicts
|
|
# For now, migrate them into dicts here.
|
|
if isinstance(data, str):
|
|
data = {'data': data}
|
|
|
|
created_block = store.create_child(
|
|
user.id,
|
|
usage_key,
|
|
dest_usage_key.block_type,
|
|
block_id=dest_usage_key.block_id,
|
|
fields=fields,
|
|
definition_data=data,
|
|
metadata=metadata,
|
|
runtime=parent.runtime,
|
|
position=child_position,
|
|
)
|
|
|
|
# Entrance Exams: Grader assignment
|
|
if ENTRANCE_EXAMS.is_enabled():
|
|
course_key = usage_key.course_key
|
|
course = store.get_course(course_key)
|
|
if hasattr(course, 'entrance_exam_enabled') and course.entrance_exam_enabled:
|
|
if category == 'sequential' and parent_locator == course.entrance_exam_id:
|
|
# Clean up any pre-existing entrance exam graders
|
|
remove_entrance_exam_graders(course_key, user)
|
|
grader = {
|
|
"type": GRADER_TYPES['ENTRANCE_EXAM'],
|
|
"min_count": 0,
|
|
"drop_count": 0,
|
|
"short_label": "Entrance",
|
|
"weight": 0
|
|
}
|
|
grading_model = CourseGradingModel.update_grader_from_json(
|
|
course.id,
|
|
grader,
|
|
user
|
|
)
|
|
CourseGradingModel.update_section_grader_type(
|
|
created_block,
|
|
grading_model['type'],
|
|
user
|
|
)
|
|
|
|
# VS[compat] cdodge: This is a hack because static_tabs also have references from the course block, so
|
|
# if we add one then we need to also add it to the policy information (i.e. metadata)
|
|
# we should remove this once we can break this reference from the course to static tabs
|
|
if category == 'static_tab':
|
|
display_name = display_name or _("Empty") # Prevent name being None
|
|
course = store.get_course(dest_usage_key.course_key)
|
|
course.tabs.append(
|
|
StaticTab(
|
|
name=display_name,
|
|
url_slug=dest_usage_key.block_id,
|
|
)
|
|
)
|
|
store.update_item(course, user.id)
|
|
|
|
return created_block
|