Teach LMS how to render XBlockAsides
[PLAT-217]
This commit is contained in:
@@ -12,16 +12,17 @@ from .models import (
|
||||
XModuleStudentInfoField
|
||||
)
|
||||
import logging
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey, Location
|
||||
from opaque_keys.edx.keys import CourseKey, UsageKey
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from opaque_keys.edx.block_types import BlockTypeKeyV1
|
||||
from opaque_keys.edx.asides import AsideUsageKeyV1
|
||||
|
||||
from django.db import DatabaseError
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from xblock.runtime import KeyValueStore
|
||||
from xblock.exceptions import KeyValueMultiSaveError, InvalidScopeError
|
||||
from xblock.fields import Scope, UserScope
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xblock.core import XBlockAside
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -46,7 +47,7 @@ class FieldDataCache(object):
|
||||
A cache of django model objects needed to supply the data
|
||||
for a module and its decendants
|
||||
"""
|
||||
def __init__(self, descriptors, course_id, user, select_for_update=False):
|
||||
def __init__(self, descriptors, course_id, user, select_for_update=False, asides=None):
|
||||
'''
|
||||
Find any courseware.models objects that are needed by any descriptor
|
||||
in descriptors. Attempts to minimize the number of queries to the database.
|
||||
@@ -58,11 +59,17 @@ class FieldDataCache(object):
|
||||
course_id: The id of the current course
|
||||
user: The user for which to cache data
|
||||
select_for_update: True if rows should be locked until end of transaction
|
||||
asides: The list of aside types to load, or None to prefetch no asides.
|
||||
'''
|
||||
self.cache = {}
|
||||
self.descriptors = descriptors
|
||||
self.select_for_update = select_for_update
|
||||
|
||||
if asides is None:
|
||||
self.asides = []
|
||||
else:
|
||||
self.asides = asides
|
||||
|
||||
assert isinstance(course_id, CourseKey)
|
||||
self.course_id = course_id
|
||||
self.user = user
|
||||
@@ -75,7 +82,7 @@ class FieldDataCache(object):
|
||||
@classmethod
|
||||
def cache_for_descriptor_descendents(cls, course_id, user, descriptor, depth=None,
|
||||
descriptor_filter=lambda descriptor: True,
|
||||
select_for_update=False):
|
||||
select_for_update=False, asides=None):
|
||||
"""
|
||||
course_id: the course in the context of which we want StudentModules.
|
||||
user: the django user for whom to load modules.
|
||||
@@ -113,7 +120,7 @@ class FieldDataCache(object):
|
||||
with modulestore().bulk_operations(descriptor.location.course_key):
|
||||
descriptors = get_child_descriptors(descriptor, depth, descriptor_filter)
|
||||
|
||||
return FieldDataCache(descriptors, course_id, user, select_for_update)
|
||||
return FieldDataCache(descriptors, course_id, user, select_for_update, asides=asides)
|
||||
|
||||
def _query(self, model_class, **kwargs):
|
||||
"""
|
||||
@@ -140,6 +147,35 @@ class FieldDataCache(object):
|
||||
)
|
||||
return res
|
||||
|
||||
@property
|
||||
def _all_usage_ids(self):
|
||||
"""
|
||||
Return a set of all usage_ids for the descriptors that this FieldDataCache is caching
|
||||
against, and well as all asides for those descriptors.
|
||||
"""
|
||||
usage_ids = set()
|
||||
for descriptor in self.descriptors:
|
||||
usage_ids.add(descriptor.scope_ids.usage_id)
|
||||
|
||||
for aside_type in self.asides:
|
||||
usage_ids.add(AsideUsageKeyV1(descriptor.scope_ids.usage_id, aside_type))
|
||||
|
||||
return usage_ids
|
||||
|
||||
@property
|
||||
def _all_block_types(self):
|
||||
"""
|
||||
Return a set of all block_types that are cached by this FieldDataCache.
|
||||
"""
|
||||
block_types = set()
|
||||
for descriptor in self.descriptors:
|
||||
block_types.add(BlockTypeKeyV1(descriptor.entry_point, descriptor.scope_ids.block_type))
|
||||
|
||||
for aside_type in self.asides:
|
||||
block_types.add(BlockTypeKeyV1(XBlockAside.entry_point, aside_type))
|
||||
|
||||
return block_types
|
||||
|
||||
def _retrieve_fields(self, scope, fields):
|
||||
"""
|
||||
Queries the database for all of the fields in the specified scope
|
||||
@@ -148,7 +184,7 @@ class FieldDataCache(object):
|
||||
return self._chunked_query(
|
||||
StudentModule,
|
||||
'module_state_key__in',
|
||||
(descriptor.scope_ids.usage_id for descriptor in self.descriptors),
|
||||
self._all_usage_ids,
|
||||
course_id=self.course_id,
|
||||
student=self.user.pk,
|
||||
)
|
||||
@@ -156,14 +192,14 @@ class FieldDataCache(object):
|
||||
return self._chunked_query(
|
||||
XModuleUserStateSummaryField,
|
||||
'usage_id__in',
|
||||
(descriptor.scope_ids.usage_id for descriptor in self.descriptors),
|
||||
self._all_usage_ids,
|
||||
field_name__in=set(field.name for field in fields),
|
||||
)
|
||||
elif scope == Scope.preferences:
|
||||
return self._chunked_query(
|
||||
XModuleStudentPrefsField,
|
||||
'module_type__in',
|
||||
set(descriptor.scope_ids.block_type for descriptor in self.descriptors),
|
||||
self._all_block_types,
|
||||
student=self.user.pk,
|
||||
field_name__in=set(field.name for field in fields),
|
||||
)
|
||||
@@ -195,7 +231,7 @@ class FieldDataCache(object):
|
||||
elif key.scope == Scope.user_state_summary:
|
||||
return (key.scope, key.block_scope_id, key.field_name)
|
||||
elif key.scope == Scope.preferences:
|
||||
return (key.scope, key.block_scope_id, key.field_name)
|
||||
return (key.scope, BlockTypeKeyV1(key.block_family, key.block_scope_id), key.field_name)
|
||||
elif key.scope == Scope.user_info:
|
||||
return (key.scope, key.field_name)
|
||||
|
||||
@@ -239,31 +275,28 @@ class FieldDataCache(object):
|
||||
return field_object
|
||||
|
||||
if key.scope == Scope.user_state:
|
||||
# When we start allowing block_scope_ids to be either Locations or Locators,
|
||||
# this assertion will fail. Fix the code here when that happens!
|
||||
assert(isinstance(key.block_scope_id, UsageKey))
|
||||
field_object, _ = StudentModule.objects.get_or_create(
|
||||
field_object, __ = StudentModule.objects.get_or_create(
|
||||
course_id=self.course_id,
|
||||
student_id=key.user_id,
|
||||
module_state_key=key.block_scope_id,
|
||||
defaults={
|
||||
'state': json.dumps({}),
|
||||
'module_type': key.block_scope_id.category,
|
||||
'module_type': key.block_scope_id.block_type,
|
||||
},
|
||||
)
|
||||
elif key.scope == Scope.user_state_summary:
|
||||
field_object, _ = XModuleUserStateSummaryField.objects.get_or_create(
|
||||
field_object, __ = XModuleUserStateSummaryField.objects.get_or_create(
|
||||
field_name=key.field_name,
|
||||
usage_id=key.block_scope_id
|
||||
)
|
||||
elif key.scope == Scope.preferences:
|
||||
field_object, _ = XModuleStudentPrefsField.objects.get_or_create(
|
||||
field_object, __ = XModuleStudentPrefsField.objects.get_or_create(
|
||||
field_name=key.field_name,
|
||||
module_type=key.block_scope_id,
|
||||
module_type=BlockTypeKeyV1(key.block_family, key.block_scope_id),
|
||||
student_id=key.user_id,
|
||||
)
|
||||
elif key.scope == Scope.user_info:
|
||||
field_object, _ = XModuleStudentInfoField.objects.get_or_create(
|
||||
field_object, __ = XModuleStudentInfoField.objects.get_or_create(
|
||||
field_name=key.field_name,
|
||||
student_id=key.user_id,
|
||||
)
|
||||
|
||||
@@ -18,7 +18,7 @@ from django.db import models
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
from xmodule_django.models import CourseKeyField, LocationKeyField
|
||||
from xmodule_django.models import CourseKeyField, LocationKeyField, BlockTypeKeyField
|
||||
|
||||
|
||||
class StudentModule(models.Model):
|
||||
@@ -36,10 +36,7 @@ class StudentModule(models.Model):
|
||||
## These three are the key for the object
|
||||
module_type = models.CharField(max_length=32, choices=MODULE_TYPES, default='problem', db_index=True)
|
||||
|
||||
# Key used to share state. By default, this is the module_id,
|
||||
# but for abtests and the like, this can be set to a shared value
|
||||
# for many instances of the module.
|
||||
# Filename for homeworks, etc.
|
||||
# Key used to share state. This is the XBlock usage_id
|
||||
module_state_key = LocationKeyField(max_length=255, db_index=True, db_column='module_id')
|
||||
student = models.ForeignKey(User, db_index=True)
|
||||
|
||||
@@ -150,7 +147,7 @@ class XBlockFieldBase(models.Model):
|
||||
return u'{}<{!r}'.format(
|
||||
self.__class__.__name__,
|
||||
{
|
||||
key:getattr(self, key)
|
||||
key: getattr(self, key)
|
||||
for key in self._meta.get_all_field_names()
|
||||
if key not in ('created', 'modified')
|
||||
}
|
||||
@@ -174,11 +171,11 @@ class XModuleStudentPrefsField(XBlockFieldBase):
|
||||
Stores data set in the Scope.preferences scope by an xmodule field
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
class Meta: # pylint: disable=missing-docstring
|
||||
unique_together = (('student', 'module_type', 'field_name'),)
|
||||
|
||||
# The type of the module for these preferences
|
||||
module_type = models.CharField(max_length=64, db_index=True)
|
||||
module_type = BlockTypeKeyField(max_length=64, db_index=True)
|
||||
|
||||
student = models.ForeignKey(User, db_index=True)
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ from courseware.masquerade import setup_masquerade
|
||||
from courseware.model_data import FieldDataCache, DjangoKeyValueStore
|
||||
from lms.djangoapps.lms_xblock.field_data import LmsFieldData
|
||||
from lms.djangoapps.lms_xblock.runtime import LmsModuleSystem, unquote_slashes, quote_slashes
|
||||
from lms.djangoapps.lms_xblock.models import XBlockAsidesConfig
|
||||
from edxmako.shortcuts import render_to_string
|
||||
from eventtracking import tracker
|
||||
from psychometrics.psychoanalyze import make_psychometrics_data_update_handler
|
||||
@@ -405,7 +406,8 @@ def get_module_system_for_user(user, field_data_cache,
|
||||
field_data_cache_real_user = FieldDataCache.cache_for_descriptor_descendents(
|
||||
course_id,
|
||||
real_user,
|
||||
module.descriptor
|
||||
module.descriptor,
|
||||
asides=XBlockAsidesConfig.possible_asides(),
|
||||
)
|
||||
|
||||
(inner_system, inner_student_data) = get_module_system_for_user(
|
||||
@@ -496,6 +498,8 @@ def get_module_system_for_user(user, field_data_cache,
|
||||
else:
|
||||
anonymous_student_id = anonymous_id_for_user(user, None)
|
||||
|
||||
field_data = LmsFieldData(descriptor._field_data, student_data) # pylint: disable=protected-access
|
||||
|
||||
system = LmsModuleSystem(
|
||||
track_function=track_function,
|
||||
render_template=render_to_string,
|
||||
@@ -541,11 +545,13 @@ def get_module_system_for_user(user, field_data_cache,
|
||||
services={
|
||||
'i18n': ModuleI18nService(),
|
||||
'fs': xblock.reference.plugins.FSService(),
|
||||
'field-data': field_data,
|
||||
},
|
||||
get_user_role=lambda: get_user_role(user, course_id),
|
||||
descriptor_runtime=descriptor.runtime,
|
||||
rebind_noauth_module_to_user=rebind_noauth_module_to_user,
|
||||
user_location=user_location,
|
||||
request_token=request_token,
|
||||
)
|
||||
|
||||
# pass position specified in URL to module through ModuleSystem
|
||||
@@ -572,7 +578,7 @@ def get_module_system_for_user(user, field_data_cache,
|
||||
else:
|
||||
system.error_descriptor_class = NonStaffErrorDescriptor
|
||||
|
||||
return system, student_data
|
||||
return system, field_data
|
||||
|
||||
|
||||
def get_module_for_descriptor_internal(user, descriptor, field_data_cache, course_id, # pylint: disable=invalid-name
|
||||
@@ -594,7 +600,7 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours
|
||||
if not has_access(user, 'load', descriptor, course_id):
|
||||
return None
|
||||
|
||||
(system, student_data) = get_module_system_for_user(
|
||||
(system, field_data) = get_module_system_for_user(
|
||||
user=user,
|
||||
field_data_cache=field_data_cache, # These have implicit user bindings, the rest of args are considered not to
|
||||
descriptor=descriptor,
|
||||
@@ -609,7 +615,7 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours
|
||||
request_token=request_token
|
||||
)
|
||||
|
||||
descriptor.bind_for_student(system, LmsFieldData(descriptor._field_data, student_data)) # pylint: disable=protected-access
|
||||
descriptor.bind_for_student(system, field_data) # pylint: disable=protected-access
|
||||
descriptor.scope_ids = descriptor.scope_ids._replace(user_id=user.id) # pylint: disable=protected-access
|
||||
return descriptor
|
||||
|
||||
|
||||
@@ -16,9 +16,10 @@ from courseware.tests.factories import UserStateSummaryFactory
|
||||
from courseware.tests.factories import StudentPrefsFactory, StudentInfoFactory
|
||||
|
||||
from xblock.fields import Scope, BlockScope, ScopeIds
|
||||
from xblock.exceptions import KeyValueMultiSaveError
|
||||
from xblock.core import XBlock
|
||||
from django.test import TestCase
|
||||
from django.db import DatabaseError
|
||||
from xblock.exceptions import KeyValueMultiSaveError
|
||||
|
||||
|
||||
def mock_field(scope, name):
|
||||
@@ -29,7 +30,7 @@ def mock_field(scope, name):
|
||||
|
||||
|
||||
def mock_descriptor(fields=[]):
|
||||
descriptor = Mock()
|
||||
descriptor = Mock(entry_point=XBlock.entry_point)
|
||||
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
|
||||
|
||||
@@ -39,6 +39,8 @@ from .module_render import toc_for_course, get_module_for_descriptor, get_module
|
||||
from courseware.models import StudentModule, StudentModuleHistory
|
||||
from course_modes.models import CourseMode
|
||||
|
||||
from lms.djangoapps.lms_xblock.models import XBlockAsidesConfig
|
||||
|
||||
from open_ended_grading import open_ended_notifications
|
||||
from student.models import UserTestGroup, CourseEnrollment
|
||||
from student.views import single_course_reverification_info, is_course_blocked
|
||||
@@ -444,7 +446,8 @@ def _index_bulk_op(request, course_key, chapter, section, position):
|
||||
# Load all descendants of the section, because we're going to display its
|
||||
# html, which in general will need all of its children
|
||||
section_field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
course_key, user, section_descriptor, depth=None)
|
||||
course_key, user, section_descriptor, depth=None, asides=XBlockAsidesConfig.possible_asides()
|
||||
)
|
||||
|
||||
# Verify that position a string is in fact an int
|
||||
if position is not None:
|
||||
|
||||
@@ -18,6 +18,7 @@ from django.utils.html import escape
|
||||
from django.http import Http404
|
||||
from django.conf import settings
|
||||
from util.json_request import JsonResponse
|
||||
from mock import patch
|
||||
|
||||
from lms.djangoapps.lms_xblock.runtime import quote_slashes
|
||||
from xmodule_modifiers import wrap_xblock
|
||||
@@ -323,17 +324,28 @@ def _section_data_download(course, access):
|
||||
return section_data
|
||||
|
||||
|
||||
def null_get_asides(block): # pylint: disable=unused-argument
|
||||
"""
|
||||
get_aside method for monkey-patching into descriptor_global_get_asides
|
||||
while rendering an HtmlDescriptor for email text editing. This returns
|
||||
an empty list.
|
||||
"""
|
||||
return []
|
||||
|
||||
|
||||
def _section_send_email(course, access):
|
||||
""" Provide data for the corresponding bulk email section """
|
||||
course_key = course.id
|
||||
|
||||
# This HtmlDescriptor is only being used to generate a nice text editor.
|
||||
html_module = HtmlDescriptor(
|
||||
course.system,
|
||||
DictFieldData({'data': ''}),
|
||||
ScopeIds(None, None, None, course_key.make_usage_key('html', 'fake'))
|
||||
)
|
||||
fragment = course.system.render(html_module, 'studio_view')
|
||||
# Monkey-patch descriptor_global_get_asides to return no asides for the duration of this render
|
||||
with patch('xmodule.x_module.descriptor_global_get_asides', null_get_asides):
|
||||
# This HtmlDescriptor is only being used to generate a nice text editor.
|
||||
html_module = HtmlDescriptor(
|
||||
course.system,
|
||||
DictFieldData({'data': ''}),
|
||||
ScopeIds(None, None, None, course_key.make_usage_key('html', 'fake'))
|
||||
)
|
||||
fragment = course.system.render(html_module, 'studio_view')
|
||||
fragment = wrap_xblock(
|
||||
'LmsRuntime', html_module, 'studio_view', fragment, None,
|
||||
extra_data={"course-id": unicode(course_key)},
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
"""
|
||||
Django admin dashboard configuration for LMS XBlock infrastructure.
|
||||
"""
|
||||
|
||||
from django.contrib import admin
|
||||
from config_models.admin import ConfigurationModelAdmin
|
||||
from lms.djangoapps.lms_xblock.models import XBlockAsidesConfig
|
||||
|
||||
@@ -16,7 +16,7 @@ class LmsFieldData(SplitFieldData):
|
||||
def __init__(self, authored_data, student_data):
|
||||
# Make sure that we don't repeatedly nest LmsFieldData instances
|
||||
if isinstance(authored_data, LmsFieldData):
|
||||
authored_data = authored_data._authored_data # pylint: disable=protected-member
|
||||
authored_data = authored_data._authored_data # pylint: disable=protected-access
|
||||
else:
|
||||
authored_data = ReadOnlyFieldData(authored_data)
|
||||
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
"""
|
||||
Models used by LMS XBlock infrastructure.
|
||||
|
||||
Includes:
|
||||
XBlockAsidesConfig: A ConfigurationModel for managing how XBlockAsides are
|
||||
rendered in the LMS.
|
||||
"""
|
||||
|
||||
from django.db.models import TextField
|
||||
|
||||
from config_models.models import ConfigurationModel
|
||||
|
||||
from xblock.core import XBlockAside
|
||||
|
||||
|
||||
class XBlockAsidesConfig(ConfigurationModel):
|
||||
"""
|
||||
Configuration for XBlockAsides.
|
||||
@@ -11,3 +22,10 @@ class XBlockAsidesConfig(ConfigurationModel):
|
||||
default="about course_info static_tab",
|
||||
help_text="Space-separated list of XBlocks on which XBlockAsides should never render."
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def possible_asides(cls):
|
||||
"""
|
||||
Return a list of all asides that are enabled across all XBlocks.
|
||||
"""
|
||||
return [aside_type for aside_type, __ in XBlockAside.load_classes()]
|
||||
|
||||
@@ -7,7 +7,9 @@ import xblock.reference.plugins
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from lms.djangoapps.lms_xblock.models import XBlockAsidesConfig
|
||||
from openedx.core.djangoapps.user_api.api import course_tag as user_course_tag_api
|
||||
from xblock.core import XBlockAside
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.x_module import ModuleSystem
|
||||
from xmodule.partitions.partitions_service import PartitionService
|
||||
@@ -87,8 +89,8 @@ class LmsHandlerUrls(object):
|
||||
view_name = 'xblock_handler_noauth'
|
||||
|
||||
url = reverse(view_name, kwargs={
|
||||
'course_id': self.course_id.to_deprecated_string(),
|
||||
'usage_id': quote_slashes(block.scope_ids.usage_id.to_deprecated_string().encode('utf-8')),
|
||||
'course_id': unicode(self.course_id),
|
||||
'usage_id': quote_slashes(unicode(block.scope_ids.usage_id).encode('utf-8')),
|
||||
'handler': handler_name,
|
||||
'suffix': suffix,
|
||||
})
|
||||
@@ -198,4 +200,50 @@ class LmsModuleSystem(LmsHandlerUrls, ModuleSystem): # pylint: disable=abstract
|
||||
track_function=kwargs.get('track_function', None),
|
||||
)
|
||||
services['fs'] = xblock.reference.plugins.FSService()
|
||||
self.request_token = kwargs.pop('request_token', None)
|
||||
super(LmsModuleSystem, self).__init__(**kwargs)
|
||||
|
||||
def wrap_aside(self, block, aside, view, frag, context):
|
||||
"""
|
||||
Creates a div which identifies the aside, points to the original block,
|
||||
and writes out the json_init_args into a script tag.
|
||||
|
||||
The default implementation creates a frag to wraps frag w/ a div identifying the xblock. If you have
|
||||
javascript, you'll need to override this impl
|
||||
"""
|
||||
extra_data = {
|
||||
'block-id': quote_slashes(unicode(block.scope_ids.usage_id)),
|
||||
'url-selector': 'asideBaseUrl',
|
||||
'runtime-class': 'LmsRuntime',
|
||||
}
|
||||
if self.request_token:
|
||||
extra_data['request-token'] = self.request_token
|
||||
|
||||
return self._wrap_ele(
|
||||
aside,
|
||||
view,
|
||||
frag,
|
||||
extra_data,
|
||||
)
|
||||
|
||||
def get_asides(self, block):
|
||||
"""
|
||||
Return all of the asides which might be decorating this `block`.
|
||||
|
||||
Arguments:
|
||||
block (:class:`.XBlock`): The block to render retrieve asides for.
|
||||
"""
|
||||
|
||||
config = XBlockAsidesConfig.current()
|
||||
|
||||
if not config.enabled:
|
||||
return []
|
||||
|
||||
if block.scope_ids.block_type in config.disabled_blocks.split():
|
||||
return []
|
||||
|
||||
return [
|
||||
self.get_aside_of_type(block, aside_type)
|
||||
for aside_type, __
|
||||
in XBlockAside.load_classes()
|
||||
]
|
||||
|
||||
@@ -10,6 +10,7 @@ from unittest import TestCase
|
||||
from urlparse import urlparse
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from lms.djangoapps.lms_xblock.runtime import quote_slashes, unquote_slashes, LmsModuleSystem
|
||||
from xblock.fields import ScopeIds
|
||||
|
||||
TEST_STRINGS = [
|
||||
'',
|
||||
@@ -42,8 +43,7 @@ class TestHandlerUrl(TestCase):
|
||||
"""Test the LMS handler_url"""
|
||||
|
||||
def setUp(self):
|
||||
self.block = Mock()
|
||||
self.block.scope_ids.usage_id.to_deprecated_string.return_value.encode.return_value = 'dummy'
|
||||
self.block = Mock(name='block', scope_ids=ScopeIds(None, None, None, 'dummy'))
|
||||
self.course_key = SlashSeparatedCourseKey("org", "course", "run")
|
||||
self.runtime = LmsModuleSystem(
|
||||
static_url='/static',
|
||||
|
||||
@@ -21,7 +21,7 @@ from xblock.fields import ScopeIds
|
||||
|
||||
from courseware.tests import factories
|
||||
from courseware.tests.helpers import LoginEnrollmentTestCase
|
||||
from lms.lib.xblock.runtime import LmsModuleSystem
|
||||
from lms.djangoapps.lms_xblock.runtime import LmsModuleSystem
|
||||
from student.roles import CourseStaffRole
|
||||
from student.models import unique_id_for_user
|
||||
from xmodule import peer_grading_module
|
||||
|
||||
@@ -1165,7 +1165,7 @@ PIPELINE_JS = {
|
||||
'application': {
|
||||
|
||||
# Application will contain all paths not in courseware_only_js
|
||||
'source_filenames': sorted(common_js) + sorted(project_js) + [
|
||||
'source_filenames': ['js/xblock/core.js'] + sorted(common_js) + sorted(project_js) + [
|
||||
'js/form.ext.js',
|
||||
'js/my_courses_dropdown.js',
|
||||
'js/toggle_login_modal.js',
|
||||
@@ -1514,6 +1514,8 @@ INSTALLED_APPS = (
|
||||
|
||||
# Surveys
|
||||
'survey',
|
||||
|
||||
'lms.djangoapps.lms_xblock',
|
||||
)
|
||||
|
||||
######################### MARKETING SITE ###############################
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
'tender': '//edxedge.tenderapp.com/tender_widget',
|
||||
'coffee/src/ajax_prefix': 'xmodule_js/common_static/coffee/src/ajax_prefix',
|
||||
'xmodule_js/common_static/js/test/add_ajax_prefix': 'xmodule_js/common_static/js/test/add_ajax_prefix',
|
||||
'xblock/core': 'xmodule_js/common_static/coffee/src/xblock/core',
|
||||
'xblock/core': 'xmodule_js/common_static/js/xblock/core',
|
||||
'xblock/runtime.v1': 'xmodule_js/common_static/coffee/src/xblock/runtime.v1',
|
||||
'xblock/lms.runtime.v1': 'coffee/src/xblock/lms.runtime.v1',
|
||||
'capa/display': 'xmodule_js/src/capa/display',
|
||||
|
||||
@@ -45,6 +45,7 @@ lib_paths:
|
||||
- xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.iframe-transport.js
|
||||
- xmodule_js/common_static/js/vendor/url.min.js
|
||||
- xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js
|
||||
- xmodule_js/common_static/js/xblock
|
||||
- xmodule_js/common_static/coffee/src/xblock
|
||||
- xmodule_js/common_static/js/vendor/sinon-1.7.1.js
|
||||
- xmodule_js/src/capa/
|
||||
|
||||
@@ -42,6 +42,7 @@ lib_paths:
|
||||
- xmodule_js/common_static/js/vendor/CodeMirror/codemirror.js
|
||||
- xmodule_js/common_static/js/vendor/URI.min.js
|
||||
- xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js
|
||||
- xmodule_js/common_static/js/xblock
|
||||
- xmodule_js/common_static/coffee/src/xblock
|
||||
- xmodule_js/src/capa/
|
||||
- xmodule_js/src/video/
|
||||
|
||||
Reference in New Issue
Block a user