Merge pull request #19366 from edx/dahlia/proctoring-master
Upgrade edx-proctoring to version 1.5
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
This module contains various configuration settings via
|
||||
waffle switches for the contentstore app.
|
||||
"""
|
||||
from openedx.core.djangoapps.waffle_utils import WaffleFlagNamespace, WaffleSwitchNamespace
|
||||
from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag, WaffleFlagNamespace, WaffleSwitchNamespace
|
||||
|
||||
# Namespace
|
||||
WAFFLE_NAMESPACE = u'studio'
|
||||
@@ -23,3 +23,10 @@ def waffle_flags():
|
||||
Returns the namespaced, cached, audited Waffle Flag class for Studio pages.
|
||||
"""
|
||||
return WaffleFlagNamespace(name=WAFFLE_NAMESPACE, log_prefix=u'Studio: ')
|
||||
|
||||
# Flags
|
||||
ENABLE_PROCTORING_PROVIDER_OVERRIDES = CourseWaffleFlag(
|
||||
waffle_namespace=waffle_flags(),
|
||||
flag_name=u'enable_proctoring_provider_overrides',
|
||||
flag_undefined_default=False
|
||||
)
|
||||
|
||||
@@ -30,7 +30,6 @@ def register_special_exams(course_key):
|
||||
subsystem. Likewise, if formerly registered exams are unmarked, then those
|
||||
registered exams are marked as inactive
|
||||
"""
|
||||
|
||||
if not settings.FEATURES.get('ENABLE_SPECIAL_EXAMS'):
|
||||
# if feature is not enabled then do a quick exit
|
||||
return
|
||||
@@ -72,52 +71,47 @@ def register_special_exams(course_key):
|
||||
)
|
||||
log.info(msg)
|
||||
|
||||
exam_metadata = {
|
||||
'exam_name': timed_exam.display_name,
|
||||
'time_limit_mins': timed_exam.default_time_limit_minutes,
|
||||
'due_date': timed_exam.due,
|
||||
'is_proctored': timed_exam.is_proctored_exam,
|
||||
'is_practice_exam': timed_exam.is_practice_exam,
|
||||
'is_active': True,
|
||||
'hide_after_due': timed_exam.hide_after_due,
|
||||
'backend': course.proctoring_provider,
|
||||
}
|
||||
|
||||
try:
|
||||
exam = get_exam_by_content_id(unicode(course_key), unicode(timed_exam.location))
|
||||
# update case, make sure everything is synced
|
||||
exam_id = update_exam(
|
||||
exam_id=exam['id'],
|
||||
exam_name=timed_exam.display_name,
|
||||
time_limit_mins=timed_exam.default_time_limit_minutes,
|
||||
due_date=timed_exam.due,
|
||||
is_proctored=timed_exam.is_proctored_exam,
|
||||
is_practice_exam=timed_exam.is_practice_exam,
|
||||
is_active=True,
|
||||
hide_after_due=timed_exam.hide_after_due,
|
||||
)
|
||||
exam_metadata['exam_id'] = exam['id']
|
||||
|
||||
exam_id = update_exam(**exam_metadata)
|
||||
msg = 'Updated timed exam {exam_id}'.format(exam_id=exam['id'])
|
||||
log.info(msg)
|
||||
|
||||
except ProctoredExamNotFoundException:
|
||||
exam_id = create_exam(
|
||||
course_id=unicode(course_key),
|
||||
content_id=unicode(timed_exam.location),
|
||||
exam_name=timed_exam.display_name,
|
||||
time_limit_mins=timed_exam.default_time_limit_minutes,
|
||||
due_date=timed_exam.due,
|
||||
is_proctored=timed_exam.is_proctored_exam,
|
||||
is_practice_exam=timed_exam.is_practice_exam,
|
||||
is_active=True,
|
||||
hide_after_due=timed_exam.hide_after_due,
|
||||
)
|
||||
exam_metadata['course_id'] = unicode(course_key)
|
||||
exam_metadata['content_id'] = unicode(timed_exam.location)
|
||||
|
||||
exam_id = create_exam(**exam_metadata)
|
||||
msg = 'Created new timed exam {exam_id}'.format(exam_id=exam_id)
|
||||
log.info(msg)
|
||||
|
||||
exam_review_policy_metadata = {
|
||||
'exam_id': exam_id,
|
||||
'set_by_user_id': timed_exam.edited_by,
|
||||
'review_policy': timed_exam.exam_review_rules,
|
||||
}
|
||||
|
||||
# only create/update exam policy for the proctored exams
|
||||
if timed_exam.is_proctored_exam and not timed_exam.is_practice_exam:
|
||||
try:
|
||||
update_review_policy(
|
||||
exam_id=exam_id,
|
||||
set_by_user_id=timed_exam.edited_by,
|
||||
review_policy=timed_exam.exam_review_rules
|
||||
)
|
||||
update_review_policy(**exam_review_policy_metadata)
|
||||
except ProctoredExamReviewPolicyNotFoundException:
|
||||
if timed_exam.exam_review_rules: # won't save an empty rule.
|
||||
create_exam_review_policy(
|
||||
exam_id=exam_id,
|
||||
set_by_user_id=timed_exam.edited_by,
|
||||
review_policy=timed_exam.exam_review_rules
|
||||
)
|
||||
create_exam_review_policy(**exam_review_policy_metadata)
|
||||
msg = 'Created new exam review policy with exam_id {exam_id}'.format(exam_id=exam_id)
|
||||
log.info(msg)
|
||||
else:
|
||||
|
||||
@@ -7,19 +7,24 @@ import json
|
||||
import unittest
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
from django.conf import settings
|
||||
from django.test import RequestFactory
|
||||
from django.test.utils import override_settings
|
||||
from pytz import UTC
|
||||
from milestones.tests.utils import MilestonesTestCaseMixin
|
||||
import mock
|
||||
from mock import Mock, patch
|
||||
from crum import set_current_request
|
||||
from milestones.tests.utils import MilestonesTestCaseMixin
|
||||
|
||||
|
||||
from contentstore.utils import reverse_course_url, reverse_usage_url
|
||||
from contentstore.config.waffle import ENABLE_PROCTORING_PROVIDER_OVERRIDES
|
||||
from milestones.models import MilestoneRelationshipType
|
||||
from models.settings.course_grading import CourseGradingModel, GRADING_POLICY_CHANGED_EVENT_TYPE, hash_grading_policy
|
||||
from models.settings.course_metadata import CourseMetadata
|
||||
from models.settings.encoder import CourseSettingsEncoder
|
||||
from openedx.core.djangoapps.models.course_details import CourseDetails
|
||||
from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag
|
||||
from student.roles import CourseInstructorRole, CourseStaffRole
|
||||
from student.tests.factories import UserFactory
|
||||
from util import milestones_helpers
|
||||
@@ -790,12 +795,18 @@ class CourseMetadataEditingTest(CourseTestCase):
|
||||
shard = 1
|
||||
|
||||
def setUp(self):
|
||||
CourseTestCase.setUp(self)
|
||||
super(CourseMetadataEditingTest, self).setUp()
|
||||
self.fullcourse = CourseFactory.create()
|
||||
self.course_setting_url = get_url(self.course.id, 'advanced_settings_handler')
|
||||
self.fullcourse_setting_url = get_url(self.fullcourse.id, 'advanced_settings_handler')
|
||||
self.notes_tab = {"type": "notes", "name": "My Notes"}
|
||||
|
||||
self.request = RequestFactory().request()
|
||||
self.user = UserFactory()
|
||||
self.request.user = self.user
|
||||
set_current_request(self.request)
|
||||
self.addCleanup(set_current_request, None)
|
||||
|
||||
def test_fetch_initial_fields(self):
|
||||
test_model = CourseMetadata.fetch(self.course)
|
||||
self.assertIn('display_name', test_model, 'Missing editable metadata field')
|
||||
@@ -1189,6 +1200,193 @@ class CourseMetadataEditingTest(CourseTestCase):
|
||||
})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
@override_waffle_flag(ENABLE_PROCTORING_PROVIDER_OVERRIDES, True)
|
||||
def test_proctoring_provider_present_when_waffle_flag_enabled(self):
|
||||
"""
|
||||
Tests that proctoring provider field is not filtered out when the waffle flag is enabled.
|
||||
"""
|
||||
test_model = CourseMetadata.fetch(self.fullcourse)
|
||||
self.assertIn('proctoring_provider', test_model)
|
||||
|
||||
@override_settings(
|
||||
PROCTORING_BACKENDS={
|
||||
'DEFAULT': 'test_proctoring_provider',
|
||||
'test_proctoring_provider': {}
|
||||
}
|
||||
)
|
||||
@override_waffle_flag(ENABLE_PROCTORING_PROVIDER_OVERRIDES, True)
|
||||
def test_validate_update_does_not_filter_out_proctoring_provider_when_waffle_flag_enabled(self):
|
||||
"""
|
||||
Tests that proctoring provider field is returned by validate_and_update_from_json method when
|
||||
waffle flag is enabled.
|
||||
"""
|
||||
field_name = "proctoring_provider"
|
||||
|
||||
_, _, test_model = CourseMetadata.validate_and_update_from_json(
|
||||
self.course,
|
||||
{
|
||||
field_name: {"value": 'test_proctoring_provider'},
|
||||
},
|
||||
user=self.user
|
||||
)
|
||||
self.assertIn(field_name, test_model)
|
||||
|
||||
@override_settings(
|
||||
PROCTORING_BACKENDS={
|
||||
'DEFAULT': 'test_proctoring_provider',
|
||||
'test_proctoring_provider': {}
|
||||
}
|
||||
)
|
||||
@override_waffle_flag(ENABLE_PROCTORING_PROVIDER_OVERRIDES, True)
|
||||
def test_update_from_json_does_not_filter_out_proctoring_provider_when_waffle_flag_enabled(self):
|
||||
"""
|
||||
Tests that proctoring provider field is returned by update_from_json method when
|
||||
waffle flag is enabled.
|
||||
"""
|
||||
field_name = "proctoring_provider"
|
||||
test_model = CourseMetadata.update_from_json(
|
||||
self.course,
|
||||
{
|
||||
field_name: {"value": 'test_proctoring_provider'},
|
||||
},
|
||||
user=self.user
|
||||
)
|
||||
self.assertIn(field_name, test_model)
|
||||
|
||||
@override_waffle_flag(ENABLE_PROCTORING_PROVIDER_OVERRIDES, False)
|
||||
def test_proctoring_provider_not_present_when_waffle_flag_not_enabled(self):
|
||||
"""
|
||||
Tests that proctoring provider field is filtered out when the waffle flag is not enabled.
|
||||
"""
|
||||
test_model = CourseMetadata.fetch(self.fullcourse)
|
||||
self.assertNotIn('proctoring_provider', test_model)
|
||||
|
||||
@override_waffle_flag(ENABLE_PROCTORING_PROVIDER_OVERRIDES, False)
|
||||
def test_validate_update_does_filter_out_proctoring_provider_when_waffle_flag_not_enabled(self):
|
||||
"""
|
||||
Tests that proctoring provider field is not returned by validate_and_update_from_json method when
|
||||
waffle flag is not enabled.
|
||||
"""
|
||||
field_name = "proctoring_provider"
|
||||
|
||||
_, _, test_model = CourseMetadata.validate_and_update_from_json(
|
||||
self.course,
|
||||
{
|
||||
field_name: {"value": 'test_proctoring_provider'},
|
||||
},
|
||||
user=self.user
|
||||
)
|
||||
self.assertNotIn(field_name, test_model)
|
||||
|
||||
@override_waffle_flag(ENABLE_PROCTORING_PROVIDER_OVERRIDES, False)
|
||||
def test_update_from_json_does_filter_out_proctoring_provider_when_waffle_flag_not_enabled(self):
|
||||
"""
|
||||
Tests that proctoring provider field is not returned by update_from_json method when
|
||||
waffle flag is not enabled.
|
||||
"""
|
||||
field_name = "proctoring_provider"
|
||||
|
||||
test_model = CourseMetadata.update_from_json(
|
||||
self.course,
|
||||
{
|
||||
field_name: {"value": 'test_proctoring_provider'},
|
||||
},
|
||||
user=self.user
|
||||
)
|
||||
self.assertNotIn(field_name, test_model)
|
||||
|
||||
def test_create_zendesk_tickets_present_for_edx_staff(self):
|
||||
"""
|
||||
Tests that create zendesk tickets field is not filtered out when the user is an edX staff member.
|
||||
"""
|
||||
self._set_request_user_to_staff()
|
||||
|
||||
test_model = CourseMetadata.fetch(self.fullcourse)
|
||||
self.assertIn('create_zendesk_tickets', test_model)
|
||||
|
||||
def test_validate_update_does_not_filter_out_create_zendesk_tickets_for_edx_staff(self):
|
||||
"""
|
||||
Tests that create zendesk tickets field is returned by validate_and_update_from_json method when
|
||||
the user is an edX staff member.
|
||||
"""
|
||||
self._set_request_user_to_staff()
|
||||
|
||||
field_name = "create_zendesk_tickets"
|
||||
|
||||
_, _, test_model = CourseMetadata.validate_and_update_from_json(
|
||||
self.course,
|
||||
{
|
||||
field_name: {"value": True},
|
||||
},
|
||||
user=self.user
|
||||
)
|
||||
self.assertIn(field_name, test_model)
|
||||
|
||||
def test_update_from_json_does_not_filter_out_create_zendesk_tickets_for_edx_staff(self):
|
||||
"""
|
||||
Tests that create zendesk tickets field is returned by update_from_json method when
|
||||
the user is an edX staff member.
|
||||
"""
|
||||
self._set_request_user_to_staff()
|
||||
|
||||
field_name = "create_zendesk_tickets"
|
||||
|
||||
test_model = CourseMetadata.update_from_json(
|
||||
self.course,
|
||||
{
|
||||
field_name: {"value": True},
|
||||
},
|
||||
user=self.user
|
||||
)
|
||||
self.assertIn(field_name, test_model)
|
||||
|
||||
def test_create_zendesk_tickets_not_present_for_course_staff(self):
|
||||
"""
|
||||
Tests that create zendesk tickets field is filtered out when the user is not an edX staff member.
|
||||
"""
|
||||
test_model = CourseMetadata.fetch(self.fullcourse)
|
||||
self.assertNotIn('create_zendesk_tickets', test_model)
|
||||
|
||||
def test_validate_update_does_filter_out_create_zendesk_tickets_for_course_staff(self):
|
||||
"""
|
||||
Tests that create zendesk tickets field is not returned by validate_and_update_from_json method when
|
||||
the user is not an edX staff member.
|
||||
"""
|
||||
field_name = "create_zendesk_tickets"
|
||||
|
||||
_, _, test_model = CourseMetadata.validate_and_update_from_json(
|
||||
self.course,
|
||||
{
|
||||
field_name: {"value": True},
|
||||
},
|
||||
user=self.user
|
||||
)
|
||||
self.assertNotIn(field_name, test_model)
|
||||
|
||||
def test_update_from_json_does_filter_out_create_zendesk_tickets_for_course_staff(self):
|
||||
"""
|
||||
Tests that create zendesk tickets field is not returned by update_from_json method when
|
||||
the user is not an edX staff member.
|
||||
"""
|
||||
field_name = "create_zendesk_tickets"
|
||||
|
||||
test_model = CourseMetadata.update_from_json(
|
||||
self.course,
|
||||
{
|
||||
field_name: {"value": True},
|
||||
},
|
||||
user=self.user
|
||||
)
|
||||
self.assertNotIn(field_name, test_model)
|
||||
|
||||
def _set_request_user_to_staff(self):
|
||||
"""
|
||||
Update the current request's user to be an edX staff member.
|
||||
"""
|
||||
self.user.is_staff = True
|
||||
self.request.user = self.user
|
||||
set_current_request(self.request)
|
||||
|
||||
|
||||
class CourseGraderUpdatesTest(CourseTestCase):
|
||||
"""
|
||||
|
||||
@@ -10,6 +10,7 @@ from mock import patch
|
||||
from pytz import UTC
|
||||
|
||||
from contentstore.signals.handlers import listen_for_course_publish
|
||||
from django.conf import settings
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
|
||||
@@ -31,7 +32,8 @@ class TestProctoredExams(ModuleStoreTestCase):
|
||||
org='edX',
|
||||
course='900',
|
||||
run='test_run',
|
||||
enable_proctored_exams=True
|
||||
enable_proctored_exams=True,
|
||||
proctoring_provider=settings.PROCTORING_BACKENDS['DEFAULT'],
|
||||
)
|
||||
|
||||
def _verify_exam_data(self, sequence, expected_active):
|
||||
@@ -61,20 +63,22 @@ class TestProctoredExams(ModuleStoreTestCase):
|
||||
self.assertEqual(exam['is_proctored'], sequence.is_proctored_exam)
|
||||
self.assertEqual(exam['is_practice_exam'], sequence.is_practice_exam)
|
||||
self.assertEqual(exam['is_active'], expected_active)
|
||||
self.assertEqual(exam['backend'], self.course.proctoring_provider)
|
||||
|
||||
@ddt.data(
|
||||
(True, 10, True, False, True, False, False),
|
||||
(True, 10, False, False, True, False, False),
|
||||
(True, 10, False, False, True, False, True),
|
||||
(True, 10, True, True, True, True, False),
|
||||
(True, False, True, False, False),
|
||||
(False, False, True, False, False),
|
||||
(False, False, True, False, True),
|
||||
(True, True, True, True, False),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_publishing_exam(self, is_time_limited, default_time_limit_minutes, is_proctored_exam,
|
||||
def test_publishing_exam(self, is_proctored_exam,
|
||||
is_practice_exam, expected_active, republish, hide_after_due):
|
||||
"""
|
||||
Happy path testing to see that when a course is published which contains
|
||||
a proctored exam, it will also put an entry into the exam tables
|
||||
"""
|
||||
default_time_limit_minutes = 10
|
||||
|
||||
chapter = ItemFactory.create(parent=self.course, category='chapter', display_name='Test Section')
|
||||
sequence = ItemFactory.create(
|
||||
@@ -82,7 +86,7 @@ class TestProctoredExams(ModuleStoreTestCase):
|
||||
category='sequential',
|
||||
display_name='Test Proctored Exam',
|
||||
graded=True,
|
||||
is_time_limited=is_time_limited,
|
||||
is_time_limited=True,
|
||||
default_time_limit_minutes=default_time_limit_minutes,
|
||||
is_proctored_exam=is_proctored_exam,
|
||||
is_practice_exam=is_practice_exam,
|
||||
@@ -104,14 +108,13 @@ class TestProctoredExams(ModuleStoreTestCase):
|
||||
listen_for_course_publish(self, self.course.id)
|
||||
|
||||
# reverify
|
||||
self._verify_exam_data(sequence, expected_active)
|
||||
self._verify_exam_data(sequence, expected_active,)
|
||||
|
||||
def test_unpublishing_proctored_exam(self):
|
||||
"""
|
||||
Make sure that if we publish and then unpublish a proctored exam,
|
||||
the exam record stays, but is marked as is_active=False
|
||||
"""
|
||||
|
||||
chapter = ItemFactory.create(parent=self.course, category='chapter', display_name='Test Section')
|
||||
sequence = ItemFactory.create(
|
||||
parent=chapter,
|
||||
@@ -178,7 +181,6 @@ class TestProctoredExams(ModuleStoreTestCase):
|
||||
"""
|
||||
Make sure the feature flag is honored
|
||||
"""
|
||||
|
||||
chapter = ItemFactory.create(parent=self.course, category='chapter', display_name='Test Section')
|
||||
ItemFactory.create(
|
||||
parent=chapter,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
Test module for Entrance Exams AJAX callback handler workflows
|
||||
"""
|
||||
import json
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.test.client import RequestFactory
|
||||
@@ -229,13 +228,14 @@ class EntranceExamHandlerTests(CourseTestCase, MilestonesTestCaseMixin):
|
||||
{'entrance_exam_minimum_score_pct': '50'},
|
||||
http_accept='application/json'
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_code, 201)
|
||||
resp = self.client.get(self.exam_url)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.course = modulestore().get_course(self.course.id)
|
||||
|
||||
# Should raise an ItemNotFoundError and return a 404
|
||||
updated_metadata = {'entrance_exam_id': 'i4x://org.4/course_4/chapter/ed7c4c6a4d68409998e2c8554c4629d1'}
|
||||
|
||||
CourseMetadata.update_from_dict(
|
||||
updated_metadata,
|
||||
self.course,
|
||||
@@ -247,6 +247,7 @@ class EntranceExamHandlerTests(CourseTestCase, MilestonesTestCaseMixin):
|
||||
|
||||
# Should raise an InvalidKeyError and return a 404
|
||||
updated_metadata = {'entrance_exam_id': '123afsdfsad90f87'}
|
||||
|
||||
CourseMetadata.update_from_dict(
|
||||
updated_metadata,
|
||||
self.course,
|
||||
|
||||
@@ -5,11 +5,14 @@ from django.conf import settings
|
||||
from django.utils.translation import ugettext as _
|
||||
from six import text_type
|
||||
from xblock.fields import Scope
|
||||
from crum import get_current_user
|
||||
|
||||
from xblock_django.models import XBlockStudioConfigurationFlag
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from student.roles import GlobalStaff
|
||||
|
||||
from openedx.features.course_experience import COURSE_ENABLE_UNENROLLED_ACCESS_FLAG
|
||||
from cms.djangoapps.contentstore.config.waffle import ENABLE_PROCTORING_PROVIDER_OVERRIDES
|
||||
|
||||
|
||||
class CourseMetadata(object):
|
||||
@@ -20,9 +23,9 @@ class CourseMetadata(object):
|
||||
editable metadata.
|
||||
'''
|
||||
# The list of fields that wouldn't be shown in Advanced Settings.
|
||||
# Should not be used directly. Instead the filtered_list method should
|
||||
# Should not be used directly. Instead the get_blacklist_of_fields method should
|
||||
# be used if the field needs to be filtered depending on the feature flag.
|
||||
FILTERED_LIST = [
|
||||
FIELDS_BLACK_LIST = [
|
||||
'cohort_config',
|
||||
'xml_attributes',
|
||||
'start',
|
||||
@@ -65,65 +68,77 @@ class CourseMetadata(object):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def filtered_list(cls, course_key=None):
|
||||
def get_blacklist_of_fields(cls, course_key):
|
||||
"""
|
||||
Filter fields based on feature flag, i.e. enabled, disabled.
|
||||
Returns a list of fields to not include in Studio Advanced settings based on a
|
||||
feature flag (i.e. enabled or disabled).
|
||||
"""
|
||||
# Copy the filtered list to avoid permanently changing the class attribute.
|
||||
filtered_list = list(cls.FILTERED_LIST)
|
||||
black_list = list(cls.FIELDS_BLACK_LIST)
|
||||
|
||||
# Do not show giturl if feature is not enabled.
|
||||
if not settings.FEATURES.get('ENABLE_EXPORT_GIT'):
|
||||
filtered_list.append('giturl')
|
||||
black_list.append('giturl')
|
||||
|
||||
# Do not show edxnotes if the feature is disabled.
|
||||
if not settings.FEATURES.get('ENABLE_EDXNOTES'):
|
||||
filtered_list.append('edxnotes')
|
||||
black_list.append('edxnotes')
|
||||
|
||||
# Do not show video auto advance if the feature is disabled
|
||||
if not settings.FEATURES.get('ENABLE_OTHER_COURSE_SETTINGS'):
|
||||
filtered_list.append('other_course_settings')
|
||||
black_list.append('other_course_settings')
|
||||
|
||||
# Do not show video_upload_pipeline if the feature is disabled.
|
||||
if not settings.FEATURES.get('ENABLE_VIDEO_UPLOAD_PIPELINE'):
|
||||
filtered_list.append('video_upload_pipeline')
|
||||
black_list.append('video_upload_pipeline')
|
||||
|
||||
# Do not show video auto advance if the feature is disabled
|
||||
if not settings.FEATURES.get('ENABLE_AUTOADVANCE_VIDEOS'):
|
||||
filtered_list.append('video_auto_advance')
|
||||
black_list.append('video_auto_advance')
|
||||
|
||||
# Do not show social sharing url field if the feature is disabled.
|
||||
if (not hasattr(settings, 'SOCIAL_SHARING_SETTINGS') or
|
||||
not getattr(settings, 'SOCIAL_SHARING_SETTINGS', {}).get("CUSTOM_COURSE_URLS")):
|
||||
filtered_list.append('social_sharing_url')
|
||||
black_list.append('social_sharing_url')
|
||||
|
||||
# Do not show teams configuration if feature is disabled.
|
||||
if not settings.FEATURES.get('ENABLE_TEAMS'):
|
||||
filtered_list.append('teams_configuration')
|
||||
black_list.append('teams_configuration')
|
||||
|
||||
if not settings.FEATURES.get('ENABLE_VIDEO_BUMPER'):
|
||||
filtered_list.append('video_bumper')
|
||||
black_list.append('video_bumper')
|
||||
|
||||
# Do not show enable_ccx if feature is not enabled.
|
||||
if not settings.FEATURES.get('CUSTOM_COURSES_EDX'):
|
||||
filtered_list.append('enable_ccx')
|
||||
filtered_list.append('ccx_connector')
|
||||
black_list.append('enable_ccx')
|
||||
black_list.append('ccx_connector')
|
||||
|
||||
# Do not show "Issue Open Badges" in Studio Advanced Settings
|
||||
# if the feature is disabled.
|
||||
if not settings.FEATURES.get('ENABLE_OPENBADGES'):
|
||||
filtered_list.append('issue_badges')
|
||||
black_list.append('issue_badges')
|
||||
|
||||
# If the XBlockStudioConfiguration table is not being used, there is no need to
|
||||
# display the "Allow Unsupported XBlocks" setting.
|
||||
if not XBlockStudioConfigurationFlag.is_enabled():
|
||||
filtered_list.append('allow_unsupported_xblocks')
|
||||
black_list.append('allow_unsupported_xblocks')
|
||||
|
||||
# If the ENABLE_PROCTORING_PROVIDER_OVERRIDES waffle flag is not enabled,
|
||||
# do not show "Proctoring Configuration" in Studio Advanced Settings.
|
||||
if not ENABLE_PROCTORING_PROVIDER_OVERRIDES.is_enabled(course_key):
|
||||
black_list.append('proctoring_provider')
|
||||
|
||||
# Do not show "Course Visibility For Unenrolled Learners" in Studio Advanced Settings
|
||||
# if the enable_anonymous_access flag is not enabled
|
||||
if not COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled(course_key=course_key):
|
||||
filtered_list.append('course_visibility')
|
||||
return filtered_list
|
||||
black_list.append('course_visibility')
|
||||
|
||||
# Do not show "Create Zendesk Tickets For Suspicious Proctored Exam Attempts" in
|
||||
# Studio Advanced Settings if the user is not edX staff.
|
||||
if not GlobalStaff().has_user(get_current_user()):
|
||||
black_list.append('create_zendesk_tickets')
|
||||
|
||||
return black_list
|
||||
|
||||
@classmethod
|
||||
def fetch(cls, descriptor):
|
||||
@@ -133,8 +148,10 @@ class CourseMetadata(object):
|
||||
"""
|
||||
result = {}
|
||||
metadata = cls.fetch_all(descriptor)
|
||||
black_list_of_fields = cls.get_blacklist_of_fields(descriptor.id)
|
||||
|
||||
for key, value in metadata.iteritems():
|
||||
if key in cls.filtered_list(descriptor.id):
|
||||
if key in black_list_of_fields:
|
||||
continue
|
||||
result[key] = value
|
||||
return result
|
||||
@@ -169,17 +186,17 @@ class CourseMetadata(object):
|
||||
|
||||
Ensures none of the fields are in the blacklist.
|
||||
"""
|
||||
filtered_list = cls.filtered_list(descriptor.id)
|
||||
blacklist_of_fields = cls.get_blacklist_of_fields(descriptor.id)
|
||||
# Don't filter on the tab attribute if filter_tabs is False.
|
||||
if not filter_tabs:
|
||||
filtered_list.remove("tabs")
|
||||
blacklist_of_fields.remove("tabs")
|
||||
|
||||
# Validate the values before actually setting them.
|
||||
key_values = {}
|
||||
|
||||
for key, model in jsondict.iteritems():
|
||||
# should it be an error if one of the filtered list items is in the payload?
|
||||
if key in filtered_list:
|
||||
if key in blacklist_of_fields:
|
||||
continue
|
||||
try:
|
||||
val = model['value']
|
||||
@@ -205,11 +222,12 @@ class CourseMetadata(object):
|
||||
errors: list of error objects
|
||||
result: the updated course metadata or None if error
|
||||
"""
|
||||
filtered_list = cls.filtered_list(descriptor.id)
|
||||
if not filter_tabs:
|
||||
filtered_list.remove("tabs")
|
||||
blacklist_of_fields = cls.get_blacklist_of_fields(descriptor.id)
|
||||
|
||||
filtered_dict = dict((k, v) for k, v in jsondict.iteritems() if k not in filtered_list)
|
||||
if not filter_tabs:
|
||||
blacklist_of_fields.remove("tabs")
|
||||
|
||||
filtered_dict = dict((k, v) for k, v in jsondict.iteritems() if k not in blacklist_of_fields)
|
||||
did_validate = True
|
||||
errors = []
|
||||
key_values = {}
|
||||
@@ -238,7 +256,7 @@ class CourseMetadata(object):
|
||||
for key, value in key_values.iteritems():
|
||||
setattr(descriptor, key, value)
|
||||
|
||||
if save and len(key_values):
|
||||
if save and key_values:
|
||||
modulestore().update_item(descriptor, user.id)
|
||||
|
||||
return cls.fetch(descriptor)
|
||||
|
||||
@@ -490,11 +490,6 @@ XBLOCK_SETTINGS = ENV_TOKENS.get('XBLOCK_SETTINGS', {})
|
||||
XBLOCK_SETTINGS.setdefault("VideoDescriptor", {})["licensing_enabled"] = FEATURES.get("LICENSING", False)
|
||||
XBLOCK_SETTINGS.setdefault("VideoModule", {})['YOUTUBE_API_KEY'] = AUTH_TOKENS.get('YOUTUBE_API_KEY', YOUTUBE_API_KEY)
|
||||
|
||||
################# PROCTORING CONFIGURATION ##################
|
||||
|
||||
PROCTORING_BACKEND_PROVIDER = AUTH_TOKENS.get("PROCTORING_BACKEND_PROVIDER", PROCTORING_BACKEND_PROVIDER)
|
||||
PROCTORING_SETTINGS = ENV_TOKENS.get("PROCTORING_SETTINGS", PROCTORING_SETTINGS)
|
||||
|
||||
################# MICROSITE ####################
|
||||
# microsite specific configurations.
|
||||
MICROSITE_CONFIGURATION = ENV_TOKENS.get('MICROSITE_CONFIGURATION', {})
|
||||
|
||||
@@ -881,6 +881,10 @@ WEBPACK_LOADER = {
|
||||
'DEFAULT': {
|
||||
'BUNDLE_DIR_NAME': 'bundles/',
|
||||
'STATS_FILE': os.path.join(STATIC_ROOT, 'webpack-stats.json')
|
||||
},
|
||||
'WORKERS': {
|
||||
'BUNDLE_DIR_NAME': 'bundles/',
|
||||
'STATS_FILE': os.path.join(STATIC_ROOT, 'webpack-worker-stats.json')
|
||||
}
|
||||
}
|
||||
WEBPACK_CONFIG_PATH = 'webpack.prod.config.js'
|
||||
@@ -1093,9 +1097,6 @@ INSTALLED_APPS = [
|
||||
|
||||
'xblock_django',
|
||||
|
||||
# edX Proctoring
|
||||
'edx_proctoring',
|
||||
|
||||
# Catalog integration
|
||||
'openedx.core.djangoapps.catalog',
|
||||
|
||||
@@ -1445,12 +1446,6 @@ MICROSITE_TEMPLATE_BACKEND = 'microsite_configuration.backends.filebased.Filebas
|
||||
# TTL for microsite database template cache
|
||||
MICROSITE_DATABASE_TEMPLATE_CACHE_TTL = 5 * 60
|
||||
|
||||
############################### PROCTORING CONFIGURATION DEFAULTS ##############
|
||||
PROCTORING_BACKEND_PROVIDER = {
|
||||
'class': 'edx_proctoring.backends.null.NullBackendProvider',
|
||||
'options': {},
|
||||
}
|
||||
PROCTORING_SETTINGS = {}
|
||||
|
||||
############################ Global Database Configuration #####################
|
||||
|
||||
|
||||
@@ -128,6 +128,7 @@ STATIC_ROOT_BASE = ENV_TOKENS.get('STATIC_ROOT_BASE', None)
|
||||
if STATIC_ROOT_BASE:
|
||||
STATIC_ROOT = path(STATIC_ROOT_BASE) / 'studio'
|
||||
WEBPACK_LOADER['DEFAULT']['STATS_FILE'] = STATIC_ROOT / "webpack-stats.json"
|
||||
WEBPACK_LOADER['WORKERS']['STATS_FILE'] = STATIC_ROOT / "webpack-worker-stats.json"
|
||||
|
||||
EMAIL_BACKEND = ENV_TOKENS.get('EMAIL_BACKEND', EMAIL_BACKEND)
|
||||
EMAIL_FILE_PATH = ENV_TOKENS.get('EMAIL_FILE_PATH', None)
|
||||
@@ -493,11 +494,6 @@ XBLOCK_SETTINGS = ENV_TOKENS.get('XBLOCK_SETTINGS', {})
|
||||
XBLOCK_SETTINGS.setdefault("VideoDescriptor", {})["licensing_enabled"] = FEATURES.get("LICENSING", False)
|
||||
XBLOCK_SETTINGS.setdefault("VideoModule", {})['YOUTUBE_API_KEY'] = AUTH_TOKENS.get('YOUTUBE_API_KEY', YOUTUBE_API_KEY)
|
||||
|
||||
################# PROCTORING CONFIGURATION ##################
|
||||
|
||||
PROCTORING_BACKEND_PROVIDER = AUTH_TOKENS.get("PROCTORING_BACKEND_PROVIDER", PROCTORING_BACKEND_PROVIDER)
|
||||
PROCTORING_SETTINGS = ENV_TOKENS.get("PROCTORING_SETTINGS", PROCTORING_SETTINGS)
|
||||
|
||||
################# MICROSITE ####################
|
||||
# microsite specific configurations.
|
||||
MICROSITE_CONFIGURATION = ENV_TOKENS.get('MICROSITE_CONFIGURATION', {})
|
||||
|
||||
@@ -23,6 +23,7 @@ DATABASES = {
|
||||
|
||||
}
|
||||
|
||||
|
||||
######################### PIPELINE ####################################
|
||||
|
||||
# Use RequireJS optimized storage
|
||||
|
||||
@@ -120,7 +120,7 @@ class GlobalStaff(AccessRole):
|
||||
The global staff role
|
||||
"""
|
||||
def has_user(self, user):
|
||||
return user.is_staff
|
||||
return bool(user and user.is_staff)
|
||||
|
||||
def add_users(self, *users):
|
||||
for user in users:
|
||||
|
||||
@@ -183,6 +183,89 @@ class TextbookList(List):
|
||||
return json_data
|
||||
|
||||
|
||||
class ProctoringProvider(String):
|
||||
"""
|
||||
ProctoringProvider field, which includes validation of the provider
|
||||
and default that pulls from edx platform settings.
|
||||
"""
|
||||
def from_json(self, value):
|
||||
"""
|
||||
Return ProctoringProvider as full featured Python type. Perform validation on the provider
|
||||
and include any inherited values from the platform default.
|
||||
"""
|
||||
errors = []
|
||||
value = super(ProctoringProvider, self).from_json(value)
|
||||
|
||||
provider_errors = self._validate_proctoring_provider(value)
|
||||
errors.extend(provider_errors)
|
||||
|
||||
if errors:
|
||||
raise ValueError(errors)
|
||||
|
||||
value = self._get_proctoring_value(value)
|
||||
|
||||
return value
|
||||
|
||||
def _get_proctoring_value(self, value):
|
||||
"""
|
||||
Return a proctoring value that includes any inherited attributes from the platform defaults
|
||||
for the provider.
|
||||
"""
|
||||
# if provider is missing from the value, return the default
|
||||
if value is None:
|
||||
return self.default
|
||||
|
||||
return value
|
||||
|
||||
def _validate_proctoring_provider(self, value):
|
||||
"""
|
||||
Validate the value for the proctoring provider. If the proctoring provider value is
|
||||
specified, and it is not one of the providers configured at the platform level, return
|
||||
a list of error messages to the caller.
|
||||
"""
|
||||
errors = []
|
||||
|
||||
available_providers = get_available_providers()
|
||||
|
||||
if value and value not in available_providers:
|
||||
errors.append(
|
||||
_('The selected proctoring provider, {proctoring_provider}, is not a valid provider. '
|
||||
'Please select from one of {available_providers}.')
|
||||
.format(
|
||||
proctoring_provider=value,
|
||||
available_providers=available_providers
|
||||
)
|
||||
)
|
||||
|
||||
return errors
|
||||
|
||||
@property
|
||||
def default(self):
|
||||
"""
|
||||
Return default value for ProctoringProvider.
|
||||
"""
|
||||
default = super(ProctoringProvider, self).default
|
||||
|
||||
proctoring_backend_settings = getattr(settings, 'PROCTORING_BACKENDS', None)
|
||||
|
||||
if proctoring_backend_settings:
|
||||
return proctoring_backend_settings.get('DEFAULT', None)
|
||||
|
||||
return default
|
||||
|
||||
|
||||
def get_available_providers():
|
||||
proctoring_backend_settings = getattr(
|
||||
settings,
|
||||
'PROCTORING_BACKENDS',
|
||||
{}
|
||||
)
|
||||
|
||||
available_providers = [provider for provider in proctoring_backend_settings if provider != 'DEFAULT']
|
||||
available_providers.sort()
|
||||
return available_providers
|
||||
|
||||
|
||||
class CourseFields(object):
|
||||
lti_passports = List(
|
||||
display_name=_("LTI Passports"),
|
||||
@@ -738,6 +821,21 @@ class CourseFields(object):
|
||||
scope=Scope.settings
|
||||
)
|
||||
|
||||
proctoring_provider = ProctoringProvider(
|
||||
display_name=_("Proctoring Provider"),
|
||||
help=_(
|
||||
"Enter the proctoring provider you want to use for this course run. "
|
||||
"Choose from the following options: {available_providers}."),
|
||||
help_format_args=dict(
|
||||
# Put the available providers into a format variable so that translators
|
||||
# don't translate them.
|
||||
available_providers=(
|
||||
', '.join(get_available_providers())
|
||||
),
|
||||
),
|
||||
scope=Scope.settings,
|
||||
)
|
||||
|
||||
allow_proctoring_opt_out = Boolean(
|
||||
display_name=_("Allow Opting Out of Proctored Exams"),
|
||||
help=_(
|
||||
@@ -1049,7 +1147,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor, LicenseMixin):
|
||||
def definition_to_xml(self, resource_fs):
|
||||
xml_object = super(CourseDescriptor, self).definition_to_xml(resource_fs)
|
||||
|
||||
if len(self.textbooks) > 0:
|
||||
if self.textbooks:
|
||||
textbook_xml_object = etree.Element('textbook')
|
||||
for textbook in self.textbooks:
|
||||
textbook_xml_object.set('title', textbook.title)
|
||||
@@ -1103,7 +1201,8 @@ class CourseDescriptor(CourseFields, SequenceDescriptor, LicenseMixin):
|
||||
|
||||
@raw_grader.setter
|
||||
def raw_grader(self, value):
|
||||
# NOTE WELL: this change will not update the processed graders. If we need that, this needs to call grader_from_conf
|
||||
# NOTE WELL: this change will not update the processed graders.
|
||||
# If we need that, this needs to call grader_from_conf.
|
||||
self._grading_policy['RAW_GRADER'] = value
|
||||
self.grading_policy['GRADER'] = value
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ from fs.memoryfs import MemoryFS
|
||||
from mock import Mock, patch
|
||||
from pytz import utc
|
||||
from xblock.runtime import KvsFieldData, DictKeyValueStore
|
||||
from django.conf import settings
|
||||
from django.test import override_settings
|
||||
|
||||
import xmodule.course_module
|
||||
from xmodule.modulestore.xml import ImportSystem, XMLModuleStore
|
||||
@@ -419,3 +421,73 @@ class CourseDescriptorTestCase(unittest.TestCase):
|
||||
"""
|
||||
expected_certificate_available_date = self.course.end + timedelta(days=2)
|
||||
self.assertEqual(expected_certificate_available_date, self.course.certificate_available_date)
|
||||
|
||||
|
||||
class ProctoringProviderTestCase(unittest.TestCase):
|
||||
"""
|
||||
Tests for ProctoringProvider, including the default value, validation, and inheritance behavior.
|
||||
"""
|
||||
shard = 1
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Initialize dummy testing course.
|
||||
"""
|
||||
super(ProctoringProviderTestCase, self).setUp()
|
||||
self.proctoring_provider = xmodule.course_module.ProctoringProvider()
|
||||
|
||||
def test_from_json_with_platform_default(self):
|
||||
"""
|
||||
Test that a proctoring provider value equivalent to the platform
|
||||
default will pass validation.
|
||||
"""
|
||||
default_provider = settings.PROCTORING_BACKENDS.get('DEFAULT')
|
||||
|
||||
# we expect the validated value to be equivalent to the value passed in,
|
||||
# since there are no validation errors or missing data
|
||||
self.assertEqual(self.proctoring_provider.from_json(default_provider), default_provider)
|
||||
|
||||
def test_from_json_with_invalid_provider(self):
|
||||
"""
|
||||
Test that an invalid provider (i.e. not one configured at the platform level)
|
||||
throws a ValueError with the correct error message.
|
||||
"""
|
||||
provider = 'invalid-provider'
|
||||
proctoring_provider_whitelist = [u'mock', u'mock_proctoring_without_rules']
|
||||
|
||||
with self.assertRaises(ValueError) as context_manager:
|
||||
self.proctoring_provider.from_json(provider)
|
||||
self.assertEqual(
|
||||
context_manager.exception.args[0],
|
||||
['The selected proctoring provider, {}, is not a valid provider. Please select from one of {}.'
|
||||
.format(provider, proctoring_provider_whitelist)]
|
||||
)
|
||||
|
||||
def test_from_json_adds_platform_default_for_missing_provider(self):
|
||||
"""
|
||||
Test that a value with no provider will inherit the default provider
|
||||
from the platform defaults.
|
||||
"""
|
||||
default_provider = 'mock'
|
||||
|
||||
self.assertEqual(self.proctoring_provider.from_json(None), default_provider)
|
||||
|
||||
@override_settings(
|
||||
PROCTORING_BACKENDS={
|
||||
'mock': {},
|
||||
'mock_proctoring_without_rules': {}
|
||||
}
|
||||
)
|
||||
def test_default_with_no_platform_default(self):
|
||||
"""
|
||||
Test that, when the platform defaults are not set, the default is correct.
|
||||
"""
|
||||
self. assertEqual(self.proctoring_provider.default, None)
|
||||
|
||||
@override_settings(PROCTORING_BACKENDS=None)
|
||||
def test_default_with_no_platform_configuration(self):
|
||||
"""
|
||||
Test that, when the platform default is not specified, the default is correct.
|
||||
"""
|
||||
default = self.proctoring_provider.default
|
||||
self.assertEqual(default, None)
|
||||
|
||||
@@ -54,7 +54,7 @@ var webpackConfig = require(path.join(appRoot, 'webpack.dev.config.js'));
|
||||
// https://github.com/webpack-contrib/karma-webpack/issues/24#issuecomment-257613167
|
||||
//
|
||||
// This should be fixed in v3 of karma-webpack
|
||||
var commonsChunkPluginIndex = webpackConfig.plugins.findIndex(function(plugin) { return plugin.chunkNames; });
|
||||
var commonsChunkPluginIndex = webpackConfig[0].plugins.findIndex(function(plugin) { return plugin.chunkNames; });
|
||||
|
||||
// Files which are needed by all lms/cms suites.
|
||||
var commonFiles = {
|
||||
@@ -80,9 +80,9 @@ var commonFiles = {
|
||||
]
|
||||
};
|
||||
|
||||
webpackConfig.plugins.splice(commonsChunkPluginIndex, 1);
|
||||
webpackConfig[0].plugins.splice(commonsChunkPluginIndex, 1);
|
||||
|
||||
delete webpackConfig.entry;
|
||||
delete webpackConfig[0].entry;
|
||||
|
||||
/**
|
||||
* Customize the name attribute in xml testcase element
|
||||
@@ -410,7 +410,7 @@ function getBaseConfig(config, useRequireJs) {
|
||||
captureConsole: false
|
||||
},
|
||||
|
||||
webpack: webpackConfig,
|
||||
webpack: webpackConfig[0],
|
||||
|
||||
webpackMiddleware: {
|
||||
watchOptions: {
|
||||
|
||||
@@ -266,7 +266,6 @@ class AdvancedSettingsPage(CoursePage):
|
||||
'enable_subsection_gating',
|
||||
'learning_info',
|
||||
'instructor_info',
|
||||
'create_zendesk_tickets',
|
||||
'ccx_connector',
|
||||
'enable_ccx',
|
||||
]
|
||||
|
||||
@@ -88,7 +88,7 @@ class InstructorService(object):
|
||||
"""
|
||||
return auth.user_has_role(user, CourseStaffRole(CourseKey.from_string(course_id)))
|
||||
|
||||
def send_support_notification(self, course_id, exam_name, student_username, review_status):
|
||||
def send_support_notification(self, course_id, exam_name, student_username, review_status, review_url=None):
|
||||
"""
|
||||
Creates a Zendesk ticket for an exam attempt review from the proctoring system.
|
||||
Currently, it sends notifications for 'Suspicious" status, but additional statuses can be supported
|
||||
@@ -103,15 +103,17 @@ class InstructorService(object):
|
||||
if course.create_zendesk_tickets:
|
||||
requester_name = "edx-proctoring"
|
||||
email = "edx-proctoring@edx.org"
|
||||
subject = _("Proctored Exam Review: {review_status}").format(review_status=review_status)
|
||||
subject = _(u"Proctored Exam Review: {review_status}").format(review_status=review_status)
|
||||
body = _(
|
||||
"A proctored exam attempt for {exam_name} in {course_name} by username: {student_username} "
|
||||
"was reviewed as {review_status} by the proctored exam review provider."
|
||||
u"A proctored exam attempt for {exam_name} in {course_name} by username: {student_username} "
|
||||
"was reviewed as {review_status} by the proctored exam review provider.\n"
|
||||
"Review link: {review_url}"
|
||||
).format(
|
||||
exam_name=exam_name,
|
||||
course_name=course.display_name,
|
||||
student_username=student_username,
|
||||
review_status=review_status
|
||||
review_status=review_status,
|
||||
review_url=review_url or u'not available',
|
||||
)
|
||||
tags = ["proctoring"]
|
||||
create_zendesk_ticket(requester_name, email, subject, body, tags)
|
||||
|
||||
@@ -147,21 +147,40 @@ class InstructorServiceTests(SharedModuleStoreTestCase):
|
||||
"""
|
||||
requester_name = "edx-proctoring"
|
||||
email = "edx-proctoring@edx.org"
|
||||
subject = "Proctored Exam Review: {review_status}".format(review_status="Suspicious")
|
||||
subject = u"Proctored Exam Review: {review_status}".format(review_status="Suspicious")
|
||||
|
||||
body = "A proctored exam attempt for {exam_name} in {course_name} by username: {student_username} was " \
|
||||
"reviewed as {review_status} by the proctored exam review provider."
|
||||
body = body.format(
|
||||
exam_name="test_exam", course_name=self.course.display_name, student_username="test_student",
|
||||
review_status="Suspicious"
|
||||
)
|
||||
"reviewed as {review_status} by the proctored exam review provider.\n" \
|
||||
"Review link: {url}"
|
||||
args = {
|
||||
'exam_name': 'test_exam',
|
||||
'student_username': 'test_student',
|
||||
'url': 'not available',
|
||||
'course_name': self.course.display_name,
|
||||
'review_status': 'Suspicious',
|
||||
}
|
||||
expected_body = body.format(**args)
|
||||
tags = ["proctoring"]
|
||||
|
||||
with mock.patch("lms.djangoapps.instructor.services.create_zendesk_ticket") as mock_create_zendesk_ticket:
|
||||
self.service.send_support_notification(
|
||||
course_id=unicode(self.course.id),
|
||||
exam_name="test_exam",
|
||||
student_username="test_student",
|
||||
review_status="Suspicious"
|
||||
exam_name=args['exam_name'],
|
||||
student_username=args["student_username"],
|
||||
review_status="Suspicious",
|
||||
review_url=None,
|
||||
)
|
||||
|
||||
mock_create_zendesk_ticket.assert_called_with(requester_name, email, subject, body, tags)
|
||||
mock_create_zendesk_ticket.assert_called_with(requester_name, email, subject, expected_body, tags)
|
||||
# Now check sending a notification with a review link
|
||||
args['url'] = 'http://review/url'
|
||||
with mock.patch("lms.djangoapps.instructor.services.create_zendesk_ticket") as mock_create_zendesk_ticket:
|
||||
self.service.send_support_notification(
|
||||
course_id=unicode(self.course.id),
|
||||
exam_name=args['exam_name'],
|
||||
student_username=args["student_username"],
|
||||
review_status="Suspicious",
|
||||
review_url=args['url'],
|
||||
)
|
||||
expected_body = body.format(**args)
|
||||
mock_create_zendesk_ticket.assert_called_with(requester_name, email, subject, expected_body, tags)
|
||||
|
||||
@@ -892,11 +892,6 @@ CREDIT_HELP_LINK_URL = ENV_TOKENS.get('CREDIT_HELP_LINK_URL', CREDIT_HELP_LINK_U
|
||||
JWT_AUTH.update(ENV_TOKENS.get('JWT_AUTH', {}))
|
||||
JWT_AUTH.update(AUTH_TOKENS.get('JWT_AUTH', {}))
|
||||
|
||||
################# PROCTORING CONFIGURATION ##################
|
||||
|
||||
PROCTORING_BACKEND_PROVIDER = AUTH_TOKENS.get("PROCTORING_BACKEND_PROVIDER", PROCTORING_BACKEND_PROVIDER)
|
||||
PROCTORING_SETTINGS = ENV_TOKENS.get("PROCTORING_SETTINGS", PROCTORING_SETTINGS)
|
||||
|
||||
################# MICROSITE ####################
|
||||
MICROSITE_CONFIGURATION = ENV_TOKENS.get('MICROSITE_CONFIGURATION', {})
|
||||
MICROSITE_ROOT_DIR = path(ENV_TOKENS.get('MICROSITE_ROOT_DIR', ''))
|
||||
|
||||
@@ -1345,28 +1345,6 @@ courseware_js = [
|
||||
'js/modules/tab.js',
|
||||
]
|
||||
|
||||
proctoring_js = (
|
||||
[
|
||||
'proctoring/js/models/proctored_exam_allowance_model.js',
|
||||
'proctoring/js/models/proctored_exam_attempt_model.js',
|
||||
'proctoring/js/models/proctored_exam_model.js'
|
||||
] +
|
||||
[
|
||||
'proctoring/js/collections/proctored_exam_allowance_collection.js',
|
||||
'proctoring/js/collections/proctored_exam_attempt_collection.js',
|
||||
'proctoring/js/collections/proctored_exam_collection.js'
|
||||
] +
|
||||
[
|
||||
'proctoring/js/views/Backbone.ModalDialog.js',
|
||||
'proctoring/js/views/proctored_exam_add_allowance_view.js',
|
||||
'proctoring/js/views/proctored_exam_allowance_view.js',
|
||||
'proctoring/js/views/proctored_exam_attempt_view.js',
|
||||
'proctoring/js/views/proctored_exam_view.js'
|
||||
] +
|
||||
[
|
||||
'proctoring/js/proctored_app.js'
|
||||
]
|
||||
)
|
||||
|
||||
# Before a student accesses courseware, we do not
|
||||
# need many of the JS dependencies. This includes
|
||||
@@ -1693,10 +1671,6 @@ PIPELINE_JS = {
|
||||
),
|
||||
'output_filename': 'js/lms-application.js',
|
||||
},
|
||||
'proctoring': {
|
||||
'source_filenames': proctoring_js,
|
||||
'output_filename': 'js/lms-proctoring.js',
|
||||
},
|
||||
'courseware': {
|
||||
'source_filenames': courseware_js,
|
||||
'output_filename': 'js/lms-courseware.js',
|
||||
@@ -1841,6 +1815,10 @@ WEBPACK_LOADER = {
|
||||
'DEFAULT': {
|
||||
'BUNDLE_DIR_NAME': 'bundles/',
|
||||
'STATS_FILE': os.path.join(STATIC_ROOT, 'webpack-stats.json')
|
||||
},
|
||||
'WORKERS': {
|
||||
'BUNDLE_DIR_NAME': 'bundles/',
|
||||
'STATS_FILE': os.path.join(STATIC_ROOT, 'webpack-worker-stats.json')
|
||||
}
|
||||
}
|
||||
WEBPACK_CONFIG_PATH = 'webpack.prod.config.js'
|
||||
@@ -2881,9 +2859,6 @@ OPTIONAL_APPS = [
|
||||
# edxval
|
||||
('edxval', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'),
|
||||
|
||||
# edX Proctoring
|
||||
('edx_proctoring', None),
|
||||
|
||||
# Organizations App (http://github.com/edx/edx-organizations)
|
||||
('organizations', None),
|
||||
|
||||
@@ -3215,14 +3190,6 @@ MICROSITE_DATABASE_TEMPLATE_CACHE_TTL = 5 * 60
|
||||
|
||||
RSS_PROXY_CACHE_TIMEOUT = 3600 # The length of time we cache RSS retrieved from remote URLs in seconds
|
||||
|
||||
#### PROCTORING CONFIGURATION DEFAULTS
|
||||
|
||||
PROCTORING_BACKEND_PROVIDER = {
|
||||
'class': 'edx_proctoring.backends.null.NullBackendProvider',
|
||||
'options': {},
|
||||
}
|
||||
PROCTORING_SETTINGS = {}
|
||||
|
||||
#### Custom Courses for EDX (CCX) configuration
|
||||
|
||||
# This is an arbitrary hard limit.
|
||||
|
||||
@@ -112,6 +112,7 @@ STATIC_ROOT_BASE = ENV_TOKENS.get('STATIC_ROOT_BASE', None)
|
||||
if STATIC_ROOT_BASE:
|
||||
STATIC_ROOT = path(STATIC_ROOT_BASE)
|
||||
WEBPACK_LOADER['DEFAULT']['STATS_FILE'] = STATIC_ROOT / "webpack-stats.json"
|
||||
WEBPACK_LOADER['WORKERS']['STATS_FILE'] = STATIC_ROOT / "webpack-worker-stats.json"
|
||||
|
||||
|
||||
# STATIC_URL_BASE specifies the base url to use for static files
|
||||
@@ -888,11 +889,6 @@ CREDIT_HELP_LINK_URL = ENV_TOKENS.get('CREDIT_HELP_LINK_URL', CREDIT_HELP_LINK_U
|
||||
JWT_AUTH.update(ENV_TOKENS.get('JWT_AUTH', {}))
|
||||
JWT_AUTH.update(AUTH_TOKENS.get('JWT_AUTH', {}))
|
||||
|
||||
################# PROCTORING CONFIGURATION ##################
|
||||
|
||||
PROCTORING_BACKEND_PROVIDER = AUTH_TOKENS.get("PROCTORING_BACKEND_PROVIDER", PROCTORING_BACKEND_PROVIDER)
|
||||
PROCTORING_SETTINGS = ENV_TOKENS.get("PROCTORING_SETTINGS", PROCTORING_SETTINGS)
|
||||
|
||||
################# MICROSITE ####################
|
||||
MICROSITE_CONFIGURATION = ENV_TOKENS.get('MICROSITE_CONFIGURATION', {})
|
||||
MICROSITE_ROOT_DIR = path(ENV_TOKENS.get('MICROSITE_ROOT_DIR', ''))
|
||||
|
||||
@@ -35,6 +35,11 @@ XQUEUE_INTERFACE = {
|
||||
"basic_auth": ('anant', 'agarwal'),
|
||||
}
|
||||
|
||||
PROCTORING_BACKENDS = {
|
||||
'DEFAULT': 'mock',
|
||||
'mock': {},
|
||||
'mock_proctoring_without_rules': {},
|
||||
}
|
||||
|
||||
######################### PIPELINE ####################################
|
||||
|
||||
|
||||
@@ -206,6 +206,9 @@ such that the value can be defined later than this assignment (file load order).
|
||||
}, {
|
||||
constructor: edx.instructor_dashboard.proctoring.ProctoredExamAttemptView,
|
||||
$element: idashContent.find('.' + CSS_IDASH_SECTION + '#special_exams')
|
||||
}, {
|
||||
constructor: edx.instructor_dashboard.proctoring.ProctoredExamDashboardView,
|
||||
$element: idashContent.find('.' + CSS_IDASH_SECTION + '#special_exams')
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -7,15 +7,9 @@ $(function() {
|
||||
$proctoringAccordionPane.accordion(
|
||||
{
|
||||
heightStyle: 'content',
|
||||
activate: function(event, ui) {
|
||||
var active = $proctoringAccordionPane.accordion('option', 'active');
|
||||
$.cookie('saved_index', null);
|
||||
$.cookie('saved_index', active);
|
||||
},
|
||||
animate: 400,
|
||||
header: '> .wrap > .hd',
|
||||
icons: icons,
|
||||
active: isNaN(parseInt($.cookie('saved_index'))) ? 0 : parseInt($.cookie('saved_index')),
|
||||
collapsible: true
|
||||
}
|
||||
);
|
||||
|
||||
@@ -14,5 +14,9 @@ import pytz
|
||||
<h3 class="hd hd-3">${_('Student Special Exam Attempts')}</h3>
|
||||
<div class="student-proctored-exam-container" data-course-id="${ section_data['course_id'] }"></div>
|
||||
</div>
|
||||
<div class="wrap">
|
||||
<h3 class="hd hd-3">${_('Review Dashboard')}</h3>
|
||||
<div class="student-review-dashboard-container" data-course-id="${ section_data['course_id'] }"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1012,11 +1012,6 @@ if 'debug_toolbar' in settings.INSTALLED_APPS:
|
||||
url(r'^__debug__/', include(debug_toolbar.urls)),
|
||||
]
|
||||
|
||||
# include into our URL patterns the HTTP REST API that comes with edx-proctoring.
|
||||
urlpatterns += [
|
||||
url(r'^api/', include('edx_proctoring.urls')),
|
||||
]
|
||||
|
||||
if settings.FEATURES.get('ENABLE_FINANCIAL_ASSISTANCE_FORM'):
|
||||
urlpatterns += [
|
||||
url(
|
||||
|
||||
@@ -51,6 +51,12 @@ DATABASES = {
|
||||
}
|
||||
}
|
||||
|
||||
PROCTORING_BACKENDS = {
|
||||
'DEFAULT': 'mock',
|
||||
'mock': {},
|
||||
'mock_proctoring_without_rules': {},
|
||||
}
|
||||
|
||||
FEATURES = {}
|
||||
|
||||
INSTALLED_APPS = (
|
||||
|
||||
173
package-lock.json
generated
173
package-lock.json
generated
@@ -85,6 +85,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@edx/edx-proctoring": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/edx-proctoring/-/edx-proctoring-1.5.0.tgz",
|
||||
"integrity": "sha512-RiNjAgh8ZMX0D5gfN2R09a0RBs/R/Blfs/DiqhLmvCSvyCoeMDGANrDDQXv1w5blxxSJbz8a2awSZkwpv6gWNQ=="
|
||||
},
|
||||
"@edx/mockprock": {
|
||||
"version": "git+https://git@github.com/edx/mockprock.git#c9e4814ace9afad7a778e2af372b3125b3e56588",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@edx/edx-proctoring": "1.5.0"
|
||||
}
|
||||
},
|
||||
"@edx/paragon": {
|
||||
"version": "2.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-2.6.4.tgz",
|
||||
@@ -247,7 +259,7 @@
|
||||
"abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
||||
"integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg="
|
||||
},
|
||||
"accepts": {
|
||||
"version": "1.3.3",
|
||||
@@ -509,7 +521,7 @@
|
||||
"arr-flatten": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
|
||||
"integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="
|
||||
"integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE="
|
||||
},
|
||||
"arr-union": {
|
||||
"version": "3.1.0",
|
||||
@@ -1523,7 +1535,7 @@
|
||||
"babylon": {
|
||||
"version": "6.18.0",
|
||||
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
|
||||
"integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ=="
|
||||
"integrity": "sha1-ry87iPpvXB5MY00aD46sT1WzleM="
|
||||
},
|
||||
"backbone": {
|
||||
"version": "1.3.3",
|
||||
@@ -1728,7 +1740,7 @@
|
||||
"bn.js": {
|
||||
"version": "4.11.8",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
|
||||
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
|
||||
"integrity": "sha1-LN4J617jQfSEdGuwMJsyU7GxRC8="
|
||||
},
|
||||
"body-parser": {
|
||||
"version": "1.18.2",
|
||||
@@ -1891,7 +1903,7 @@
|
||||
"browserify-zlib": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
|
||||
"integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
|
||||
"integrity": "sha1-KGlFnZqjviRf6P4sofRuLn9U1z8=",
|
||||
"requires": {
|
||||
"pako": "1.0.6"
|
||||
}
|
||||
@@ -2182,7 +2194,7 @@
|
||||
"cipher-base": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
|
||||
"integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
|
||||
"integrity": "sha1-h2Dk7MJy9MNjUy+SbYdKriwTl94=",
|
||||
"requires": {
|
||||
"inherits": "2.0.3",
|
||||
"safe-buffer": "5.1.1"
|
||||
@@ -2203,7 +2215,7 @@
|
||||
"clap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz",
|
||||
"integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==",
|
||||
"integrity": "sha1-TzZ0WzIAhJJVf0ZBLWbVDLmbzlE=",
|
||||
"requires": {
|
||||
"chalk": "1.1.3"
|
||||
},
|
||||
@@ -2401,7 +2413,7 @@
|
||||
"color-convert": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
|
||||
"integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
|
||||
"integrity": "sha1-wSYRB66y8pTr/+ye2eytUppgl+0=",
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
@@ -2568,7 +2580,7 @@
|
||||
"content-type": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
||||
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
|
||||
"integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=",
|
||||
"dev": true
|
||||
},
|
||||
"convert-source-map": {
|
||||
@@ -2608,7 +2620,7 @@
|
||||
"cosmiconfig": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-3.1.0.tgz",
|
||||
"integrity": "sha512-zedsBhLSbPBms+kE7AH4vHg6JsKDz6epSv2/+5XHs8ILHlgDciSJfSWf8sX9aQ52Jb7KI7VswUTsLpR/G0cr2Q==",
|
||||
"integrity": "sha1-ZAqUv5hH8yGABAPNJzr2BmXHM5c=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-directory": "0.3.1",
|
||||
@@ -2620,7 +2632,7 @@
|
||||
"esprima": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
|
||||
"integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==",
|
||||
"integrity": "sha1-RJnt3NERDgshi6zy+n9/WfVcqAQ=",
|
||||
"dev": true
|
||||
},
|
||||
"js-yaml": {
|
||||
@@ -2707,7 +2719,7 @@
|
||||
"crypto-browserify": {
|
||||
"version": "3.12.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
|
||||
"integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
|
||||
"integrity": "sha1-OWz58xN/A+S45TLFj2mCVOAPgOw=",
|
||||
"requires": {
|
||||
"browserify-cipher": "1.0.0",
|
||||
"browserify-sign": "4.0.4",
|
||||
@@ -2945,6 +2957,31 @@
|
||||
"whatwg-url": "6.5.0"
|
||||
}
|
||||
},
|
||||
"datatables": {
|
||||
"version": "1.10.18",
|
||||
"resolved": "https://registry.npmjs.org/datatables/-/datatables-1.10.18.tgz",
|
||||
"integrity": "sha512-ntatMgS9NN6UMpwbmO+QkYJuKlVeMA2Mi0Gu/QxyIh+dW7ZjLSDhPT2tWlzjpIWEkDYgieDzS9Nu7bdQCW0sbQ==",
|
||||
"requires": {
|
||||
"jquery": "2.2.4"
|
||||
}
|
||||
},
|
||||
"datatables.net": {
|
||||
"version": "1.10.19",
|
||||
"resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.10.19.tgz",
|
||||
"integrity": "sha512-+ljXcI6Pj3PTGy5pesp3E5Dr3x3AV45EZe0o1r0gKENN2gafBKXodVnk2ypKwl2tTmivjxbkiqoWnipTefyBTA==",
|
||||
"requires": {
|
||||
"jquery": "2.2.4"
|
||||
}
|
||||
},
|
||||
"datatables.net-fixedcolumns": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/datatables.net-fixedcolumns/-/datatables.net-fixedcolumns-3.2.6.tgz",
|
||||
"integrity": "sha512-PtEs2tllcHRVZj7fwmAQBWGJ5URRQZpDG2pJsh5jusvnRje3w1+KueMZm60iCtfOkIlUn+/j2+MghxLx/8yfKQ==",
|
||||
"requires": {
|
||||
"datatables.net": "1.10.19",
|
||||
"jquery": "2.2.4"
|
||||
}
|
||||
},
|
||||
"date-now": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
|
||||
@@ -2958,7 +2995,7 @@
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
@@ -3338,6 +3375,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"edx-proctoring-proctortrack": {
|
||||
"version": "git+https://git@github.com/joshivj/edx-proctoring-proctortrack.git#66650ed6cd39bf489a86723d5ad3593c2ec8992f",
|
||||
"requires": {
|
||||
"@edx/edx-proctoring": "1.5.0"
|
||||
}
|
||||
},
|
||||
"edx-ui-toolkit": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/edx-ui-toolkit/-/edx-ui-toolkit-1.5.2.tgz",
|
||||
@@ -3431,7 +3474,7 @@
|
||||
"emoji-regex": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.5.1.tgz",
|
||||
"integrity": "sha512-PAHp6TxrCy7MGMFidro8uikr+zlJJKJ/Q6mm2ExZ7HwkyR9lSVFfE3kt36qcwa24BQL7y0G9axycGjK1A/0uNQ==",
|
||||
"integrity": "sha1-m66pKbFVVlwR6kHGYm6qZc75ksI=",
|
||||
"dev": true
|
||||
},
|
||||
"emojis-list": {
|
||||
@@ -4017,7 +4060,7 @@
|
||||
"eslint-config-airbnb-base": {
|
||||
"version": "11.3.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-11.3.2.tgz",
|
||||
"integrity": "sha512-/fhjt/VqzBA2SRsx7ErDtv6Ayf+XLw9LIOqmpBuHFCVwyJo2EtzGWMB9fYRFBoWWQLxmNmCpenNiH0RxyeS41w==",
|
||||
"integrity": "sha1-hwOxGr48iKx+wrdFt/31LgCuaAo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"eslint-restricted-globals": "0.1.1"
|
||||
@@ -4315,7 +4358,7 @@
|
||||
"evp_bytestokey": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
|
||||
"integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
|
||||
"integrity": "sha1-f8vbGY3HGVlDLv4ThCaE4FJaywI=",
|
||||
"requires": {
|
||||
"md5.js": "1.3.4",
|
||||
"safe-buffer": "5.1.1"
|
||||
@@ -4522,7 +4565,7 @@
|
||||
"async": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz",
|
||||
"integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==",
|
||||
"integrity": "sha1-YaKau2/MAm/qd+VtHG7FOnlZUfQ=",
|
||||
"requires": {
|
||||
"lodash": "4.17.5"
|
||||
}
|
||||
@@ -4899,7 +4942,7 @@
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
"integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0="
|
||||
},
|
||||
"function.prototype.name": {
|
||||
"version": "1.1.0",
|
||||
@@ -4987,7 +5030,7 @@
|
||||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
||||
"integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
|
||||
"requires": {
|
||||
"fs.realpath": "1.0.0",
|
||||
"inflight": "1.0.6",
|
||||
@@ -5017,7 +5060,7 @@
|
||||
"globals": {
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
|
||||
"integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ=="
|
||||
"integrity": "sha1-qjiWs+abSH8X4x7SFD1pqOMMLYo="
|
||||
},
|
||||
"globby": {
|
||||
"version": "7.1.1",
|
||||
@@ -5570,7 +5613,7 @@
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
"integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -5582,7 +5625,7 @@
|
||||
"ignore": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz",
|
||||
"integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==",
|
||||
"integrity": "sha1-YSKJv7PCIOGGpYEYYY1b6MG6sCE=",
|
||||
"dev": true
|
||||
},
|
||||
"import-local": {
|
||||
@@ -5851,7 +5894,7 @@
|
||||
"is-buffer": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
|
||||
"integrity": "sha1-76ouqdqg16suoTqXsritUf776L4="
|
||||
},
|
||||
"is-builtin-module": {
|
||||
"version": "1.0.0",
|
||||
@@ -6048,7 +6091,7 @@
|
||||
"is-plain-object": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
|
||||
"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
|
||||
"integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=",
|
||||
"requires": {
|
||||
"isobject": "3.0.1"
|
||||
}
|
||||
@@ -8231,7 +8274,7 @@
|
||||
"string_decoder": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
|
||||
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
@@ -8376,7 +8419,7 @@
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
|
||||
"requires": {
|
||||
"brace-expansion": "1.1.11"
|
||||
}
|
||||
@@ -8562,7 +8605,7 @@
|
||||
"node-fetch": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
|
||||
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
|
||||
"integrity": "sha1-mA9vcthSEaU0fGsrwYxbhMPrR+8=",
|
||||
"requires": {
|
||||
"encoding": "0.1.12",
|
||||
"is-stream": "1.1.0"
|
||||
@@ -8604,7 +8647,7 @@
|
||||
"node-libs-browser": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz",
|
||||
"integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==",
|
||||
"integrity": "sha1-X5QmPUBPbkR2fXJpAf/wVHjWAN8=",
|
||||
"requires": {
|
||||
"assert": "1.4.1",
|
||||
"browserify-zlib": "0.2.0",
|
||||
@@ -8775,7 +8818,7 @@
|
||||
"normalize-package-data": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
|
||||
"integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
|
||||
"integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=",
|
||||
"requires": {
|
||||
"hosted-git-info": "2.5.0",
|
||||
"is-builtin-module": "1.0.0",
|
||||
@@ -9203,7 +9246,7 @@
|
||||
"pako": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz",
|
||||
"integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg=="
|
||||
"integrity": "sha1-AQEhG6pwxLykoPY/Igbpe3368lg="
|
||||
},
|
||||
"parse-asn1": {
|
||||
"version": "5.1.0",
|
||||
@@ -9356,7 +9399,7 @@
|
||||
"pbkdf2": {
|
||||
"version": "3.0.14",
|
||||
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz",
|
||||
"integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==",
|
||||
"integrity": "sha1-o14TxkeZsGzhUyD0WcIw5o5zut4=",
|
||||
"requires": {
|
||||
"create-hash": "1.1.3",
|
||||
"create-hmac": "1.1.6",
|
||||
@@ -9790,7 +9833,7 @@
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
"integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -9878,7 +9921,7 @@
|
||||
"postcss-reporter": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-5.0.0.tgz",
|
||||
"integrity": "sha512-rBkDbaHAu5uywbCR2XE8a25tats3xSOsGNx6mppK6Q9kSFGKc/FyAzfci+fWM2l+K402p1D0pNcfDGxeje5IKg==",
|
||||
"integrity": "sha1-oUF3/RNCgp0pFlPyeG79ZxEDMsM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "2.3.1",
|
||||
@@ -9901,7 +9944,7 @@
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -9964,7 +10007,7 @@
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -10080,7 +10123,7 @@
|
||||
"private": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
|
||||
"integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg=="
|
||||
"integrity": "sha1-I4Hts2ifelPWUxkAYPz4ItLzaP8="
|
||||
},
|
||||
"process": {
|
||||
"version": "0.11.10",
|
||||
@@ -10101,7 +10144,7 @@
|
||||
"promise": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
|
||||
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
|
||||
"integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=",
|
||||
"requires": {
|
||||
"asap": "2.0.6"
|
||||
}
|
||||
@@ -10236,7 +10279,7 @@
|
||||
"randomatic": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz",
|
||||
"integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==",
|
||||
"integrity": "sha1-x6vpzIuHwLqodrGf3oP9RkeX44w=",
|
||||
"requires": {
|
||||
"is-number": "3.0.0",
|
||||
"kind-of": "4.0.0"
|
||||
@@ -10590,7 +10633,7 @@
|
||||
"string_decoder": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
|
||||
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
@@ -10670,7 +10713,7 @@
|
||||
"redux": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz",
|
||||
"integrity": "sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==",
|
||||
"integrity": "sha1-BrcxIyFZAdJdBlvjQusCa8HIU3s=",
|
||||
"requires": {
|
||||
"lodash": "4.17.5",
|
||||
"lodash-es": "4.17.6",
|
||||
@@ -10706,7 +10749,7 @@
|
||||
"regenerator-transform": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz",
|
||||
"integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==",
|
||||
"integrity": "sha1-HkmWg3Ix2ot/PPQRTXG1aRoGgN0=",
|
||||
"requires": {
|
||||
"babel-runtime": "6.26.0",
|
||||
"babel-types": "6.26.0",
|
||||
@@ -11050,7 +11093,7 @@
|
||||
"rtlcss": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-2.2.1.tgz",
|
||||
"integrity": "sha512-JjQ5DlrmwiItAjlmhoxrJq5ihgZcE0wMFxt7S17bIrt4Lw0WwKKFk+viRhvodB/0falyG/5fiO043ZDh6/aqTw==",
|
||||
"integrity": "sha1-+FN+QVUggWawXhiYAhMZNvzv0p4=",
|
||||
"requires": {
|
||||
"chalk": "2.3.1",
|
||||
"findup": "0.1.5",
|
||||
@@ -11072,7 +11115,7 @@
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
"integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -11094,7 +11137,7 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
||||
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
|
||||
"integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM="
|
||||
},
|
||||
"safe-regex": {
|
||||
"version": "1.1.0",
|
||||
@@ -11488,7 +11531,7 @@
|
||||
"sass-loader": {
|
||||
"version": "6.0.6",
|
||||
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-6.0.6.tgz",
|
||||
"integrity": "sha512-c3/Zc+iW+qqDip6kXPYLEgsAu2lf4xz0EZDplB7EmSUMda12U1sGJPetH55B/j9eu0bTtKzKlNPWWyYC7wFNyQ==",
|
||||
"integrity": "sha1-6dXmwfFV+qMqSybXqbcQfCJeQPk=",
|
||||
"requires": {
|
||||
"async": "2.6.0",
|
||||
"clone-deep": "0.3.0",
|
||||
@@ -11500,7 +11543,7 @@
|
||||
"async": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz",
|
||||
"integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==",
|
||||
"integrity": "sha1-YaKau2/MAm/qd+VtHG7FOnlZUfQ=",
|
||||
"requires": {
|
||||
"lodash": "4.17.5"
|
||||
}
|
||||
@@ -11770,7 +11813,7 @@
|
||||
"slice-ansi": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
|
||||
"integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==",
|
||||
"integrity": "sha1-BE8aSdiEL/MHqta1Be0Xi9lQE00=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-fullwidth-code-point": "2.0.0"
|
||||
@@ -12046,7 +12089,7 @@
|
||||
"source-list-map": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz",
|
||||
"integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A=="
|
||||
"integrity": "sha1-qqR0A/eyRakvvJfqCPJQ1gh+0IU="
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.5.7",
|
||||
@@ -12116,7 +12159,7 @@
|
||||
"specificity": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/specificity/-/specificity-0.3.2.tgz",
|
||||
"integrity": "sha512-Nc/QN/A425Qog7j9aHmwOrlwX2e7pNI47ciwxwy4jOlvbbMHkNNJchit+FX+UjF3IAdiaaV5BKeWuDUnws6G1A==",
|
||||
"integrity": "sha1-meZRHs7vD42bV5JJN6rCyxPRPEI=",
|
||||
"dev": true
|
||||
},
|
||||
"split-string": {
|
||||
@@ -12283,7 +12326,7 @@
|
||||
"string_decoder": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
|
||||
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
@@ -12506,7 +12549,7 @@
|
||||
"style-loader": {
|
||||
"version": "0.18.2",
|
||||
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.18.2.tgz",
|
||||
"integrity": "sha512-WPpJPZGUxWYHWIUMNNOYqql7zh85zGmr84FdTVWq52WTIkqlW9xSxD3QYWi/T31cqn9UNSsietVEgGn2aaSCzw==",
|
||||
"integrity": "sha1-zDFFmvvNbYC3Ig7lSykan9Zv9es=",
|
||||
"requires": {
|
||||
"loader-utils": "1.1.0",
|
||||
"schema-utils": "0.3.0"
|
||||
@@ -12617,7 +12660,7 @@
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
@@ -12772,13 +12815,13 @@
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
||||
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
|
||||
"integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-fullwidth-code-point": "2.0.0",
|
||||
@@ -12823,7 +12866,7 @@
|
||||
"stylelint-config-recommended-scss": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-2.0.0.tgz",
|
||||
"integrity": "sha512-DUIW3daRl5EAyU4ZR6xfPa+bqV5wDccS7X1je6Enes9edpbmWUBR/5XLfDPnjMJgqOe2QwqwaE/qnG4lXID9rg==",
|
||||
"integrity": "sha1-P0SzOK+zv1tr2e663UaO7ydxOSI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"stylelint-config-recommended": "1.0.0"
|
||||
@@ -12832,7 +12875,7 @@
|
||||
"stylelint-config-standard": {
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-17.0.0.tgz",
|
||||
"integrity": "sha512-G8jMZ0KsaVH7leur9XLZVhwOBHZ2vdbuJV8Bgy0ta7/PpBhEHo6fjVDaNchyCGXB5sRcWVq6O9rEU/MvY9cQDQ==",
|
||||
"integrity": "sha1-QhA6CQBU7io93p7K7VXl1NnQWfw=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"stylelint-config-recommended": "1.0.0"
|
||||
@@ -12841,7 +12884,7 @@
|
||||
"stylelint-formatter-pretty": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/stylelint-formatter-pretty/-/stylelint-formatter-pretty-1.0.3.tgz",
|
||||
"integrity": "sha512-Jg39kL6kkjUrdKIiHwwz/fbElcF5dOS48ZhvGrEJeWijUbmY1yudclfXv9H61eBqKKu0E33nfez2r0G4EvPtFA==",
|
||||
"integrity": "sha1-prQ8PzoTIGvft3fQ2ozvxsdsNsM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-escapes": "2.0.0",
|
||||
@@ -12900,7 +12943,7 @@
|
||||
"string-width": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
||||
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
|
||||
"integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-fullwidth-code-point": "2.0.0",
|
||||
@@ -12955,7 +12998,7 @@
|
||||
"sugarss": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/sugarss/-/sugarss-1.0.1.tgz",
|
||||
"integrity": "sha512-3qgLZytikQQEVn1/FrhY7B68gPUUGY3R1Q1vTiD5xT+Ti1DP/8iZuwFet9ONs5+bmL8pZoDQ6JrQHVgrNlK6mA==",
|
||||
"integrity": "sha1-voJtkAPg8kdzX5I2XcP9fxuunkQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"postcss": "6.0.19"
|
||||
@@ -12975,7 +13018,7 @@
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -13446,7 +13489,7 @@
|
||||
"string_decoder": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
|
||||
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
@@ -13469,7 +13512,7 @@
|
||||
"tmp": {
|
||||
"version": "0.0.33",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
|
||||
"integrity": "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"os-tmpdir": "1.0.2"
|
||||
@@ -14403,7 +14446,7 @@
|
||||
"webpack-merge": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.1.tgz",
|
||||
"integrity": "sha512-geQsZ86YkXOVOjvPC5yv3JSNnL6/X3Kzh935AQ/gJNEYXEfJDQFu/sdFuktS9OW2JcH/SJec8TGfRdrpHshH7A==",
|
||||
"integrity": "sha1-8Rl6Cpc+acb77rbWWCGaqMDBNVU=",
|
||||
"requires": {
|
||||
"lodash": "4.17.5"
|
||||
}
|
||||
@@ -14420,7 +14463,7 @@
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
"integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -14571,7 +14614,7 @@
|
||||
"xml2js": {
|
||||
"version": "0.4.19",
|
||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
|
||||
"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
|
||||
"integrity": "sha1-aGwg8hMgnpSr8NG88e+qKRx4J6c=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"sax": "1.2.4",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"dependencies": {
|
||||
"@edx/cookie-policy-banner": "1.1.10",
|
||||
"@edx/edx-bootstrap": "1.0.3",
|
||||
"@edx/edx-proctoring": "^1.5.0",
|
||||
"@edx/paragon": "2.6.4",
|
||||
"@edx/studio-frontend": "1.16.12",
|
||||
"babel-core": "6.26.0",
|
||||
@@ -21,7 +22,10 @@
|
||||
"camelize": "1.0.0",
|
||||
"classnames": "2.2.5",
|
||||
"css-loader": "0.28.8",
|
||||
"datatables": "1.10.18",
|
||||
"datatables.net-fixedcolumns": "3.2.6",
|
||||
"edx-pattern-library": "0.18.1",
|
||||
"edx-proctoring-proctortrack": "git+https://git@github.com/joshivj/edx-proctoring-proctortrack.git",
|
||||
"edx-ui-toolkit": "1.5.2",
|
||||
"exports-loader": "0.6.4",
|
||||
"extract-text-webpack-plugin": "2.1.2",
|
||||
@@ -63,6 +67,7 @@
|
||||
"which-country": "1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@edx/mockprock": "^1.0.1",
|
||||
"@edx/stylelint-config-edx": "1.1.0",
|
||||
"babel-jest": "23.0.1",
|
||||
"edx-custom-a11y-rules": "1.0.5",
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
# https://github.com/transifex/transifex-client/issues/252
|
||||
six==1.11.0
|
||||
|
||||
|
||||
|
||||
# Convert text markup to HTML; used in capa problems, forums, and course wikis; pin Markdown version as tests failed for its upgrade to latest release
|
||||
Markdown==2.6.11
|
||||
|
||||
@@ -21,9 +19,6 @@ Markdown==2.6.11
|
||||
# Can be removed when we get to Python 3.
|
||||
pylint-plugin-utils==0.3
|
||||
|
||||
# Testing framework # Pinned due to https://github.com/pytest-dev/pytest/issues/3749
|
||||
pytest==3.6.3
|
||||
|
||||
# pytest plugin for measuring code coverage. # Pinned due to https://openedx.atlassian.net/browse/TE-2731
|
||||
pytest-cov<2.6
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ edx-enterprise
|
||||
edx-milestones
|
||||
edx-oauth2-provider
|
||||
edx-organizations
|
||||
edx-proctoring
|
||||
edx-proctoring>=1.5.0
|
||||
edx-rest-api-client
|
||||
edx-search
|
||||
edx-submissions
|
||||
@@ -133,7 +133,7 @@ rfc6266-parser # Used to generate Content-Disposition heade
|
||||
social-auth-app-django<3.0.0
|
||||
social-auth-core<2.0.0
|
||||
pysrt==0.4.7 # Support for SubRip subtitle files, used in the video XModule
|
||||
pytz==2016.10 # Time zone information database
|
||||
pytz # Time zone information database
|
||||
PyYAML # Used to parse XModule resource templates
|
||||
redis==2.10.6 # celery task broker
|
||||
requests-oauthlib # Simplifies use of OAuth via the requests library, used for CCX and LTI
|
||||
|
||||
@@ -118,13 +118,13 @@ edx-django-release-util==0.3.1
|
||||
edx-django-sites-extensions==2.3.1
|
||||
edx-django-utils==1.0.3
|
||||
edx-drf-extensions==2.0.1
|
||||
edx-enterprise==1.2.0
|
||||
edx-enterprise==1.2.1
|
||||
edx-i18n-tools==0.4.6
|
||||
edx-milestones==0.1.13
|
||||
edx-oauth2-provider==1.2.2
|
||||
edx-opaque-keys[django]==0.4.4
|
||||
edx-organizations==1.0.0
|
||||
edx-proctoring==1.4.0
|
||||
edx-proctoring==1.5.0
|
||||
edx-rest-api-client==1.9.2
|
||||
edx-search==1.2.1
|
||||
edx-submissions==2.0.12
|
||||
@@ -204,7 +204,7 @@ python-memcached==1.48
|
||||
python-openid==2.2.5
|
||||
python-saml==2.4.0
|
||||
python-swiftclient==3.6.0
|
||||
pytz==2016.10
|
||||
pytz==2018.7
|
||||
pyuca==1.1
|
||||
pyyaml==3.13
|
||||
redis==2.10.6
|
||||
|
||||
@@ -14,3 +14,4 @@
|
||||
|
||||
coverage==4.4 # Code coverage testing for Python
|
||||
diff-cover==0.9.8 # Automatically find diff lines that need test coverage
|
||||
six==1.11.0 # Pinned because diff-cover needs it, but later transifex-client says ==1.11.0
|
||||
|
||||
@@ -11,4 +11,4 @@ jinja2-pluralize==0.3.0 # via diff-cover
|
||||
jinja2==2.10 # via diff-cover, jinja2-pluralize
|
||||
markupsafe==1.1.0 # via jinja2
|
||||
pygments==2.3.1 # via diff-cover
|
||||
six==1.11.0 # via diff-cover
|
||||
six==1.11.0
|
||||
|
||||
@@ -137,14 +137,14 @@ edx-django-release-util==0.3.1
|
||||
edx-django-sites-extensions==2.3.1
|
||||
edx-django-utils==1.0.3
|
||||
edx-drf-extensions==2.0.1
|
||||
edx-enterprise==1.2.0
|
||||
edx-enterprise==1.2.1
|
||||
edx-i18n-tools==0.4.6
|
||||
edx-lint==1.0.0
|
||||
edx-milestones==0.1.13
|
||||
edx-oauth2-provider==1.2.2
|
||||
edx-opaque-keys[django]==0.4.4
|
||||
edx-organizations==1.0.0
|
||||
edx-proctoring==1.4.0
|
||||
edx-proctoring==1.5.0
|
||||
edx-rest-api-client==1.9.2
|
||||
edx-search==1.2.1
|
||||
edx-sphinx-theme==1.4.0
|
||||
@@ -228,6 +228,7 @@ pa11ycrawler==1.6.2
|
||||
packaging==18.0 # via sphinx
|
||||
parsel==1.5.1
|
||||
path.py==8.2.1
|
||||
pathlib2==2.3.3
|
||||
pathtools==0.1.2
|
||||
paver==1.3.4
|
||||
pbr==5.1.1
|
||||
@@ -235,7 +236,7 @@ pdfminer==20140328
|
||||
piexif==1.0.2
|
||||
pillow==5.3.0
|
||||
pip-tools==3.2.0
|
||||
pluggy==0.6.0
|
||||
pluggy==0.8.0
|
||||
polib==1.1.0
|
||||
psutil==1.2.1
|
||||
py2neo==3.1.2
|
||||
@@ -271,7 +272,7 @@ pytest-django==3.1.2
|
||||
pytest-forked==0.2
|
||||
pytest-randomly==1.2.3
|
||||
pytest-xdist==1.25.0
|
||||
pytest==3.6.3
|
||||
pytest==4.0.2
|
||||
python-dateutil==2.4.0
|
||||
python-levenshtein==0.12.0
|
||||
python-memcached==1.48
|
||||
@@ -281,7 +282,7 @@ python-saml==2.4.0
|
||||
python-slugify==1.2.6
|
||||
python-subunit==1.3.0
|
||||
python-swiftclient==3.6.0
|
||||
pytz==2016.10
|
||||
pytz==2018.7
|
||||
pyuca==1.1
|
||||
pyyaml==3.13
|
||||
queuelib==1.5.0
|
||||
@@ -295,6 +296,7 @@ rfc6266-parser==0.0.5.post2
|
||||
rules==2.0.1
|
||||
s3transfer==0.1.13
|
||||
sailthru-client==2.2.3
|
||||
scandir==1.9.0
|
||||
scipy==0.14.0
|
||||
scrapy==1.1.2
|
||||
selenium==3.141.0
|
||||
@@ -312,20 +314,20 @@ social-auth-app-django==2.1.0
|
||||
social-auth-core==1.7.0
|
||||
sorl-thumbnail==12.3
|
||||
sortedcontainers==0.9.2
|
||||
sphinx==1.8.2
|
||||
sphinx==1.8.3
|
||||
sphinxcontrib-websupport==1.1.0 # via sphinx
|
||||
splinter==0.9.0
|
||||
sqlparse==0.2.4
|
||||
stevedore==1.10.0
|
||||
sure==1.4.11
|
||||
sympy==0.7.1
|
||||
testfixtures==6.4.0
|
||||
testfixtures==6.4.1
|
||||
testtools==2.3.0
|
||||
text-unidecode==1.2
|
||||
tincan==0.0.5
|
||||
toml==0.10.0
|
||||
tox-battery==0.5.1
|
||||
tox==3.6.0
|
||||
tox==3.6.1
|
||||
traceback2==1.4.0
|
||||
transifex-client==0.13.5
|
||||
twisted==16.6.0
|
||||
|
||||
@@ -24,3 +24,5 @@ requests # Simple interface for making HTTP requests
|
||||
stevedore==1.10.0 # via edx-opaque-keys
|
||||
watchdog # Used in paver watch_assets
|
||||
wrapt==1.10.5 # Decorator utilities used in the @timed paver task decorator
|
||||
|
||||
six==1.11.0 # Pinned because a few things here need it, but later transifex-client says ==1.11.0
|
||||
|
||||
@@ -23,7 +23,7 @@ pymongo==2.9.1
|
||||
python-memcached==1.48
|
||||
pyyaml==3.13 # via watchdog
|
||||
requests==2.21.0
|
||||
six==1.11.0 # via edx-opaque-keys, libsass, paver, stevedore
|
||||
six==1.11.0
|
||||
stevedore==1.10.0
|
||||
urllib3==1.23 # via requests
|
||||
watchdog==0.9.0
|
||||
|
||||
@@ -10,3 +10,4 @@
|
||||
-c ../constraints.txt
|
||||
|
||||
pip-tools # Contains pip-compile, used to generate pip requirements files
|
||||
six==1.11.0 # Pinned because pip-tools needs it, but later transifex-client says ==1.11.0
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
#
|
||||
click==7.0 # via pip-tools
|
||||
pip-tools==3.2.0
|
||||
six==1.11.0 # via pip-tools
|
||||
six==1.11.0
|
||||
|
||||
@@ -132,14 +132,14 @@ edx-django-release-util==0.3.1
|
||||
edx-django-sites-extensions==2.3.1
|
||||
edx-django-utils==1.0.3
|
||||
edx-drf-extensions==2.0.1
|
||||
edx-enterprise==1.2.0
|
||||
edx-enterprise==1.2.1
|
||||
edx-i18n-tools==0.4.6
|
||||
edx-lint==1.0.0
|
||||
edx-milestones==0.1.13
|
||||
edx-oauth2-provider==1.2.2
|
||||
edx-opaque-keys[django]==0.4.4
|
||||
edx-organizations==1.0.0
|
||||
edx-proctoring==1.4.0
|
||||
edx-proctoring==1.5.0
|
||||
edx-rest-api-client==1.9.2
|
||||
edx-search==1.2.1
|
||||
edx-submissions==2.0.12
|
||||
@@ -219,13 +219,14 @@ openapi-codec==1.3.2
|
||||
pa11ycrawler==1.6.2
|
||||
parsel==1.5.1 # via scrapy
|
||||
path.py==8.2.1
|
||||
pathlib2==2.3.3 # via pytest
|
||||
pathtools==0.1.2
|
||||
paver==1.3.4
|
||||
pbr==5.1.1
|
||||
pdfminer==20140328
|
||||
piexif==1.0.2
|
||||
pillow==5.3.0
|
||||
pluggy==0.6.0 # via pytest, tox
|
||||
pluggy==0.8.0 # via pytest, tox
|
||||
polib==1.1.0
|
||||
psutil==1.2.1
|
||||
py2neo==3.1.2
|
||||
@@ -246,7 +247,7 @@ pyjwt==1.5.2
|
||||
pylint-celery==0.3 # via edx-lint
|
||||
pylint-django==0.7.2 # via edx-lint
|
||||
pylint-plugin-utils==0.3 # via pylint-celery, pylint-django
|
||||
pylint==1.7.6 # via edx-lint, pylint-celery, pylint-django, pylint-plugin-utils
|
||||
pylint==1.7.6 # via edx-lint, pylint-celery, pylint-django
|
||||
pymongo==2.9.1
|
||||
pynliner==0.8.0
|
||||
pyopenssl==18.0.0 # via scrapy
|
||||
@@ -260,7 +261,7 @@ pytest-django==3.1.2
|
||||
pytest-forked==0.2 # via pytest-xdist
|
||||
pytest-randomly==1.2.3
|
||||
pytest-xdist==1.25.0
|
||||
pytest==3.6.3
|
||||
pytest==4.0.2
|
||||
python-dateutil==2.4.0
|
||||
python-levenshtein==0.12.0
|
||||
python-memcached==1.48
|
||||
@@ -270,7 +271,7 @@ python-saml==2.4.0
|
||||
python-slugify==1.2.6 # via transifex-client
|
||||
python-subunit==1.3.0
|
||||
python-swiftclient==3.6.0
|
||||
pytz==2016.10
|
||||
pytz==2018.7
|
||||
pyuca==1.1
|
||||
pyyaml==3.13
|
||||
queuelib==1.5.0 # via scrapy
|
||||
@@ -284,6 +285,7 @@ rfc6266-parser==0.0.5.post2
|
||||
rules==2.0.1
|
||||
s3transfer==0.1.13
|
||||
sailthru-client==2.2.3
|
||||
scandir==1.9.0 # via pathlib2
|
||||
scipy==0.14.0
|
||||
scrapy==1.1.2 # via pa11ycrawler
|
||||
selenium==3.141.0
|
||||
@@ -304,13 +306,13 @@ sqlparse==0.2.4
|
||||
stevedore==1.10.0
|
||||
sure==1.4.11
|
||||
sympy==0.7.1
|
||||
testfixtures==6.4.0
|
||||
testfixtures==6.4.1
|
||||
testtools==2.3.0 # via fixtures, python-subunit
|
||||
text-unidecode==1.2 # via faker
|
||||
tincan==0.0.5
|
||||
toml==0.10.0 # via tox
|
||||
tox-battery==0.5.1
|
||||
tox==3.6.0
|
||||
tox==3.6.1
|
||||
traceback2==1.4.0 # via testtools, unittest2
|
||||
transifex-client==0.13.5
|
||||
twisted==16.6.0 # via pa11ycrawler, scrapy
|
||||
|
||||
@@ -26,362 +26,405 @@ var defineFooter = new RegExp('(' + defineCallFooter.source + ')|('
|
||||
+ defineDirectFooter.source + ')|('
|
||||
+ defineFancyFooter.source + ')', 'm');
|
||||
|
||||
module.exports = Merge.smart({
|
||||
context: __dirname,
|
||||
|
||||
entry: {
|
||||
// Studio
|
||||
Import: './cms/static/js/features/import/factories/import.js',
|
||||
CourseOrLibraryListing: './cms/static/js/features_jsx/studio/CourseOrLibraryListing.jsx',
|
||||
'js/factories/login': './cms/static/js/factories/login.js',
|
||||
'js/factories/textbooks': './cms/static/js/factories/textbooks.js',
|
||||
'js/factories/container': './cms/static/js/factories/container.js',
|
||||
'js/factories/context_course': './cms/static/js/factories/context_course.js',
|
||||
'js/factories/library': './cms/static/js/factories/library.js',
|
||||
'js/factories/xblock_validation': './cms/static/js/factories/xblock_validation.js',
|
||||
'js/factories/edit_tabs': './cms/static/js/factories/edit_tabs.js',
|
||||
'js/sock': './cms/static/js/sock.js',
|
||||
|
||||
// LMS
|
||||
SingleSupportForm: './lms/static/support/jsx/single_support_form.jsx',
|
||||
AlertStatusBar: './lms/static/js/accessible_components/StatusBarAlert.jsx',
|
||||
LearnerAnalyticsDashboard: './lms/static/js/learner_analytics_dashboard/LearnerAnalyticsDashboard.jsx',
|
||||
UpsellExperimentModal: './lms/static/common/js/components/UpsellExperimentModal.jsx',
|
||||
PortfolioExperimentUpsellModal: './lms/static/common/js/components/PortfolioExperimentUpsellModal.jsx',
|
||||
EntitlementSupportPage: './lms/djangoapps/support/static/support/jsx/entitlements/index.jsx',
|
||||
PasswordResetConfirmation: './lms/static/js/student_account/components/PasswordResetConfirmation.jsx',
|
||||
StudentAccountDeletion: './lms/static/js/student_account/components/StudentAccountDeletion.jsx',
|
||||
StudentAccountDeletionInitializer: './lms/static/js/student_account/StudentAccountDeletionInitializer.js',
|
||||
ProblemBrowser: './lms/djangoapps/instructor/static/instructor/ProblemBrowser/index.jsx',
|
||||
|
||||
// Learner Dashboard
|
||||
EntitlementFactory: './lms/static/js/learner_dashboard/course_entitlement_factory.js',
|
||||
EntitlementUnenrollmentFactory: './lms/static/js/learner_dashboard/entitlement_unenrollment_factory.js',
|
||||
ProgramDetailsFactory: './lms/static/js/learner_dashboard/program_details_factory.js',
|
||||
ProgramListFactory: './lms/static/js/learner_dashboard/program_list_factory.js',
|
||||
UnenrollmentFactory: './lms/static/js/learner_dashboard/unenrollment_factory.js',
|
||||
CompletionOnViewService: './lms/static/completion/js/CompletionOnViewService.js',
|
||||
|
||||
// Features
|
||||
CourseGoals: './openedx/features/course_experience/static/course_experience/js/CourseGoals.js',
|
||||
CourseHome: './openedx/features/course_experience/static/course_experience/js/CourseHome.js',
|
||||
CourseOutline: './openedx/features/course_experience/static/course_experience/js/CourseOutline.js',
|
||||
CourseSock: './openedx/features/course_experience/static/course_experience/js/CourseSock.js',
|
||||
CourseTalkReviews: './openedx/features/course_experience/static/course_experience/js/CourseTalkReviews.js',
|
||||
Currency: './openedx/features/course_experience/static/course_experience/js/currency.js',
|
||||
Enrollment: './openedx/features/course_experience/static/course_experience/js/Enrollment.js',
|
||||
LatestUpdate: './openedx/features/course_experience/static/course_experience/js/LatestUpdate.js',
|
||||
WelcomeMessage: './openedx/features/course_experience/static/course_experience/js/WelcomeMessage.js',
|
||||
|
||||
CookiePolicyBanner: './common/static/js/src/CookiePolicyBanner.jsx',
|
||||
|
||||
// Common
|
||||
ReactRenderer: './common/static/js/src/ReactRenderer.jsx',
|
||||
XModuleShim: 'xmodule/js/src/xmodule.js',
|
||||
|
||||
VerticalStudentView: './common/lib/xmodule/xmodule/assets/vertical/public/js/vertical_student_view.js',
|
||||
commons: 'babel-polyfill'
|
||||
},
|
||||
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'common/static/bundles'),
|
||||
libraryTarget: 'window'
|
||||
},
|
||||
|
||||
plugins: [
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
new webpack.NamedModulesPlugin(),
|
||||
new BundleTracker({
|
||||
path: process.env.STATIC_ROOT_CMS,
|
||||
filename: 'webpack-stats.json'
|
||||
}),
|
||||
new BundleTracker({
|
||||
path: process.env.STATIC_ROOT_LMS,
|
||||
filename: 'webpack-stats.json'
|
||||
}),
|
||||
new webpack.ProvidePlugin({
|
||||
_: 'underscore',
|
||||
$: 'jquery',
|
||||
jQuery: 'jquery',
|
||||
'window.jQuery': 'jquery',
|
||||
Popper: 'popper.js', // used by bootstrap
|
||||
CodeMirror: 'codemirror',
|
||||
'edx.HtmlUtils': 'edx-ui-toolkit/js/utils/html-utils',
|
||||
AjaxPrefix: 'ajax_prefix',
|
||||
// This is used by some XModules/XBlocks, which don't have
|
||||
// any other way to declare that dependency.
|
||||
$script: 'scriptjs'
|
||||
}),
|
||||
|
||||
// Note: Until karma-webpack releases v3, it doesn't play well with
|
||||
// the CommonsChunkPlugin. We have a kludge in karma.common.conf.js
|
||||
// that dynamically removes this plugin from webpack config when
|
||||
// running those tests (the details are in that file). This is a
|
||||
// recommended workaround, as this plugin is just an optimization. But
|
||||
// because of this, we really don't want to get too fancy with how we
|
||||
// invoke this plugin until we can upgrade karma-webpack.
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
// If the value below changes, update the render_bundle call in
|
||||
// common/djangoapps/pipeline_mako/templates/static_content.html
|
||||
name: 'commons',
|
||||
filename: 'commons.js',
|
||||
minChunks: 3
|
||||
})
|
||||
],
|
||||
|
||||
module: {
|
||||
noParse: [
|
||||
// See sinon/webpack interaction weirdness:
|
||||
// https://github.com/webpack/webpack/issues/304#issuecomment-272150177
|
||||
// (I've tried every other suggestion solution on that page, this
|
||||
// was the only one that worked.)
|
||||
/\/sinon\.js|codemirror-compressed\.js|hls\.js|tinymce\.full\.min\.js/
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
test: files.namespacedRequire.concat(files.textBangUnderscore, filesWithRequireJSBlocks),
|
||||
loader: StringReplace.replace(
|
||||
['babel-loader'],
|
||||
{
|
||||
replacements: [
|
||||
{
|
||||
pattern: defineHeader,
|
||||
replacement: function() { return ''; }
|
||||
},
|
||||
{
|
||||
pattern: defineFooter,
|
||||
replacement: function() { return ''; }
|
||||
},
|
||||
{
|
||||
pattern: /(\/\* RequireJS) \*\//g,
|
||||
replacement: function(match, p1) { return p1; }
|
||||
},
|
||||
{
|
||||
pattern: /\/\* Webpack/g,
|
||||
replacement: function(match) { return match + ' */'; }
|
||||
},
|
||||
{
|
||||
pattern: /text!(.*?\.underscore)/g,
|
||||
replacement: function(match, p1) { return p1; }
|
||||
},
|
||||
{
|
||||
pattern: /RequireJS.require/g,
|
||||
replacement: function() {
|
||||
return 'require';
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
},
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
exclude: [
|
||||
/node_modules/,
|
||||
files.namespacedRequire,
|
||||
files.textBangUnderscore,
|
||||
filesWithRequireJSBlocks
|
||||
var workerConfig = function() {
|
||||
try {
|
||||
return {
|
||||
webworker: {
|
||||
target: 'webworker',
|
||||
context: __dirname,
|
||||
entry: require('../workers.json'),
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
path: path.resolve(__dirname, 'common/static/bundles')
|
||||
},
|
||||
plugins: [
|
||||
new BundleTracker({
|
||||
path: process.env.STATIC_ROOT_LMS,
|
||||
filename: 'webpack-worker-stats.json'
|
||||
})
|
||||
],
|
||||
use: 'babel-loader'
|
||||
},
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
include: [
|
||||
/paragon/
|
||||
],
|
||||
use: 'babel-loader'
|
||||
},
|
||||
{
|
||||
test: path.resolve(__dirname, 'common/static/js/src/ajax_prefix.js'),
|
||||
use: [
|
||||
'babel-loader',
|
||||
{
|
||||
loader: 'exports-loader',
|
||||
options: {
|
||||
'this.AjaxPrefix': true
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
include: [
|
||||
/node_modules\//
|
||||
],
|
||||
use: 'babel-loader'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.underscore$/,
|
||||
use: 'raw-loader'
|
||||
},
|
||||
{
|
||||
// This file is used by both RequireJS and Webpack and depends on window globals
|
||||
// This is a dirty hack and shouldn't be replicated for other files.
|
||||
test: path.resolve(__dirname, 'cms/static/cms/js/main.js'),
|
||||
loader: StringReplace.replace(
|
||||
['babel-loader'],
|
||||
{
|
||||
replacements: [
|
||||
{
|
||||
pattern: /\(function\(AjaxPrefix\) {/,
|
||||
replacement: function() { return ''; }
|
||||
},
|
||||
{
|
||||
pattern: /], function\(domReady, \$, str, Backbone, gettext, NotificationView\) {/,
|
||||
replacement: function() {
|
||||
// eslint-disable-next-line
|
||||
return '], function(domReady, $, str, Backbone, gettext, NotificationView, AjaxPrefix) {';
|
||||
}
|
||||
},
|
||||
{
|
||||
pattern: /'..\/..\/common\/js\/components\/views\/feedback_notification',/,
|
||||
replacement: function() {
|
||||
return "'../../common/js/components/views/feedback_notification', 'AjaxPrefix',";
|
||||
}
|
||||
},
|
||||
{
|
||||
pattern: /}\).call\(this, AjaxPrefix\);/,
|
||||
replacement: function() { return ''; }
|
||||
},
|
||||
{
|
||||
pattern: /'..\/..\/common\/js\/components\/views\/feedback_notification',/,
|
||||
replacement: function() {
|
||||
return "'../../common/js/components/views/feedback_notification', 'AjaxPrefix',";
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
},
|
||||
{
|
||||
test: /\.(woff2?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
|
||||
loader: 'file-loader'
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
loader: 'svg-inline-loader'
|
||||
},
|
||||
{
|
||||
test: /xblock\/core/,
|
||||
loader: 'exports-loader?window.XBlock!imports-loader?jquery,jquery.immediateDescendents,this=>window'
|
||||
},
|
||||
{
|
||||
test: /xblock\/runtime.v1/,
|
||||
loader: 'exports-loader?window.XBlock!imports-loader?XBlock=xblock/core,this=>window'
|
||||
},
|
||||
{
|
||||
test: /descriptors\/js/,
|
||||
loader: 'imports-loader?this=>window'
|
||||
},
|
||||
{
|
||||
test: /modules\/js/,
|
||||
loader: 'imports-loader?this=>window'
|
||||
},
|
||||
{
|
||||
test: /codemirror/,
|
||||
loader: 'exports-loader?window.CodeMirror'
|
||||
},
|
||||
{
|
||||
test: /tinymce/,
|
||||
loader: 'imports-loader?this=>window'
|
||||
},
|
||||
{
|
||||
test: /xmodule\/js\/src\/xmodule/,
|
||||
loader: 'exports-loader?window.XModule!imports-loader?this=>window'
|
||||
},
|
||||
{
|
||||
test: /mock-ajax/,
|
||||
loader: 'imports-loader?exports=>false'
|
||||
},
|
||||
{
|
||||
test: /d3.min/,
|
||||
use: [
|
||||
'babel-loader',
|
||||
{
|
||||
loader: 'exports-loader',
|
||||
options: {
|
||||
d3: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /logger/,
|
||||
loader: 'imports-loader?this=>window'
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js']
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx', '.json'],
|
||||
alias: {
|
||||
AjaxPrefix: 'ajax_prefix',
|
||||
accessibility: 'accessibility_tools',
|
||||
codemirror: 'codemirror-compressed',
|
||||
datepair: 'timepicker/datepair',
|
||||
'edx-ui-toolkit': 'edx-ui-toolkit/src/', // @TODO: some paths in toolkit are not valid relative paths
|
||||
ieshim: 'ie_shim',
|
||||
jquery: 'jquery/src/jquery', // Use the non-diqst form of jQuery for better debugging + optimization
|
||||
'jquery.flot': 'flot/jquery.flot.min',
|
||||
'jquery.ui': 'jquery-ui.min',
|
||||
'jquery.tinymce': 'jquery.tinymce.min',
|
||||
'jquery.inputnumber': 'html5-input-polyfills/number-polyfill',
|
||||
'jquery.qtip': 'jquery.qtip.min',
|
||||
'jquery.smoothScroll': 'jquery.smooth-scroll.min',
|
||||
'jquery.timepicker': 'timepicker/jquery.timepicker',
|
||||
'backbone.associations': 'backbone-associations/backbone-associations-min',
|
||||
squire: 'Squire',
|
||||
tinymce: 'tinymce.full.min',
|
||||
|
||||
// See sinon/webpack interaction weirdness:
|
||||
// https://github.com/webpack/webpack/issues/304#issuecomment-272150177
|
||||
// (I've tried every other suggestion solution on that page, this
|
||||
// was the only one that worked.)
|
||||
sinon: __dirname + '/node_modules/sinon/pkg/sinon.js',
|
||||
hls: 'hls.js/dist/hls.js'
|
||||
},
|
||||
modules: [
|
||||
'cms/djangoapps/pipeline_js/js',
|
||||
'cms/static',
|
||||
'cms/static/cms/js',
|
||||
'cms/templates/js',
|
||||
'lms/static',
|
||||
'common/lib/xmodule',
|
||||
'common/lib/xmodule/xmodule/js/src',
|
||||
'common/lib/xmodule/xmodule/assets/word_cloud/src/js',
|
||||
'common/static',
|
||||
'common/static/coffee/src',
|
||||
'common/static/common/js',
|
||||
'common/static/common/js/vendor/',
|
||||
'common/static/common/js/components',
|
||||
'common/static/js/src',
|
||||
'common/static/js/vendor/',
|
||||
'common/static/js/vendor/jQuery-File-Upload/js/',
|
||||
'common/static/js/vendor/tinymce/js/tinymce',
|
||||
'node_modules',
|
||||
'common/static/xmodule'
|
||||
]
|
||||
},
|
||||
|
||||
resolveLoader: {
|
||||
alias: {
|
||||
text: 'raw-loader' // Compatibility with RequireJSText's text! loader, uses raw-loader under the hood
|
||||
}
|
||||
},
|
||||
|
||||
externals: {
|
||||
$: 'jQuery',
|
||||
backbone: 'Backbone',
|
||||
canvas: 'canvas',
|
||||
coursetalk: 'CourseTalk',
|
||||
gettext: 'gettext',
|
||||
jquery: 'jQuery',
|
||||
logger: 'Logger',
|
||||
underscore: '_',
|
||||
URI: 'URI',
|
||||
XBlockToXModuleShim: 'XBlockToXModuleShim',
|
||||
XModule: 'XModule'
|
||||
},
|
||||
|
||||
watchOptions: {
|
||||
poll: true
|
||||
},
|
||||
|
||||
node: {
|
||||
fs: 'empty'
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Merge.smart({
|
||||
web: {
|
||||
context: __dirname,
|
||||
|
||||
entry: {
|
||||
// Studio
|
||||
Import: './cms/static/js/features/import/factories/import.js',
|
||||
CourseOrLibraryListing: './cms/static/js/features_jsx/studio/CourseOrLibraryListing.jsx',
|
||||
'js/factories/login': './cms/static/js/factories/login.js',
|
||||
'js/factories/textbooks': './cms/static/js/factories/textbooks.js',
|
||||
'js/factories/container': './cms/static/js/factories/container.js',
|
||||
'js/factories/context_course': './cms/static/js/factories/context_course.js',
|
||||
'js/factories/library': './cms/static/js/factories/library.js',
|
||||
'js/factories/xblock_validation': './cms/static/js/factories/xblock_validation.js',
|
||||
'js/factories/edit_tabs': './cms/static/js/factories/edit_tabs.js',
|
||||
'js/sock': './cms/static/js/sock.js',
|
||||
|
||||
// LMS
|
||||
SingleSupportForm: './lms/static/support/jsx/single_support_form.jsx',
|
||||
AlertStatusBar: './lms/static/js/accessible_components/StatusBarAlert.jsx',
|
||||
LearnerAnalyticsDashboard: './lms/static/js/learner_analytics_dashboard/LearnerAnalyticsDashboard.jsx',
|
||||
UpsellExperimentModal: './lms/static/common/js/components/UpsellExperimentModal.jsx',
|
||||
PortfolioExperimentUpsellModal: './lms/static/common/js/components/PortfolioExperimentUpsellModal.jsx',
|
||||
EntitlementSupportPage: './lms/djangoapps/support/static/support/jsx/entitlements/index.jsx',
|
||||
PasswordResetConfirmation: './lms/static/js/student_account/components/PasswordResetConfirmation.jsx',
|
||||
StudentAccountDeletion: './lms/static/js/student_account/components/StudentAccountDeletion.jsx',
|
||||
StudentAccountDeletionInitializer: './lms/static/js/student_account/StudentAccountDeletionInitializer.js',
|
||||
ProblemBrowser: './lms/djangoapps/instructor/static/instructor/ProblemBrowser/index.jsx',
|
||||
|
||||
// Learner Dashboard
|
||||
EntitlementFactory: './lms/static/js/learner_dashboard/course_entitlement_factory.js',
|
||||
EntitlementUnenrollmentFactory: './lms/static/js/learner_dashboard/entitlement_unenrollment_factory.js',
|
||||
ProgramDetailsFactory: './lms/static/js/learner_dashboard/program_details_factory.js',
|
||||
ProgramListFactory: './lms/static/js/learner_dashboard/program_list_factory.js',
|
||||
UnenrollmentFactory: './lms/static/js/learner_dashboard/unenrollment_factory.js',
|
||||
CompletionOnViewService: './lms/static/completion/js/CompletionOnViewService.js',
|
||||
|
||||
// Features
|
||||
CourseGoals: './openedx/features/course_experience/static/course_experience/js/CourseGoals.js',
|
||||
CourseHome: './openedx/features/course_experience/static/course_experience/js/CourseHome.js',
|
||||
CourseOutline: './openedx/features/course_experience/static/course_experience/js/CourseOutline.js',
|
||||
CourseSock: './openedx/features/course_experience/static/course_experience/js/CourseSock.js',
|
||||
CourseTalkReviews: './openedx/features/course_experience/static/course_experience/js/CourseTalkReviews.js',
|
||||
Currency: './openedx/features/course_experience/static/course_experience/js/currency.js',
|
||||
Enrollment: './openedx/features/course_experience/static/course_experience/js/Enrollment.js',
|
||||
LatestUpdate: './openedx/features/course_experience/static/course_experience/js/LatestUpdate.js',
|
||||
WelcomeMessage: './openedx/features/course_experience/static/course_experience/js/WelcomeMessage.js',
|
||||
|
||||
CookiePolicyBanner: './common/static/js/src/CookiePolicyBanner.jsx',
|
||||
|
||||
// Common
|
||||
ReactRenderer: './common/static/js/src/ReactRenderer.jsx',
|
||||
XModuleShim: 'xmodule/js/src/xmodule.js',
|
||||
VerticalStudentView: './common/lib/xmodule/xmodule/assets/vertical/public/js/vertical_student_view.js',
|
||||
commons: 'babel-polyfill'
|
||||
},
|
||||
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'common/static/bundles'),
|
||||
libraryTarget: 'window'
|
||||
},
|
||||
|
||||
plugins: [
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
new webpack.NamedModulesPlugin(),
|
||||
new BundleTracker({
|
||||
path: process.env.STATIC_ROOT_CMS,
|
||||
filename: 'webpack-stats.json'
|
||||
}),
|
||||
new BundleTracker({
|
||||
path: process.env.STATIC_ROOT_LMS,
|
||||
filename: 'webpack-stats.json'
|
||||
}),
|
||||
new webpack.ProvidePlugin({
|
||||
_: 'underscore',
|
||||
$: 'jquery',
|
||||
jQuery: 'jquery',
|
||||
'window.jQuery': 'jquery',
|
||||
Popper: 'popper.js', // used by bootstrap
|
||||
CodeMirror: 'codemirror',
|
||||
'edx.HtmlUtils': 'edx-ui-toolkit/js/utils/html-utils',
|
||||
AjaxPrefix: 'ajax_prefix',
|
||||
// This is used by some XModules/XBlocks, which don't have
|
||||
// any other way to declare that dependency.
|
||||
$script: 'scriptjs'
|
||||
}),
|
||||
|
||||
// Note: Until karma-webpack releases v3, it doesn't play well with
|
||||
// the CommonsChunkPlugin. We have a kludge in karma.common.conf.js
|
||||
// that dynamically removes this plugin from webpack config when
|
||||
// running those tests (the details are in that file). This is a
|
||||
// recommended workaround, as this plugin is just an optimization. But
|
||||
// because of this, we really don't want to get too fancy with how we
|
||||
// invoke this plugin until we can upgrade karma-webpack.
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
// If the value below changes, update the render_bundle call in
|
||||
// common/djangoapps/pipeline_mako/templates/static_content.html
|
||||
name: 'commons',
|
||||
filename: 'commons.js',
|
||||
minChunks: 3
|
||||
})
|
||||
],
|
||||
|
||||
module: {
|
||||
noParse: [
|
||||
// See sinon/webpack interaction weirdness:
|
||||
// https://github.com/webpack/webpack/issues/304#issuecomment-272150177
|
||||
// (I've tried every other suggestion solution on that page, this
|
||||
// was the only one that worked.)
|
||||
/\/sinon\.js|codemirror-compressed\.js|hls\.js|tinymce\.full\.min\.js/
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
test: files.namespacedRequire.concat(files.textBangUnderscore, filesWithRequireJSBlocks),
|
||||
loader: StringReplace.replace(
|
||||
['babel-loader'],
|
||||
{
|
||||
replacements: [
|
||||
{
|
||||
pattern: defineHeader,
|
||||
replacement: function() { return ''; }
|
||||
},
|
||||
{
|
||||
pattern: defineFooter,
|
||||
replacement: function() { return ''; }
|
||||
},
|
||||
{
|
||||
pattern: /(\/\* RequireJS) \*\//g,
|
||||
replacement: function(match, p1) { return p1; }
|
||||
},
|
||||
{
|
||||
pattern: /\/\* Webpack/g,
|
||||
replacement: function(match) { return match + ' */'; }
|
||||
},
|
||||
{
|
||||
pattern: /text!(.*?\.underscore)/g,
|
||||
replacement: function(match, p1) { return p1; }
|
||||
},
|
||||
{
|
||||
pattern: /RequireJS.require/g,
|
||||
replacement: function() {
|
||||
return 'require';
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
},
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
exclude: [
|
||||
/node_modules/,
|
||||
files.namespacedRequire,
|
||||
files.textBangUnderscore,
|
||||
filesWithRequireJSBlocks
|
||||
],
|
||||
use: 'babel-loader'
|
||||
},
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
include: [
|
||||
/paragon/
|
||||
],
|
||||
use: 'babel-loader'
|
||||
},
|
||||
{
|
||||
test: path.resolve(__dirname, 'common/static/js/src/ajax_prefix.js'),
|
||||
use: [
|
||||
'babel-loader',
|
||||
{
|
||||
loader: 'exports-loader',
|
||||
options: {
|
||||
'this.AjaxPrefix': true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.underscore$/,
|
||||
use: 'raw-loader'
|
||||
},
|
||||
{
|
||||
// This file is used by both RequireJS and Webpack and depends on window globals
|
||||
// This is a dirty hack and shouldn't be replicated for other files.
|
||||
test: path.resolve(__dirname, 'cms/static/cms/js/main.js'),
|
||||
loader: StringReplace.replace(
|
||||
['babel-loader'],
|
||||
{
|
||||
replacements: [
|
||||
{
|
||||
pattern: /\(function\(AjaxPrefix\) {/,
|
||||
replacement: function() { return ''; }
|
||||
},
|
||||
{
|
||||
pattern: /], function\(domReady, \$, str, Backbone, gettext, NotificationView\) {/,
|
||||
replacement: function() {
|
||||
// eslint-disable-next-line
|
||||
return '], function(domReady, $, str, Backbone, gettext, NotificationView, AjaxPrefix) {';
|
||||
}
|
||||
},
|
||||
{
|
||||
pattern: /'..\/..\/common\/js\/components\/views\/feedback_notification',/,
|
||||
replacement: function() {
|
||||
return "'../../common/js/components/views/feedback_notification'," +
|
||||
"'AjaxPrefix',";
|
||||
}
|
||||
},
|
||||
{
|
||||
pattern: /}\).call\(this, AjaxPrefix\);/,
|
||||
replacement: function() { return ''; }
|
||||
},
|
||||
{
|
||||
pattern: /'..\/..\/common\/js\/components\/views\/feedback_notification',/,
|
||||
replacement: function() {
|
||||
return "'../../common/js/components/views/feedback_notification'," +
|
||||
"'AjaxPrefix',";
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
},
|
||||
{
|
||||
test: /\.(woff2?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
|
||||
loader: 'file-loader'
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
loader: 'svg-inline-loader'
|
||||
},
|
||||
{
|
||||
test: /xblock\/core/,
|
||||
loader: 'exports-loader?window.XBlock!' +
|
||||
'imports-loader?jquery,jquery.immediateDescendents,this=>window'
|
||||
},
|
||||
{
|
||||
test: /xblock\/runtime.v1/,
|
||||
loader: 'exports-loader?window.XBlock!imports-loader?XBlock=xblock/core,this=>window'
|
||||
},
|
||||
{
|
||||
test: /descriptors\/js/,
|
||||
loader: 'imports-loader?this=>window'
|
||||
},
|
||||
{
|
||||
test: /modules\/js/,
|
||||
loader: 'imports-loader?this=>window'
|
||||
},
|
||||
{
|
||||
test: /codemirror/,
|
||||
loader: 'exports-loader?window.CodeMirror'
|
||||
},
|
||||
{
|
||||
test: /tinymce/,
|
||||
loader: 'imports-loader?this=>window'
|
||||
},
|
||||
{
|
||||
test: /xmodule\/js\/src\/xmodule/,
|
||||
loader: 'exports-loader?window.XModule!imports-loader?this=>window'
|
||||
},
|
||||
{
|
||||
test: /mock-ajax/,
|
||||
loader: 'imports-loader?exports=>false'
|
||||
},
|
||||
{
|
||||
test: /d3.min/,
|
||||
use: [
|
||||
'babel-loader',
|
||||
{
|
||||
loader: 'exports-loader',
|
||||
options: {
|
||||
d3: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /logger/,
|
||||
loader: 'imports-loader?this=>window'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx', '.json'],
|
||||
alias: {
|
||||
AjaxPrefix: 'ajax_prefix',
|
||||
accessibility: 'accessibility_tools',
|
||||
codemirror: 'codemirror-compressed',
|
||||
datepair: 'timepicker/datepair',
|
||||
'edx-ui-toolkit': 'edx-ui-toolkit/src/', // @TODO: some paths in toolkit are not valid relative paths
|
||||
ieshim: 'ie_shim',
|
||||
jquery: 'jquery/src/jquery', // Use the non-diqst form of jQuery for better debugging + optimization
|
||||
'jquery.flot': 'flot/jquery.flot.min',
|
||||
'jquery.ui': 'jquery-ui.min',
|
||||
'jquery.tinymce': 'jquery.tinymce.min',
|
||||
'jquery.inputnumber': 'html5-input-polyfills/number-polyfill',
|
||||
'jquery.qtip': 'jquery.qtip.min',
|
||||
'jquery.smoothScroll': 'jquery.smooth-scroll.min',
|
||||
'jquery.timepicker': 'timepicker/jquery.timepicker',
|
||||
'backbone.associations': 'backbone-associations/backbone-associations-min',
|
||||
squire: 'Squire',
|
||||
tinymce: 'tinymce.full.min',
|
||||
|
||||
// See sinon/webpack interaction weirdness:
|
||||
// https://github.com/webpack/webpack/issues/304#issuecomment-272150177
|
||||
// (I've tried every other suggestion solution on that page, this
|
||||
// was the only one that worked.)
|
||||
sinon: __dirname + '/node_modules/sinon/pkg/sinon.js',
|
||||
hls: 'hls.js/dist/hls.js'
|
||||
},
|
||||
modules: [
|
||||
'cms/djangoapps/pipeline_js/js',
|
||||
'cms/static',
|
||||
'cms/static/cms/js',
|
||||
'cms/templates/js',
|
||||
'lms/static',
|
||||
'common/lib/xmodule',
|
||||
'common/lib/xmodule/xmodule/js/src',
|
||||
'common/lib/xmodule/xmodule/assets/word_cloud/src/js',
|
||||
'common/static',
|
||||
'common/static/coffee/src',
|
||||
'common/static/common/js',
|
||||
'common/static/common/js/vendor/',
|
||||
'common/static/common/js/components',
|
||||
'common/static/js/src',
|
||||
'common/static/js/vendor/',
|
||||
'common/static/js/vendor/jQuery-File-Upload/js/',
|
||||
'common/static/js/vendor/tinymce/js/tinymce',
|
||||
'node_modules',
|
||||
'common/static/xmodule'
|
||||
]
|
||||
},
|
||||
|
||||
resolveLoader: {
|
||||
alias: {
|
||||
text: 'raw-loader' // Compatibility with RequireJSText's text! loader, uses raw-loader under the hood
|
||||
}
|
||||
},
|
||||
|
||||
externals: {
|
||||
$: 'jQuery',
|
||||
backbone: 'Backbone',
|
||||
canvas: 'canvas',
|
||||
coursetalk: 'CourseTalk',
|
||||
gettext: 'gettext',
|
||||
jquery: 'jQuery',
|
||||
logger: 'Logger',
|
||||
underscore: '_',
|
||||
URI: 'URI',
|
||||
XBlockToXModuleShim: 'XBlockToXModuleShim',
|
||||
XModule: 'XModule'
|
||||
},
|
||||
|
||||
watchOptions: {
|
||||
poll: true
|
||||
},
|
||||
|
||||
node: {
|
||||
fs: 'empty'
|
||||
}
|
||||
|
||||
}
|
||||
}, {web: xmoduleJS}, workerConfig());
|
||||
|
||||
}, xmoduleJS);
|
||||
|
||||
@@ -5,56 +5,59 @@
|
||||
var Merge = require('webpack-merge');
|
||||
var path = require('path');
|
||||
var webpack = require('webpack');
|
||||
var _ = require('underscore');
|
||||
|
||||
var commonConfig = require('./webpack.common.config.js');
|
||||
|
||||
module.exports = Merge.smart(commonConfig, {
|
||||
output: {
|
||||
filename: '[name].js'
|
||||
},
|
||||
devtool: 'source-map',
|
||||
plugins: [
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
debug: true
|
||||
}),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify('development')
|
||||
})
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /(.scss|.css)$/,
|
||||
include: [
|
||||
/paragon/,
|
||||
/font-awesome/
|
||||
],
|
||||
use: [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
sourceMap: true,
|
||||
modules: true,
|
||||
localIdentName: '[name]__[local]'
|
||||
module.exports = _.values(Merge.smart(commonConfig, {
|
||||
web: {
|
||||
output: {
|
||||
filename: '[name].js'
|
||||
},
|
||||
devtool: 'source-map',
|
||||
plugins: [
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
debug: true
|
||||
}),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify('development')
|
||||
})
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /(.scss|.css)$/,
|
||||
include: [
|
||||
/paragon/,
|
||||
/font-awesome/
|
||||
],
|
||||
use: [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
sourceMap: true,
|
||||
modules: true,
|
||||
localIdentName: '[name]__[local]'
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
data: '$base-rem-size: 0.625; @import "paragon-reset";',
|
||||
includePaths: [
|
||||
path.join(__dirname, './node_modules/@edx/paragon/src/utils'),
|
||||
path.join(__dirname, './node_modules/')
|
||||
],
|
||||
sourceMap: true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
data: '$base-rem-size: 0.625; @import "paragon-reset";',
|
||||
includePaths: [
|
||||
path.join(__dirname, './node_modules/@edx/paragon/src/utils'),
|
||||
path.join(__dirname, './node_modules/')
|
||||
],
|
||||
sourceMap: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
watchOptions: {
|
||||
ignored: [/node_modules/, /\.git/]
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
watchOptions: {
|
||||
ignored: [/node_modules/, /\.git/]
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
|
||||
var Merge = require('webpack-merge');
|
||||
var webpack = require('webpack');
|
||||
var BundleTracker = require('webpack-bundle-tracker');
|
||||
var _ = require('underscore');
|
||||
|
||||
var commonConfig = require('./webpack.common.config.js');
|
||||
|
||||
var optimizedConfig = Merge.smart(commonConfig, {
|
||||
web: {
|
||||
output: {
|
||||
filename: '[name].[chunkhash].js'
|
||||
},
|
||||
@@ -28,7 +31,7 @@ var optimizedConfig = Merge.smart(commonConfig, {
|
||||
minChunks: 3
|
||||
})
|
||||
]
|
||||
});
|
||||
}});
|
||||
|
||||
// requireCompatConfig only exists so that you can use RequireJS to require a
|
||||
// Webpack bundle (but try not to do that if you can help it). RequireJS knows
|
||||
@@ -44,6 +47,7 @@ var optimizedConfig = Merge.smart(commonConfig, {
|
||||
|
||||
// Step 1: Alter the bundle output names to omit the chunkhash.
|
||||
var requireCompatConfig = Merge.smart(optimizedConfig, {
|
||||
web: {
|
||||
output: {
|
||||
filename: '[name].js'
|
||||
},
|
||||
@@ -56,14 +60,12 @@ var requireCompatConfig = Merge.smart(optimizedConfig, {
|
||||
minChunks: 3
|
||||
})
|
||||
]
|
||||
});
|
||||
}});
|
||||
|
||||
// Step 2: Remove the plugin entries that generate the webpack-stats.json files
|
||||
// that Django needs to look up resources. We never want to accidentally
|
||||
// overwrite those because it means that we'll be serving assets with shorter
|
||||
// cache times. RequireJS never looks at the webpack-stats.json file.
|
||||
requireCompatConfig.plugins = requireCompatConfig.plugins.filter(
|
||||
function(plugin) { return !plugin.options || (plugin.options && plugin.options.filename !== 'webpack-stats.json'); }
|
||||
);
|
||||
requireCompatConfig.web.plugins = requireCompatConfig.web.plugins.filter((plugin) => !(plugin instanceof BundleTracker));
|
||||
|
||||
module.exports = [optimizedConfig, requireCompatConfig];
|
||||
module.exports = [..._.values(optimizedConfig), ..._.values(requireCompatConfig)];
|
||||
|
||||
Reference in New Issue
Block a user