From 7316111b35c8db0b93665e00aa4071685d772ab3 Mon Sep 17 00:00:00 2001
From: Braden MacDonald
Date: Tue, 8 Oct 2024 10:22:41 -0700
Subject: [PATCH 01/32] fix: Improve v2 library block permissions checks for
read-only authors (#35598)
---
.../templates/xblock_v2}/xblock_iframe.html | 2 +-
lms/templates/xblock_v2/xblock_iframe.html | 1 -
.../content_libraries/library_context.py | 73 ++++++++++---------
.../content_libraries/permissions.py | 15 +++-
.../content_libraries/tests/base.py | 9 +++
.../tests/test_content_libraries.py | 42 ++++++++---
.../djangoapps/content_libraries/views.py | 2 +-
openedx/core/djangoapps/xblock/api.py | 42 ++++++++---
.../learning_context/learning_context.py | 25 +++++--
.../core/djangoapps/xblock/rest_api/views.py | 8 +-
10 files changed, 149 insertions(+), 70 deletions(-)
rename {cms/templates/content_libraries => common/templates/xblock_v2}/xblock_iframe.html (99%)
delete mode 120000 lms/templates/xblock_v2/xblock_iframe.html
diff --git a/cms/templates/content_libraries/xblock_iframe.html b/common/templates/xblock_v2/xblock_iframe.html
similarity index 99%
rename from cms/templates/content_libraries/xblock_iframe.html
rename to common/templates/xblock_v2/xblock_iframe.html
index b6e455f785..8b733373bd 100644
--- a/cms/templates/content_libraries/xblock_iframe.html
+++ b/common/templates/xblock_v2/xblock_iframe.html
@@ -156,7 +156,7 @@
-
+
{{ fragment.body_html | safe }}
diff --git a/lms/templates/xblock_v2/xblock_iframe.html b/lms/templates/xblock_v2/xblock_iframe.html
deleted file mode 120000
index 7264c25346..0000000000
--- a/lms/templates/xblock_v2/xblock_iframe.html
+++ /dev/null
@@ -1 +0,0 @@
-../../../cms/templates/content_libraries/xblock_iframe.html
\ No newline at end of file
diff --git a/openedx/core/djangoapps/content_libraries/library_context.py b/openedx/core/djangoapps/content_libraries/library_context.py
index 6ff426e735..4bda10eb12 100644
--- a/openedx/core/djangoapps/content_libraries/library_context.py
+++ b/openedx/core/djangoapps/content_libraries/library_context.py
@@ -1,19 +1,21 @@
"""
Definition of "Library" as a learning context.
"""
-
import logging
from django.core.exceptions import PermissionDenied
+from rest_framework.exceptions import NotFound
from openedx_events.content_authoring.data import LibraryBlockData
from openedx_events.content_authoring.signals import LIBRARY_BLOCK_UPDATED
+from opaque_keys.edx.keys import UsageKeyV2
+from opaque_keys.edx.locator import LibraryUsageLocatorV2, LibraryLocatorV2
+from openedx_learning.api import authoring as authoring_api
from openedx.core.djangoapps.content_libraries import api, permissions
from openedx.core.djangoapps.content_libraries.models import ContentLibrary
from openedx.core.djangoapps.xblock.api import LearningContext
-
-from openedx_learning.api import authoring as authoring_api
+from openedx.core.types import User as UserType
log = logging.getLogger(__name__)
@@ -30,47 +32,51 @@ class LibraryContextImpl(LearningContext):
super().__init__(**kwargs)
self.use_draft = kwargs.get('use_draft', None)
- def can_edit_block(self, user, usage_key):
+ def can_edit_block(self, user: UserType, usage_key: UsageKeyV2) -> bool:
"""
- Does the specified usage key exist in its context, and if so, does the
- specified user have permission to edit it (make changes to the authored
- data store)?
+ Assuming a block with the specified ID (usage_key) exists, does the
+ specified user have permission to edit it (make changes to the
+ fields / authored data store)?
- user: a Django User object (may be an AnonymousUser)
-
- usage_key: the UsageKeyV2 subclass used for this learning context
-
- Must return a boolean.
+ May raise ContentLibraryNotFound if the library does not exist.
"""
- try:
- api.require_permission_for_library_key(usage_key.lib_key, user, permissions.CAN_EDIT_THIS_CONTENT_LIBRARY)
- except (PermissionDenied, api.ContentLibraryNotFound):
- return False
+ assert isinstance(usage_key, LibraryUsageLocatorV2)
+ return self._check_perm(user, usage_key.lib_key, permissions.CAN_EDIT_THIS_CONTENT_LIBRARY)
- return self.block_exists(usage_key)
+ def can_view_block_for_editing(self, user: UserType, usage_key: UsageKeyV2) -> bool:
+ """
+ Assuming a block with the specified ID (usage_key) exists, does the
+ specified user have permission to view its fields and OLX details (but
+ not necessarily to make changes to it)?
- def can_view_block(self, user, usage_key):
+ May raise ContentLibraryNotFound if the library does not exist.
+ """
+ assert isinstance(usage_key, LibraryUsageLocatorV2)
+ return self._check_perm(user, usage_key.lib_key, permissions.CAN_VIEW_THIS_CONTENT_LIBRARY)
+
+ def can_view_block(self, user: UserType, usage_key: UsageKeyV2) -> bool:
"""
Does the specified usage key exist in its context, and if so, does the
specified user have permission to view it and interact with it (call
handlers, save user state, etc.)?
- user: a Django User object (may be an AnonymousUser)
-
- usage_key: the UsageKeyV2 subclass used for this learning context
-
- Must return a boolean.
+ May raise ContentLibraryNotFound if the library does not exist.
"""
+ assert isinstance(usage_key, LibraryUsageLocatorV2)
+ return self._check_perm(user, usage_key.lib_key, permissions.CAN_LEARN_FROM_THIS_CONTENT_LIBRARY)
+
+ def _check_perm(self, user: UserType, lib_key: LibraryLocatorV2, perm) -> bool:
+ """ Helper method to check a permission for the various can_ methods"""
try:
- api.require_permission_for_library_key(
- usage_key.lib_key, user, permissions.CAN_LEARN_FROM_THIS_CONTENT_LIBRARY,
- )
- except (PermissionDenied, api.ContentLibraryNotFound):
+ api.require_permission_for_library_key(lib_key, user, perm)
+ return True
+ except PermissionDenied:
return False
+ except api.ContentLibraryNotFound as exc:
+ # A 404 is probably what you want in this case, not a 500 error, so do that by default.
+ raise NotFound(f"Content Library '{lib_key}' does not exist") from exc
- return self.block_exists(usage_key)
-
- def block_exists(self, usage_key):
+ def block_exists(self, usage_key: LibraryUsageLocatorV2):
"""
Does the block for this usage_key exist in this Library?
@@ -82,7 +88,7 @@ class LibraryContextImpl(LearningContext):
version of it.
"""
try:
- content_lib = ContentLibrary.objects.get_by_key(usage_key.context_key)
+ content_lib = ContentLibrary.objects.get_by_key(usage_key.context_key) # type: ignore[attr-defined]
except ContentLibrary.DoesNotExist:
return False
@@ -97,12 +103,11 @@ class LibraryContextImpl(LearningContext):
local_key=usage_key.block_id,
)
- def send_block_updated_event(self, usage_key):
+ def send_block_updated_event(self, usage_key: UsageKeyV2):
"""
Send a "block updated" event for the library block with the given usage_key.
-
- usage_key: the UsageKeyV2 subclass used for this learning context
"""
+ assert isinstance(usage_key, LibraryUsageLocatorV2)
LIBRARY_BLOCK_UPDATED.send_event(
library_block=LibraryBlockData(
library_key=usage_key.lib_key,
diff --git a/openedx/core/djangoapps/content_libraries/permissions.py b/openedx/core/djangoapps/content_libraries/permissions.py
index c7da012c9f..17671b5659 100644
--- a/openedx/core/djangoapps/content_libraries/permissions.py
+++ b/openedx/core/djangoapps/content_libraries/permissions.py
@@ -2,7 +2,8 @@
Permissions for Content Libraries (v2, Learning-Core-based)
"""
from bridgekeeper import perms, rules
-from bridgekeeper.rules import Attribute, ManyRelation, Relation, in_current_groups
+from bridgekeeper.rules import Attribute, ManyRelation, Relation, blanket_rule, in_current_groups
+from django.conf import settings
from openedx.core.djangoapps.content_libraries.models import ContentLibraryPermission
@@ -41,6 +42,12 @@ has_explicit_admin_permission_for_library = (
)
+# Are we in Studio? (Is there a better or more contextual way to define this, e.g. get from learning context?)
+@blanket_rule
+def is_studio_request(_):
+ return settings.SERVICE_VARIANT == "cms"
+
+
########################### Permissions ###########################
# Is the user allowed to view XBlocks from the specified content library
@@ -51,10 +58,12 @@ CAN_LEARN_FROM_THIS_CONTENT_LIBRARY = 'content_libraries.learn_from_library'
perms[CAN_LEARN_FROM_THIS_CONTENT_LIBRARY] = (
# Global staff can learn from any library:
is_global_staff |
- # Regular users can learn if the library allows public learning:
+ # Regular and even anonymous users can learn if the library allows public learning:
Attribute('allow_public_learning', True) |
# Users/groups who are explicitly granted permission can learn from the library:
- (is_user_active & has_explicit_read_permission_for_library)
+ (is_user_active & has_explicit_read_permission_for_library) |
+ # Or, in Studio (but not the LMS) any users can access libraries with "public read" permissions:
+ (is_studio_request & is_user_active & Attribute('allow_public_read', True))
)
# Is the user allowed to create content libraries?
diff --git a/openedx/core/djangoapps/content_libraries/tests/base.py b/openedx/core/djangoapps/content_libraries/tests/base.py
index 987133255f..0ed47f995f 100644
--- a/openedx/core/djangoapps/content_libraries/tests/base.py
+++ b/openedx/core/djangoapps/content_libraries/tests/base.py
@@ -308,3 +308,12 @@ class ContentLibrariesRestApiTest(APITransactionTestCase):
"""
url = URL_BLOCK_GET_HANDLER_URL.format(block_key=block_key, handler_name=handler_name)
return self._api('get', url, None, expect_response=200)["handler_url"]
+
+ def _get_library_block_fields(self, block_key, expect_response=200):
+ """ Get the fields of a specific block in the library. This API is only used by the MFE editors. """
+ result = self._api('get', URL_BLOCK_FIELDS_URL.format(block_key=block_key), None, expect_response)
+ return result
+
+ def _set_library_block_fields(self, block_key, new_fields, expect_response=200):
+ """ Set the fields of a specific block in the library. This API is only used by the MFE editors. """
+ return self._api('post', URL_BLOCK_FIELDS_URL.format(block_key=block_key), new_fields, expect_response)
diff --git a/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py b/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py
index d995a2c796..56e258f8a1 100644
--- a/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py
+++ b/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py
@@ -502,6 +502,30 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix
# Add a 'problem' XBlock to the library:
self._add_block_to_library(lib_id, block_type, 'test-block', expect_response=expect_response)
+ def test_library_not_found(self):
+ """Test that requests fail with 404 when the library does not exist"""
+ valid_not_found_key = 'lb:valid:key:video:1'
+ response = self.client.get(URL_BLOCK_METADATA_URL.format(block_key=valid_not_found_key))
+ self.assertEqual(response.status_code, 404)
+ self.assertEqual(response.json(), {
+ 'detail': "Content Library 'lib:valid:key' does not exist",
+ })
+
+ def test_block_not_found(self):
+ """Test that requests fail with 404 when the library exists but the XBlock does not"""
+ lib = self._create_library(
+ slug="test_lib_block_event_delete",
+ title="Event Test Library",
+ description="Testing event in library"
+ )
+ library_key = LibraryLocatorV2.from_string(lib['id'])
+ non_existent_block_key = LibraryUsageLocatorV2(lib_key=library_key, block_type='video', usage_id='123')
+ response = self.client.get(URL_BLOCK_METADATA_URL.format(block_key=non_existent_block_key))
+ self.assertEqual(response.status_code, 404)
+ self.assertEqual(response.json(), {
+ 'detail': f"The component '{non_existent_block_key}' does not exist.",
+ })
+
# Test that permissions are enforced for content libraries
def test_library_permissions(self): # pylint: disable=too-many-statements
@@ -635,21 +659,27 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix
# A random user cannot read OLX nor assets (this library has allow_public_read False):
with self.as_user(random_user):
self._get_library_block_olx(block3_key, expect_response=403)
+ self._get_library_block_fields(block3_key, expect_response=403)
self._get_library_block_assets(block3_key, expect_response=403)
self._get_library_block_asset(block3_key, file_name="whatever.png", expect_response=403)
+ # Nor can they preview the block:
+ self._render_block_view(block3_key, view_name="student_view", expect_response=403)
# But if we grant allow_public_read, then they can:
with self.as_user(admin):
self._update_library(lib_id, allow_public_read=True)
# self._set_library_block_asset(block3_key, "whatever.png", b"data")
with self.as_user(random_user):
self._get_library_block_olx(block3_key)
+ self._render_block_view(block3_key, view_name="student_view")
+ f = self._get_library_block_fields(block3_key)
# self._get_library_block_assets(block3_key)
# self._get_library_block_asset(block3_key, file_name="whatever.png")
- # Users without authoring permission cannot edit nor delete XBlocks (this library has allow_public_read False):
+ # Users without authoring permission cannot edit nor delete XBlocks:
for user in [reader, random_user]:
with self.as_user(user):
self._set_library_block_olx(block3_key, "", expect_response=403)
+ self._set_library_block_fields(block3_key, {"data": "", "metadata": {}}, expect_response=403)
# self._set_library_block_asset(block3_key, "test.txt", b"data", expect_response=403)
self._delete_library_block(block3_key, expect_response=403)
self._commit_library_changes(lib_id, expect_response=403)
@@ -659,6 +689,7 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix
with self.as_user(author_group_member):
olx = self._get_library_block_olx(block3_key)
self._set_library_block_olx(block3_key, olx)
+ self._set_library_block_fields(block3_key, {"data": olx, "metadata": {}})
# self._get_library_block_assets(block3_key)
# self._set_library_block_asset(block3_key, "test.txt", b"data")
# self._get_library_block_asset(block3_key, file_name="test.txt")
@@ -1087,12 +1118,3 @@ class ContentLibraryXBlockValidationTest(APITestCase):
secure_token='random',
)))
self.assertEqual(response.status_code, 404)
-
- def test_not_found_fails_correctly(self):
- """Test fails with 404 when xblock key is valid but not found."""
- valid_not_found_key = 'lb:valid:key:video:1'
- response = self.client.get(URL_BLOCK_METADATA_URL.format(block_key=valid_not_found_key))
- self.assertEqual(response.status_code, 404)
- self.assertEqual(response.json(), {
- 'detail': f"XBlock {valid_not_found_key} does not exist, or you don't have permission to view it.",
- })
diff --git a/openedx/core/djangoapps/content_libraries/views.py b/openedx/core/djangoapps/content_libraries/views.py
index 835a5de1f1..3712af6e59 100644
--- a/openedx/core/djangoapps/content_libraries/views.py
+++ b/openedx/core/djangoapps/content_libraries/views.py
@@ -919,7 +919,7 @@ class LtiToolLaunchView(TemplateResponseMixin, LtiToolView):
LTI platform. Other features and resouces are ignored.
"""
- template_name = 'content_libraries/xblock_iframe.html'
+ template_name = 'xblock_v2/xblock_iframe.html'
@property
def launch_data(self):
diff --git a/openedx/core/djangoapps/xblock/api.py b/openedx/core/djangoapps/xblock/api.py
index 80a4bd57ea..43ec3909bc 100644
--- a/openedx/core/djangoapps/xblock/api.py
+++ b/openedx/core/djangoapps/xblock/api.py
@@ -8,11 +8,12 @@ Note that these views are only for interacting with existing blocks. Other
Studio APIs cover use cases like adding/deleting/editing blocks.
"""
# pylint: disable=unused-import
-
+from enum import Enum
from datetime import datetime
import logging
import threading
+from django.core.exceptions import PermissionDenied
from django.urls import reverse
from django.utils.translation import gettext as _
from openedx_learning.api import authoring as authoring_api
@@ -21,7 +22,7 @@ from opaque_keys.edx.keys import UsageKeyV2
from opaque_keys.edx.locator import BundleDefinitionLocator, LibraryUsageLocatorV2
from rest_framework.exceptions import NotFound
from xblock.core import XBlock
-from xblock.exceptions import NoSuchViewError
+from xblock.exceptions import NoSuchUsage, NoSuchViewError
from xblock.plugin import PluginMissingError
from openedx.core.djangoapps.xblock.apps import get_xblock_app_config
@@ -43,6 +44,16 @@ from openedx.core.djangoapps.xblock.learning_context import LearningContext
log = logging.getLogger(__name__)
+class CheckPerm(Enum):
+ """ Options for the default permission check done by load_block() """
+ # can view the published block and call handlers etc. but not necessarily view its OLX source nor field data
+ CAN_LEARN = 1
+ # read-only studio view: can see the block (draft or published), see its OLX, see its field data, etc.
+ CAN_READ_AS_AUTHOR = 2
+ # can view everything and make changes to the block
+ CAN_EDIT = 3
+
+
def get_runtime_system():
"""
Return a new XBlockRuntimeSystem.
@@ -74,15 +85,15 @@ def get_runtime_system():
return runtime
-def load_block(usage_key, user):
+def load_block(usage_key, user, *, check_permission: CheckPerm | None = CheckPerm.CAN_LEARN):
"""
Load the specified XBlock for the given user.
Returns an instantiated XBlock.
Exceptions:
- NotFound - if the XBlock doesn't exist or if the user doesn't have the
- necessary permissions
+ NotFound - if the XBlock doesn't exist
+ PermissionDenied - if the user doesn't have the necessary permissions
Args:
usage_key(OpaqueKey): block identifier
@@ -94,10 +105,17 @@ def load_block(usage_key, user):
# Now, check if the block exists in this context and if the user has
# permission to render this XBlock view:
- if user is not None and not context_impl.can_view_block(user, usage_key):
- # We do not know if the block was not found or if the user doesn't have
- # permission, but we want to return the same result in either case:
- raise NotFound(f"XBlock {usage_key} does not exist, or you don't have permission to view it.")
+ if check_permission and user is not None:
+ if check_permission == CheckPerm.CAN_EDIT:
+ has_perm = context_impl.can_edit_block(user, usage_key)
+ elif check_permission == CheckPerm.CAN_READ_AS_AUTHOR:
+ has_perm = context_impl.can_view_block_for_editing(user, usage_key)
+ elif check_permission == CheckPerm.CAN_LEARN:
+ has_perm = context_impl.can_view_block(user, usage_key)
+ else:
+ has_perm = False
+ if not has_perm:
+ raise PermissionDenied(f"You don't have permission to access the component '{usage_key}'.")
# TODO: load field overrides from the context
# e.g. a course might specify that all 'problem' XBlocks have 'max_attempts'
@@ -105,7 +123,11 @@ def load_block(usage_key, user):
# field_overrides = context_impl.get_field_overrides(usage_key)
runtime = get_runtime_system().get_runtime(user=user)
- return runtime.get_block(usage_key)
+ try:
+ return runtime.get_block(usage_key)
+ except NoSuchUsage as exc:
+ # Convert NoSuchUsage to NotFound so we do the right thing (404 not 500) by default.
+ raise NotFound(f"The component '{usage_key}' does not exist.") from exc
def get_block_metadata(block, includes=()):
diff --git a/openedx/core/djangoapps/xblock/learning_context/learning_context.py b/openedx/core/djangoapps/xblock/learning_context/learning_context.py
index 2dc5155dc4..b535e84ca7 100644
--- a/openedx/core/djangoapps/xblock/learning_context/learning_context.py
+++ b/openedx/core/djangoapps/xblock/learning_context/learning_context.py
@@ -2,6 +2,8 @@
A "Learning Context" is a course, a library, a program, or some other collection
of content where learning happens.
"""
+from openedx.core.types import User as UserType
+from opaque_keys.edx.keys import UsageKeyV2
class LearningContext:
@@ -23,11 +25,11 @@ class LearningContext:
parameters without changing the API.
"""
- def can_edit_block(self, user, usage_key): # pylint: disable=unused-argument
+ def can_edit_block(self, user: UserType, usage_key: UsageKeyV2) -> bool: # pylint: disable=unused-argument
"""
- Does the specified usage key exist in its context, and if so, does the
- specified user have permission to edit it (make changes to the authored
- data store)?
+ Assuming a block with the specified ID (usage_key) exists, does the
+ specified user have permission to edit it (make changes to the
+ fields / authored data store)?
user: a Django User object (may be an AnonymousUser)
@@ -37,11 +39,20 @@ class LearningContext:
"""
return False
- def can_view_block(self, user, usage_key): # pylint: disable=unused-argument
+ def can_view_block_for_editing(self, user: UserType, usage_key: UsageKeyV2) -> bool:
"""
- Does the specified usage key exist in its context, and if so, does the
+ Assuming a block with the specified ID (usage_key) exists, does the
+ specified user have permission to view its fields and OLX details (but
+ not necessarily to make changes to it)?
+ """
+ return self.can_edit_block(user, usage_key)
+
+ def can_view_block(self, user: UserType, usage_key: UsageKeyV2) -> bool: # pylint: disable=unused-argument
+ """
+ Assuming a block with the specified ID (usage_key) exists, does the
specified user have permission to view it and interact with it (call
- handlers, save user state, etc.)?
+ handlers, save user state, etc.)? This is also sometimes called the
+ "can_learn" permission.
user: a Django User object (may be an AnonymousUser)
diff --git a/openedx/core/djangoapps/xblock/rest_api/views.py b/openedx/core/djangoapps/xblock/rest_api/views.py
index 7934e24bd2..20e6a6dde0 100644
--- a/openedx/core/djangoapps/xblock/rest_api/views.py
+++ b/openedx/core/djangoapps/xblock/rest_api/views.py
@@ -29,6 +29,7 @@ import openedx.core.djangoapps.site_configuration.helpers as configuration_helpe
from openedx.core.djangoapps.xblock.learning_context.manager import get_learning_context_impl
from openedx.core.lib.api.view_utils import view_auth_classes
from ..api import (
+ CheckPerm,
get_block_metadata,
get_block_display_name,
get_handler_url as _get_handler_url,
@@ -108,7 +109,7 @@ def embed_block_view(request, usage_key_str, view_name):
raise NotFound(invalid_not_found_fmt.format(usage_key=usage_key_str)) from e
try:
- block = load_block(usage_key, request.user)
+ block = load_block(usage_key, request.user, check_permission=CheckPerm.CAN_LEARN)
except NoSuchUsage as exc:
raise NotFound(f"{usage_key} not found") from exc
@@ -246,7 +247,8 @@ class BlockFieldsView(APIView):
except InvalidKeyError as e:
raise NotFound(invalid_not_found_fmt.format(usage_key=usage_key_str)) from e
- block = load_block(usage_key, request.user)
+ # The "fields" view requires "read as author" permissions because the fields can contain answers, etc.
+ block = load_block(usage_key, request.user, check_permission=CheckPerm.CAN_READ_AS_AUTHOR)
block_dict = {
"display_name": get_block_display_name(block), # potentially duplicated from metadata
"data": block.data,
@@ -265,7 +267,7 @@ class BlockFieldsView(APIView):
raise NotFound(invalid_not_found_fmt.format(usage_key=usage_key_str)) from e
user = request.user
- block = load_block(usage_key, user)
+ block = load_block(usage_key, user, check_permission=CheckPerm.CAN_EDIT)
data = request.data.get("data")
metadata = request.data.get("metadata")
From 0a4d24adecbb23165b4e6472c5b79546e9707ef4 Mon Sep 17 00:00:00 2001
From: katrinan029 <71999631+katrinan029@users.noreply.github.com>
Date: Tue, 8 Oct 2024 21:35:39 +0000
Subject: [PATCH 02/32] feat: Upgrade Python dependency edx-enterprise
version bump
Commit generated by workflow `openedx/edx-platform/.github/workflows/upgrade-one-python-dependency.yml@refs/heads/master`
---
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 3809b753a5..396b95d838 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -26,7 +26,7 @@ celery>=5.2.2,<6.0.0
# The team that owns this package will manually bump this package rather than having it pulled in automatically.
# This is to allow them to better control its deployment and to do it in a process that works better
# for them.
-edx-enterprise==4.27.0
+edx-enterprise==4.27.2
# Stay on LTS version, remove once this is added to common constraint
Django<5.0
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 0eca9a532a..55793bd0c5 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -467,7 +467,7 @@ edx-drf-extensions==10.4.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.27.0
+edx-enterprise==4.27.2
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index abd9922253..1bdd373651 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -741,7 +741,7 @@ edx-drf-extensions==10.4.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.27.0
+edx-enterprise==4.27.2
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index f8b45c5ddc..6f12d80467 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -547,7 +547,7 @@ edx-drf-extensions==10.4.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.27.0
+edx-enterprise==4.27.2
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 7fa8962c4f..d3b14a6f43 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -571,7 +571,7 @@ edx-drf-extensions==10.4.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.27.0
+edx-enterprise==4.27.2
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
From 7cf12d861c28310b13f2e94d9e63aa45e3bef4b6 Mon Sep 17 00:00:00 2001
From: Muhammad Adeel Tajamul
<77053848+muhammadadeeltajamul@users.noreply.github.com>
Date: Wed, 9 Oct 2024 13:56:58 +0500
Subject: [PATCH 03/32] chore: removed style from email digest content (#35592)
---
.../rest_api/discussions_notifications.py | 13 +++--
.../tests/test_discussions_notifications.py | 50 ++++++++++---------
2 files changed, 35 insertions(+), 28 deletions(-)
diff --git a/lms/djangoapps/discussion/rest_api/discussions_notifications.py b/lms/djangoapps/discussion/rest_api/discussions_notifications.py
index 4e372280ce..498a05fdb9 100644
--- a/lms/djangoapps/discussion/rest_api/discussions_notifications.py
+++ b/lms/djangoapps/discussion/rest_api/discussions_notifications.py
@@ -392,7 +392,8 @@ def clean_thread_html_body(html_body):
"video", "track", # Video Tags
"audio", # Audio Tags
"embed", "object", "iframe", # Embedded Content
- "script"
+ "script",
+ "b", "strong", "i", "em", "u", "s", "strike", "del", "ins", "mark", "sub", "sup", # Text Formatting
]
# Remove the specified tags while keeping their content
@@ -403,9 +404,10 @@ def clean_thread_html_body(html_body):
# Replace tags that are not allowed in email
tags_to_update = [
{"source": "button", "target": "span"},
- {"source": "h1", "target": "h4"},
- {"source": "h2", "target": "h4"},
- {"source": "h3", "target": "h4"},
+ *[
+ {"source": tag, "target": "p"}
+ for tag in ["div", "section", "article", "h1", "h2", "h3", "h4", "h5", "h6"]
+ ],
]
for tag_dict in tags_to_update:
for source_tag in html_body.find_all(tag_dict['source']):
@@ -414,4 +416,7 @@ def clean_thread_html_body(html_body):
target_tag.string = source_tag.string
source_tag.replace_with(target_tag)
+ for tag in html_body.find_all(True):
+ tag.attrs = {}
+ tag['style'] = 'margin: 0'
return str(html_body)
diff --git a/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py b/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py
index d92e1000fe..0a8d750416 100644
--- a/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py
+++ b/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py
@@ -104,14 +104,14 @@ class TestCleanThreadHtmlBody(unittest.TestCase):
')
result = clean_thread_html_body(html_body)
@@ -132,19 +132,16 @@ class TestCleanThreadHtmlBody(unittest.TestCase):
"""
Test that the clean_thread_html_body function truncates the HTML body to 500 characters
"""
- html_body = """
-
This is a long text that should be truncated to 500 characters.
- """ * 20 # Repeat to exceed 500 characters
-
- result = clean_thread_html_body(html_body)
- self.assertGreaterEqual(500, len(result))
+ html_body = "This is a long text that should be truncated to 500 characters." * 20
+ result = clean_thread_html_body(f"
{html_body}
")
+ self.assertGreaterEqual(525, len(result)) # 500 characters + 25 characters for the HTML tags
def test_no_tags_to_remove(self):
"""
Test that the clean_thread_html_body function does not remove any tags if there are no unwanted tags
"""
html_body = "
This paragraph has no tags to remove.
"
- expected_output = "
This paragraph has no tags to remove.
"
+ expected_output = '
This paragraph has no tags to remove.
'
result = clean_thread_html_body(html_body)
self.assertEqual(result, expected_output)
@@ -169,28 +166,33 @@ class TestCleanThreadHtmlBody(unittest.TestCase):
result = clean_thread_html_body(html_body)
self.assertEqual(result.strip(), expected_output)
+ def test_tag_replace(self):
+ """
+ Tests if the clean_thread_html_body function replaces tags
+ """
+ for tag in ["div", "section", "article", "h1", "h2", "h3", "h4", "h5", "h6"]:
+ html_body = f'<{tag}>Text{tag}>'
+ result = clean_thread_html_body(html_body)
+ self.assertEqual(result, '
Text
')
+
def test_button_tag_replace(self):
"""
Tests that the clean_thread_html_body function replaces the button tag with span tag
"""
# Tests for button replacement tag with text
html_body = ''
- expected_output = 'Button'
+ expected_output = 'Button'
result = clean_thread_html_body(html_body)
self.assertEqual(result, expected_output)
# Tests button tag replacement without text
html_body = ''
- expected_output = ''
+ expected_output = ''
result = clean_thread_html_body(html_body)
self.assertEqual(result, expected_output)
- def test_heading_tag_replace(self):
- """
- Tests that the clean_thread_html_body function replaces the h1, h2 and h3 tags with h4 tag
- """
- for tag in ['h1', 'h2', 'h3']:
- html_body = f'<{tag}>Heading{tag}>'
- expected_output = '
Heading
'
- result = clean_thread_html_body(html_body)
- self.assertEqual(result, expected_output)
+ def test_attributes_removal_from_tag(self):
+ # Tests for removal of attributes from tags
+ html_body = '
Paragraph
'
+ result = clean_thread_html_body(html_body)
+ self.assertEqual(result, '
Paragraph
')
From f5b88392a545c3efcda7015526d0701382b70220 Mon Sep 17 00:00:00 2001
From: Hunia Fatima
Date: Wed, 9 Oct 2024 14:44:07 +0500
Subject: [PATCH 04/32] chore: cleanup constraint file and format it (#35601)
* chore: cleanup constraint file and format it
---
requirements/constraints.txt | 218 +++++++++++++++++++++--------------
1 file changed, 134 insertions(+), 84 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 3809b753a5..996391b2be 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -7,7 +7,8 @@
# link to other information that will help people in the future to remove the
# pin when possible. Writing an issue against the offending project and
# linking to it here is good.
-
+# For further details on how to properly write constraints here please consult
+# https://openedx.atlassian.net/wiki/spaces/COMM/pages/4400250883/Adding+pinned+dependencies+in+constraint+file
# This file contains all common constraints for edx-repos
-c common_constraints.txt
@@ -18,127 +19,176 @@
# Ticket: https://github.com/openedx/edx-platform/issues/35334
algoliasearch<4.0.0
+# Date: 2024-03-14
+# Temporary to Support the python 3.11 Upgrade
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35281
+backports.zoneinfo;python_version<"3.9" # Newer versions have zoneinfo available in the standard library
+
+# Date: 2020-02-26
# As it is not clarified what exact breaking changes will be introduced as per
# the next major release, ensure the installed version is within boundaries.
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35280
celery>=5.2.2,<6.0.0
+# Date: 2021-05-17
+# greater version breaking upgrade builds
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35279
+click==8.1.6
+# Date: 2022-07-20
+# edx-enterprise, snowflake-connector-python require charset-normalizer==2.0.0
+# Can be removed once snowflake-connector-python>2.7.9 is released with the fix.
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35278
+charset-normalizer<2.1.0
+
+# Date: 2024-02-02
+# Stay on LTS version, remove once this is added to common constraint
+Django<5.0
+
+# Date: 2020-02-10
+# django-oauth-toolkit version >=2.0.0 has breaking changes. More details
+# mentioned on this issue https://github.com/openedx/edx-platform/issues/32884
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35277
+django-oauth-toolkit==1.7.1
+
+# Date: 2024-02-02
+# incremental upgrade
+django-simple-history==3.4.0
+
+# Date: 2021-05-17
+# greater version has breaking changes and requires some migration steps.
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35276
+django-webpack-loader==0.7.0
+
+# Date: 2023-06-20
+# Adding pin to avoid any major upgrade
+djangorestframework<3.15.0
+
+# Date: 2023-07-19
+# The version of django-stubs we can use depends on which Django release we're using
+# 1.16.0 works with Django 3.2 through 4.1
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35275
+django-stubs==1.16.0
+djangorestframework-stubs==3.14.0 # Pinned to match django-stubs. Remove this when we can remove the above pin.
+
+# Date: 2024-07-23
+# django-storages==1.14.4 breaks course imports
+# Two lines were added in 1.14.4 that make file_exists_in_storage function always return False,
+# as the default value of AWS_S3_FILE_OVERWRITE is True
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35170
+django-storages<1.14.4
+
+# Date: 2019-08-16
# The team that owns this package will manually bump this package rather than having it pulled in automatically.
# This is to allow them to better control its deployment and to do it in a process that works better
# for them.
edx-enterprise==4.27.0
-# Stay on LTS version, remove once this is added to common constraint
-Django<5.0
-
-# django-oauth-toolkit version >=2.0.0 has breaking changes. More details
-# mentioned on this issue https://github.com/openedx/edx-platform/issues/32884
-django-oauth-toolkit==1.7.1
-
-# incremental upgrade
-django-simple-history==3.4.0
-
-# Adding pin to avoid any major upgrade
-pymongo<4.4.1
+# Date: 2024-05-09
+# This has to be constrained as well because newer versions of edx-i18n-tools need the
+# newer version of lxml but that requirement was not made expilict in the 1.6.0 version
+# of the package. This can be un-pinned when we're upgrading lxml.
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35274
+edx-i18n-tools<1.6.0
+# Date: 2024-07-26
# To override the constraint of edx-lint
# This can be removed once https://github.com/openedx/edx-platform/issues/34586 is resolved
# and the upstream constraint in edx-lint has been removed.
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35273
event-tracking==3.0.0
-# greater version has breaking changes and requires some migration steps.
-django-webpack-loader==0.7.0
-
-# At the time of writing this comment, we do not know whether py2neo>=2022
-# will support our currently-deployed Neo4j version (3.5).
-# Feel free to loosen this constraint if/when it is confirmed that a later
-# version of py2neo will work with Neo4j 3.5.
-py2neo<2022
-
-# edx-enterprise, snowflake-connector-python require charset-normalizer==2.0.0
-# Can be removed once snowflake-connector-python>2.7.9 is released with the fix.
-charset-normalizer<2.1.0
-
-# markdown>=3.4.0 has failures due to internal refactorings which causes the tests to fail
-# pinning the version untill the issue gets resolved in the package itself
-markdown<3.4.0
-
-# pycodestyle==2.9.0 generates false positive error E275.
-# Constraint can be removed once the issue https://github.com/PyCQA/pycodestyle/issues/1090 is fixed.
-pycodestyle<2.9.0
-
-pylint<2.16.0 # greater version failing quality test. Fix them in seperate ticket.
-
-# urllib3>=2.0.0 conflicts with elastic search && snowflake-connector-python packages
-# which require urllib3<2 for now.
-# Issue for unpinning: https://github.com/openedx/edx-platform/issues/32222
-urllib3<2.0.0
-
-
-# Adding pin to avoid any major upgrade
-djangorestframework<3.15.0
-
-# The version of django-stubs we can use depends on which Django release we're using
-# 1.16.0 works with Django 3.2 through 4.1
-django-stubs==1.16.0
-djangorestframework-stubs==3.14.0 # Pinned to match django-stubs. Remove this when we can remove the above pin.
-
+# Date: 2023-07-26
# Our legacy Sass code is incompatible with anything except this ancient libsass version.
# Here is a ticket to upgrade, but it's of debatable importance given that we are rapidly moving
# away from legacy LMS/CMS frontends:
# https://github.com/openedx/edx-platform/issues/31616
libsass==0.10.0
-# greater version breaking upgrade builds
-click==8.1.6
-
-# pinning this version to avoid updates while the library is being developed
-openedx-learning==0.13.1
-
-# Open AI version 1.0.0 dropped support for openai.ChatCompletion which is currently in use in enterprise.
-openai<=0.28.1
-
-# optimizely-sdk 5.0.0 is breaking following test with segmentation fault
-# common/djangoapps/third_party_auth/tests/test_views.py::SAMLMetadataTest::test_secure_key_configuration
-# needs to be fixed in the follow up issue
-# https://github.com/openedx/edx-platform/issues/34103
-optimizely-sdk<5.0
-
+# Date: 2024-04-30
# lxml>=5.0 introduced breaking changes related to system dependencies
# lxml==5.2.1 introduced new extra so we'll nee to rename lxml --> lxml[html-clean]
# This constraint can be removed once we upgrade to Python 3.11
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35272
lxml<5.0
-# This has to be constrained as well because newer versions of edx-i18n-tools need the
-# newer version of lxml but that requirement was not made expilict in the 1.6.0 version
-# of the package. This can be un-pinned when we're upgrading lxml.
-edx-i18n-tools<1.6.0
-# xmlsec==1.3.14 breaking tests for all builds, can be removed once a fix is available
-xmlsec<1.3.14
+# Date: 2018-12-14
+# markdown>=3.4.0 has failures due to internal refactorings which causes the tests to fail
+# pinning the version untill the issue gets resolved in the package itself
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35271
+markdown<3.4.0
+# Date: 2024-04-24
# moto==5.0 contains breaking changes. Needs to be updated separately.
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35270
moto<5.0
-# path==16.12.0 breaks the unit test collections check
-# needs to be investigated and fixed separately
-path<16.12.0
-
-# Temporary to Support the python 3.11 Upgrade
-backports.zoneinfo;python_version<"3.9" # Newer versions have zoneinfo available in the standard library
-
-# Relevant GitHub Issue: https://github.com/openedx/edx-platform/issues/35126
+# Date: 2024-07-16
# We need to upgrade the version of elasticsearch to atleast 7.15 before we can upgrade to Numpy 2.0.0
# Otherwise we see a failure while running the following command:
# export DJANGO_SETTINGS_MODULE=cms.envs.test; python manage.py cms check_reserved_keywords --override_file db_keyword_overrides.yml --report_path reports/reserved_keywords --report_file cms_reserved_keyword_report.csv
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35126
numpy<2.0.0
-# django-storages==1.14.4 breaks course imports
-# Two lines were added in 1.14.4 that make file_exists_in_storage function always return False,
-# as the default value of AWS_S3_FILE_OVERWRITE is True
-django-storages<1.14.4
+# Date: 2024-01-26
+# optimizely-sdk 5.0.0 is breaking following test with segmentation fault
+# common/djangoapps/third_party_auth/tests/test_views.py::SAMLMetadataTest::test_secure_key_configuration
+# needs to be fixed in the follow up issue
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/34103
+optimizely-sdk<5.0
+# Date: 2023-09-18
+# pinning this version to avoid updates while the library is being developed
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35269
+openedx-learning==0.13.1
+
+# Date: 2023-11-29
+# Open AI version 1.0.0 dropped support for openai.ChatCompletion which is currently in use in enterprise.
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35268
+openai<=0.28.1
+
+# Date: 2024-04-26
+# path==16.12.0 breaks the unit test collections check
+# needs to be investigated and fixed separately
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35267
+path<16.12.0
+
+# Date: 2022-08-03
+# pycodestyle==2.9.0 generates false positive error E275.
+# Constraint can be removed once the issue https://github.com/PyCQA/pycodestyle/issues/1090 is fixed.
+pycodestyle<2.9.0
+
+# Date: 2021-07-12
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/33560
+pylint<2.16.0 # greater version failing quality test. Fix them in seperate ticket.
+
+# Date: 2021-08-25
+# At the time of writing this comment, we do not know whether py2neo>=2022
+# will support our currently-deployed Neo4j version (3.5).
+# Feel free to loosen this constraint if/when it is confirmed that a later
+# version of py2neo will work with Neo4j 3.5.
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35266
+py2neo<2022
+
+# Date: 2020-04-08
+# Adding pin to avoid any major upgrade
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35265
+pymongo<4.4.1
+
+# Date: 2024-08-06
# social-auth-app-django 5.4.2 introduces a new migration that will not play nicely with large installations. This will touch
# user tables, which are quite large, especially on instances like edx.org.
# We are pinning this until after all the smaller migrations get handled and then we can migrate this all at once.
-# Ticket to unpin: https://github.com/edx/edx-arch-experiments/issues/760
+# Issue for unpinning: https://github.com/edx/edx-arch-experiments/issues/760
social-auth-app-django<=5.4.1
+
+# Date: 2023-11-05
+# urllib3>=2.0.0 conflicts with elastic search && snowflake-connector-python packages
+# which require urllib3<2 for now.
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/32222
+urllib3<2.0.0
+
+# Date: 2024-04-24
+# xmlsec==1.3.14 breaking tests or all builds, can be removed once a fix is available
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35264
+xmlsec<1.3.14
From f1a9286f731f20fde47ca3bdd797b3c615831536 Mon Sep 17 00:00:00 2001
From: Deborah Kaplan
Date: Wed, 9 Oct 2024 09:45:05 -0400
Subject: [PATCH 05/32] chore: changing codeowners for several components
(#35574)
making sure the codeowners for some maintainership is accurate.
---
.github/CODEOWNERS | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index a05b78e883..fc452f7acd 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -15,6 +15,7 @@ lms/djangoapps/grades/
lms/djangoapps/instructor/
lms/djangoapps/instructor_task/
lms/djangoapps/mobile_api/
+openedx/core/djangoapps/commerce/ @openedx/2u-infinity
openedx/core/djangoapps/credentials @openedx/2U-aperture
openedx/core/djangoapps/credit @openedx/2U-aperture
openedx/core/djangoapps/enrollments/ @openedx/2U-aperture
@@ -22,6 +23,7 @@ openedx/core/djangoapps/heartbeat/
openedx/core/djangoapps/oauth_dispatch
openedx/core/djangoapps/user_api/ @openedx/2U-aperture
openedx/core/djangoapps/user_authn/ @openedx/2U-vanguards
+openedx/core/djangoapps/verified_track_content/ @openedx/2u-infinity
openedx/features/course_experience/
xmodule/
@@ -36,16 +38,18 @@ common/djangoapps/track/
lms/djangoapps/certificates/ @openedx/2U-aperture
# Discovery
-common/djangoapps/course_modes/
+common/djangoapps/course_modes/ @openedx/2U-aperture
common/djangoapps/enrollment/
-lms/djangoapps/branding/ @openedx/2U-aperture
-lms/djangoapps/commerce/
-lms/djangoapps/experiments/ @openedx/2U-aperture
-lms/djangoapps/learner_dashboard/ @openedx/2U-aperture
-lms/djangoapps/learner_home/ @openedx/2U-aperture
-openedx/features/content_type_gating/
+common/djangoapps/entitlements/ @openedx/2U-aperture
+lms/djangoapps/branding/ @openedx/2U-aperture
+lms/djangoapps/commerce/ @openedx/2u-infinity
+lms/djangoapps/experiments/ @openedx/2u-infinity
+lms/djangoapps/gating/ @openedx/2u-infinity
+lms/djangoapps/learner_dashboard/ @openedx/2U-aperture
+lms/djangoapps/learner_home/ @openedx/2U-aperture
+openedx/features/content_type_gating/ @openedx/2u-infinity
openedx/features/course_duration_limits/
-openedx/features/discounts/
+openedx/features/discounts/ @openedx/2u-infinity
# Ping Axim On-call if someone uses the QuickStart
# https://docs.openedx.org/en/latest/developers/quickstarts/first_openedx_pr.html
From 243b1b4e2e6e065b685f678556164d5dcd47eb67 Mon Sep 17 00:00:00 2001
From: Alison Langston <46360176+alangsto@users.noreply.github.com>
Date: Wed, 9 Oct 2024 11:16:02 -0400
Subject: [PATCH 06/32] feat: update management command to manually create
verified names (#35619)
* feat: update management command to manually create verified names
* fix: update function name
---
.../commands/approve_id_verifications.py | 34 +++++++++++
.../tests/test_approve_id_verifications.py | 59 +++++++++++++++++++
2 files changed, 93 insertions(+)
diff --git a/lms/djangoapps/verify_student/management/commands/approve_id_verifications.py b/lms/djangoapps/verify_student/management/commands/approve_id_verifications.py
index 3a08ede0aa..4c45f415cf 100644
--- a/lms/djangoapps/verify_student/management/commands/approve_id_verifications.py
+++ b/lms/djangoapps/verify_student/management/commands/approve_id_verifications.py
@@ -10,9 +10,11 @@ from pprint import pformat
from django.core.management.base import BaseCommand, CommandError
+from common.djangoapps.student.models_api import get_name, get_pending_name_change
from lms.djangoapps.verify_student.api import send_approval_email
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
from lms.djangoapps.verify_student.utils import earliest_allowed_verification_date
+from openedx.features.name_affirmation_api.utils import get_name_affirmation_service
log = logging.getLogger(__name__)
@@ -149,5 +151,37 @@ class Command(BaseCommand):
for verification in existing_id_verifications:
verification.approve(service='idv_verifications command')
send_approval_email(verification)
+ self._approve_verified_name_for_software_secure_verification(verification)
return list(failed_user_ids)
+
+ def _approve_verified_name_for_software_secure_verification(self, verification):
+ """
+ This method manually creates a verified name given a SoftwareSecurePhotoVerification object.
+ """
+
+ name_affirmation_service = get_name_affirmation_service()
+
+ if name_affirmation_service:
+ from edx_name_affirmation.exceptions import VerifiedNameDoesNotExist # pylint: disable=import-error
+
+ pending_name_change = get_pending_name_change(verification.user)
+ if pending_name_change:
+ full_name = pending_name_change.new_name
+ else:
+ full_name = get_name(verification.user.id)
+
+ try:
+ name_affirmation_service.update_verified_name_status(
+ verification.user,
+ 'approved',
+ verification_attempt_id=verification.id
+ )
+ except VerifiedNameDoesNotExist:
+ name_affirmation_service.create_verified_name(
+ verification.user,
+ verification.name,
+ full_name,
+ verification_attempt_id=verification.id,
+ status='approved',
+ )
diff --git a/lms/djangoapps/verify_student/management/commands/tests/test_approve_id_verifications.py b/lms/djangoapps/verify_student/management/commands/tests/test_approve_id_verifications.py
index e6e580c1d1..6eccee1947 100644
--- a/lms/djangoapps/verify_student/management/commands/tests/test_approve_id_verifications.py
+++ b/lms/djangoapps/verify_student/management/commands/tests/test_approve_id_verifications.py
@@ -6,6 +6,8 @@ import ddt
import logging
import os
import tempfile
+from unittest import skipUnless
+from unittest.mock import MagicMock, patch
import pytest
from django.core import mail
@@ -15,9 +17,12 @@ from testfixtures import LogCapture
from common.djangoapps.student.tests.factories import UserFactory, UserProfileFactory
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
+from openedx.features.name_affirmation_api.utils import get_name_affirmation_service
LOGGER_NAME = 'lms.djangoapps.verify_student.management.commands.approve_id_verifications'
+name_affirmation_service = get_name_affirmation_service()
+
@ddt.ddt
class TestApproveIDVerificationsCommand(TestCase):
@@ -158,3 +163,57 @@ class TestApproveIDVerificationsCommand(TestCase):
"""
with pytest.raises(CommandError):
call_command('approve_id_verifications', 'invalid/user_id/file/path')
+
+ @skipUnless(name_affirmation_service is not None, 'Requires Name Affirmation')
+ @patch('lms.djangoapps.verify_student.management.commands.approve_id_verifications.get_name_affirmation_service')
+ def test_create_verified_names(self, mock_get_service):
+ mock_service = MagicMock()
+ mock_get_service.return_value = mock_service
+
+ verification = SoftwareSecurePhotoVerification.objects.create(
+ user=self.user1_profile.user,
+ name=self.user1_profile.name,
+ status='submitted',
+ )
+
+ call_command('approve_id_verifications', self.tmp_file_path)
+ mock_service.update_verified_name_status.assert_called_with(
+ self.user1_profile.user,
+ 'approved',
+ verification_attempt_id=verification.id,
+ )
+
+ @skipUnless(name_affirmation_service is not None, 'Requires Name Affirmation')
+ @patch('lms.djangoapps.verify_student.management.commands.approve_id_verifications.get_name')
+ @patch('lms.djangoapps.verify_student.management.commands.approve_id_verifications.get_pending_name_change')
+ @patch('lms.djangoapps.verify_student.management.commands.approve_id_verifications.get_name_affirmation_service')
+ @ddt.data(
+ '',
+ MagicMock(new_name='test')
+ )
+ def test_create_update_verified_names(self, pending_name, mock_get_service, mock_get_pending, mock_get_name):
+ from edx_name_affirmation.exceptions import VerifiedNameDoesNotExist # pylint: disable=import-error
+
+ mock_service = MagicMock()
+ mock_get_service.return_value = mock_service
+ mock_service.update_verified_name_status.side_effect = VerifiedNameDoesNotExist()
+
+ mock_get_pending.return_value = pending_name
+ mock_get_name.return_value = self.user1_profile.name
+
+ verification = SoftwareSecurePhotoVerification.objects.create(
+ user=self.user1_profile.user,
+ name=self.user1_profile.name,
+ status='submitted',
+ )
+
+ expected_name = 'test' if pending_name else self.user1_profile.name
+
+ call_command('approve_id_verifications', self.tmp_file_path)
+ mock_service.create_verified_name.assert_called_with(
+ verification.user,
+ verification.name,
+ expected_name,
+ verification_attempt_id=verification.id,
+ status='approved',
+ )
From a245dec4f4ff431b336832174f53421a5a1b987c Mon Sep 17 00:00:00 2001
From: Diana Huang
Date: Thu, 10 Oct 2024 12:32:14 -0400
Subject: [PATCH 07/32] feat: Use jammy repositories for mongo installation.
Unclear if there was a change in the focal repositories
or if there was an issue with something else. The noble
repositories don't support 7.0, so we're stuck here
until we upgrade to 8.0.
---
.github/workflows/unit-tests.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
index 5fef1c8352..900489256a 100644
--- a/.github/workflows/unit-tests.yml
+++ b/.github/workflows/unit-tests.yml
@@ -94,7 +94,7 @@ jobs:
run: |
if [[ "${{ matrix.mongo-version }}" != "4.4" ]]; then
wget -qO - https://www.mongodb.org/static/pgp/server-${{ matrix.mongo-version }}.asc | sudo apt-key add -
- echo "deb https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/${{ matrix.mongo-version }} multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-${{ matrix.mongo-version }}.list
+ echo "deb https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/${{ matrix.mongo-version }} multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-${{ matrix.mongo-version }}.list
sudo apt-get update && sudo apt-get install -y mongodb-org="${{ matrix.mongo-version }}.*"
fi
From 6c045c7b390aa051484cee459a767b246f04e7d8 Mon Sep 17 00:00:00 2001
From: Diana Huang
Date: Thu, 10 Oct 2024 14:07:17 -0400
Subject: [PATCH 08/32] feat: Unpin xmlsec and lxml.
We are now on Python 3.11 and running tests
on the latest version of Ubuntu, which seems to mean
we don't need these pins anymore. In fact, it seems
to break while on these pins.
---
requirements/constraints.txt | 19 ---
requirements/edx-sandbox/base.txt | 5 +-
requirements/edx/base.txt | 74 ++++++------
requirements/edx/coverage.txt | 4 +-
requirements/edx/development.txt | 110 ++++++++++--------
requirements/edx/doc.txt | 77 ++++++------
requirements/edx/paver.txt | 6 +-
requirements/edx/semgrep.txt | 8 +-
requirements/edx/testing.txt | 96 ++++++++-------
requirements/pip-tools.txt | 4 +-
.../structures_pruning/requirements/base.txt | 2 +-
.../requirements/testing.txt | 2 +-
scripts/user_retirement/requirements/base.txt | 23 ++--
.../user_retirement/requirements/testing.txt | 23 ++--
14 files changed, 224 insertions(+), 229 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index dd727a4b18..2fc1a1e745 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -84,13 +84,6 @@ django-storages<1.14.4
# for them.
edx-enterprise==4.27.2
-# Date: 2024-05-09
-# This has to be constrained as well because newer versions of edx-i18n-tools need the
-# newer version of lxml but that requirement was not made expilict in the 1.6.0 version
-# of the package. This can be un-pinned when we're upgrading lxml.
-# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35274
-edx-i18n-tools<1.6.0
-
# Date: 2024-07-26
# To override the constraint of edx-lint
# This can be removed once https://github.com/openedx/edx-platform/issues/34586 is resolved
@@ -105,13 +98,6 @@ event-tracking==3.0.0
# https://github.com/openedx/edx-platform/issues/31616
libsass==0.10.0
-# Date: 2024-04-30
-# lxml>=5.0 introduced breaking changes related to system dependencies
-# lxml==5.2.1 introduced new extra so we'll nee to rename lxml --> lxml[html-clean]
-# This constraint can be removed once we upgrade to Python 3.11
-# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35272
-lxml<5.0
-
# Date: 2018-12-14
# markdown>=3.4.0 has failures due to internal refactorings which causes the tests to fail
# pinning the version untill the issue gets resolved in the package itself
@@ -187,8 +173,3 @@ social-auth-app-django<=5.4.1
# which require urllib3<2 for now.
# Issue for unpinning: https://github.com/openedx/edx-platform/issues/32222
urllib3<2.0.0
-
-# Date: 2024-04-24
-# xmlsec==1.3.14 breaking tests or all builds, can be removed once a fix is available
-# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35264
-xmlsec<1.3.14
diff --git a/requirements/edx-sandbox/base.txt b/requirements/edx-sandbox/base.txt
index bf0a4376da..0a708b2bbf 100644
--- a/requirements/edx-sandbox/base.txt
+++ b/requirements/edx-sandbox/base.txt
@@ -26,12 +26,11 @@ joblib==1.4.2
# via nltk
kiwisolver==1.4.7
# via matplotlib
-lxml==4.9.4
+lxml==5.3.0
# via
- # -c requirements/edx-sandbox/../constraints.txt
# -r requirements/edx-sandbox/base.in
# openedx-calc
-markupsafe==2.1.5
+markupsafe==3.0.1
# via
# chem
# openedx-calc
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 55793bd0c5..c7f7828f41 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -6,11 +6,11 @@
#
-e git+https://github.com/anupdhabarde/edx-proctoring-proctortrack.git@31c6c9923a51c903ae83760ecbbac191363aa2a2#egg=edx_proctoring_proctortrack
# via -r requirements/edx/github.in
-acid-xblock==0.3.1
+acid-xblock==0.4.1
# via -r requirements/edx/kernel.in
-aiohappyeyeballs==2.4.0
+aiohappyeyeballs==2.4.3
# via aiohttp
-aiohttp==3.10.6
+aiohttp==3.10.9
# via
# geoip2
# openai
@@ -70,13 +70,13 @@ bleach[css]==6.1.0
# xblock-poll
boto==2.49.0
# via -r requirements/edx/kernel.in
-boto3==1.35.27
+boto3==1.35.37
# via
# -r requirements/edx/kernel.in
# django-ses
# fs-s3fs
# ora2
-botocore==1.35.27
+botocore==1.35.37
# via
# -r requirements/edx/kernel.in
# boto3
@@ -87,7 +87,7 @@ cachecontrol==0.14.0
# via firebase-admin
cachetools==5.5.0
# via google-auth
-camel-converter[pydantic]==3.1.2
+camel-converter[pydantic]==4.0.1
# via meilisearch
celery==5.4.0
# via
@@ -328,7 +328,7 @@ django-sekizai==4.1.0
# via
# -r requirements/edx/kernel.in
# openedx-django-wiki
-django-ses==4.1.1
+django-ses==4.2.0
# via -r requirements/edx/bundled.in
django-simple-history==3.4.0
# via
@@ -387,7 +387,7 @@ djangorestframework==3.14.0
# super-csv
djangorestframework-xml==2.0.0
# via edx-enterprise
-dnspython==2.6.1
+dnspython==2.7.0
# via
# -r requirements/edx/paver.txt
# pymongo
@@ -429,7 +429,7 @@ edx-celeryutils==1.3.0
# super-csv
edx-codejail==3.4.1
# via -r requirements/edx/kernel.in
-edx-completion==4.7.1
+edx-completion==4.7.2
# via -r requirements/edx/kernel.in
edx-django-release-util==1.4.0
# via
@@ -438,7 +438,7 @@ edx-django-release-util==1.4.0
# edxval
edx-django-sites-extensions==4.2.0
# via -r requirements/edx/kernel.in
-edx-django-utils==5.16.0
+edx-django-utils==6.0.0
# via
# -r requirements/edx/kernel.in
# django-config-models
@@ -475,9 +475,8 @@ edx-event-bus-kafka==5.8.1
# via -r requirements/edx/kernel.in
edx-event-bus-redis==0.5.0
# via -r requirements/edx/kernel.in
-edx-i18n-tools==1.5.0
+edx-i18n-tools==1.6.3
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/bundled.in
# ora2
edx-milestones==0.6.0
@@ -517,7 +516,7 @@ edx-search==4.0.0
# via -r requirements/edx/kernel.in
edx-sga==0.25.0
# via -r requirements/edx/bundled.in
-edx-submissions==3.8.0
+edx-submissions==3.8.1
# via
# -r requirements/edx/kernel.in
# ora2
@@ -584,14 +583,14 @@ geoip2==4.8.0
# via -r requirements/edx/kernel.in
glob2==0.7
# via -r requirements/edx/kernel.in
-google-api-core[grpc]==2.20.0
+google-api-core[grpc]==2.21.0
# via
# firebase-admin
# google-api-python-client
# google-cloud-core
# google-cloud-firestore
# google-cloud-storage
-google-api-python-client==2.147.0
+google-api-python-client==2.149.0
# via firebase-admin
google-auth==2.35.0
# via
@@ -621,11 +620,11 @@ googleapis-common-protos==1.65.0
# via
# google-api-core
# grpcio-status
-grpcio==1.66.1
+grpcio==1.66.2
# via
# google-api-core
# grpcio-status
-grpcio-status==1.66.1
+grpcio-status==1.66.2
# via google-api-core
gunicorn==23.0.0
# via -r requirements/edx/kernel.in
@@ -639,7 +638,7 @@ httplib2==0.22.0
# via
# google-api-python-client
# google-auth-httplib2
-icalendar==5.0.13
+icalendar==6.0.0
# via -r requirements/edx/kernel.in
idna==3.10
# via
@@ -658,7 +657,7 @@ interchange==2021.0.4
# via py2neo
ipaddress==1.0.23
# via -r requirements/edx/kernel.in
-isodate==0.6.1
+isodate==0.7.2
# via python3-saml
jinja2==3.1.4
# via code-annotations
@@ -683,7 +682,7 @@ jsonschema==4.23.0
# via
# drf-spectacular
# optimizely-sdk
-jsonschema-specifications==2023.12.1
+jsonschema-specifications==2024.10.1
# via jsonschema
jwcrypto==1.5.6
# via
@@ -708,19 +707,21 @@ loremipsum==1.0.5
# via ora2
lti-consumer-xblock==9.11.3
# via -r requirements/edx/kernel.in
-lxml==4.9.4
+lxml[html-clean]==5.3.0
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
# edx-i18n-tools
# edxval
# lti-consumer-xblock
+ # lxml-html-clean
# olxcleaner
# openedx-calc
# ora2
# python3-saml
# xblock
# xmlsec
+lxml-html-clean==0.3.1
+ # via lxml
mailsnake==1.6.4
# via -r requirements/edx/bundled.in
mako==1.3.5
@@ -737,7 +738,7 @@ markdown==3.3.7
# openedx-django-wiki
# staff-graded-xblock
# xblock-poll
-markupsafe==2.1.5
+markupsafe==3.0.1
# via
# -r requirements/edx/paver.txt
# chem
@@ -769,7 +770,7 @@ multidict==6.1.0
# yarl
mysqlclient==2.2.4
# via -r requirements/edx/kernel.in
-newrelic==9.13.0
+newrelic==10.0.0
# via
# -r requirements/edx/bundled.in
# edx-django-utils
@@ -820,7 +821,7 @@ openedx-events==9.14.1
# edx-name-affirmation
# event-tracking
# ora2
-openedx-filters==1.10.0
+openedx-filters==1.11.0
# via
# -r requirements/edx/kernel.in
# lti-consumer-xblock
@@ -829,7 +830,7 @@ openedx-learning==0.13.1
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
-openedx-mongodbproxy==0.2.1
+openedx-mongodbproxy==0.2.2
# via -r requirements/edx/kernel.in
optimizely-sdk==4.1.1
# via
@@ -881,6 +882,8 @@ polib==1.2.0
# via edx-i18n-tools
prompt-toolkit==3.0.48
# via click-repl
+propcache==0.2.0
+ # via yarl
proto-plus==1.24.0
# via
# google-api-core
@@ -911,7 +914,7 @@ pycountry==24.6.1
# via -r requirements/edx/kernel.in
pycparser==2.22
# via cffi
-pycryptodomex==3.20.0
+pycryptodomex==3.21.0
# via
# -r requirements/edx/kernel.in
# edx-proctoring
@@ -1017,7 +1020,6 @@ pytz==2024.2
# edx-tincan-py35
# event-tracking
# fs
- # icalendar
# interchange
# olxcleaner
# ora2
@@ -1039,7 +1041,7 @@ random2==1.0.2
# via -r requirements/edx/kernel.in
recommender-xblock==2.2.1
# via -r requirements/edx/bundled.in
-redis==5.0.8
+redis==5.1.1
# via
# -r requirements/edx/kernel.in
# walrus
@@ -1092,7 +1094,7 @@ rules==3.5
# edx-enterprise
# edx-proctoring
# openedx-learning
-s3transfer==0.10.2
+s3transfer==0.10.3
# via boto3
sailthru-client==2.2.3
# via edx-ace
@@ -1131,7 +1133,6 @@ six==1.16.0
# fs-s3fs
# html5lib
# interchange
- # isodate
# libsass
# optimizely-sdk
# pansi
@@ -1208,6 +1209,7 @@ typing-extensions==4.12.2
tzdata==2024.2
# via
# celery
+ # icalendar
# kombu
unicodecsv==0.14.1
# via
@@ -1237,7 +1239,7 @@ voluptuous==0.15.2
# via ora2
walrus==0.9.4
# via edx-event-bus-redis
-watchdog==5.0.2
+watchdog==5.0.3
# via -r requirements/edx/paver.txt
wcwidth==0.2.13
# via prompt-toolkit
@@ -1285,13 +1287,11 @@ xblock-utils==4.0.0
# via
# edx-sga
# xblock-poll
-xmlsec==1.3.13
- # via
- # -c requirements/edx/../constraints.txt
- # python3-saml
+xmlsec==1.3.14
+ # via python3-saml
xss-utils==0.6.0
# via -r requirements/edx/kernel.in
-yarl==1.12.1
+yarl==1.14.0
# via aiohttp
zipp==3.20.2
# via importlib-metadata
diff --git a/requirements/edx/coverage.txt b/requirements/edx/coverage.txt
index a1faf5e740..45f2429cb2 100644
--- a/requirements/edx/coverage.txt
+++ b/requirements/edx/coverage.txt
@@ -6,13 +6,13 @@
#
chardet==5.2.0
# via diff-cover
-coverage==7.6.1
+coverage==7.6.2
# via -r requirements/edx/coverage.in
diff-cover==9.2.0
# via -r requirements/edx/coverage.in
jinja2==3.1.4
# via diff-cover
-markupsafe==2.1.5
+markupsafe==3.0.1
# via jinja2
pluggy==1.5.0
# via diff-cover
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index 1bdd373651..13f3f6d5e6 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -12,16 +12,16 @@ accessible-pygments==0.0.5
# via
# -r requirements/edx/doc.txt
# pydata-sphinx-theme
-acid-xblock==0.3.1
+acid-xblock==0.4.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-aiohappyeyeballs==2.4.0
+aiohappyeyeballs==2.4.3
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# aiohttp
-aiohttp==3.10.6
+aiohttp==3.10.9
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -140,14 +140,14 @@ boto==2.49.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-boto3==1.35.27
+boto3==1.35.37
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# django-ses
# fs-s3fs
# ora2
-botocore==1.35.27
+botocore==1.35.37
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -157,7 +157,7 @@ bridgekeeper==0.9
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-build==1.2.2
+build==1.2.2.post1
# via
# -r requirements/edx/../pip-tools.txt
# pip-tools
@@ -172,7 +172,7 @@ cachetools==5.5.0
# -r requirements/edx/testing.txt
# google-auth
# tox
-camel-converter[pydantic]==3.1.2
+camel-converter[pydantic]==4.0.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -278,7 +278,7 @@ colorama==0.4.6
# via
# -r requirements/edx/testing.txt
# tox
-coverage[toml]==7.6.1
+coverage[toml]==7.6.2
# via
# -r requirements/edx/testing.txt
# pytest-cov
@@ -325,11 +325,11 @@ defusedxml==0.7.1
# social-auth-core
diff-cover==9.2.0
# via -r requirements/edx/testing.txt
-dill==0.3.8
+dill==0.3.9
# via
# -r requirements/edx/testing.txt
# pylint
-distlib==0.3.8
+distlib==0.3.9
# via
# -r requirements/edx/testing.txt
# virtualenv
@@ -544,7 +544,7 @@ django-sekizai==4.1.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# openedx-django-wiki
-django-ses==4.1.1
+django-ses==4.2.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -627,7 +627,7 @@ djangorestframework-xml==2.0.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# edx-enterprise
-dnspython==2.6.1
+dnspython==2.7.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -696,7 +696,7 @@ edx-codejail==3.4.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-edx-completion==4.7.1
+edx-completion==4.7.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -710,7 +710,7 @@ edx-django-sites-extensions==4.2.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-edx-django-utils==5.16.0
+edx-django-utils==6.0.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -754,9 +754,8 @@ edx-event-bus-redis==0.5.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-edx-i18n-tools==1.5.0
+edx-i18n-tools==1.6.3
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# ora2
@@ -814,7 +813,7 @@ edx-sga==0.25.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-edx-submissions==3.8.0
+edx-submissions==3.8.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -879,7 +878,7 @@ execnet==2.1.1
# pytest-xdist
factory-boy==3.3.1
# via -r requirements/edx/testing.txt
-faker==30.0.0
+faker==30.3.0
# via
# -r requirements/edx/testing.txt
# factory-boy
@@ -943,7 +942,7 @@ glob2==0.7
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-google-api-core[grpc]==2.20.0
+google-api-core[grpc]==2.21.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -952,7 +951,7 @@ google-api-core[grpc]==2.20.0
# google-cloud-core
# google-cloud-firestore
# google-cloud-storage
-google-api-python-client==2.147.0
+google-api-python-client==2.149.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1005,17 +1004,17 @@ googleapis-common-protos==1.65.0
# -r requirements/edx/testing.txt
# google-api-core
# grpcio-status
-grimp==3.4.1
+grimp==3.5
# via
# -r requirements/edx/testing.txt
# import-linter
-grpcio==1.66.1
+grpcio==1.66.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# google-api-core
# grpcio-status
-grpcio-status==1.66.1
+grpcio-status==1.66.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1045,7 +1044,7 @@ httplib2==0.22.0
# google-auth-httplib2
httpretty==1.1.4
# via -r requirements/edx/testing.txt
-icalendar==5.0.13
+icalendar==6.0.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1062,7 +1061,7 @@ imagesize==1.4.1
# via
# -r requirements/edx/doc.txt
# sphinx
-import-linter==2.0
+import-linter==2.1
# via -r requirements/edx/testing.txt
importlib-metadata==8.5.0
# via
@@ -1087,7 +1086,7 @@ ipaddress==1.0.23
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-isodate==0.6.1
+isodate==0.7.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1136,7 +1135,7 @@ jsonschema==4.23.0
# drf-spectacular
# optimizely-sdk
# sphinxcontrib-openapi
-jsonschema-specifications==2023.12.1
+jsonschema-specifications==2024.10.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1183,14 +1182,14 @@ lti-consumer-xblock==9.11.3
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-lxml==4.9.4
+lxml[html-clean]==5.3.0
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# edx-i18n-tools
# edxval
# lti-consumer-xblock
+ # lxml-html-clean
# olxcleaner
# openedx-calc
# ora2
@@ -1198,6 +1197,11 @@ lxml==4.9.4
# python3-saml
# xblock
# xmlsec
+lxml-html-clean==0.3.1
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # lxml
mailsnake==1.6.4
# via
# -r requirements/edx/doc.txt
@@ -1218,7 +1222,7 @@ markdown==3.3.7
# openedx-django-wiki
# staff-graded-xblock
# xblock-poll
-markupsafe==2.1.5
+markupsafe==3.0.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1290,7 +1294,7 @@ mysqlclient==2.2.4
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-newrelic==9.13.0
+newrelic==10.0.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1368,7 +1372,7 @@ openedx-events==9.14.1
# edx-name-affirmation
# event-tracking
# ora2
-openedx-filters==1.10.0
+openedx-filters==1.11.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1379,7 +1383,7 @@ openedx-learning==0.13.1
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-openedx-mongodbproxy==0.2.1
+openedx-mongodbproxy==0.2.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1407,7 +1411,7 @@ packaging==24.1
# snowflake-connector-python
# sphinx
# tox
-pact-python==2.2.1
+pact-python==2.2.2
# via -r requirements/edx/testing.txt
pansi==2020.7.3
# via
@@ -1488,6 +1492,11 @@ prompt-toolkit==3.0.48
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# click-repl
+propcache==0.2.0
+ # via
+ # -r requirements/edx/doc.txt
+ # -r requirements/edx/testing.txt
+ # yarl
proto-plus==1.24.0
# via
# -r requirements/edx/doc.txt
@@ -1542,7 +1551,7 @@ pycparser==2.22
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# cffi
-pycryptodomex==3.20.0
+pycryptodomex==3.21.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1666,7 +1675,7 @@ pyproject-api==1.8.0
# via
# -r requirements/edx/testing.txt
# tox
-pyproject-hooks==1.1.0
+pyproject-hooks==1.2.0
# via
# -r requirements/edx/../pip-tools.txt
# build
@@ -1767,7 +1776,6 @@ pytz==2024.2
# edx-tincan-py35
# event-tracking
# fs
- # icalendar
# interchange
# olxcleaner
# ora2
@@ -1799,7 +1807,7 @@ recommender-xblock==2.2.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-redis==5.0.8
+redis==5.1.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1869,7 +1877,7 @@ rules==3.5
# edx-enterprise
# edx-proctoring
# openedx-learning
-s3transfer==0.10.2
+s3transfer==0.10.3
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1926,7 +1934,6 @@ six==1.16.0
# fs-s3fs
# html5lib
# interchange
- # isodate
# libsass
# optimizely-sdk
# pact-python
@@ -1986,7 +1993,7 @@ soupsieve==2.6
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# beautifulsoup4
-sphinx==8.0.2
+sphinx==8.1.0
# via
# -r requirements/edx/doc.txt
# pydata-sphinx-theme
@@ -2087,7 +2094,7 @@ tinycss2==1.2.1
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# bleach
-tomli==2.0.1
+tomli==2.0.2
# via django-stubs
tomlkit==0.13.2
# via
@@ -2095,7 +2102,7 @@ tomlkit==0.13.2
# -r requirements/edx/testing.txt
# pylint
# snowflake-connector-python
-tox==4.20.0
+tox==4.21.2
# via -r requirements/edx/testing.txt
tqdm==4.66.5
# via
@@ -2103,7 +2110,7 @@ tqdm==4.66.5
# -r requirements/edx/testing.txt
# nltk
# openai
-types-pytz==2024.2.0.20240913
+types-pytz==2024.2.0.20241003
# via django-stubs
types-pyyaml==6.0.12.20240917
# via
@@ -2122,6 +2129,7 @@ typing-extensions==4.12.2
# django-stubs-ext
# djangorestframework-stubs
# edx-opaque-keys
+ # faker
# fastapi
# grimp
# import-linter
@@ -2137,6 +2145,7 @@ tzdata==2024.2
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# celery
+ # icalendar
# kombu
unicodecsv==0.14.1
# via
@@ -2165,7 +2174,7 @@ user-util==1.1.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-uvicorn==0.30.6
+uvicorn==0.31.1
# via
# -r requirements/edx/testing.txt
# pact-python
@@ -2176,7 +2185,7 @@ vine==5.1.0
# amqp
# celery
# kombu
-virtualenv==20.26.5
+virtualenv==20.26.6
# via
# -r requirements/edx/testing.txt
# tox
@@ -2185,14 +2194,14 @@ voluptuous==0.15.2
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# ora2
-vulture==2.12
+vulture==2.13
# via -r requirements/edx/development.in
walrus==0.9.4
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# edx-event-bus-redis
-watchdog==5.0.2
+watchdog==5.0.3
# via
# -r requirements/edx/development.in
# -r requirements/edx/doc.txt
@@ -2266,9 +2275,8 @@ xblock-utils==4.0.0
# -r requirements/edx/testing.txt
# edx-sga
# xblock-poll
-xmlsec==1.3.13
+xmlsec==1.3.14
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# python3-saml
@@ -2276,7 +2284,7 @@ xss-utils==0.6.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-yarl==1.12.1
+yarl==1.14.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 6f12d80467..30736a21a3 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -8,13 +8,13 @@
# via -r requirements/edx/base.txt
accessible-pygments==0.0.5
# via pydata-sphinx-theme
-acid-xblock==0.3.1
+acid-xblock==0.4.1
# via -r requirements/edx/base.txt
-aiohappyeyeballs==2.4.0
+aiohappyeyeballs==2.4.3
# via
# -r requirements/edx/base.txt
# aiohttp
-aiohttp==3.10.6
+aiohttp==3.10.9
# via
# -r requirements/edx/base.txt
# geoip2
@@ -102,13 +102,13 @@ bleach[css]==6.1.0
# xblock-poll
boto==2.49.0
# via -r requirements/edx/base.txt
-boto3==1.35.27
+boto3==1.35.37
# via
# -r requirements/edx/base.txt
# django-ses
# fs-s3fs
# ora2
-botocore==1.35.27
+botocore==1.35.37
# via
# -r requirements/edx/base.txt
# boto3
@@ -123,7 +123,7 @@ cachetools==5.5.0
# via
# -r requirements/edx/base.txt
# google-auth
-camel-converter[pydantic]==3.1.2
+camel-converter[pydantic]==4.0.1
# via
# -r requirements/edx/base.txt
# meilisearch
@@ -398,7 +398,7 @@ django-sekizai==4.1.0
# via
# -r requirements/edx/base.txt
# openedx-django-wiki
-django-ses==4.1.1
+django-ses==4.2.0
# via -r requirements/edx/base.txt
django-simple-history==3.4.0
# via
@@ -459,7 +459,7 @@ djangorestframework-xml==2.0.0
# via
# -r requirements/edx/base.txt
# edx-enterprise
-dnspython==2.6.1
+dnspython==2.7.0
# via
# -r requirements/edx/base.txt
# pymongo
@@ -509,7 +509,7 @@ edx-celeryutils==1.3.0
# super-csv
edx-codejail==3.4.1
# via -r requirements/edx/base.txt
-edx-completion==4.7.1
+edx-completion==4.7.2
# via -r requirements/edx/base.txt
edx-django-release-util==1.4.0
# via
@@ -518,7 +518,7 @@ edx-django-release-util==1.4.0
# edxval
edx-django-sites-extensions==4.2.0
# via -r requirements/edx/base.txt
-edx-django-utils==5.16.0
+edx-django-utils==6.0.0
# via
# -r requirements/edx/base.txt
# django-config-models
@@ -555,9 +555,8 @@ edx-event-bus-kafka==5.8.1
# via -r requirements/edx/base.txt
edx-event-bus-redis==0.5.0
# via -r requirements/edx/base.txt
-edx-i18n-tools==1.5.0
+edx-i18n-tools==1.6.3
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# ora2
edx-milestones==0.6.0
@@ -598,7 +597,7 @@ edx-search==4.0.0
# via -r requirements/edx/base.txt
edx-sga==0.25.0
# via -r requirements/edx/base.txt
-edx-submissions==3.8.0
+edx-submissions==3.8.1
# via
# -r requirements/edx/base.txt
# ora2
@@ -683,7 +682,7 @@ gitpython==3.1.43
# via -r requirements/edx/doc.in
glob2==0.7
# via -r requirements/edx/base.txt
-google-api-core[grpc]==2.20.0
+google-api-core[grpc]==2.21.0
# via
# -r requirements/edx/base.txt
# firebase-admin
@@ -691,7 +690,7 @@ google-api-core[grpc]==2.20.0
# google-cloud-core
# google-cloud-firestore
# google-cloud-storage
-google-api-python-client==2.147.0
+google-api-python-client==2.149.0
# via
# -r requirements/edx/base.txt
# firebase-admin
@@ -735,12 +734,12 @@ googleapis-common-protos==1.65.0
# -r requirements/edx/base.txt
# google-api-core
# grpcio-status
-grpcio==1.66.1
+grpcio==1.66.2
# via
# -r requirements/edx/base.txt
# google-api-core
# grpcio-status
-grpcio-status==1.66.1
+grpcio-status==1.66.2
# via
# -r requirements/edx/base.txt
# google-api-core
@@ -757,7 +756,7 @@ httplib2==0.22.0
# -r requirements/edx/base.txt
# google-api-python-client
# google-auth-httplib2
-icalendar==5.0.13
+icalendar==6.0.0
# via -r requirements/edx/base.txt
idna==3.10
# via
@@ -781,7 +780,7 @@ interchange==2021.0.4
# py2neo
ipaddress==1.0.23
# via -r requirements/edx/base.txt
-isodate==0.6.1
+isodate==0.7.2
# via
# -r requirements/edx/base.txt
# python3-saml
@@ -818,7 +817,7 @@ jsonschema==4.23.0
# drf-spectacular
# optimizely-sdk
# sphinxcontrib-openapi
-jsonschema-specifications==2023.12.1
+jsonschema-specifications==2024.10.1
# via
# -r requirements/edx/base.txt
# jsonschema
@@ -850,19 +849,23 @@ loremipsum==1.0.5
# ora2
lti-consumer-xblock==9.11.3
# via -r requirements/edx/base.txt
-lxml==4.9.4
+lxml[html-clean]==5.3.0
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# edx-i18n-tools
# edxval
# lti-consumer-xblock
+ # lxml-html-clean
# olxcleaner
# openedx-calc
# ora2
# python3-saml
# xblock
# xmlsec
+lxml-html-clean==0.3.1
+ # via
+ # -r requirements/edx/base.txt
+ # lxml
mailsnake==1.6.4
# via -r requirements/edx/base.txt
mako==1.3.5
@@ -879,7 +882,7 @@ markdown==3.3.7
# openedx-django-wiki
# staff-graded-xblock
# xblock-poll
-markupsafe==2.1.5
+markupsafe==3.0.1
# via
# -r requirements/edx/base.txt
# chem
@@ -923,7 +926,7 @@ multidict==6.1.0
# yarl
mysqlclient==2.2.4
# via -r requirements/edx/base.txt
-newrelic==9.13.0
+newrelic==10.0.0
# via
# -r requirements/edx/base.txt
# edx-django-utils
@@ -979,7 +982,7 @@ openedx-events==9.14.1
# edx-name-affirmation
# event-tracking
# ora2
-openedx-filters==1.10.0
+openedx-filters==1.11.0
# via
# -r requirements/edx/base.txt
# lti-consumer-xblock
@@ -988,7 +991,7 @@ openedx-learning==0.13.1
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
-openedx-mongodbproxy==0.2.1
+openedx-mongodbproxy==0.2.2
# via -r requirements/edx/base.txt
optimizely-sdk==4.1.1
# via
@@ -1057,6 +1060,10 @@ prompt-toolkit==3.0.48
# via
# -r requirements/edx/base.txt
# click-repl
+propcache==0.2.0
+ # via
+ # -r requirements/edx/base.txt
+ # yarl
proto-plus==1.24.0
# via
# -r requirements/edx/base.txt
@@ -1094,7 +1101,7 @@ pycparser==2.22
# via
# -r requirements/edx/base.txt
# cffi
-pycryptodomex==3.20.0
+pycryptodomex==3.21.0
# via
# -r requirements/edx/base.txt
# edx-proctoring
@@ -1222,7 +1229,6 @@ pytz==2024.2
# edx-tincan-py35
# event-tracking
# fs
- # icalendar
# interchange
# olxcleaner
# ora2
@@ -1245,7 +1251,7 @@ random2==1.0.2
# via -r requirements/edx/base.txt
recommender-xblock==2.2.1
# via -r requirements/edx/base.txt
-redis==5.0.8
+redis==5.1.1
# via
# -r requirements/edx/base.txt
# walrus
@@ -1305,7 +1311,7 @@ rules==3.5
# edx-enterprise
# edx-proctoring
# openedx-learning
-s3transfer==0.10.2
+s3transfer==0.10.3
# via
# -r requirements/edx/base.txt
# boto3
@@ -1350,7 +1356,6 @@ six==1.16.0
# fs-s3fs
# html5lib
# interchange
- # isodate
# libsass
# optimizely-sdk
# pansi
@@ -1394,7 +1399,7 @@ soupsieve==2.6
# via
# -r requirements/edx/base.txt
# beautifulsoup4
-sphinx==8.0.2
+sphinx==8.1.0
# via
# -r requirements/edx/doc.in
# pydata-sphinx-theme
@@ -1489,6 +1494,7 @@ tzdata==2024.2
# via
# -r requirements/edx/base.txt
# celery
+ # icalendar
# kombu
unicodecsv==0.14.1
# via
@@ -1524,7 +1530,7 @@ walrus==0.9.4
# via
# -r requirements/edx/base.txt
# edx-event-bus-redis
-watchdog==5.0.2
+watchdog==5.0.3
# via -r requirements/edx/base.txt
wcwidth==0.2.13
# via
@@ -1576,14 +1582,13 @@ xblock-utils==4.0.0
# -r requirements/edx/base.txt
# edx-sga
# xblock-poll
-xmlsec==1.3.13
+xmlsec==1.3.14
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# python3-saml
xss-utils==0.6.0
# via -r requirements/edx/base.txt
-yarl==1.12.1
+yarl==1.14.0
# via
# -r requirements/edx/base.txt
# aiohttp
diff --git a/requirements/edx/paver.txt b/requirements/edx/paver.txt
index a0b1896919..2d8f510e03 100644
--- a/requirements/edx/paver.txt
+++ b/requirements/edx/paver.txt
@@ -10,7 +10,7 @@ charset-normalizer==2.0.12
# via
# -c requirements/edx/../constraints.txt
# requests
-dnspython==2.6.1
+dnspython==2.7.0
# via pymongo
edx-opaque-keys==2.11.0
# via -r requirements/edx/paver.in
@@ -22,7 +22,7 @@ libsass==0.10.0
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/paver.in
-markupsafe==2.1.5
+markupsafe==3.0.1
# via -r requirements/edx/paver.in
mock==5.1.0
# via -r requirements/edx/paver.in
@@ -61,7 +61,7 @@ urllib3==1.26.20
# via
# -c requirements/edx/../constraints.txt
# requests
-watchdog==5.0.2
+watchdog==5.0.3
# via -r requirements/edx/paver.in
wrapt==1.16.0
# via -r requirements/edx/paver.in
diff --git a/requirements/edx/semgrep.txt b/requirements/edx/semgrep.txt
index 102289def2..174fa87d08 100644
--- a/requirements/edx/semgrep.txt
+++ b/requirements/edx/semgrep.txt
@@ -15,7 +15,7 @@ boltons==21.0.0
# face
# glom
# semgrep
-bracex==2.5
+bracex==2.5.post1
# via wcmatch
certifi==2024.8.30
# via requests
@@ -42,7 +42,7 @@ idna==3.10
# via requests
jsonschema==4.23.0
# via semgrep
-jsonschema-specifications==2023.12.1
+jsonschema-specifications==2024.10.1
# via jsonschema
markdown-it-py==3.0.0
# via rich
@@ -60,7 +60,7 @@ referencing==0.35.1
# jsonschema-specifications
requests==2.32.3
# via semgrep
-rich==13.8.1
+rich==13.9.2
# via semgrep
rpds-py==0.20.0
# via
@@ -72,7 +72,7 @@ ruamel-yaml-clib==0.2.8
# via ruamel-yaml
semgrep==1.52.0
# via -r requirements/edx/semgrep.in
-tomli==2.0.1
+tomli==2.0.2
# via semgrep
typing-extensions==4.12.2
# via semgrep
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index d3b14a6f43..473da12534 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -6,13 +6,13 @@
#
-e git+https://github.com/anupdhabarde/edx-proctoring-proctortrack.git@31c6c9923a51c903ae83760ecbbac191363aa2a2#egg=edx_proctoring_proctortrack
# via -r requirements/edx/base.txt
-acid-xblock==0.3.1
+acid-xblock==0.4.1
# via -r requirements/edx/base.txt
-aiohappyeyeballs==2.4.0
+aiohappyeyeballs==2.4.3
# via
# -r requirements/edx/base.txt
# aiohttp
-aiohttp==3.10.6
+aiohttp==3.10.9
# via
# -r requirements/edx/base.txt
# geoip2
@@ -102,13 +102,13 @@ bleach[css]==6.1.0
# xblock-poll
boto==2.49.0
# via -r requirements/edx/base.txt
-boto3==1.35.27
+boto3==1.35.37
# via
# -r requirements/edx/base.txt
# django-ses
# fs-s3fs
# ora2
-botocore==1.35.27
+botocore==1.35.37
# via
# -r requirements/edx/base.txt
# boto3
@@ -124,7 +124,7 @@ cachetools==5.5.0
# -r requirements/edx/base.txt
# google-auth
# tox
-camel-converter[pydantic]==3.1.2
+camel-converter[pydantic]==4.0.1
# via
# -r requirements/edx/base.txt
# meilisearch
@@ -209,7 +209,7 @@ codejail-includes==1.0.0
# via -r requirements/edx/base.txt
colorama==0.4.6
# via tox
-coverage[toml]==7.6.1
+coverage[toml]==7.6.2
# via
# -r requirements/edx/coverage.txt
# pytest-cov
@@ -247,9 +247,9 @@ defusedxml==0.7.1
# social-auth-core
diff-cover==9.2.0
# via -r requirements/edx/coverage.txt
-dill==0.3.8
+dill==0.3.9
# via pylint
-distlib==0.3.8
+distlib==0.3.9
# via virtualenv
django==4.2.16
# via
@@ -427,7 +427,7 @@ django-sekizai==4.1.0
# via
# -r requirements/edx/base.txt
# openedx-django-wiki
-django-ses==4.1.1
+django-ses==4.2.0
# via -r requirements/edx/base.txt
django-simple-history==3.4.0
# via
@@ -488,7 +488,7 @@ djangorestframework-xml==2.0.0
# via
# -r requirements/edx/base.txt
# edx-enterprise
-dnspython==2.6.1
+dnspython==2.7.0
# via
# -r requirements/edx/base.txt
# pymongo
@@ -533,7 +533,7 @@ edx-celeryutils==1.3.0
# super-csv
edx-codejail==3.4.1
# via -r requirements/edx/base.txt
-edx-completion==4.7.1
+edx-completion==4.7.2
# via -r requirements/edx/base.txt
edx-django-release-util==1.4.0
# via
@@ -542,7 +542,7 @@ edx-django-release-util==1.4.0
# edxval
edx-django-sites-extensions==4.2.0
# via -r requirements/edx/base.txt
-edx-django-utils==5.16.0
+edx-django-utils==6.0.0
# via
# -r requirements/edx/base.txt
# django-config-models
@@ -579,9 +579,8 @@ edx-event-bus-kafka==5.8.1
# via -r requirements/edx/base.txt
edx-event-bus-redis==0.5.0
# via -r requirements/edx/base.txt
-edx-i18n-tools==1.5.0
+edx-i18n-tools==1.6.3
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# ora2
edx-lint==5.4.0
@@ -624,7 +623,7 @@ edx-search==4.0.0
# via -r requirements/edx/base.txt
edx-sga==0.25.0
# via -r requirements/edx/base.txt
-edx-submissions==3.8.0
+edx-submissions==3.8.1
# via
# -r requirements/edx/base.txt
# ora2
@@ -674,7 +673,7 @@ execnet==2.1.1
# via pytest-xdist
factory-boy==3.3.1
# via -r requirements/edx/testing.in
-faker==30.0.0
+faker==30.3.0
# via factory-boy
fastapi==0.115.0
# via pact-python
@@ -717,7 +716,7 @@ geoip2==4.8.0
# via -r requirements/edx/base.txt
glob2==0.7
# via -r requirements/edx/base.txt
-google-api-core[grpc]==2.20.0
+google-api-core[grpc]==2.21.0
# via
# -r requirements/edx/base.txt
# firebase-admin
@@ -725,7 +724,7 @@ google-api-core[grpc]==2.20.0
# google-cloud-core
# google-cloud-firestore
# google-cloud-storage
-google-api-python-client==2.147.0
+google-api-python-client==2.149.0
# via
# -r requirements/edx/base.txt
# firebase-admin
@@ -769,14 +768,14 @@ googleapis-common-protos==1.65.0
# -r requirements/edx/base.txt
# google-api-core
# grpcio-status
-grimp==3.4.1
+grimp==3.5
# via import-linter
-grpcio==1.66.1
+grpcio==1.66.2
# via
# -r requirements/edx/base.txt
# google-api-core
# grpcio-status
-grpcio-status==1.66.1
+grpcio-status==1.66.2
# via
# -r requirements/edx/base.txt
# google-api-core
@@ -797,7 +796,7 @@ httplib2==0.22.0
# google-auth-httplib2
httpretty==1.1.4
# via -r requirements/edx/testing.in
-icalendar==5.0.13
+icalendar==6.0.0
# via -r requirements/edx/base.txt
idna==3.10
# via
@@ -807,7 +806,7 @@ idna==3.10
# requests
# snowflake-connector-python
# yarl
-import-linter==2.0
+import-linter==2.1
# via -r requirements/edx/testing.in
importlib-metadata==8.5.0
# via -r requirements/edx/base.txt
@@ -824,7 +823,7 @@ interchange==2021.0.4
# py2neo
ipaddress==1.0.23
# via -r requirements/edx/base.txt
-isodate==0.6.1
+isodate==0.7.2
# via
# -r requirements/edx/base.txt
# python3-saml
@@ -865,7 +864,7 @@ jsonschema==4.23.0
# -r requirements/edx/base.txt
# drf-spectacular
# optimizely-sdk
-jsonschema-specifications==2023.12.1
+jsonschema-specifications==2024.10.1
# via
# -r requirements/edx/base.txt
# jsonschema
@@ -899,13 +898,13 @@ loremipsum==1.0.5
# ora2
lti-consumer-xblock==9.11.3
# via -r requirements/edx/base.txt
-lxml==4.9.4
+lxml[html-clean]==5.3.0
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# edx-i18n-tools
# edxval
# lti-consumer-xblock
+ # lxml-html-clean
# olxcleaner
# openedx-calc
# ora2
@@ -913,6 +912,10 @@ lxml==4.9.4
# python3-saml
# xblock
# xmlsec
+lxml-html-clean==0.3.1
+ # via
+ # -r requirements/edx/base.txt
+ # lxml
mailsnake==1.6.4
# via -r requirements/edx/base.txt
mako==1.3.5
@@ -929,7 +932,7 @@ markdown==3.3.7
# openedx-django-wiki
# staff-graded-xblock
# xblock-poll
-markupsafe==2.1.5
+markupsafe==3.0.1
# via
# -r requirements/edx/base.txt
# -r requirements/edx/coverage.txt
@@ -974,7 +977,7 @@ multidict==6.1.0
# yarl
mysqlclient==2.2.4
# via -r requirements/edx/base.txt
-newrelic==9.13.0
+newrelic==10.0.0
# via
# -r requirements/edx/base.txt
# edx-django-utils
@@ -1030,7 +1033,7 @@ openedx-events==9.14.1
# edx-name-affirmation
# event-tracking
# ora2
-openedx-filters==1.10.0
+openedx-filters==1.11.0
# via
# -r requirements/edx/base.txt
# lti-consumer-xblock
@@ -1039,7 +1042,7 @@ openedx-learning==0.13.1
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
-openedx-mongodbproxy==0.2.1
+openedx-mongodbproxy==0.2.2
# via -r requirements/edx/base.txt
optimizely-sdk==4.1.1
# via
@@ -1057,7 +1060,7 @@ packaging==24.1
# pytest
# snowflake-connector-python
# tox
-pact-python==2.2.1
+pact-python==2.2.2
# via -r requirements/edx/testing.in
pansi==2020.7.3
# via
@@ -1119,6 +1122,10 @@ prompt-toolkit==3.0.48
# via
# -r requirements/edx/base.txt
# click-repl
+propcache==0.2.0
+ # via
+ # -r requirements/edx/base.txt
+ # yarl
proto-plus==1.24.0
# via
# -r requirements/edx/base.txt
@@ -1164,7 +1171,7 @@ pycparser==2.22
# via
# -r requirements/edx/base.txt
# cffi
-pycryptodomex==3.20.0
+pycryptodomex==3.21.0
# via
# -r requirements/edx/base.txt
# edx-proctoring
@@ -1340,7 +1347,6 @@ pytz==2024.2
# edx-tincan-py35
# event-tracking
# fs
- # icalendar
# interchange
# olxcleaner
# ora2
@@ -1362,7 +1368,7 @@ random2==1.0.2
# via -r requirements/edx/base.txt
recommender-xblock==2.2.1
# via -r requirements/edx/base.txt
-redis==5.0.8
+redis==5.1.1
# via
# -r requirements/edx/base.txt
# walrus
@@ -1422,7 +1428,7 @@ rules==3.5
# edx-enterprise
# edx-proctoring
# openedx-learning
-s3transfer==0.10.2
+s3transfer==0.10.3
# via
# -r requirements/edx/base.txt
# boto3
@@ -1470,7 +1476,6 @@ six==1.16.0
# fs-s3fs
# html5lib
# interchange
- # isodate
# libsass
# optimizely-sdk
# pact-python
@@ -1554,7 +1559,7 @@ tomlkit==0.13.2
# -r requirements/edx/base.txt
# pylint
# snowflake-connector-python
-tox==4.20.0
+tox==4.21.2
# via -r requirements/edx/testing.in
tqdm==4.66.5
# via
@@ -1566,6 +1571,7 @@ typing-extensions==4.12.2
# -r requirements/edx/base.txt
# django-countries
# edx-opaque-keys
+ # faker
# fastapi
# grimp
# import-linter
@@ -1578,6 +1584,7 @@ tzdata==2024.2
# via
# -r requirements/edx/base.txt
# celery
+ # icalendar
# kombu
unicodecsv==0.14.1
# via
@@ -1601,7 +1608,7 @@ urllib3==1.26.20
# requests
user-util==1.1.0
# via -r requirements/edx/base.txt
-uvicorn==0.30.6
+uvicorn==0.31.1
# via pact-python
vine==5.1.0
# via
@@ -1609,7 +1616,7 @@ vine==5.1.0
# amqp
# celery
# kombu
-virtualenv==20.26.5
+virtualenv==20.26.6
# via tox
voluptuous==0.15.2
# via
@@ -1619,7 +1626,7 @@ walrus==0.9.4
# via
# -r requirements/edx/base.txt
# edx-event-bus-redis
-watchdog==5.0.2
+watchdog==5.0.3
# via -r requirements/edx/base.txt
wcwidth==0.2.13
# via
@@ -1673,14 +1680,13 @@ xblock-utils==4.0.0
# -r requirements/edx/base.txt
# edx-sga
# xblock-poll
-xmlsec==1.3.13
+xmlsec==1.3.14
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# python3-saml
xss-utils==0.6.0
# via -r requirements/edx/base.txt
-yarl==1.12.1
+yarl==1.14.0
# via
# -r requirements/edx/base.txt
# aiohttp
diff --git a/requirements/pip-tools.txt b/requirements/pip-tools.txt
index 5bcb2aa550..110663ff6a 100644
--- a/requirements/pip-tools.txt
+++ b/requirements/pip-tools.txt
@@ -4,7 +4,7 @@
#
# make upgrade
#
-build==1.2.2
+build==1.2.2.post1
# via pip-tools
click==8.1.6
# via
@@ -14,7 +14,7 @@ packaging==24.1
# via build
pip-tools==7.4.1
# via -r requirements/pip-tools.in
-pyproject-hooks==1.1.0
+pyproject-hooks==1.2.0
# via
# build
# pip-tools
diff --git a/scripts/structures_pruning/requirements/base.txt b/scripts/structures_pruning/requirements/base.txt
index b80c660b87..a3fcacad2f 100644
--- a/scripts/structures_pruning/requirements/base.txt
+++ b/scripts/structures_pruning/requirements/base.txt
@@ -11,7 +11,7 @@ click==8.1.6
# click-log
click-log==0.4.0
# via -r scripts/structures_pruning/requirements/base.in
-dnspython==2.6.1
+dnspython==2.7.0
# via pymongo
edx-opaque-keys==2.11.0
# via -r scripts/structures_pruning/requirements/base.in
diff --git a/scripts/structures_pruning/requirements/testing.txt b/scripts/structures_pruning/requirements/testing.txt
index 8be2e15973..94c6ac6982 100644
--- a/scripts/structures_pruning/requirements/testing.txt
+++ b/scripts/structures_pruning/requirements/testing.txt
@@ -12,7 +12,7 @@ click-log==0.4.0
# via -r scripts/structures_pruning/requirements/base.txt
ddt==1.7.2
# via -r scripts/structures_pruning/requirements/testing.in
-dnspython==2.6.1
+dnspython==2.7.0
# via
# -r scripts/structures_pruning/requirements/base.txt
# pymongo
diff --git a/scripts/user_retirement/requirements/base.txt b/scripts/user_retirement/requirements/base.txt
index 9f57da73d0..576fa342c3 100644
--- a/scripts/user_retirement/requirements/base.txt
+++ b/scripts/user_retirement/requirements/base.txt
@@ -10,9 +10,9 @@ attrs==24.2.0
# via zeep
backoff==2.2.1
# via -r scripts/user_retirement/requirements/base.in
-boto3==1.35.27
+boto3==1.35.37
# via -r scripts/user_retirement/requirements/base.in
-botocore==1.35.27
+botocore==1.35.37
# via
# boto3
# s3transfer
@@ -46,13 +46,13 @@ django-crum==0.7.9
# via edx-django-utils
django-waffle==4.1.0
# via edx-django-utils
-edx-django-utils==5.16.0
+edx-django-utils==6.0.0
# via edx-rest-api-client
edx-rest-api-client==6.0.0
# via -r scripts/user_retirement/requirements/base.in
-google-api-core==2.20.0
+google-api-core==2.21.0
# via google-api-python-client
-google-api-python-client==2.147.0
+google-api-python-client==2.149.0
# via -r scripts/user_retirement/requirements/base.in
google-auth==2.35.0
# via
@@ -69,7 +69,7 @@ httplib2==0.22.0
# google-auth-httplib2
idna==3.10
# via requests
-isodate==0.6.1
+isodate==0.7.2
# via zeep
jenkinsapi==0.3.13
# via -r scripts/user_retirement/requirements/base.in
@@ -77,13 +77,11 @@ jmespath==1.0.1
# via
# boto3
# botocore
-lxml==4.9.4
- # via
- # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
- # zeep
+lxml==5.3.0
+ # via zeep
more-itertools==10.5.0
# via simple-salesforce
-newrelic==9.13.0
+newrelic==10.0.0
# via edx-django-utils
pbr==6.1.0
# via stevedore
@@ -138,7 +136,7 @@ requests-toolbelt==1.0.0
# via zeep
rsa==4.9
# via google-auth
-s3transfer==0.10.2
+s3transfer==0.10.3
# via boto3
simple-salesforce==1.12.6
# via -r scripts/user_retirement/requirements/base.in
@@ -146,7 +144,6 @@ simplejson==3.19.3
# via -r scripts/user_retirement/requirements/base.in
six==1.16.0
# via
- # isodate
# jenkinsapi
# python-dateutil
sqlparse==0.5.1
diff --git a/scripts/user_retirement/requirements/testing.txt b/scripts/user_retirement/requirements/testing.txt
index d5aac1cd06..6a165d7356 100644
--- a/scripts/user_retirement/requirements/testing.txt
+++ b/scripts/user_retirement/requirements/testing.txt
@@ -14,11 +14,11 @@ attrs==24.2.0
# zeep
backoff==2.2.1
# via -r scripts/user_retirement/requirements/base.txt
-boto3==1.35.27
+boto3==1.35.37
# via
# -r scripts/user_retirement/requirements/base.txt
# moto
-botocore==1.35.27
+botocore==1.35.37
# via
# -r scripts/user_retirement/requirements/base.txt
# boto3
@@ -66,17 +66,17 @@ django-waffle==4.1.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
-edx-django-utils==5.16.0
+edx-django-utils==6.0.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-rest-api-client
edx-rest-api-client==6.0.0
# via -r scripts/user_retirement/requirements/base.txt
-google-api-core==2.20.0
+google-api-core==2.21.0
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-python-client
-google-api-python-client==2.147.0
+google-api-python-client==2.149.0
# via -r scripts/user_retirement/requirements/base.txt
google-auth==2.35.0
# via
@@ -103,7 +103,7 @@ idna==3.10
# requests
iniconfig==2.0.0
# via pytest
-isodate==0.6.1
+isodate==0.7.2
# via
# -r scripts/user_retirement/requirements/base.txt
# zeep
@@ -116,11 +116,11 @@ jmespath==1.0.1
# -r scripts/user_retirement/requirements/base.txt
# boto3
# botocore
-lxml==4.9.4
+lxml==5.3.0
# via
# -r scripts/user_retirement/requirements/base.txt
# zeep
-markupsafe==2.1.5
+markupsafe==3.0.1
# via
# jinja2
# werkzeug
@@ -132,7 +132,7 @@ more-itertools==10.5.0
# simple-salesforce
moto==4.2.14
# via -r scripts/user_retirement/requirements/testing.in
-newrelic==9.13.0
+newrelic==10.0.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
@@ -235,7 +235,7 @@ rsa==4.9
# via
# -r scripts/user_retirement/requirements/base.txt
# google-auth
-s3transfer==0.10.2
+s3transfer==0.10.3
# via
# -r scripts/user_retirement/requirements/base.txt
# boto3
@@ -246,7 +246,6 @@ simplejson==3.19.3
six==1.16.0
# via
# -r scripts/user_retirement/requirements/base.txt
- # isodate
# jenkinsapi
# python-dateutil
sqlparse==0.5.1
@@ -275,7 +274,7 @@ urllib3==1.26.20
# responses
werkzeug==3.0.4
# via moto
-xmltodict==0.13.0
+xmltodict==0.14.1
# via moto
zeep==4.2.1
# via
From a39367b11a4880e124c1cd384b5c486484519414 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 10 Oct 2024 19:41:46 +0000
Subject: [PATCH 09/32] fix(deps): update dependency
@edx/frontend-component-cookie-policy-banner to v2.6.0
---
package-lock.json | 1094 ++++++++++++++++++++++++++++++++++++++++++---
package.json | 2 +-
2 files changed, 1024 insertions(+), 72 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 82a6611d3a..063a004ed8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,7 +17,7 @@
"@edx/brand-edx.org": "^2.0.7",
"@edx/edx-bootstrap": "1.0.4",
"@edx/edx-proctoring": "^4.18.1",
- "@edx/frontend-component-cookie-policy-banner": "2.2.0",
+ "@edx/frontend-component-cookie-policy-banner": "2.6.0",
"@edx/paragon": "2.6.4",
"@edx/studio-frontend": "^2.1.0",
"babel-loader": "^9.1.3",
@@ -1883,6 +1883,25 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/runtime-corejs3": {
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.25.7.tgz",
+ "integrity": "sha512-gMmIEhg35sXk9Te5qbGp3W9YKrvLt3HV658/d3odWrHSqT0JeG5OzsJWFHRLiOohRyjRsJc/x03DhJm3i8VJxg==",
+ "license": "MIT",
+ "dependencies": {
+ "core-js-pure": "^3.30.2",
+ "regenerator-runtime": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/runtime-corejs3/node_modules/regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
+ "license": "MIT"
+ },
"node_modules/@babel/runtime/node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
@@ -2106,14 +2125,16 @@
}
},
"node_modules/@edx/frontend-component-cookie-policy-banner": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@edx/frontend-component-cookie-policy-banner/-/frontend-component-cookie-policy-banner-2.2.0.tgz",
- "integrity": "sha512-Ye5tL8dU1v77Vtva9QkrgNG0I50sR317OK51/aQwHn50Aoq45FML29dbP8E5dy36QSqvF99bNFA4rptLigiTGQ==",
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@edx/frontend-component-cookie-policy-banner/-/frontend-component-cookie-policy-banner-2.6.0.tgz",
+ "integrity": "sha512-Em/7v41BtVJsHWyaxGOgefif3YOe2Bw8rDZO6Rci40Mcx035Q7L0IlyG3Sxz+GKQIoSOJDYf+EfODS5i01AJOg==",
+ "license": "AGPL-3.0",
"dependencies": {
"@edx/brand-edx.org": "2.0.3",
- "@edx/paragon": "^12.0.5",
+ "@openedx/paragon": "^21.13.1",
"babel-preset-minify": "^0.5.0",
"classnames": "^2.3.1",
+ "identity-obj-proxy": "^3.0.0",
"prop-types": "^15.6.1",
"type-fest": "^2.14.0",
"universal-cookie": "^4.0.0"
@@ -2126,36 +2147,83 @@
"node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/@edx/brand-edx.org": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@edx/brand-edx.org/-/brand-edx.org-2.0.3.tgz",
- "integrity": "sha512-QRmq2su1Xy+9GhY3NRZ+WdjtYWHmgfuKbVCW2skxgfgW9Q6kea8L6LrgigfrZtW+kQq/KdX2tVJcYBkB9xALtQ=="
+ "integrity": "sha512-QRmq2su1Xy+9GhY3NRZ+WdjtYWHmgfuKbVCW2skxgfgW9Q6kea8L6LrgigfrZtW+kQq/KdX2tVJcYBkB9xALtQ==",
+ "license": "UNLICENSED"
},
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/@edx/paragon": {
- "version": "12.8.0",
- "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-12.8.0.tgz",
- "integrity": "sha512-gJVnozu4V1e2PCI0lFICvnrs2yi0ZzcaO5gJB9OjB1Pzf5GXpkKNWLtdTwjgp4fvzL9w4sqvyB30apbQzWW8yw==",
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/@openedx/paragon": {
+ "version": "21.13.1",
+ "resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-21.13.1.tgz",
+ "integrity": "sha512-sLL+Z3ZWIRM6x+OrKZV0S7/SQpEcSeRcDm7E3FzhsnAWudsJCTELvSW+84uy/8dwV7mJhttsBPqQEtNafbCyYA==",
+ "license": "Apache-2.0",
+ "workspaces": [
+ "example",
+ "component-generator",
+ "www",
+ "icons",
+ "dependent-usage-analyzer"
+ ],
"dependencies": {
- "@fortawesome/fontawesome-svg-core": "^1.2.30",
- "@fortawesome/free-solid-svg-icons": "^5.14.0",
- "@fortawesome/react-fontawesome": "^0.1.11",
- "airbnb-prop-types": "^2.12.0",
- "bootstrap": "^4.4.1",
- "classnames": "^2.2.6",
+ "@fortawesome/fontawesome-svg-core": "^6.1.1",
+ "@fortawesome/react-fontawesome": "^0.1.18",
+ "@popperjs/core": "^2.11.4",
+ "bootstrap": "^4.6.2",
+ "chalk": "^4.1.2",
+ "child_process": "^1.0.2",
+ "classnames": "^2.3.1",
"email-prop-type": "^3.0.0",
+ "file-selector": "^0.6.0",
"font-awesome": "^4.7.0",
- "mailto-link": "^1.0.0",
- "prop-types": "^15.7.2",
- "react-bootstrap": "^1.2.2",
- "react-focus-on": "^3.5.0",
+ "glob": "^8.0.3",
+ "inquirer": "^8.2.5",
+ "lodash.uniqby": "^4.7.0",
+ "mailto-link": "^2.0.0",
+ "prop-types": "^15.8.1",
+ "react-bootstrap": "^1.6.5",
+ "react-colorful": "^5.6.1",
+ "react-dropzone": "^14.2.1",
+ "react-focus-on": "^3.5.4",
+ "react-imask": "^7.1.3",
+ "react-loading-skeleton": "^3.1.0",
+ "react-popper": "^2.2.5",
"react-proptype-conditional-require": "^1.0.4",
- "react-responsive": "^6.1.1",
- "react-table": "^7.6.1",
- "react-transition-group": "^4.0.0",
- "sanitize-html": "^1.20.0",
- "tabbable": "^4.0.0"
+ "react-responsive": "^8.2.0",
+ "react-table": "^7.7.0",
+ "react-transition-group": "^4.4.2",
+ "tabbable": "^5.3.3",
+ "uncontrollable": "^7.2.1",
+ "uuid": "^9.0.0"
+ },
+ "bin": {
+ "paragon": "bin/paragon-scripts.js"
},
"peerDependencies": {
- "prop-types": "^15.7.2",
- "react": "^16.8.6",
- "react-dom": "^16.8.6"
+ "react": "^16.8.6 || ^17.0.0",
+ "react-dom": "^16.8.6 || ^17.0.0",
+ "react-intl": "^5.25.1 || ^6.4.0"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/attr-accept": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.4.tgz",
+ "integrity": "sha512-2pA6xFIbdTUDCAwjN8nQwI+842VwzbDUXO2IYlpPXQIORgKnavorcr4Ce3rwh+zsNg9zK7QPsdvDj3Lum4WX4w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
}
},
"node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/bootstrap": {
@@ -2172,15 +2240,81 @@
"url": "https://opencollective.com/bootstrap"
}
],
+ "license": "MIT",
"peerDependencies": {
"jquery": "1.9.1 - 3",
"popper.js": "^1.16.1"
}
},
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "license": "MIT",
+ "dependencies": {
+ "restore-cursor": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/cli-width": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
+ "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT"
+ },
"node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
@@ -2190,24 +2324,206 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/email-prop-type/-/email-prop-type-3.0.1.tgz",
"integrity": "sha512-tONZGMEOOkadp5OBftuVXU8DsceWmINxYK+pqPFB4LT5ODjrPX/esel3WGqbV7d6in5/MnZE4n4QcqOr4gh7dg==",
+ "license": "MIT",
"dependencies": {
"email-validator": "^2.0.4"
}
},
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/figures": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+ "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+ "license": "MIT",
+ "dependencies": {
+ "escape-string-regexp": "^1.0.5"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/glob": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/inquirer": {
+ "version": "8.2.6",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz",
+ "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.1.1",
+ "cli-cursor": "^3.1.0",
+ "cli-width": "^3.0.0",
+ "external-editor": "^3.0.3",
+ "figures": "^3.0.0",
+ "lodash": "^4.17.21",
+ "mute-stream": "0.0.8",
+ "ora": "^5.4.1",
+ "run-async": "^2.4.0",
+ "rxjs": "^7.5.5",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "through": "^2.3.6",
+ "wrap-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/intl-messageformat": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.6.0.tgz",
+ "integrity": "sha512-AYKl/DY1nl75pJU8EK681JOVL40uQTNJe3yEMXKfydDFoz+5hNrM/PqjchueSMKGKCZKBVgeexqZwy3uC2B36Q==",
+ "license": "BSD-3-Clause",
+ "peer": true,
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "2.1.0",
+ "@formatjs/fast-memoize": "2.2.0",
+ "@formatjs/icu-messageformat-parser": "2.7.9",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/mailto-link": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mailto-link/-/mailto-link-2.0.0.tgz",
+ "integrity": "sha512-b5FErkZ4t6mpH1IFZSw7Mm2IQHXQ2R0/5Q4xd7Rv8dVkWvE54mFG/UW7HjfFazXFjXTNsM+dSX2tTeIDrV9K9A==",
+ "license": "MIT",
+ "dependencies": {
+ "assert-ok": "~1.0.0",
+ "cast-array": "~1.0.1",
+ "object-filter": "~1.0.2",
+ "query-string": "~7.0.0"
+ },
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/mute-stream": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
+ "license": "ISC"
+ },
"node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/query-string": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.0.1.tgz",
+ "integrity": "sha512-uIw3iRvHnk9to1blJCG3BTc+Ro56CBowJXKmNNAm3RulvPBzWLRqKSiiDk+IplJhsydwtuNMHi8UGQFcCLVfkA==",
+ "license": "MIT",
+ "dependencies": {
+ "decode-uri-component": "^0.2.0",
+ "filter-obj": "^1.1.0",
+ "split-on-first": "^1.0.0",
+ "strict-uri-encode": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/react-dropzone": {
+ "version": "14.2.9",
+ "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.9.tgz",
+ "integrity": "sha512-jRZsMC7h48WONsOLHcmhyn3cRWJoIPQjPApvt/sJVfnYaB3Qltn025AoRTTJaj4WdmmgmLl6tUQg1s0wOhpodQ==",
+ "license": "MIT",
+ "dependencies": {
+ "attr-accept": "^2.2.2",
+ "file-selector": "^0.6.0",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">= 10.13"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8 || 18.0.0"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/react-intl": {
+ "version": "6.7.2",
+ "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.7.2.tgz",
+ "integrity": "sha512-v/lvAORTE70welhzqoIi1YI1yHvGE4/QX4W3JYNZoqRxH8ab8Q/Ed4Zem/ZVPZJN4byQ52U+2GESLy0zvY6IBw==",
+ "license": "BSD-3-Clause",
+ "peer": true,
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "2.1.0",
+ "@formatjs/icu-messageformat-parser": "2.7.9",
+ "@formatjs/intl": "2.10.7",
+ "@formatjs/intl-displaynames": "6.6.9",
+ "@formatjs/intl-listformat": "7.5.8",
+ "@types/hoist-non-react-statics": "^3.3.1",
+ "@types/react": "16 || 17 || 18",
+ "hoist-non-react-statics": "^3.3.2",
+ "intl-messageformat": "10.6.0",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "react": "^16.6.0 || 17 || 18",
+ "typescript": "^4.7 || 5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "license": "BSD-3-Clause",
"dependencies": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
@@ -2219,6 +2535,62 @@
"react-dom": ">=16.6.0"
}
},
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "license": "MIT",
+ "dependencies": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/run-async": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
+ "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/strict-uri-encode": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
+ "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/@edx/mockprock": {
"version": "1.0.2",
"resolved": "git+ssh://git@github.com/openedx/mockprock.git#3ad18c6888e6521e9bf7a4df0db6f8579b928235",
@@ -3138,35 +3510,139 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
- "node_modules/@fortawesome/fontawesome-common-types": {
- "version": "0.2.36",
- "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
- "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==",
- "hasInstallScript": true,
- "engines": {
- "node": ">=6"
+ "node_modules/@formatjs/ecma402-abstract": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.1.0.tgz",
+ "integrity": "sha512-SE2V2PE03K9U/YQZ3nxEOysRkQ/CfSwLHR789Uk9N0PTiWT6I+17UTDI97zYEwC1mbnjefqmtjbL8nunjPwGjw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@formatjs/fast-memoize": "2.2.0",
+ "@formatjs/intl-localematcher": "0.5.4",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@formatjs/fast-memoize": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz",
+ "integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@formatjs/icu-messageformat-parser": {
+ "version": "2.7.9",
+ "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.9.tgz",
+ "integrity": "sha512-9Z5buDRMsTbplXknvRlDmnpWhZrayNVcVvkH0+SSz8Ll4XD/7Tcn8m1IjxM3iBJSwQbxwxb7/g0Fkx3d4j2osw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "2.1.0",
+ "@formatjs/icu-skeleton-parser": "1.8.3",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@formatjs/icu-skeleton-parser": {
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.3.tgz",
+ "integrity": "sha512-TsKAP013ayZFbWWR2KWy+f9QVZh0yDFTPK3yE4OqU2gnzafvmKTodRtJLVpfZmpXWJ5y7BWD1AsyT14mcbLzig==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "2.1.0",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@formatjs/intl": {
+ "version": "2.10.7",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.10.7.tgz",
+ "integrity": "sha512-26rNxo2nwQbbsVkV54ngml9XIA7bBzfQmELG6FFFF8eKzqzFrLKZzF8JBoBpPHgML4HKEUbGCQaBaARpKCN/sw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "2.1.0",
+ "@formatjs/fast-memoize": "2.2.0",
+ "@formatjs/icu-messageformat-parser": "2.7.9",
+ "@formatjs/intl-displaynames": "6.6.9",
+ "@formatjs/intl-listformat": "7.5.8",
+ "intl-messageformat": "10.6.0",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "typescript": "^4.7 || 5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@formatjs/intl-displaynames": {
+ "version": "6.6.9",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.6.9.tgz",
+ "integrity": "sha512-2hmS+YJwiXB1deNYXO2/pY7Zv4QUrZHghZxkcnWxBLEODk990h9cNbkjNg/u/RaDeCRkKVrZ3ERTdBcgkTvn2Q==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "2.1.0",
+ "@formatjs/intl-localematcher": "0.5.4",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@formatjs/intl-listformat": {
+ "version": "7.5.8",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.5.8.tgz",
+ "integrity": "sha512-WzMiw6nA2uP0ZqbbuPs7tQ+gmYRTbE20lwur4QcKp5K5cgPhkCzRAhovkDFLhrc885c3p7Wjigx8kyg0hypmZw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "2.1.0",
+ "@formatjs/intl-localematcher": "0.5.4",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@formatjs/intl-localematcher": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz",
+ "integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@formatjs/intl/node_modules/intl-messageformat": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.6.0.tgz",
+ "integrity": "sha512-AYKl/DY1nl75pJU8EK681JOVL40uQTNJe3yEMXKfydDFoz+5hNrM/PqjchueSMKGKCZKBVgeexqZwy3uC2B36Q==",
+ "license": "BSD-3-Clause",
+ "peer": true,
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "2.1.0",
+ "@formatjs/fast-memoize": "2.2.0",
+ "@formatjs/icu-messageformat-parser": "2.7.9",
+ "tslib": "^2.4.0"
}
},
"node_modules/@fortawesome/fontawesome-svg-core": {
- "version": "1.2.36",
- "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz",
- "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==",
- "hasInstallScript": true,
+ "version": "6.6.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.6.0.tgz",
+ "integrity": "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==",
+ "license": "MIT",
"dependencies": {
- "@fortawesome/fontawesome-common-types": "^0.2.36"
+ "@fortawesome/fontawesome-common-types": "6.6.0"
},
"engines": {
"node": ">=6"
}
},
- "node_modules/@fortawesome/free-solid-svg-icons": {
- "version": "5.15.4",
- "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz",
- "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==",
- "hasInstallScript": true,
- "dependencies": {
- "@fortawesome/fontawesome-common-types": "^0.2.36"
- },
+ "node_modules/@fortawesome/fontawesome-svg-core/node_modules/@fortawesome/fontawesome-common-types": {
+ "version": "6.6.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz",
+ "integrity": "sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
@@ -4237,6 +4713,17 @@
"@types/node": "*"
}
},
+ "node_modules/@types/hoist-non-react-statics": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz",
+ "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/react": "*",
+ "hoist-non-react-statics": "^3.3.0"
+ }
+ },
"node_modules/@types/invariant": {
"version": "2.2.37",
"resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.37.tgz",
@@ -4820,7 +5307,6 @@
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
"integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
- "dev": true,
"dependencies": {
"type-fest": "^0.21.3"
},
@@ -4835,7 +5321,6 @@
"version": "0.21.3",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
- "dev": true,
"engines": {
"node": ">=10"
},
@@ -6124,6 +6609,26 @@
"node": ">= 0.6.0"
}
},
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/base64id": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
@@ -6187,6 +6692,31 @@
"file-uri-to-path": "1.0.0"
}
},
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/bl/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/blob": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz",
@@ -6323,6 +6853,30 @@
"node-int64": "^0.4.0"
}
},
+ "node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
"node_modules/buffer-alloc": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
@@ -6663,6 +7217,12 @@
"node": ">=10"
}
},
+ "node_modules/chardet": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+ "license": "MIT"
+ },
"node_modules/cheerio": {
"version": "1.0.0-rc.12",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
@@ -6701,6 +7261,12 @@
"url": "https://github.com/sponsors/fb55"
}
},
+ "node_modules/child_process": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz",
+ "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g==",
+ "license": "ISC"
+ },
"node_modules/chokidar": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
@@ -7042,6 +7608,18 @@
"node": ">=0.10.0"
}
},
+ "node_modules/cli-spinners": {
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
+ "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/cli-width": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
@@ -7423,6 +8001,17 @@
"url": "https://opencollective.com/core-js"
}
},
+ "node_modules/core-js-pure": {
+ "version": "3.38.1",
+ "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.38.1.tgz",
+ "integrity": "sha512-BY8Etc1FZqdw1glX0XNOq2FDwfrg/VGqoZOZCdaL+UmdaqDwQwYXkMJT4t6In+zfEfOJDcM9T0KdbBeJg8KKCQ==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
@@ -7860,7 +8449,6 @@
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
- "dev": true,
"engines": {
"node": ">=0.10"
}
@@ -7907,6 +8495,18 @@
"node": ">=0.10.0"
}
},
+ "node_modules/defaults": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
+ "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+ "license": "MIT",
+ "dependencies": {
+ "clone": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
@@ -9985,6 +10585,32 @@
"node": ">=0.10.0"
}
},
+ "node_modules/external-editor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+ "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+ "license": "MIT",
+ "dependencies": {
+ "chardet": "^0.7.0",
+ "iconv-lite": "^0.4.24",
+ "tmp": "^0.0.33"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/external-editor/node_modules/tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "license": "MIT",
+ "dependencies": {
+ "os-tmpdir": "~1.0.2"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
"node_modules/extglob": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
@@ -10201,6 +10827,18 @@
"url": "https://opencollective.com/webpack"
}
},
+ "node_modules/file-selector": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz",
+ "integrity": "sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.4.0"
+ },
+ "engines": {
+ "node": ">= 12"
+ }
+ },
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
@@ -10266,6 +10904,15 @@
"node": ">=0.10"
}
},
+ "node_modules/filter-obj": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
+ "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
@@ -11066,6 +11713,12 @@
"node": ">=6"
}
},
+ "node_modules/harmony-reflect": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz",
+ "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==",
+ "license": "(Apache-2.0 OR MPL-1.1)"
+ },
"node_modules/has": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz",
@@ -11491,6 +12144,38 @@
"node": ">=4.0.0"
}
},
+ "node_modules/identity-obj-proxy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz",
+ "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==",
+ "license": "MIT",
+ "dependencies": {
+ "harmony-reflect": "^1.4.6"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
"node_modules/ignore": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
@@ -11500,6 +12185,18 @@
"node": ">= 4"
}
},
+ "node_modules/imask": {
+ "version": "7.6.1",
+ "resolved": "https://registry.npmjs.org/imask/-/imask-7.6.1.tgz",
+ "integrity": "sha512-sJlIFM7eathUEMChTh9Mrfw/IgiWgJqBKq2VNbyXvBZ7ev/IlO6/KQTKlV/Fm+viQMLrFLG/zCuudrLIwgK2dg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime-corejs3": "^7.24.4"
+ },
+ "engines": {
+ "npm": ">=4.0.0"
+ }
+ },
"node_modules/immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
@@ -12146,6 +12843,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-interactive": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-lambda": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
@@ -12398,6 +13104,18 @@
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
"dev": true
},
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/is-utf8": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
@@ -16157,6 +16875,12 @@
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
"integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ=="
},
+ "node_modules/lodash.uniqby": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz",
+ "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==",
+ "license": "MIT"
+ },
"node_modules/log-symbols": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
@@ -16600,7 +17324,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
- "dev": true,
"engines": {
"node": ">=6"
}
@@ -17695,7 +18418,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
- "dev": true,
"dependencies": {
"mimic-fn": "^2.1.0"
},
@@ -17758,6 +18480,140 @@
"node": ">=0.4.0"
}
},
+ "node_modules/ora": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
+ "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+ "license": "MIT",
+ "dependencies": {
+ "bl": "^4.1.0",
+ "chalk": "^4.1.0",
+ "cli-cursor": "^3.1.0",
+ "cli-spinners": "^2.5.0",
+ "is-interactive": "^1.0.0",
+ "is-unicode-supported": "^0.1.0",
+ "log-symbols": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "wcwidth": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/ora/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/ora/node_modules/cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "license": "MIT",
+ "dependencies": {
+ "restore-cursor": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ora/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/ora/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT"
+ },
+ "node_modules/ora/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ora/node_modules/log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "license": "MIT",
+ "dependencies": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ora/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/os-homedir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
@@ -17771,7 +18627,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -19698,6 +20553,16 @@
"react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
+ "node_modules/react-colorful": {
+ "version": "5.6.1",
+ "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz",
+ "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
"node_modules/react-dom": {
"version": "16.14.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz",
@@ -19739,6 +20604,12 @@
"resolved": "https://registry.npmjs.org/react-element-proptypes/-/react-element-proptypes-1.0.0.tgz",
"integrity": "sha512-unJTkc58D9n1xTXKA8swrwrbDQAsCF/13oT6fDYtBVHPvFxITFuI20HCMNbNzI7tTUzsYmJ3iqjskwfLJkOUFA=="
},
+ "node_modules/react-fast-compare": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==",
+ "license": "MIT"
+ },
"node_modules/react-focus-lock": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-1.19.1.tgz",
@@ -19832,6 +20703,33 @@
}
}
},
+ "node_modules/react-imask": {
+ "version": "7.6.1",
+ "resolved": "https://registry.npmjs.org/react-imask/-/react-imask-7.6.1.tgz",
+ "integrity": "sha512-vLNfzcCz62Yzx/GRGh5tiCph9Gbh2cZu+Tz8OiO5it2eNuuhpA0DWhhSlOtVtSJ80+Bx+vFK5De8eQ9AmbkXzA==",
+ "license": "MIT",
+ "dependencies": {
+ "imask": "^7.6.1",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "npm": ">=4.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=0.14.0"
+ }
+ },
+ "node_modules/react-imask/node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
"node_modules/react-intl": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-2.9.0.tgz",
@@ -19869,6 +20767,15 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
+ "node_modules/react-loading-skeleton": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/react-loading-skeleton/-/react-loading-skeleton-3.5.0.tgz",
+ "integrity": "sha512-gxxSyLbrEAdXTKgfbpBEFZCO/P153DnqSCQau2+o6lNy1jgMRr2MmRmOzMmyrwSaSYLRB8g7b0waYPmUjz7IhQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
"node_modules/react-overlays": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-5.2.1.tgz",
@@ -19907,6 +20814,21 @@
"react-is": "^16.13.1"
}
},
+ "node_modules/react-popper": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz",
+ "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "react-fast-compare": "^3.0.1",
+ "warning": "^4.0.2"
+ },
+ "peerDependencies": {
+ "@popperjs/core": "^2.0.0",
+ "react": "^16.8.0 || ^17 || ^18",
+ "react-dom": "^16.8.0 || ^17 || ^18"
+ }
+ },
"node_modules/react-proptype-conditional-require": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/react-proptype-conditional-require/-/react-proptype-conditional-require-1.0.4.tgz",
@@ -19980,19 +20902,21 @@
}
},
"node_modules/react-responsive": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-6.1.2.tgz",
- "integrity": "sha512-AXentVC/kN3KED9zhzJv2pu4vZ0i6cSHdTtbCScVV1MT6F5KXaG2qs5D7WLmhdaOvmiMX8UfmS4ZSO+WPwDt4g==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.2.0.tgz",
+ "integrity": "sha512-iagCqVrw4QSjhxKp3I/YK6+ODkWY6G+YPElvdYKiUUbywwh9Ds0M7r26Fj2/7dWFFbOpcGnJE6uE7aMck8j5Qg==",
+ "license": "MIT",
"dependencies": {
"hyphenate-style-name": "^1.0.0",
"matchmediaquery": "^0.3.0",
- "prop-types": "^15.6.1"
+ "prop-types": "^15.6.1",
+ "shallow-equal": "^1.1.0"
},
"engines": {
"node": ">= 0.10"
},
"peerDependencies": {
- "react": "^16.3.0"
+ "react": ">=16.8.0"
}
},
"node_modules/react-responsive/node_modules/prop-types": {
@@ -21269,6 +22193,15 @@
"integrity": "sha512-1I1+G2gteLB8Tkt8YI1sJvSIfa0lWuRtC8GjvtyPBcLSF5jBCCJJqKrpER5JU5r6Bhe+i9/pK3VMuUcXu0kdwQ==",
"dev": true
},
+ "node_modules/rxjs": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
+ "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
"node_modules/safe-array-concat": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz",
@@ -22276,6 +23209,12 @@
"node": ">=8"
}
},
+ "node_modules/shallow-equal": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz",
+ "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==",
+ "license": "MIT"
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -22334,8 +23273,7 @@
"node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
- "dev": true
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
},
"node_modules/simple-html-tokenizer": {
"version": "0.1.1",
@@ -22837,6 +23775,15 @@
"integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==",
"dev": true
},
+ "node_modules/split-on-first": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
+ "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/split-string": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
@@ -23718,9 +24665,10 @@
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="
},
"node_modules/tabbable": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-4.0.0.tgz",
- "integrity": "sha512-H1XoH1URcBOa/rZZWxLxHCtOdVUEev+9vo5YdYhC9tCY4wnybX+VQrCYuy9ubkg69fCBxCONJOSLGfw0DWMffQ=="
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz",
+ "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==",
+ "license": "MIT"
},
"node_modules/table": {
"version": "6.8.2",
@@ -23985,8 +24933,7 @@
"node_modules/through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
- "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
- "dev": true
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
},
"node_modules/through2": {
"version": "3.0.2",
@@ -25101,6 +26048,15 @@
"node": ">=10.13.0"
}
},
+ "node_modules/wcwidth": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+ "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+ "license": "MIT",
+ "dependencies": {
+ "defaults": "^1.0.3"
+ }
+ },
"node_modules/webidl-conversions": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",
@@ -25527,7 +26483,6 @@
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
- "dev": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@@ -25588,7 +26543,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -25603,7 +26557,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -25614,8 +26567,7 @@
"node_modules/wrap-ansi/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/wrappy": {
"version": "1.0.2",
diff --git a/package.json b/package.json
index 1f48500e6e..6173420ab0 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,7 @@
"@edx/brand-edx.org": "^2.0.7",
"@edx/edx-bootstrap": "1.0.4",
"@edx/edx-proctoring": "^4.18.1",
- "@edx/frontend-component-cookie-policy-banner": "2.2.0",
+ "@edx/frontend-component-cookie-policy-banner": "2.6.0",
"@edx/paragon": "2.6.4",
"@edx/studio-frontend": "^2.1.0",
"babel-loader": "^9.1.3",
From e99769b0dd160ccb4c8364887f7bd59ce119f9ac Mon Sep 17 00:00:00 2001
From: Diana Huang
Date: Thu, 10 Oct 2024 16:21:17 -0400
Subject: [PATCH 10/32] Revert "feat: Unpin xmlsec and lxml."
This reverts commit 6c045c7b390aa051484cee459a767b246f04e7d8.
---
requirements/constraints.txt | 19 +++
requirements/edx-sandbox/base.txt | 5 +-
requirements/edx/base.txt | 74 ++++++------
requirements/edx/coverage.txt | 4 +-
requirements/edx/development.txt | 110 ++++++++----------
requirements/edx/doc.txt | 77 ++++++------
requirements/edx/paver.txt | 6 +-
requirements/edx/semgrep.txt | 8 +-
requirements/edx/testing.txt | 96 +++++++--------
requirements/pip-tools.txt | 4 +-
.../structures_pruning/requirements/base.txt | 2 +-
.../requirements/testing.txt | 2 +-
scripts/user_retirement/requirements/base.txt | 23 ++--
.../user_retirement/requirements/testing.txt | 23 ++--
14 files changed, 229 insertions(+), 224 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 2fc1a1e745..dd727a4b18 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -84,6 +84,13 @@ django-storages<1.14.4
# for them.
edx-enterprise==4.27.2
+# Date: 2024-05-09
+# This has to be constrained as well because newer versions of edx-i18n-tools need the
+# newer version of lxml but that requirement was not made expilict in the 1.6.0 version
+# of the package. This can be un-pinned when we're upgrading lxml.
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35274
+edx-i18n-tools<1.6.0
+
# Date: 2024-07-26
# To override the constraint of edx-lint
# This can be removed once https://github.com/openedx/edx-platform/issues/34586 is resolved
@@ -98,6 +105,13 @@ event-tracking==3.0.0
# https://github.com/openedx/edx-platform/issues/31616
libsass==0.10.0
+# Date: 2024-04-30
+# lxml>=5.0 introduced breaking changes related to system dependencies
+# lxml==5.2.1 introduced new extra so we'll nee to rename lxml --> lxml[html-clean]
+# This constraint can be removed once we upgrade to Python 3.11
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35272
+lxml<5.0
+
# Date: 2018-12-14
# markdown>=3.4.0 has failures due to internal refactorings which causes the tests to fail
# pinning the version untill the issue gets resolved in the package itself
@@ -173,3 +187,8 @@ social-auth-app-django<=5.4.1
# which require urllib3<2 for now.
# Issue for unpinning: https://github.com/openedx/edx-platform/issues/32222
urllib3<2.0.0
+
+# Date: 2024-04-24
+# xmlsec==1.3.14 breaking tests or all builds, can be removed once a fix is available
+# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35264
+xmlsec<1.3.14
diff --git a/requirements/edx-sandbox/base.txt b/requirements/edx-sandbox/base.txt
index 0a708b2bbf..bf0a4376da 100644
--- a/requirements/edx-sandbox/base.txt
+++ b/requirements/edx-sandbox/base.txt
@@ -26,11 +26,12 @@ joblib==1.4.2
# via nltk
kiwisolver==1.4.7
# via matplotlib
-lxml==5.3.0
+lxml==4.9.4
# via
+ # -c requirements/edx-sandbox/../constraints.txt
# -r requirements/edx-sandbox/base.in
# openedx-calc
-markupsafe==3.0.1
+markupsafe==2.1.5
# via
# chem
# openedx-calc
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index c7f7828f41..55793bd0c5 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -6,11 +6,11 @@
#
-e git+https://github.com/anupdhabarde/edx-proctoring-proctortrack.git@31c6c9923a51c903ae83760ecbbac191363aa2a2#egg=edx_proctoring_proctortrack
# via -r requirements/edx/github.in
-acid-xblock==0.4.1
+acid-xblock==0.3.1
# via -r requirements/edx/kernel.in
-aiohappyeyeballs==2.4.3
+aiohappyeyeballs==2.4.0
# via aiohttp
-aiohttp==3.10.9
+aiohttp==3.10.6
# via
# geoip2
# openai
@@ -70,13 +70,13 @@ bleach[css]==6.1.0
# xblock-poll
boto==2.49.0
# via -r requirements/edx/kernel.in
-boto3==1.35.37
+boto3==1.35.27
# via
# -r requirements/edx/kernel.in
# django-ses
# fs-s3fs
# ora2
-botocore==1.35.37
+botocore==1.35.27
# via
# -r requirements/edx/kernel.in
# boto3
@@ -87,7 +87,7 @@ cachecontrol==0.14.0
# via firebase-admin
cachetools==5.5.0
# via google-auth
-camel-converter[pydantic]==4.0.1
+camel-converter[pydantic]==3.1.2
# via meilisearch
celery==5.4.0
# via
@@ -328,7 +328,7 @@ django-sekizai==4.1.0
# via
# -r requirements/edx/kernel.in
# openedx-django-wiki
-django-ses==4.2.0
+django-ses==4.1.1
# via -r requirements/edx/bundled.in
django-simple-history==3.4.0
# via
@@ -387,7 +387,7 @@ djangorestframework==3.14.0
# super-csv
djangorestframework-xml==2.0.0
# via edx-enterprise
-dnspython==2.7.0
+dnspython==2.6.1
# via
# -r requirements/edx/paver.txt
# pymongo
@@ -429,7 +429,7 @@ edx-celeryutils==1.3.0
# super-csv
edx-codejail==3.4.1
# via -r requirements/edx/kernel.in
-edx-completion==4.7.2
+edx-completion==4.7.1
# via -r requirements/edx/kernel.in
edx-django-release-util==1.4.0
# via
@@ -438,7 +438,7 @@ edx-django-release-util==1.4.0
# edxval
edx-django-sites-extensions==4.2.0
# via -r requirements/edx/kernel.in
-edx-django-utils==6.0.0
+edx-django-utils==5.16.0
# via
# -r requirements/edx/kernel.in
# django-config-models
@@ -475,8 +475,9 @@ edx-event-bus-kafka==5.8.1
# via -r requirements/edx/kernel.in
edx-event-bus-redis==0.5.0
# via -r requirements/edx/kernel.in
-edx-i18n-tools==1.6.3
+edx-i18n-tools==1.5.0
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/bundled.in
# ora2
edx-milestones==0.6.0
@@ -516,7 +517,7 @@ edx-search==4.0.0
# via -r requirements/edx/kernel.in
edx-sga==0.25.0
# via -r requirements/edx/bundled.in
-edx-submissions==3.8.1
+edx-submissions==3.8.0
# via
# -r requirements/edx/kernel.in
# ora2
@@ -583,14 +584,14 @@ geoip2==4.8.0
# via -r requirements/edx/kernel.in
glob2==0.7
# via -r requirements/edx/kernel.in
-google-api-core[grpc]==2.21.0
+google-api-core[grpc]==2.20.0
# via
# firebase-admin
# google-api-python-client
# google-cloud-core
# google-cloud-firestore
# google-cloud-storage
-google-api-python-client==2.149.0
+google-api-python-client==2.147.0
# via firebase-admin
google-auth==2.35.0
# via
@@ -620,11 +621,11 @@ googleapis-common-protos==1.65.0
# via
# google-api-core
# grpcio-status
-grpcio==1.66.2
+grpcio==1.66.1
# via
# google-api-core
# grpcio-status
-grpcio-status==1.66.2
+grpcio-status==1.66.1
# via google-api-core
gunicorn==23.0.0
# via -r requirements/edx/kernel.in
@@ -638,7 +639,7 @@ httplib2==0.22.0
# via
# google-api-python-client
# google-auth-httplib2
-icalendar==6.0.0
+icalendar==5.0.13
# via -r requirements/edx/kernel.in
idna==3.10
# via
@@ -657,7 +658,7 @@ interchange==2021.0.4
# via py2neo
ipaddress==1.0.23
# via -r requirements/edx/kernel.in
-isodate==0.7.2
+isodate==0.6.1
# via python3-saml
jinja2==3.1.4
# via code-annotations
@@ -682,7 +683,7 @@ jsonschema==4.23.0
# via
# drf-spectacular
# optimizely-sdk
-jsonschema-specifications==2024.10.1
+jsonschema-specifications==2023.12.1
# via jsonschema
jwcrypto==1.5.6
# via
@@ -707,21 +708,19 @@ loremipsum==1.0.5
# via ora2
lti-consumer-xblock==9.11.3
# via -r requirements/edx/kernel.in
-lxml[html-clean]==5.3.0
+lxml==4.9.4
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
# edx-i18n-tools
# edxval
# lti-consumer-xblock
- # lxml-html-clean
# olxcleaner
# openedx-calc
# ora2
# python3-saml
# xblock
# xmlsec
-lxml-html-clean==0.3.1
- # via lxml
mailsnake==1.6.4
# via -r requirements/edx/bundled.in
mako==1.3.5
@@ -738,7 +737,7 @@ markdown==3.3.7
# openedx-django-wiki
# staff-graded-xblock
# xblock-poll
-markupsafe==3.0.1
+markupsafe==2.1.5
# via
# -r requirements/edx/paver.txt
# chem
@@ -770,7 +769,7 @@ multidict==6.1.0
# yarl
mysqlclient==2.2.4
# via -r requirements/edx/kernel.in
-newrelic==10.0.0
+newrelic==9.13.0
# via
# -r requirements/edx/bundled.in
# edx-django-utils
@@ -821,7 +820,7 @@ openedx-events==9.14.1
# edx-name-affirmation
# event-tracking
# ora2
-openedx-filters==1.11.0
+openedx-filters==1.10.0
# via
# -r requirements/edx/kernel.in
# lti-consumer-xblock
@@ -830,7 +829,7 @@ openedx-learning==0.13.1
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
-openedx-mongodbproxy==0.2.2
+openedx-mongodbproxy==0.2.1
# via -r requirements/edx/kernel.in
optimizely-sdk==4.1.1
# via
@@ -882,8 +881,6 @@ polib==1.2.0
# via edx-i18n-tools
prompt-toolkit==3.0.48
# via click-repl
-propcache==0.2.0
- # via yarl
proto-plus==1.24.0
# via
# google-api-core
@@ -914,7 +911,7 @@ pycountry==24.6.1
# via -r requirements/edx/kernel.in
pycparser==2.22
# via cffi
-pycryptodomex==3.21.0
+pycryptodomex==3.20.0
# via
# -r requirements/edx/kernel.in
# edx-proctoring
@@ -1020,6 +1017,7 @@ pytz==2024.2
# edx-tincan-py35
# event-tracking
# fs
+ # icalendar
# interchange
# olxcleaner
# ora2
@@ -1041,7 +1039,7 @@ random2==1.0.2
# via -r requirements/edx/kernel.in
recommender-xblock==2.2.1
# via -r requirements/edx/bundled.in
-redis==5.1.1
+redis==5.0.8
# via
# -r requirements/edx/kernel.in
# walrus
@@ -1094,7 +1092,7 @@ rules==3.5
# edx-enterprise
# edx-proctoring
# openedx-learning
-s3transfer==0.10.3
+s3transfer==0.10.2
# via boto3
sailthru-client==2.2.3
# via edx-ace
@@ -1133,6 +1131,7 @@ six==1.16.0
# fs-s3fs
# html5lib
# interchange
+ # isodate
# libsass
# optimizely-sdk
# pansi
@@ -1209,7 +1208,6 @@ typing-extensions==4.12.2
tzdata==2024.2
# via
# celery
- # icalendar
# kombu
unicodecsv==0.14.1
# via
@@ -1239,7 +1237,7 @@ voluptuous==0.15.2
# via ora2
walrus==0.9.4
# via edx-event-bus-redis
-watchdog==5.0.3
+watchdog==5.0.2
# via -r requirements/edx/paver.txt
wcwidth==0.2.13
# via prompt-toolkit
@@ -1287,11 +1285,13 @@ xblock-utils==4.0.0
# via
# edx-sga
# xblock-poll
-xmlsec==1.3.14
- # via python3-saml
+xmlsec==1.3.13
+ # via
+ # -c requirements/edx/../constraints.txt
+ # python3-saml
xss-utils==0.6.0
# via -r requirements/edx/kernel.in
-yarl==1.14.0
+yarl==1.12.1
# via aiohttp
zipp==3.20.2
# via importlib-metadata
diff --git a/requirements/edx/coverage.txt b/requirements/edx/coverage.txt
index 45f2429cb2..a1faf5e740 100644
--- a/requirements/edx/coverage.txt
+++ b/requirements/edx/coverage.txt
@@ -6,13 +6,13 @@
#
chardet==5.2.0
# via diff-cover
-coverage==7.6.2
+coverage==7.6.1
# via -r requirements/edx/coverage.in
diff-cover==9.2.0
# via -r requirements/edx/coverage.in
jinja2==3.1.4
# via diff-cover
-markupsafe==3.0.1
+markupsafe==2.1.5
# via jinja2
pluggy==1.5.0
# via diff-cover
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index 13f3f6d5e6..1bdd373651 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -12,16 +12,16 @@ accessible-pygments==0.0.5
# via
# -r requirements/edx/doc.txt
# pydata-sphinx-theme
-acid-xblock==0.4.1
+acid-xblock==0.3.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-aiohappyeyeballs==2.4.3
+aiohappyeyeballs==2.4.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# aiohttp
-aiohttp==3.10.9
+aiohttp==3.10.6
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -140,14 +140,14 @@ boto==2.49.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-boto3==1.35.37
+boto3==1.35.27
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# django-ses
# fs-s3fs
# ora2
-botocore==1.35.37
+botocore==1.35.27
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -157,7 +157,7 @@ bridgekeeper==0.9
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-build==1.2.2.post1
+build==1.2.2
# via
# -r requirements/edx/../pip-tools.txt
# pip-tools
@@ -172,7 +172,7 @@ cachetools==5.5.0
# -r requirements/edx/testing.txt
# google-auth
# tox
-camel-converter[pydantic]==4.0.1
+camel-converter[pydantic]==3.1.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -278,7 +278,7 @@ colorama==0.4.6
# via
# -r requirements/edx/testing.txt
# tox
-coverage[toml]==7.6.2
+coverage[toml]==7.6.1
# via
# -r requirements/edx/testing.txt
# pytest-cov
@@ -325,11 +325,11 @@ defusedxml==0.7.1
# social-auth-core
diff-cover==9.2.0
# via -r requirements/edx/testing.txt
-dill==0.3.9
+dill==0.3.8
# via
# -r requirements/edx/testing.txt
# pylint
-distlib==0.3.9
+distlib==0.3.8
# via
# -r requirements/edx/testing.txt
# virtualenv
@@ -544,7 +544,7 @@ django-sekizai==4.1.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# openedx-django-wiki
-django-ses==4.2.0
+django-ses==4.1.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -627,7 +627,7 @@ djangorestframework-xml==2.0.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# edx-enterprise
-dnspython==2.7.0
+dnspython==2.6.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -696,7 +696,7 @@ edx-codejail==3.4.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-edx-completion==4.7.2
+edx-completion==4.7.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -710,7 +710,7 @@ edx-django-sites-extensions==4.2.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-edx-django-utils==6.0.0
+edx-django-utils==5.16.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -754,8 +754,9 @@ edx-event-bus-redis==0.5.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-edx-i18n-tools==1.6.3
+edx-i18n-tools==1.5.0
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# ora2
@@ -813,7 +814,7 @@ edx-sga==0.25.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-edx-submissions==3.8.1
+edx-submissions==3.8.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -878,7 +879,7 @@ execnet==2.1.1
# pytest-xdist
factory-boy==3.3.1
# via -r requirements/edx/testing.txt
-faker==30.3.0
+faker==30.0.0
# via
# -r requirements/edx/testing.txt
# factory-boy
@@ -942,7 +943,7 @@ glob2==0.7
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-google-api-core[grpc]==2.21.0
+google-api-core[grpc]==2.20.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -951,7 +952,7 @@ google-api-core[grpc]==2.21.0
# google-cloud-core
# google-cloud-firestore
# google-cloud-storage
-google-api-python-client==2.149.0
+google-api-python-client==2.147.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1004,17 +1005,17 @@ googleapis-common-protos==1.65.0
# -r requirements/edx/testing.txt
# google-api-core
# grpcio-status
-grimp==3.5
+grimp==3.4.1
# via
# -r requirements/edx/testing.txt
# import-linter
-grpcio==1.66.2
+grpcio==1.66.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# google-api-core
# grpcio-status
-grpcio-status==1.66.2
+grpcio-status==1.66.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1044,7 +1045,7 @@ httplib2==0.22.0
# google-auth-httplib2
httpretty==1.1.4
# via -r requirements/edx/testing.txt
-icalendar==6.0.0
+icalendar==5.0.13
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1061,7 +1062,7 @@ imagesize==1.4.1
# via
# -r requirements/edx/doc.txt
# sphinx
-import-linter==2.1
+import-linter==2.0
# via -r requirements/edx/testing.txt
importlib-metadata==8.5.0
# via
@@ -1086,7 +1087,7 @@ ipaddress==1.0.23
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-isodate==0.7.2
+isodate==0.6.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1135,7 +1136,7 @@ jsonschema==4.23.0
# drf-spectacular
# optimizely-sdk
# sphinxcontrib-openapi
-jsonschema-specifications==2024.10.1
+jsonschema-specifications==2023.12.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1182,14 +1183,14 @@ lti-consumer-xblock==9.11.3
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-lxml[html-clean]==5.3.0
+lxml==4.9.4
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# edx-i18n-tools
# edxval
# lti-consumer-xblock
- # lxml-html-clean
# olxcleaner
# openedx-calc
# ora2
@@ -1197,11 +1198,6 @@ lxml[html-clean]==5.3.0
# python3-saml
# xblock
# xmlsec
-lxml-html-clean==0.3.1
- # via
- # -r requirements/edx/doc.txt
- # -r requirements/edx/testing.txt
- # lxml
mailsnake==1.6.4
# via
# -r requirements/edx/doc.txt
@@ -1222,7 +1218,7 @@ markdown==3.3.7
# openedx-django-wiki
# staff-graded-xblock
# xblock-poll
-markupsafe==3.0.1
+markupsafe==2.1.5
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1294,7 +1290,7 @@ mysqlclient==2.2.4
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-newrelic==10.0.0
+newrelic==9.13.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1372,7 +1368,7 @@ openedx-events==9.14.1
# edx-name-affirmation
# event-tracking
# ora2
-openedx-filters==1.11.0
+openedx-filters==1.10.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1383,7 +1379,7 @@ openedx-learning==0.13.1
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-openedx-mongodbproxy==0.2.2
+openedx-mongodbproxy==0.2.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1411,7 +1407,7 @@ packaging==24.1
# snowflake-connector-python
# sphinx
# tox
-pact-python==2.2.2
+pact-python==2.2.1
# via -r requirements/edx/testing.txt
pansi==2020.7.3
# via
@@ -1492,11 +1488,6 @@ prompt-toolkit==3.0.48
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# click-repl
-propcache==0.2.0
- # via
- # -r requirements/edx/doc.txt
- # -r requirements/edx/testing.txt
- # yarl
proto-plus==1.24.0
# via
# -r requirements/edx/doc.txt
@@ -1551,7 +1542,7 @@ pycparser==2.22
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# cffi
-pycryptodomex==3.21.0
+pycryptodomex==3.20.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1675,7 +1666,7 @@ pyproject-api==1.8.0
# via
# -r requirements/edx/testing.txt
# tox
-pyproject-hooks==1.2.0
+pyproject-hooks==1.1.0
# via
# -r requirements/edx/../pip-tools.txt
# build
@@ -1776,6 +1767,7 @@ pytz==2024.2
# edx-tincan-py35
# event-tracking
# fs
+ # icalendar
# interchange
# olxcleaner
# ora2
@@ -1807,7 +1799,7 @@ recommender-xblock==2.2.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-redis==5.1.1
+redis==5.0.8
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1877,7 +1869,7 @@ rules==3.5
# edx-enterprise
# edx-proctoring
# openedx-learning
-s3transfer==0.10.3
+s3transfer==0.10.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1934,6 +1926,7 @@ six==1.16.0
# fs-s3fs
# html5lib
# interchange
+ # isodate
# libsass
# optimizely-sdk
# pact-python
@@ -1993,7 +1986,7 @@ soupsieve==2.6
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# beautifulsoup4
-sphinx==8.1.0
+sphinx==8.0.2
# via
# -r requirements/edx/doc.txt
# pydata-sphinx-theme
@@ -2094,7 +2087,7 @@ tinycss2==1.2.1
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# bleach
-tomli==2.0.2
+tomli==2.0.1
# via django-stubs
tomlkit==0.13.2
# via
@@ -2102,7 +2095,7 @@ tomlkit==0.13.2
# -r requirements/edx/testing.txt
# pylint
# snowflake-connector-python
-tox==4.21.2
+tox==4.20.0
# via -r requirements/edx/testing.txt
tqdm==4.66.5
# via
@@ -2110,7 +2103,7 @@ tqdm==4.66.5
# -r requirements/edx/testing.txt
# nltk
# openai
-types-pytz==2024.2.0.20241003
+types-pytz==2024.2.0.20240913
# via django-stubs
types-pyyaml==6.0.12.20240917
# via
@@ -2129,7 +2122,6 @@ typing-extensions==4.12.2
# django-stubs-ext
# djangorestframework-stubs
# edx-opaque-keys
- # faker
# fastapi
# grimp
# import-linter
@@ -2145,7 +2137,6 @@ tzdata==2024.2
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# celery
- # icalendar
# kombu
unicodecsv==0.14.1
# via
@@ -2174,7 +2165,7 @@ user-util==1.1.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-uvicorn==0.31.1
+uvicorn==0.30.6
# via
# -r requirements/edx/testing.txt
# pact-python
@@ -2185,7 +2176,7 @@ vine==5.1.0
# amqp
# celery
# kombu
-virtualenv==20.26.6
+virtualenv==20.26.5
# via
# -r requirements/edx/testing.txt
# tox
@@ -2194,14 +2185,14 @@ voluptuous==0.15.2
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# ora2
-vulture==2.13
+vulture==2.12
# via -r requirements/edx/development.in
walrus==0.9.4
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# edx-event-bus-redis
-watchdog==5.0.3
+watchdog==5.0.2
# via
# -r requirements/edx/development.in
# -r requirements/edx/doc.txt
@@ -2275,8 +2266,9 @@ xblock-utils==4.0.0
# -r requirements/edx/testing.txt
# edx-sga
# xblock-poll
-xmlsec==1.3.14
+xmlsec==1.3.13
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# python3-saml
@@ -2284,7 +2276,7 @@ xss-utils==0.6.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-yarl==1.14.0
+yarl==1.12.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 30736a21a3..6f12d80467 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -8,13 +8,13 @@
# via -r requirements/edx/base.txt
accessible-pygments==0.0.5
# via pydata-sphinx-theme
-acid-xblock==0.4.1
+acid-xblock==0.3.1
# via -r requirements/edx/base.txt
-aiohappyeyeballs==2.4.3
+aiohappyeyeballs==2.4.0
# via
# -r requirements/edx/base.txt
# aiohttp
-aiohttp==3.10.9
+aiohttp==3.10.6
# via
# -r requirements/edx/base.txt
# geoip2
@@ -102,13 +102,13 @@ bleach[css]==6.1.0
# xblock-poll
boto==2.49.0
# via -r requirements/edx/base.txt
-boto3==1.35.37
+boto3==1.35.27
# via
# -r requirements/edx/base.txt
# django-ses
# fs-s3fs
# ora2
-botocore==1.35.37
+botocore==1.35.27
# via
# -r requirements/edx/base.txt
# boto3
@@ -123,7 +123,7 @@ cachetools==5.5.0
# via
# -r requirements/edx/base.txt
# google-auth
-camel-converter[pydantic]==4.0.1
+camel-converter[pydantic]==3.1.2
# via
# -r requirements/edx/base.txt
# meilisearch
@@ -398,7 +398,7 @@ django-sekizai==4.1.0
# via
# -r requirements/edx/base.txt
# openedx-django-wiki
-django-ses==4.2.0
+django-ses==4.1.1
# via -r requirements/edx/base.txt
django-simple-history==3.4.0
# via
@@ -459,7 +459,7 @@ djangorestframework-xml==2.0.0
# via
# -r requirements/edx/base.txt
# edx-enterprise
-dnspython==2.7.0
+dnspython==2.6.1
# via
# -r requirements/edx/base.txt
# pymongo
@@ -509,7 +509,7 @@ edx-celeryutils==1.3.0
# super-csv
edx-codejail==3.4.1
# via -r requirements/edx/base.txt
-edx-completion==4.7.2
+edx-completion==4.7.1
# via -r requirements/edx/base.txt
edx-django-release-util==1.4.0
# via
@@ -518,7 +518,7 @@ edx-django-release-util==1.4.0
# edxval
edx-django-sites-extensions==4.2.0
# via -r requirements/edx/base.txt
-edx-django-utils==6.0.0
+edx-django-utils==5.16.0
# via
# -r requirements/edx/base.txt
# django-config-models
@@ -555,8 +555,9 @@ edx-event-bus-kafka==5.8.1
# via -r requirements/edx/base.txt
edx-event-bus-redis==0.5.0
# via -r requirements/edx/base.txt
-edx-i18n-tools==1.6.3
+edx-i18n-tools==1.5.0
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# ora2
edx-milestones==0.6.0
@@ -597,7 +598,7 @@ edx-search==4.0.0
# via -r requirements/edx/base.txt
edx-sga==0.25.0
# via -r requirements/edx/base.txt
-edx-submissions==3.8.1
+edx-submissions==3.8.0
# via
# -r requirements/edx/base.txt
# ora2
@@ -682,7 +683,7 @@ gitpython==3.1.43
# via -r requirements/edx/doc.in
glob2==0.7
# via -r requirements/edx/base.txt
-google-api-core[grpc]==2.21.0
+google-api-core[grpc]==2.20.0
# via
# -r requirements/edx/base.txt
# firebase-admin
@@ -690,7 +691,7 @@ google-api-core[grpc]==2.21.0
# google-cloud-core
# google-cloud-firestore
# google-cloud-storage
-google-api-python-client==2.149.0
+google-api-python-client==2.147.0
# via
# -r requirements/edx/base.txt
# firebase-admin
@@ -734,12 +735,12 @@ googleapis-common-protos==1.65.0
# -r requirements/edx/base.txt
# google-api-core
# grpcio-status
-grpcio==1.66.2
+grpcio==1.66.1
# via
# -r requirements/edx/base.txt
# google-api-core
# grpcio-status
-grpcio-status==1.66.2
+grpcio-status==1.66.1
# via
# -r requirements/edx/base.txt
# google-api-core
@@ -756,7 +757,7 @@ httplib2==0.22.0
# -r requirements/edx/base.txt
# google-api-python-client
# google-auth-httplib2
-icalendar==6.0.0
+icalendar==5.0.13
# via -r requirements/edx/base.txt
idna==3.10
# via
@@ -780,7 +781,7 @@ interchange==2021.0.4
# py2neo
ipaddress==1.0.23
# via -r requirements/edx/base.txt
-isodate==0.7.2
+isodate==0.6.1
# via
# -r requirements/edx/base.txt
# python3-saml
@@ -817,7 +818,7 @@ jsonschema==4.23.0
# drf-spectacular
# optimizely-sdk
# sphinxcontrib-openapi
-jsonschema-specifications==2024.10.1
+jsonschema-specifications==2023.12.1
# via
# -r requirements/edx/base.txt
# jsonschema
@@ -849,23 +850,19 @@ loremipsum==1.0.5
# ora2
lti-consumer-xblock==9.11.3
# via -r requirements/edx/base.txt
-lxml[html-clean]==5.3.0
+lxml==4.9.4
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# edx-i18n-tools
# edxval
# lti-consumer-xblock
- # lxml-html-clean
# olxcleaner
# openedx-calc
# ora2
# python3-saml
# xblock
# xmlsec
-lxml-html-clean==0.3.1
- # via
- # -r requirements/edx/base.txt
- # lxml
mailsnake==1.6.4
# via -r requirements/edx/base.txt
mako==1.3.5
@@ -882,7 +879,7 @@ markdown==3.3.7
# openedx-django-wiki
# staff-graded-xblock
# xblock-poll
-markupsafe==3.0.1
+markupsafe==2.1.5
# via
# -r requirements/edx/base.txt
# chem
@@ -926,7 +923,7 @@ multidict==6.1.0
# yarl
mysqlclient==2.2.4
# via -r requirements/edx/base.txt
-newrelic==10.0.0
+newrelic==9.13.0
# via
# -r requirements/edx/base.txt
# edx-django-utils
@@ -982,7 +979,7 @@ openedx-events==9.14.1
# edx-name-affirmation
# event-tracking
# ora2
-openedx-filters==1.11.0
+openedx-filters==1.10.0
# via
# -r requirements/edx/base.txt
# lti-consumer-xblock
@@ -991,7 +988,7 @@ openedx-learning==0.13.1
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
-openedx-mongodbproxy==0.2.2
+openedx-mongodbproxy==0.2.1
# via -r requirements/edx/base.txt
optimizely-sdk==4.1.1
# via
@@ -1060,10 +1057,6 @@ prompt-toolkit==3.0.48
# via
# -r requirements/edx/base.txt
# click-repl
-propcache==0.2.0
- # via
- # -r requirements/edx/base.txt
- # yarl
proto-plus==1.24.0
# via
# -r requirements/edx/base.txt
@@ -1101,7 +1094,7 @@ pycparser==2.22
# via
# -r requirements/edx/base.txt
# cffi
-pycryptodomex==3.21.0
+pycryptodomex==3.20.0
# via
# -r requirements/edx/base.txt
# edx-proctoring
@@ -1229,6 +1222,7 @@ pytz==2024.2
# edx-tincan-py35
# event-tracking
# fs
+ # icalendar
# interchange
# olxcleaner
# ora2
@@ -1251,7 +1245,7 @@ random2==1.0.2
# via -r requirements/edx/base.txt
recommender-xblock==2.2.1
# via -r requirements/edx/base.txt
-redis==5.1.1
+redis==5.0.8
# via
# -r requirements/edx/base.txt
# walrus
@@ -1311,7 +1305,7 @@ rules==3.5
# edx-enterprise
# edx-proctoring
# openedx-learning
-s3transfer==0.10.3
+s3transfer==0.10.2
# via
# -r requirements/edx/base.txt
# boto3
@@ -1356,6 +1350,7 @@ six==1.16.0
# fs-s3fs
# html5lib
# interchange
+ # isodate
# libsass
# optimizely-sdk
# pansi
@@ -1399,7 +1394,7 @@ soupsieve==2.6
# via
# -r requirements/edx/base.txt
# beautifulsoup4
-sphinx==8.1.0
+sphinx==8.0.2
# via
# -r requirements/edx/doc.in
# pydata-sphinx-theme
@@ -1494,7 +1489,6 @@ tzdata==2024.2
# via
# -r requirements/edx/base.txt
# celery
- # icalendar
# kombu
unicodecsv==0.14.1
# via
@@ -1530,7 +1524,7 @@ walrus==0.9.4
# via
# -r requirements/edx/base.txt
# edx-event-bus-redis
-watchdog==5.0.3
+watchdog==5.0.2
# via -r requirements/edx/base.txt
wcwidth==0.2.13
# via
@@ -1582,13 +1576,14 @@ xblock-utils==4.0.0
# -r requirements/edx/base.txt
# edx-sga
# xblock-poll
-xmlsec==1.3.14
+xmlsec==1.3.13
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# python3-saml
xss-utils==0.6.0
# via -r requirements/edx/base.txt
-yarl==1.14.0
+yarl==1.12.1
# via
# -r requirements/edx/base.txt
# aiohttp
diff --git a/requirements/edx/paver.txt b/requirements/edx/paver.txt
index 2d8f510e03..a0b1896919 100644
--- a/requirements/edx/paver.txt
+++ b/requirements/edx/paver.txt
@@ -10,7 +10,7 @@ charset-normalizer==2.0.12
# via
# -c requirements/edx/../constraints.txt
# requests
-dnspython==2.7.0
+dnspython==2.6.1
# via pymongo
edx-opaque-keys==2.11.0
# via -r requirements/edx/paver.in
@@ -22,7 +22,7 @@ libsass==0.10.0
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/paver.in
-markupsafe==3.0.1
+markupsafe==2.1.5
# via -r requirements/edx/paver.in
mock==5.1.0
# via -r requirements/edx/paver.in
@@ -61,7 +61,7 @@ urllib3==1.26.20
# via
# -c requirements/edx/../constraints.txt
# requests
-watchdog==5.0.3
+watchdog==5.0.2
# via -r requirements/edx/paver.in
wrapt==1.16.0
# via -r requirements/edx/paver.in
diff --git a/requirements/edx/semgrep.txt b/requirements/edx/semgrep.txt
index 174fa87d08..102289def2 100644
--- a/requirements/edx/semgrep.txt
+++ b/requirements/edx/semgrep.txt
@@ -15,7 +15,7 @@ boltons==21.0.0
# face
# glom
# semgrep
-bracex==2.5.post1
+bracex==2.5
# via wcmatch
certifi==2024.8.30
# via requests
@@ -42,7 +42,7 @@ idna==3.10
# via requests
jsonschema==4.23.0
# via semgrep
-jsonschema-specifications==2024.10.1
+jsonschema-specifications==2023.12.1
# via jsonschema
markdown-it-py==3.0.0
# via rich
@@ -60,7 +60,7 @@ referencing==0.35.1
# jsonschema-specifications
requests==2.32.3
# via semgrep
-rich==13.9.2
+rich==13.8.1
# via semgrep
rpds-py==0.20.0
# via
@@ -72,7 +72,7 @@ ruamel-yaml-clib==0.2.8
# via ruamel-yaml
semgrep==1.52.0
# via -r requirements/edx/semgrep.in
-tomli==2.0.2
+tomli==2.0.1
# via semgrep
typing-extensions==4.12.2
# via semgrep
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 473da12534..d3b14a6f43 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -6,13 +6,13 @@
#
-e git+https://github.com/anupdhabarde/edx-proctoring-proctortrack.git@31c6c9923a51c903ae83760ecbbac191363aa2a2#egg=edx_proctoring_proctortrack
# via -r requirements/edx/base.txt
-acid-xblock==0.4.1
+acid-xblock==0.3.1
# via -r requirements/edx/base.txt
-aiohappyeyeballs==2.4.3
+aiohappyeyeballs==2.4.0
# via
# -r requirements/edx/base.txt
# aiohttp
-aiohttp==3.10.9
+aiohttp==3.10.6
# via
# -r requirements/edx/base.txt
# geoip2
@@ -102,13 +102,13 @@ bleach[css]==6.1.0
# xblock-poll
boto==2.49.0
# via -r requirements/edx/base.txt
-boto3==1.35.37
+boto3==1.35.27
# via
# -r requirements/edx/base.txt
# django-ses
# fs-s3fs
# ora2
-botocore==1.35.37
+botocore==1.35.27
# via
# -r requirements/edx/base.txt
# boto3
@@ -124,7 +124,7 @@ cachetools==5.5.0
# -r requirements/edx/base.txt
# google-auth
# tox
-camel-converter[pydantic]==4.0.1
+camel-converter[pydantic]==3.1.2
# via
# -r requirements/edx/base.txt
# meilisearch
@@ -209,7 +209,7 @@ codejail-includes==1.0.0
# via -r requirements/edx/base.txt
colorama==0.4.6
# via tox
-coverage[toml]==7.6.2
+coverage[toml]==7.6.1
# via
# -r requirements/edx/coverage.txt
# pytest-cov
@@ -247,9 +247,9 @@ defusedxml==0.7.1
# social-auth-core
diff-cover==9.2.0
# via -r requirements/edx/coverage.txt
-dill==0.3.9
+dill==0.3.8
# via pylint
-distlib==0.3.9
+distlib==0.3.8
# via virtualenv
django==4.2.16
# via
@@ -427,7 +427,7 @@ django-sekizai==4.1.0
# via
# -r requirements/edx/base.txt
# openedx-django-wiki
-django-ses==4.2.0
+django-ses==4.1.1
# via -r requirements/edx/base.txt
django-simple-history==3.4.0
# via
@@ -488,7 +488,7 @@ djangorestframework-xml==2.0.0
# via
# -r requirements/edx/base.txt
# edx-enterprise
-dnspython==2.7.0
+dnspython==2.6.1
# via
# -r requirements/edx/base.txt
# pymongo
@@ -533,7 +533,7 @@ edx-celeryutils==1.3.0
# super-csv
edx-codejail==3.4.1
# via -r requirements/edx/base.txt
-edx-completion==4.7.2
+edx-completion==4.7.1
# via -r requirements/edx/base.txt
edx-django-release-util==1.4.0
# via
@@ -542,7 +542,7 @@ edx-django-release-util==1.4.0
# edxval
edx-django-sites-extensions==4.2.0
# via -r requirements/edx/base.txt
-edx-django-utils==6.0.0
+edx-django-utils==5.16.0
# via
# -r requirements/edx/base.txt
# django-config-models
@@ -579,8 +579,9 @@ edx-event-bus-kafka==5.8.1
# via -r requirements/edx/base.txt
edx-event-bus-redis==0.5.0
# via -r requirements/edx/base.txt
-edx-i18n-tools==1.6.3
+edx-i18n-tools==1.5.0
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# ora2
edx-lint==5.4.0
@@ -623,7 +624,7 @@ edx-search==4.0.0
# via -r requirements/edx/base.txt
edx-sga==0.25.0
# via -r requirements/edx/base.txt
-edx-submissions==3.8.1
+edx-submissions==3.8.0
# via
# -r requirements/edx/base.txt
# ora2
@@ -673,7 +674,7 @@ execnet==2.1.1
# via pytest-xdist
factory-boy==3.3.1
# via -r requirements/edx/testing.in
-faker==30.3.0
+faker==30.0.0
# via factory-boy
fastapi==0.115.0
# via pact-python
@@ -716,7 +717,7 @@ geoip2==4.8.0
# via -r requirements/edx/base.txt
glob2==0.7
# via -r requirements/edx/base.txt
-google-api-core[grpc]==2.21.0
+google-api-core[grpc]==2.20.0
# via
# -r requirements/edx/base.txt
# firebase-admin
@@ -724,7 +725,7 @@ google-api-core[grpc]==2.21.0
# google-cloud-core
# google-cloud-firestore
# google-cloud-storage
-google-api-python-client==2.149.0
+google-api-python-client==2.147.0
# via
# -r requirements/edx/base.txt
# firebase-admin
@@ -768,14 +769,14 @@ googleapis-common-protos==1.65.0
# -r requirements/edx/base.txt
# google-api-core
# grpcio-status
-grimp==3.5
+grimp==3.4.1
# via import-linter
-grpcio==1.66.2
+grpcio==1.66.1
# via
# -r requirements/edx/base.txt
# google-api-core
# grpcio-status
-grpcio-status==1.66.2
+grpcio-status==1.66.1
# via
# -r requirements/edx/base.txt
# google-api-core
@@ -796,7 +797,7 @@ httplib2==0.22.0
# google-auth-httplib2
httpretty==1.1.4
# via -r requirements/edx/testing.in
-icalendar==6.0.0
+icalendar==5.0.13
# via -r requirements/edx/base.txt
idna==3.10
# via
@@ -806,7 +807,7 @@ idna==3.10
# requests
# snowflake-connector-python
# yarl
-import-linter==2.1
+import-linter==2.0
# via -r requirements/edx/testing.in
importlib-metadata==8.5.0
# via -r requirements/edx/base.txt
@@ -823,7 +824,7 @@ interchange==2021.0.4
# py2neo
ipaddress==1.0.23
# via -r requirements/edx/base.txt
-isodate==0.7.2
+isodate==0.6.1
# via
# -r requirements/edx/base.txt
# python3-saml
@@ -864,7 +865,7 @@ jsonschema==4.23.0
# -r requirements/edx/base.txt
# drf-spectacular
# optimizely-sdk
-jsonschema-specifications==2024.10.1
+jsonschema-specifications==2023.12.1
# via
# -r requirements/edx/base.txt
# jsonschema
@@ -898,13 +899,13 @@ loremipsum==1.0.5
# ora2
lti-consumer-xblock==9.11.3
# via -r requirements/edx/base.txt
-lxml[html-clean]==5.3.0
+lxml==4.9.4
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# edx-i18n-tools
# edxval
# lti-consumer-xblock
- # lxml-html-clean
# olxcleaner
# openedx-calc
# ora2
@@ -912,10 +913,6 @@ lxml[html-clean]==5.3.0
# python3-saml
# xblock
# xmlsec
-lxml-html-clean==0.3.1
- # via
- # -r requirements/edx/base.txt
- # lxml
mailsnake==1.6.4
# via -r requirements/edx/base.txt
mako==1.3.5
@@ -932,7 +929,7 @@ markdown==3.3.7
# openedx-django-wiki
# staff-graded-xblock
# xblock-poll
-markupsafe==3.0.1
+markupsafe==2.1.5
# via
# -r requirements/edx/base.txt
# -r requirements/edx/coverage.txt
@@ -977,7 +974,7 @@ multidict==6.1.0
# yarl
mysqlclient==2.2.4
# via -r requirements/edx/base.txt
-newrelic==10.0.0
+newrelic==9.13.0
# via
# -r requirements/edx/base.txt
# edx-django-utils
@@ -1033,7 +1030,7 @@ openedx-events==9.14.1
# edx-name-affirmation
# event-tracking
# ora2
-openedx-filters==1.11.0
+openedx-filters==1.10.0
# via
# -r requirements/edx/base.txt
# lti-consumer-xblock
@@ -1042,7 +1039,7 @@ openedx-learning==0.13.1
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
-openedx-mongodbproxy==0.2.2
+openedx-mongodbproxy==0.2.1
# via -r requirements/edx/base.txt
optimizely-sdk==4.1.1
# via
@@ -1060,7 +1057,7 @@ packaging==24.1
# pytest
# snowflake-connector-python
# tox
-pact-python==2.2.2
+pact-python==2.2.1
# via -r requirements/edx/testing.in
pansi==2020.7.3
# via
@@ -1122,10 +1119,6 @@ prompt-toolkit==3.0.48
# via
# -r requirements/edx/base.txt
# click-repl
-propcache==0.2.0
- # via
- # -r requirements/edx/base.txt
- # yarl
proto-plus==1.24.0
# via
# -r requirements/edx/base.txt
@@ -1171,7 +1164,7 @@ pycparser==2.22
# via
# -r requirements/edx/base.txt
# cffi
-pycryptodomex==3.21.0
+pycryptodomex==3.20.0
# via
# -r requirements/edx/base.txt
# edx-proctoring
@@ -1347,6 +1340,7 @@ pytz==2024.2
# edx-tincan-py35
# event-tracking
# fs
+ # icalendar
# interchange
# olxcleaner
# ora2
@@ -1368,7 +1362,7 @@ random2==1.0.2
# via -r requirements/edx/base.txt
recommender-xblock==2.2.1
# via -r requirements/edx/base.txt
-redis==5.1.1
+redis==5.0.8
# via
# -r requirements/edx/base.txt
# walrus
@@ -1428,7 +1422,7 @@ rules==3.5
# edx-enterprise
# edx-proctoring
# openedx-learning
-s3transfer==0.10.3
+s3transfer==0.10.2
# via
# -r requirements/edx/base.txt
# boto3
@@ -1476,6 +1470,7 @@ six==1.16.0
# fs-s3fs
# html5lib
# interchange
+ # isodate
# libsass
# optimizely-sdk
# pact-python
@@ -1559,7 +1554,7 @@ tomlkit==0.13.2
# -r requirements/edx/base.txt
# pylint
# snowflake-connector-python
-tox==4.21.2
+tox==4.20.0
# via -r requirements/edx/testing.in
tqdm==4.66.5
# via
@@ -1571,7 +1566,6 @@ typing-extensions==4.12.2
# -r requirements/edx/base.txt
# django-countries
# edx-opaque-keys
- # faker
# fastapi
# grimp
# import-linter
@@ -1584,7 +1578,6 @@ tzdata==2024.2
# via
# -r requirements/edx/base.txt
# celery
- # icalendar
# kombu
unicodecsv==0.14.1
# via
@@ -1608,7 +1601,7 @@ urllib3==1.26.20
# requests
user-util==1.1.0
# via -r requirements/edx/base.txt
-uvicorn==0.31.1
+uvicorn==0.30.6
# via pact-python
vine==5.1.0
# via
@@ -1616,7 +1609,7 @@ vine==5.1.0
# amqp
# celery
# kombu
-virtualenv==20.26.6
+virtualenv==20.26.5
# via tox
voluptuous==0.15.2
# via
@@ -1626,7 +1619,7 @@ walrus==0.9.4
# via
# -r requirements/edx/base.txt
# edx-event-bus-redis
-watchdog==5.0.3
+watchdog==5.0.2
# via -r requirements/edx/base.txt
wcwidth==0.2.13
# via
@@ -1680,13 +1673,14 @@ xblock-utils==4.0.0
# -r requirements/edx/base.txt
# edx-sga
# xblock-poll
-xmlsec==1.3.14
+xmlsec==1.3.13
# via
+ # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# python3-saml
xss-utils==0.6.0
# via -r requirements/edx/base.txt
-yarl==1.14.0
+yarl==1.12.1
# via
# -r requirements/edx/base.txt
# aiohttp
diff --git a/requirements/pip-tools.txt b/requirements/pip-tools.txt
index 110663ff6a..5bcb2aa550 100644
--- a/requirements/pip-tools.txt
+++ b/requirements/pip-tools.txt
@@ -4,7 +4,7 @@
#
# make upgrade
#
-build==1.2.2.post1
+build==1.2.2
# via pip-tools
click==8.1.6
# via
@@ -14,7 +14,7 @@ packaging==24.1
# via build
pip-tools==7.4.1
# via -r requirements/pip-tools.in
-pyproject-hooks==1.2.0
+pyproject-hooks==1.1.0
# via
# build
# pip-tools
diff --git a/scripts/structures_pruning/requirements/base.txt b/scripts/structures_pruning/requirements/base.txt
index a3fcacad2f..b80c660b87 100644
--- a/scripts/structures_pruning/requirements/base.txt
+++ b/scripts/structures_pruning/requirements/base.txt
@@ -11,7 +11,7 @@ click==8.1.6
# click-log
click-log==0.4.0
# via -r scripts/structures_pruning/requirements/base.in
-dnspython==2.7.0
+dnspython==2.6.1
# via pymongo
edx-opaque-keys==2.11.0
# via -r scripts/structures_pruning/requirements/base.in
diff --git a/scripts/structures_pruning/requirements/testing.txt b/scripts/structures_pruning/requirements/testing.txt
index 94c6ac6982..8be2e15973 100644
--- a/scripts/structures_pruning/requirements/testing.txt
+++ b/scripts/structures_pruning/requirements/testing.txt
@@ -12,7 +12,7 @@ click-log==0.4.0
# via -r scripts/structures_pruning/requirements/base.txt
ddt==1.7.2
# via -r scripts/structures_pruning/requirements/testing.in
-dnspython==2.7.0
+dnspython==2.6.1
# via
# -r scripts/structures_pruning/requirements/base.txt
# pymongo
diff --git a/scripts/user_retirement/requirements/base.txt b/scripts/user_retirement/requirements/base.txt
index 576fa342c3..9f57da73d0 100644
--- a/scripts/user_retirement/requirements/base.txt
+++ b/scripts/user_retirement/requirements/base.txt
@@ -10,9 +10,9 @@ attrs==24.2.0
# via zeep
backoff==2.2.1
# via -r scripts/user_retirement/requirements/base.in
-boto3==1.35.37
+boto3==1.35.27
# via -r scripts/user_retirement/requirements/base.in
-botocore==1.35.37
+botocore==1.35.27
# via
# boto3
# s3transfer
@@ -46,13 +46,13 @@ django-crum==0.7.9
# via edx-django-utils
django-waffle==4.1.0
# via edx-django-utils
-edx-django-utils==6.0.0
+edx-django-utils==5.16.0
# via edx-rest-api-client
edx-rest-api-client==6.0.0
# via -r scripts/user_retirement/requirements/base.in
-google-api-core==2.21.0
+google-api-core==2.20.0
# via google-api-python-client
-google-api-python-client==2.149.0
+google-api-python-client==2.147.0
# via -r scripts/user_retirement/requirements/base.in
google-auth==2.35.0
# via
@@ -69,7 +69,7 @@ httplib2==0.22.0
# google-auth-httplib2
idna==3.10
# via requests
-isodate==0.7.2
+isodate==0.6.1
# via zeep
jenkinsapi==0.3.13
# via -r scripts/user_retirement/requirements/base.in
@@ -77,11 +77,13 @@ jmespath==1.0.1
# via
# boto3
# botocore
-lxml==5.3.0
- # via zeep
+lxml==4.9.4
+ # via
+ # -c scripts/user_retirement/requirements/../../../requirements/constraints.txt
+ # zeep
more-itertools==10.5.0
# via simple-salesforce
-newrelic==10.0.0
+newrelic==9.13.0
# via edx-django-utils
pbr==6.1.0
# via stevedore
@@ -136,7 +138,7 @@ requests-toolbelt==1.0.0
# via zeep
rsa==4.9
# via google-auth
-s3transfer==0.10.3
+s3transfer==0.10.2
# via boto3
simple-salesforce==1.12.6
# via -r scripts/user_retirement/requirements/base.in
@@ -144,6 +146,7 @@ simplejson==3.19.3
# via -r scripts/user_retirement/requirements/base.in
six==1.16.0
# via
+ # isodate
# jenkinsapi
# python-dateutil
sqlparse==0.5.1
diff --git a/scripts/user_retirement/requirements/testing.txt b/scripts/user_retirement/requirements/testing.txt
index 6a165d7356..d5aac1cd06 100644
--- a/scripts/user_retirement/requirements/testing.txt
+++ b/scripts/user_retirement/requirements/testing.txt
@@ -14,11 +14,11 @@ attrs==24.2.0
# zeep
backoff==2.2.1
# via -r scripts/user_retirement/requirements/base.txt
-boto3==1.35.37
+boto3==1.35.27
# via
# -r scripts/user_retirement/requirements/base.txt
# moto
-botocore==1.35.37
+botocore==1.35.27
# via
# -r scripts/user_retirement/requirements/base.txt
# boto3
@@ -66,17 +66,17 @@ django-waffle==4.1.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
-edx-django-utils==6.0.0
+edx-django-utils==5.16.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-rest-api-client
edx-rest-api-client==6.0.0
# via -r scripts/user_retirement/requirements/base.txt
-google-api-core==2.21.0
+google-api-core==2.20.0
# via
# -r scripts/user_retirement/requirements/base.txt
# google-api-python-client
-google-api-python-client==2.149.0
+google-api-python-client==2.147.0
# via -r scripts/user_retirement/requirements/base.txt
google-auth==2.35.0
# via
@@ -103,7 +103,7 @@ idna==3.10
# requests
iniconfig==2.0.0
# via pytest
-isodate==0.7.2
+isodate==0.6.1
# via
# -r scripts/user_retirement/requirements/base.txt
# zeep
@@ -116,11 +116,11 @@ jmespath==1.0.1
# -r scripts/user_retirement/requirements/base.txt
# boto3
# botocore
-lxml==5.3.0
+lxml==4.9.4
# via
# -r scripts/user_retirement/requirements/base.txt
# zeep
-markupsafe==3.0.1
+markupsafe==2.1.5
# via
# jinja2
# werkzeug
@@ -132,7 +132,7 @@ more-itertools==10.5.0
# simple-salesforce
moto==4.2.14
# via -r scripts/user_retirement/requirements/testing.in
-newrelic==10.0.0
+newrelic==9.13.0
# via
# -r scripts/user_retirement/requirements/base.txt
# edx-django-utils
@@ -235,7 +235,7 @@ rsa==4.9
# via
# -r scripts/user_retirement/requirements/base.txt
# google-auth
-s3transfer==0.10.3
+s3transfer==0.10.2
# via
# -r scripts/user_retirement/requirements/base.txt
# boto3
@@ -246,6 +246,7 @@ simplejson==3.19.3
six==1.16.0
# via
# -r scripts/user_retirement/requirements/base.txt
+ # isodate
# jenkinsapi
# python-dateutil
sqlparse==0.5.1
@@ -274,7 +275,7 @@ urllib3==1.26.20
# responses
werkzeug==3.0.4
# via moto
-xmltodict==0.14.1
+xmltodict==0.13.0
# via moto
zeep==4.2.1
# via
From 96c682a25b1a191874ee77a3ab91bdb554508769 Mon Sep 17 00:00:00 2001
From: Diana Huang
Date: Thu, 10 Oct 2024 16:21:17 -0400
Subject: [PATCH 11/32] Revert "feat: Use jammy repositories for mongo
installation."
This reverts commit a245dec4f4ff431b336832174f53421a5a1b987c.
---
.github/workflows/unit-tests.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
index 900489256a..5fef1c8352 100644
--- a/.github/workflows/unit-tests.yml
+++ b/.github/workflows/unit-tests.yml
@@ -94,7 +94,7 @@ jobs:
run: |
if [[ "${{ matrix.mongo-version }}" != "4.4" ]]; then
wget -qO - https://www.mongodb.org/static/pgp/server-${{ matrix.mongo-version }}.asc | sudo apt-key add -
- echo "deb https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/${{ matrix.mongo-version }} multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-${{ matrix.mongo-version }}.list
+ echo "deb https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/${{ matrix.mongo-version }} multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-${{ matrix.mongo-version }}.list
sudo apt-get update && sudo apt-get install -y mongodb-org="${{ matrix.mongo-version }}.*"
fi
From 8ee942e411ae84ca010bb7226e85dd2eadfef9d6 Mon Sep 17 00:00:00 2001
From: Diana Huang
Date: Thu, 10 Oct 2024 16:36:02 -0400
Subject: [PATCH 12/32] fix: Pin select jobs to ubuntu 22.04.
Using Ubuntu 24.04 breaks Mongo installation
and some thing involving lxml/xmlsec. We are
going to pin this until we're ready to upgrade both.
---
.github/workflows/ci-static-analysis.yml | 2 +-
.github/workflows/migrations-check.yml | 4 ++--
.github/workflows/pylint-checks.yml | 4 ++--
.github/workflows/quality-checks.yml | 2 +-
.github/workflows/static-assets-check.yml | 2 +-
.github/workflows/unit-tests.yml | 12 ++++++------
6 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/.github/workflows/ci-static-analysis.yml b/.github/workflows/ci-static-analysis.yml
index a3b0527aad..458e00fc6b 100644
--- a/.github/workflows/ci-static-analysis.yml
+++ b/.github/workflows/ci-static-analysis.yml
@@ -10,7 +10,7 @@ jobs:
matrix:
python-version:
- "3.11"
- os: ["ubuntu-latest"]
+ os: ["ubuntu-22.04"]
steps:
- uses: actions/checkout@v4
diff --git a/.github/workflows/migrations-check.yml b/.github/workflows/migrations-check.yml
index f253d48e4f..624caddd53 100644
--- a/.github/workflows/migrations-check.yml
+++ b/.github/workflows/migrations-check.yml
@@ -13,7 +13,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
- os: [ubuntu-latest]
+ os: [ubuntu-22.04]
python-version:
- "3.11"
# 'pinned' is used to install the latest patch version of Django
@@ -126,7 +126,7 @@ jobs:
if: always()
needs:
- check_migrations
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
steps:
- name: Decide whether the needed jobs succeeded or failed
# uses: re-actors/alls-green@v1.2.1
diff --git a/.github/workflows/pylint-checks.yml b/.github/workflows/pylint-checks.yml
index 144cc77a3d..ad3aad3887 100644
--- a/.github/workflows/pylint-checks.yml
+++ b/.github/workflows/pylint-checks.yml
@@ -8,7 +8,7 @@ on:
jobs:
run-pylint:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
@@ -75,7 +75,7 @@ jobs:
if: always()
needs:
- run-pylint
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
steps:
- name: Decide whether the needed jobs succeeded or failed
# uses: re-actors/alls-green@v1.2.1
diff --git a/.github/workflows/quality-checks.yml b/.github/workflows/quality-checks.yml
index 5445d70e3b..8461012349 100644
--- a/.github/workflows/quality-checks.yml
+++ b/.github/workflows/quality-checks.yml
@@ -13,7 +13,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
- os: [ubuntu-latest]
+ os: [ubuntu-22.04]
python-version:
- "3.11"
node-version: [20]
diff --git a/.github/workflows/static-assets-check.yml b/.github/workflows/static-assets-check.yml
index 0a417f9b1c..4fe66e2a77 100644
--- a/.github/workflows/static-assets-check.yml
+++ b/.github/workflows/static-assets-check.yml
@@ -12,7 +12,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
- os: [ubuntu-latest]
+ os: [ubuntu-22.04]
python-version:
- "3.11"
node-version: [18, 20]
diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
index 5fef1c8352..854677b93c 100644
--- a/.github/workflows/unit-tests.yml
+++ b/.github/workflows/unit-tests.yml
@@ -15,7 +15,7 @@ concurrency:
jobs:
run-tests:
name: ${{ matrix.shard_name }}(py=${{ matrix.python-version }},dj=${{ matrix.django-version }},mongo=${{ matrix.mongo-version }})
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
strategy:
matrix:
python-version:
@@ -164,7 +164,7 @@ jobs:
overwrite: true
collect-and-verify:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Setup Python
@@ -229,7 +229,7 @@ jobs:
# https://github.com/orgs/community/discussions/33579
success:
name: Unit tests successful
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
if: always()
needs: [run-tests]
steps:
@@ -240,7 +240,7 @@ jobs:
jobs: ${{ toJSON(needs) }}
compile-warnings-report:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
needs: [run-tests]
steps:
- uses: actions/checkout@v4
@@ -268,7 +268,7 @@ jobs:
overwrite: true
merge-artifacts:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
needs: [compile-warnings-report]
steps:
- name: Merge Pytest Warnings JSON Artifacts
@@ -288,7 +288,7 @@ jobs:
# Combine and upload coverage reports.
coverage:
if: (github.repository == 'edx/edx-platform-private') || (github.repository == 'openedx/edx-platform' && (startsWith(github.base_ref, 'open-release') == false))
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
needs: [run-tests]
strategy:
matrix:
From 0cc9dee4e3c94768f753bf859070675480ed1887 Mon Sep 17 00:00:00 2001
From: Diana Huang
Date: Fri, 11 Oct 2024 10:09:09 -0400
Subject: [PATCH 13/32] Revert "fix(deps): update dependency
@edx/frontend-component-cookie-policy-banner to v2.6.0"
This reverts commit a39367b11a4880e124c1cd384b5c486484519414.
---
package-lock.json | 1094 +++------------------------------------------
package.json | 2 +-
2 files changed, 72 insertions(+), 1024 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 063a004ed8..82a6611d3a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,7 +17,7 @@
"@edx/brand-edx.org": "^2.0.7",
"@edx/edx-bootstrap": "1.0.4",
"@edx/edx-proctoring": "^4.18.1",
- "@edx/frontend-component-cookie-policy-banner": "2.6.0",
+ "@edx/frontend-component-cookie-policy-banner": "2.2.0",
"@edx/paragon": "2.6.4",
"@edx/studio-frontend": "^2.1.0",
"babel-loader": "^9.1.3",
@@ -1883,25 +1883,6 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/runtime-corejs3": {
- "version": "7.25.7",
- "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.25.7.tgz",
- "integrity": "sha512-gMmIEhg35sXk9Te5qbGp3W9YKrvLt3HV658/d3odWrHSqT0JeG5OzsJWFHRLiOohRyjRsJc/x03DhJm3i8VJxg==",
- "license": "MIT",
- "dependencies": {
- "core-js-pure": "^3.30.2",
- "regenerator-runtime": "^0.14.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/runtime-corejs3/node_modules/regenerator-runtime": {
- "version": "0.14.1",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
- "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
- "license": "MIT"
- },
"node_modules/@babel/runtime/node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
@@ -2125,16 +2106,14 @@
}
},
"node_modules/@edx/frontend-component-cookie-policy-banner": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/@edx/frontend-component-cookie-policy-banner/-/frontend-component-cookie-policy-banner-2.6.0.tgz",
- "integrity": "sha512-Em/7v41BtVJsHWyaxGOgefif3YOe2Bw8rDZO6Rci40Mcx035Q7L0IlyG3Sxz+GKQIoSOJDYf+EfODS5i01AJOg==",
- "license": "AGPL-3.0",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@edx/frontend-component-cookie-policy-banner/-/frontend-component-cookie-policy-banner-2.2.0.tgz",
+ "integrity": "sha512-Ye5tL8dU1v77Vtva9QkrgNG0I50sR317OK51/aQwHn50Aoq45FML29dbP8E5dy36QSqvF99bNFA4rptLigiTGQ==",
"dependencies": {
"@edx/brand-edx.org": "2.0.3",
- "@openedx/paragon": "^21.13.1",
+ "@edx/paragon": "^12.0.5",
"babel-preset-minify": "^0.5.0",
"classnames": "^2.3.1",
- "identity-obj-proxy": "^3.0.0",
"prop-types": "^15.6.1",
"type-fest": "^2.14.0",
"universal-cookie": "^4.0.0"
@@ -2147,83 +2126,36 @@
"node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/@edx/brand-edx.org": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@edx/brand-edx.org/-/brand-edx.org-2.0.3.tgz",
- "integrity": "sha512-QRmq2su1Xy+9GhY3NRZ+WdjtYWHmgfuKbVCW2skxgfgW9Q6kea8L6LrgigfrZtW+kQq/KdX2tVJcYBkB9xALtQ==",
- "license": "UNLICENSED"
+ "integrity": "sha512-QRmq2su1Xy+9GhY3NRZ+WdjtYWHmgfuKbVCW2skxgfgW9Q6kea8L6LrgigfrZtW+kQq/KdX2tVJcYBkB9xALtQ=="
},
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/@openedx/paragon": {
- "version": "21.13.1",
- "resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-21.13.1.tgz",
- "integrity": "sha512-sLL+Z3ZWIRM6x+OrKZV0S7/SQpEcSeRcDm7E3FzhsnAWudsJCTELvSW+84uy/8dwV7mJhttsBPqQEtNafbCyYA==",
- "license": "Apache-2.0",
- "workspaces": [
- "example",
- "component-generator",
- "www",
- "icons",
- "dependent-usage-analyzer"
- ],
+ "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/@edx/paragon": {
+ "version": "12.8.0",
+ "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-12.8.0.tgz",
+ "integrity": "sha512-gJVnozu4V1e2PCI0lFICvnrs2yi0ZzcaO5gJB9OjB1Pzf5GXpkKNWLtdTwjgp4fvzL9w4sqvyB30apbQzWW8yw==",
"dependencies": {
- "@fortawesome/fontawesome-svg-core": "^6.1.1",
- "@fortawesome/react-fontawesome": "^0.1.18",
- "@popperjs/core": "^2.11.4",
- "bootstrap": "^4.6.2",
- "chalk": "^4.1.2",
- "child_process": "^1.0.2",
- "classnames": "^2.3.1",
+ "@fortawesome/fontawesome-svg-core": "^1.2.30",
+ "@fortawesome/free-solid-svg-icons": "^5.14.0",
+ "@fortawesome/react-fontawesome": "^0.1.11",
+ "airbnb-prop-types": "^2.12.0",
+ "bootstrap": "^4.4.1",
+ "classnames": "^2.2.6",
"email-prop-type": "^3.0.0",
- "file-selector": "^0.6.0",
"font-awesome": "^4.7.0",
- "glob": "^8.0.3",
- "inquirer": "^8.2.5",
- "lodash.uniqby": "^4.7.0",
- "mailto-link": "^2.0.0",
- "prop-types": "^15.8.1",
- "react-bootstrap": "^1.6.5",
- "react-colorful": "^5.6.1",
- "react-dropzone": "^14.2.1",
- "react-focus-on": "^3.5.4",
- "react-imask": "^7.1.3",
- "react-loading-skeleton": "^3.1.0",
- "react-popper": "^2.2.5",
+ "mailto-link": "^1.0.0",
+ "prop-types": "^15.7.2",
+ "react-bootstrap": "^1.2.2",
+ "react-focus-on": "^3.5.0",
"react-proptype-conditional-require": "^1.0.4",
- "react-responsive": "^8.2.0",
- "react-table": "^7.7.0",
- "react-transition-group": "^4.4.2",
- "tabbable": "^5.3.3",
- "uncontrollable": "^7.2.1",
- "uuid": "^9.0.0"
- },
- "bin": {
- "paragon": "bin/paragon-scripts.js"
+ "react-responsive": "^6.1.1",
+ "react-table": "^7.6.1",
+ "react-transition-group": "^4.0.0",
+ "sanitize-html": "^1.20.0",
+ "tabbable": "^4.0.0"
},
"peerDependencies": {
- "react": "^16.8.6 || ^17.0.0",
- "react-dom": "^16.8.6 || ^17.0.0",
- "react-intl": "^5.25.1 || ^6.4.0"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "license": "MIT",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/attr-accept": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.4.tgz",
- "integrity": "sha512-2pA6xFIbdTUDCAwjN8nQwI+842VwzbDUXO2IYlpPXQIORgKnavorcr4Ce3rwh+zsNg9zK7QPsdvDj3Lum4WX4w==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
+ "prop-types": "^15.7.2",
+ "react": "^16.8.6",
+ "react-dom": "^16.8.6"
}
},
"node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/bootstrap": {
@@ -2240,81 +2172,15 @@
"url": "https://opencollective.com/bootstrap"
}
],
- "license": "MIT",
"peerDependencies": {
"jquery": "1.9.1 - 3",
"popper.js": "^1.16.1"
}
},
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/cli-cursor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
- "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
- "license": "MIT",
- "dependencies": {
- "restore-cursor": "^3.1.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/cli-width": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
- "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
- "license": "ISC",
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "license": "MIT",
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "license": "MIT"
- },
"node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
- "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
@@ -2324,206 +2190,24 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/email-prop-type/-/email-prop-type-3.0.1.tgz",
"integrity": "sha512-tONZGMEOOkadp5OBftuVXU8DsceWmINxYK+pqPFB4LT5ODjrPX/esel3WGqbV7d6in5/MnZE4n4QcqOr4gh7dg==",
- "license": "MIT",
"dependencies": {
"email-validator": "^2.0.4"
}
},
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/figures": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
- "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
- "license": "MIT",
- "dependencies": {
- "escape-string-regexp": "^1.0.5"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/glob": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
- "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
- "license": "ISC",
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^5.0.1",
- "once": "^1.3.0"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/inquirer": {
- "version": "8.2.6",
- "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz",
- "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==",
- "license": "MIT",
- "dependencies": {
- "ansi-escapes": "^4.2.1",
- "chalk": "^4.1.1",
- "cli-cursor": "^3.1.0",
- "cli-width": "^3.0.0",
- "external-editor": "^3.0.3",
- "figures": "^3.0.0",
- "lodash": "^4.17.21",
- "mute-stream": "0.0.8",
- "ora": "^5.4.1",
- "run-async": "^2.4.0",
- "rxjs": "^7.5.5",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0",
- "through": "^2.3.6",
- "wrap-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=12.0.0"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/intl-messageformat": {
- "version": "10.6.0",
- "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.6.0.tgz",
- "integrity": "sha512-AYKl/DY1nl75pJU8EK681JOVL40uQTNJe3yEMXKfydDFoz+5hNrM/PqjchueSMKGKCZKBVgeexqZwy3uC2B36Q==",
- "license": "BSD-3-Clause",
- "peer": true,
- "dependencies": {
- "@formatjs/ecma402-abstract": "2.1.0",
- "@formatjs/fast-memoize": "2.2.0",
- "@formatjs/icu-messageformat-parser": "2.7.9",
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/mailto-link": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/mailto-link/-/mailto-link-2.0.0.tgz",
- "integrity": "sha512-b5FErkZ4t6mpH1IFZSw7Mm2IQHXQ2R0/5Q4xd7Rv8dVkWvE54mFG/UW7HjfFazXFjXTNsM+dSX2tTeIDrV9K9A==",
- "license": "MIT",
- "dependencies": {
- "assert-ok": "~1.0.0",
- "cast-array": "~1.0.1",
- "object-filter": "~1.0.2",
- "query-string": "~7.0.0"
- },
- "engines": {
- "node": ">= 12"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/minimatch": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
- "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/mute-stream": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
- "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
- "license": "ISC"
- },
"node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/query-string": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.0.1.tgz",
- "integrity": "sha512-uIw3iRvHnk9to1blJCG3BTc+Ro56CBowJXKmNNAm3RulvPBzWLRqKSiiDk+IplJhsydwtuNMHi8UGQFcCLVfkA==",
- "license": "MIT",
- "dependencies": {
- "decode-uri-component": "^0.2.0",
- "filter-obj": "^1.1.0",
- "split-on-first": "^1.0.0",
- "strict-uri-encode": "^2.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/react-dropzone": {
- "version": "14.2.9",
- "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.9.tgz",
- "integrity": "sha512-jRZsMC7h48WONsOLHcmhyn3cRWJoIPQjPApvt/sJVfnYaB3Qltn025AoRTTJaj4WdmmgmLl6tUQg1s0wOhpodQ==",
- "license": "MIT",
- "dependencies": {
- "attr-accept": "^2.2.2",
- "file-selector": "^0.6.0",
- "prop-types": "^15.8.1"
- },
- "engines": {
- "node": ">= 10.13"
- },
- "peerDependencies": {
- "react": ">= 16.8 || 18.0.0"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/react-intl": {
- "version": "6.7.2",
- "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.7.2.tgz",
- "integrity": "sha512-v/lvAORTE70welhzqoIi1YI1yHvGE4/QX4W3JYNZoqRxH8ab8Q/Ed4Zem/ZVPZJN4byQ52U+2GESLy0zvY6IBw==",
- "license": "BSD-3-Clause",
- "peer": true,
- "dependencies": {
- "@formatjs/ecma402-abstract": "2.1.0",
- "@formatjs/icu-messageformat-parser": "2.7.9",
- "@formatjs/intl": "2.10.7",
- "@formatjs/intl-displaynames": "6.6.9",
- "@formatjs/intl-listformat": "7.5.8",
- "@types/hoist-non-react-statics": "^3.3.1",
- "@types/react": "16 || 17 || 18",
- "hoist-non-react-statics": "^3.3.2",
- "intl-messageformat": "10.6.0",
- "tslib": "^2.4.0"
- },
- "peerDependencies": {
- "react": "^16.6.0 || 17 || 18",
- "typescript": "^4.7 || 5"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
"node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
- "license": "BSD-3-Clause",
"dependencies": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
@@ -2535,62 +2219,6 @@
"react-dom": ">=16.6.0"
}
},
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/restore-cursor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
- "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
- "license": "MIT",
- "dependencies": {
- "onetime": "^5.1.0",
- "signal-exit": "^3.0.2"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/run-async": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
- "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/strict-uri-encode": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
- "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@edx/frontend-component-cookie-policy-banner/node_modules/uuid": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
- "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
- "funding": [
- "https://github.com/sponsors/broofa",
- "https://github.com/sponsors/ctavan"
- ],
- "license": "MIT",
- "bin": {
- "uuid": "dist/bin/uuid"
- }
- },
"node_modules/@edx/mockprock": {
"version": "1.0.2",
"resolved": "git+ssh://git@github.com/openedx/mockprock.git#3ad18c6888e6521e9bf7a4df0db6f8579b928235",
@@ -3510,139 +3138,35 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
- "node_modules/@formatjs/ecma402-abstract": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.1.0.tgz",
- "integrity": "sha512-SE2V2PE03K9U/YQZ3nxEOysRkQ/CfSwLHR789Uk9N0PTiWT6I+17UTDI97zYEwC1mbnjefqmtjbL8nunjPwGjw==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@formatjs/fast-memoize": "2.2.0",
- "@formatjs/intl-localematcher": "0.5.4",
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@formatjs/fast-memoize": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz",
- "integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@formatjs/icu-messageformat-parser": {
- "version": "2.7.9",
- "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.9.tgz",
- "integrity": "sha512-9Z5buDRMsTbplXknvRlDmnpWhZrayNVcVvkH0+SSz8Ll4XD/7Tcn8m1IjxM3iBJSwQbxwxb7/g0Fkx3d4j2osw==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@formatjs/ecma402-abstract": "2.1.0",
- "@formatjs/icu-skeleton-parser": "1.8.3",
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@formatjs/icu-skeleton-parser": {
- "version": "1.8.3",
- "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.3.tgz",
- "integrity": "sha512-TsKAP013ayZFbWWR2KWy+f9QVZh0yDFTPK3yE4OqU2gnzafvmKTodRtJLVpfZmpXWJ5y7BWD1AsyT14mcbLzig==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@formatjs/ecma402-abstract": "2.1.0",
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@formatjs/intl": {
- "version": "2.10.7",
- "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.10.7.tgz",
- "integrity": "sha512-26rNxo2nwQbbsVkV54ngml9XIA7bBzfQmELG6FFFF8eKzqzFrLKZzF8JBoBpPHgML4HKEUbGCQaBaARpKCN/sw==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@formatjs/ecma402-abstract": "2.1.0",
- "@formatjs/fast-memoize": "2.2.0",
- "@formatjs/icu-messageformat-parser": "2.7.9",
- "@formatjs/intl-displaynames": "6.6.9",
- "@formatjs/intl-listformat": "7.5.8",
- "intl-messageformat": "10.6.0",
- "tslib": "^2.4.0"
- },
- "peerDependencies": {
- "typescript": "^4.7 || 5"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
- "node_modules/@formatjs/intl-displaynames": {
- "version": "6.6.9",
- "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.6.9.tgz",
- "integrity": "sha512-2hmS+YJwiXB1deNYXO2/pY7Zv4QUrZHghZxkcnWxBLEODk990h9cNbkjNg/u/RaDeCRkKVrZ3ERTdBcgkTvn2Q==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@formatjs/ecma402-abstract": "2.1.0",
- "@formatjs/intl-localematcher": "0.5.4",
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@formatjs/intl-listformat": {
- "version": "7.5.8",
- "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.5.8.tgz",
- "integrity": "sha512-WzMiw6nA2uP0ZqbbuPs7tQ+gmYRTbE20lwur4QcKp5K5cgPhkCzRAhovkDFLhrc885c3p7Wjigx8kyg0hypmZw==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@formatjs/ecma402-abstract": "2.1.0",
- "@formatjs/intl-localematcher": "0.5.4",
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@formatjs/intl-localematcher": {
- "version": "0.5.4",
- "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz",
- "integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@formatjs/intl/node_modules/intl-messageformat": {
- "version": "10.6.0",
- "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.6.0.tgz",
- "integrity": "sha512-AYKl/DY1nl75pJU8EK681JOVL40uQTNJe3yEMXKfydDFoz+5hNrM/PqjchueSMKGKCZKBVgeexqZwy3uC2B36Q==",
- "license": "BSD-3-Clause",
- "peer": true,
- "dependencies": {
- "@formatjs/ecma402-abstract": "2.1.0",
- "@formatjs/fast-memoize": "2.2.0",
- "@formatjs/icu-messageformat-parser": "2.7.9",
- "tslib": "^2.4.0"
+ "node_modules/@fortawesome/fontawesome-common-types": {
+ "version": "0.2.36",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
+ "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==",
+ "hasInstallScript": true,
+ "engines": {
+ "node": ">=6"
}
},
"node_modules/@fortawesome/fontawesome-svg-core": {
- "version": "6.6.0",
- "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.6.0.tgz",
- "integrity": "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==",
- "license": "MIT",
+ "version": "1.2.36",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz",
+ "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==",
+ "hasInstallScript": true,
"dependencies": {
- "@fortawesome/fontawesome-common-types": "6.6.0"
+ "@fortawesome/fontawesome-common-types": "^0.2.36"
},
"engines": {
"node": ">=6"
}
},
- "node_modules/@fortawesome/fontawesome-svg-core/node_modules/@fortawesome/fontawesome-common-types": {
- "version": "6.6.0",
- "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz",
- "integrity": "sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==",
- "license": "MIT",
+ "node_modules/@fortawesome/free-solid-svg-icons": {
+ "version": "5.15.4",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz",
+ "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "^0.2.36"
+ },
"engines": {
"node": ">=6"
}
@@ -4713,17 +4237,6 @@
"@types/node": "*"
}
},
- "node_modules/@types/hoist-non-react-statics": {
- "version": "3.3.5",
- "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz",
- "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@types/react": "*",
- "hoist-non-react-statics": "^3.3.0"
- }
- },
"node_modules/@types/invariant": {
"version": "2.2.37",
"resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.37.tgz",
@@ -5307,6 +4820,7 @@
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
"integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
"dependencies": {
"type-fest": "^0.21.3"
},
@@ -5321,6 +4835,7 @@
"version": "0.21.3",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true,
"engines": {
"node": ">=10"
},
@@ -6609,26 +6124,6 @@
"node": ">= 0.6.0"
}
},
- "node_modules/base64-js": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
"node_modules/base64id": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
@@ -6692,31 +6187,6 @@
"file-uri-to-path": "1.0.0"
}
},
- "node_modules/bl": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
- "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
- "license": "MIT",
- "dependencies": {
- "buffer": "^5.5.0",
- "inherits": "^2.0.4",
- "readable-stream": "^3.4.0"
- }
- },
- "node_modules/bl/node_modules/readable-stream": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
- "license": "MIT",
- "dependencies": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/blob": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz",
@@ -6853,30 +6323,6 @@
"node-int64": "^0.4.0"
}
},
- "node_modules/buffer": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
- "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "base64-js": "^1.3.1",
- "ieee754": "^1.1.13"
- }
- },
"node_modules/buffer-alloc": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
@@ -7217,12 +6663,6 @@
"node": ">=10"
}
},
- "node_modules/chardet": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
- "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
- "license": "MIT"
- },
"node_modules/cheerio": {
"version": "1.0.0-rc.12",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
@@ -7261,12 +6701,6 @@
"url": "https://github.com/sponsors/fb55"
}
},
- "node_modules/child_process": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz",
- "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g==",
- "license": "ISC"
- },
"node_modules/chokidar": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
@@ -7608,18 +7042,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/cli-spinners": {
- "version": "2.9.2",
- "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
- "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/cli-width": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
@@ -8001,17 +7423,6 @@
"url": "https://opencollective.com/core-js"
}
},
- "node_modules/core-js-pure": {
- "version": "3.38.1",
- "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.38.1.tgz",
- "integrity": "sha512-BY8Etc1FZqdw1glX0XNOq2FDwfrg/VGqoZOZCdaL+UmdaqDwQwYXkMJT4t6In+zfEfOJDcM9T0KdbBeJg8KKCQ==",
- "hasInstallScript": true,
- "license": "MIT",
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/core-js"
- }
- },
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
@@ -8449,6 +7860,7 @@
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
+ "dev": true,
"engines": {
"node": ">=0.10"
}
@@ -8495,18 +7907,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/defaults": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
- "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
- "license": "MIT",
- "dependencies": {
- "clone": "^1.0.2"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
@@ -10585,32 +9985,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/external-editor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
- "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
- "license": "MIT",
- "dependencies": {
- "chardet": "^0.7.0",
- "iconv-lite": "^0.4.24",
- "tmp": "^0.0.33"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/external-editor/node_modules/tmp": {
- "version": "0.0.33",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
- "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
- "license": "MIT",
- "dependencies": {
- "os-tmpdir": "~1.0.2"
- },
- "engines": {
- "node": ">=0.6.0"
- }
- },
"node_modules/extglob": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
@@ -10827,18 +10201,6 @@
"url": "https://opencollective.com/webpack"
}
},
- "node_modules/file-selector": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz",
- "integrity": "sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==",
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.4.0"
- },
- "engines": {
- "node": ">= 12"
- }
- },
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
@@ -10904,15 +10266,6 @@
"node": ">=0.10"
}
},
- "node_modules/filter-obj": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
- "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
@@ -11713,12 +11066,6 @@
"node": ">=6"
}
},
- "node_modules/harmony-reflect": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz",
- "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==",
- "license": "(Apache-2.0 OR MPL-1.1)"
- },
"node_modules/has": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz",
@@ -12144,38 +11491,6 @@
"node": ">=4.0.0"
}
},
- "node_modules/identity-obj-proxy": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz",
- "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==",
- "license": "MIT",
- "dependencies": {
- "harmony-reflect": "^1.4.6"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/ieee754": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
- "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "BSD-3-Clause"
- },
"node_modules/ignore": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
@@ -12185,18 +11500,6 @@
"node": ">= 4"
}
},
- "node_modules/imask": {
- "version": "7.6.1",
- "resolved": "https://registry.npmjs.org/imask/-/imask-7.6.1.tgz",
- "integrity": "sha512-sJlIFM7eathUEMChTh9Mrfw/IgiWgJqBKq2VNbyXvBZ7ev/IlO6/KQTKlV/Fm+viQMLrFLG/zCuudrLIwgK2dg==",
- "license": "MIT",
- "dependencies": {
- "@babel/runtime-corejs3": "^7.24.4"
- },
- "engines": {
- "npm": ">=4.0.0"
- }
- },
"node_modules/immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
@@ -12843,15 +12146,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/is-interactive": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
- "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/is-lambda": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
@@ -13104,18 +12398,6 @@
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
"dev": true
},
- "node_modules/is-unicode-supported": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
- "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/is-utf8": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
@@ -16875,12 +16157,6 @@
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
"integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ=="
},
- "node_modules/lodash.uniqby": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz",
- "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==",
- "license": "MIT"
- },
"node_modules/log-symbols": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
@@ -17324,6 +16600,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
"engines": {
"node": ">=6"
}
@@ -18418,6 +17695,7 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
"dependencies": {
"mimic-fn": "^2.1.0"
},
@@ -18480,140 +17758,6 @@
"node": ">=0.4.0"
}
},
- "node_modules/ora": {
- "version": "5.4.1",
- "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
- "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
- "license": "MIT",
- "dependencies": {
- "bl": "^4.1.0",
- "chalk": "^4.1.0",
- "cli-cursor": "^3.1.0",
- "cli-spinners": "^2.5.0",
- "is-interactive": "^1.0.0",
- "is-unicode-supported": "^0.1.0",
- "log-symbols": "^4.1.0",
- "strip-ansi": "^6.0.0",
- "wcwidth": "^1.0.1"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/ora/node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "license": "MIT",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/ora/node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/ora/node_modules/cli-cursor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
- "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
- "license": "MIT",
- "dependencies": {
- "restore-cursor": "^3.1.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ora/node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "license": "MIT",
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/ora/node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "license": "MIT"
- },
- "node_modules/ora/node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ora/node_modules/log-symbols": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
- "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
- "license": "MIT",
- "dependencies": {
- "chalk": "^4.1.0",
- "is-unicode-supported": "^0.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/ora/node_modules/restore-cursor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
- "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
- "license": "MIT",
- "dependencies": {
- "onetime": "^5.1.0",
- "signal-exit": "^3.0.2"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ora/node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/os-homedir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
@@ -18627,6 +17771,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -20553,16 +19698,6 @@
"react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
- "node_modules/react-colorful": {
- "version": "5.6.1",
- "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz",
- "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==",
- "license": "MIT",
- "peerDependencies": {
- "react": ">=16.8.0",
- "react-dom": ">=16.8.0"
- }
- },
"node_modules/react-dom": {
"version": "16.14.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz",
@@ -20604,12 +19739,6 @@
"resolved": "https://registry.npmjs.org/react-element-proptypes/-/react-element-proptypes-1.0.0.tgz",
"integrity": "sha512-unJTkc58D9n1xTXKA8swrwrbDQAsCF/13oT6fDYtBVHPvFxITFuI20HCMNbNzI7tTUzsYmJ3iqjskwfLJkOUFA=="
},
- "node_modules/react-fast-compare": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
- "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==",
- "license": "MIT"
- },
"node_modules/react-focus-lock": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-1.19.1.tgz",
@@ -20703,33 +19832,6 @@
}
}
},
- "node_modules/react-imask": {
- "version": "7.6.1",
- "resolved": "https://registry.npmjs.org/react-imask/-/react-imask-7.6.1.tgz",
- "integrity": "sha512-vLNfzcCz62Yzx/GRGh5tiCph9Gbh2cZu+Tz8OiO5it2eNuuhpA0DWhhSlOtVtSJ80+Bx+vFK5De8eQ9AmbkXzA==",
- "license": "MIT",
- "dependencies": {
- "imask": "^7.6.1",
- "prop-types": "^15.8.1"
- },
- "engines": {
- "npm": ">=4.0.0"
- },
- "peerDependencies": {
- "react": ">=0.14.0"
- }
- },
- "node_modules/react-imask/node_modules/prop-types": {
- "version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "license": "MIT",
- "dependencies": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.13.1"
- }
- },
"node_modules/react-intl": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-2.9.0.tgz",
@@ -20767,15 +19869,6 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
- "node_modules/react-loading-skeleton": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/react-loading-skeleton/-/react-loading-skeleton-3.5.0.tgz",
- "integrity": "sha512-gxxSyLbrEAdXTKgfbpBEFZCO/P153DnqSCQau2+o6lNy1jgMRr2MmRmOzMmyrwSaSYLRB8g7b0waYPmUjz7IhQ==",
- "license": "MIT",
- "peerDependencies": {
- "react": ">=16.8.0"
- }
- },
"node_modules/react-overlays": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-5.2.1.tgz",
@@ -20814,21 +19907,6 @@
"react-is": "^16.13.1"
}
},
- "node_modules/react-popper": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz",
- "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==",
- "license": "MIT",
- "dependencies": {
- "react-fast-compare": "^3.0.1",
- "warning": "^4.0.2"
- },
- "peerDependencies": {
- "@popperjs/core": "^2.0.0",
- "react": "^16.8.0 || ^17 || ^18",
- "react-dom": "^16.8.0 || ^17 || ^18"
- }
- },
"node_modules/react-proptype-conditional-require": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/react-proptype-conditional-require/-/react-proptype-conditional-require-1.0.4.tgz",
@@ -20902,21 +19980,19 @@
}
},
"node_modules/react-responsive": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.2.0.tgz",
- "integrity": "sha512-iagCqVrw4QSjhxKp3I/YK6+ODkWY6G+YPElvdYKiUUbywwh9Ds0M7r26Fj2/7dWFFbOpcGnJE6uE7aMck8j5Qg==",
- "license": "MIT",
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-6.1.2.tgz",
+ "integrity": "sha512-AXentVC/kN3KED9zhzJv2pu4vZ0i6cSHdTtbCScVV1MT6F5KXaG2qs5D7WLmhdaOvmiMX8UfmS4ZSO+WPwDt4g==",
"dependencies": {
"hyphenate-style-name": "^1.0.0",
"matchmediaquery": "^0.3.0",
- "prop-types": "^15.6.1",
- "shallow-equal": "^1.1.0"
+ "prop-types": "^15.6.1"
},
"engines": {
"node": ">= 0.10"
},
"peerDependencies": {
- "react": ">=16.8.0"
+ "react": "^16.3.0"
}
},
"node_modules/react-responsive/node_modules/prop-types": {
@@ -22193,15 +21269,6 @@
"integrity": "sha512-1I1+G2gteLB8Tkt8YI1sJvSIfa0lWuRtC8GjvtyPBcLSF5jBCCJJqKrpER5JU5r6Bhe+i9/pK3VMuUcXu0kdwQ==",
"dev": true
},
- "node_modules/rxjs": {
- "version": "7.8.1",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
- "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.1.0"
- }
- },
"node_modules/safe-array-concat": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz",
@@ -23209,12 +22276,6 @@
"node": ">=8"
}
},
- "node_modules/shallow-equal": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz",
- "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==",
- "license": "MIT"
- },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -23273,7 +22334,8 @@
"node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true
},
"node_modules/simple-html-tokenizer": {
"version": "0.1.1",
@@ -23775,15 +22837,6 @@
"integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==",
"dev": true
},
- "node_modules/split-on-first": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
- "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/split-string": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
@@ -24665,10 +23718,9 @@
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="
},
"node_modules/tabbable": {
- "version": "5.3.3",
- "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz",
- "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==",
- "license": "MIT"
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-4.0.0.tgz",
+ "integrity": "sha512-H1XoH1URcBOa/rZZWxLxHCtOdVUEev+9vo5YdYhC9tCY4wnybX+VQrCYuy9ubkg69fCBxCONJOSLGfw0DWMffQ=="
},
"node_modules/table": {
"version": "6.8.2",
@@ -24933,7 +23985,8 @@
"node_modules/through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
- "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+ "dev": true
},
"node_modules/through2": {
"version": "3.0.2",
@@ -26048,15 +25101,6 @@
"node": ">=10.13.0"
}
},
- "node_modules/wcwidth": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
- "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
- "license": "MIT",
- "dependencies": {
- "defaults": "^1.0.3"
- }
- },
"node_modules/webidl-conversions": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",
@@ -26483,6 +25527,7 @@
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@@ -26543,6 +25588,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -26557,6 +25603,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -26567,7 +25614,8 @@
"node_modules/wrap-ansi/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
},
"node_modules/wrappy": {
"version": "1.0.2",
diff --git a/package.json b/package.json
index 6173420ab0..1f48500e6e 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,7 @@
"@edx/brand-edx.org": "^2.0.7",
"@edx/edx-bootstrap": "1.0.4",
"@edx/edx-proctoring": "^4.18.1",
- "@edx/frontend-component-cookie-policy-banner": "2.6.0",
+ "@edx/frontend-component-cookie-policy-banner": "2.2.0",
"@edx/paragon": "2.6.4",
"@edx/studio-frontend": "^2.1.0",
"babel-loader": "^9.1.3",
From b8c79abd2c8a7951cd374a14dded60187258d297 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 11 Oct 2024 11:05:01 -0600
Subject: [PATCH 14/32] feat: Upgrade Python dependency edx-enterprise (#35625)
Commit generated by workflow `openedx/edx-platform/.github/workflows/upgrade-one-python-dependency.yml@refs/heads/master`
Co-authored-by: kiram15 <31229189+kiram15@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index dd727a4b18..6fb109d62f 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -82,7 +82,7 @@ django-storages<1.14.4
# The team that owns this package will manually bump this package rather than having it pulled in automatically.
# This is to allow them to better control its deployment and to do it in a process that works better
# for them.
-edx-enterprise==4.27.2
+edx-enterprise==4.27.3
# Date: 2024-05-09
# This has to be constrained as well because newer versions of edx-i18n-tools need the
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 55793bd0c5..b92ad29f8a 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -467,7 +467,7 @@ edx-drf-extensions==10.4.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.27.2
+edx-enterprise==4.27.3
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index 1bdd373651..a5713bc3dd 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -741,7 +741,7 @@ edx-drf-extensions==10.4.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.27.2
+edx-enterprise==4.27.3
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 6f12d80467..57f36577a5 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -547,7 +547,7 @@ edx-drf-extensions==10.4.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.27.2
+edx-enterprise==4.27.3
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index d3b14a6f43..4f26caa958 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -571,7 +571,7 @@ edx-drf-extensions==10.4.0
# edx-when
# edxval
# openedx-learning
-edx-enterprise==4.27.2
+edx-enterprise==4.27.3
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
From 5b809673b6162a1962ab7f9ca11ec9219e422d92 Mon Sep 17 00:00:00 2001
From: Braden MacDonald
Date: Fri, 11 Oct 2024 10:23:32 -0700
Subject: [PATCH 15/32] refactor: get rid of XBlockRuntimeSystem for v2
libraries (#35624)
---
openedx/core/djangoapps/xblock/api.py | 32 ++----
openedx/core/djangoapps/xblock/apps.py | 12 +--
.../xblock/runtime/learning_core_runtime.py | 6 +-
.../core/djangoapps/xblock/runtime/runtime.py | 101 +++++-------------
4 files changed, 45 insertions(+), 106 deletions(-)
diff --git a/openedx/core/djangoapps/xblock/api.py b/openedx/core/djangoapps/xblock/api.py
index 43ec3909bc..dbb7c82467 100644
--- a/openedx/core/djangoapps/xblock/api.py
+++ b/openedx/core/djangoapps/xblock/api.py
@@ -25,13 +25,13 @@ from xblock.core import XBlock
from xblock.exceptions import NoSuchUsage, NoSuchViewError
from xblock.plugin import PluginMissingError
+from openedx.core.types import User as UserType
from openedx.core.djangoapps.xblock.apps import get_xblock_app_config
from openedx.core.djangoapps.xblock.learning_context.manager import get_learning_context_impl
from openedx.core.djangoapps.xblock.runtime.learning_core_runtime import (
LearningCoreFieldData,
LearningCoreXBlockRuntime,
)
-from openedx.core.djangoapps.xblock.runtime.runtime import XBlockRuntimeSystem as _XBlockRuntimeSystem
from .utils import get_secure_token_for_xblock_handler, get_xblock_id_for_anonymous_user
from .runtime.learning_core_runtime import LearningCoreXBlockRuntime
@@ -54,33 +54,21 @@ class CheckPerm(Enum):
CAN_EDIT = 3
-def get_runtime_system():
+def get_runtime(user: UserType):
"""
- Return a new XBlockRuntimeSystem.
+ Return a new XBlockRuntime.
- TODO: Refactor to get rid of the XBlockRuntimeSystem entirely and just
- create the LearningCoreXBlockRuntime and return it. We used to want to keep
- around a long lived runtime system (a factory that returns runtimes) for
- caching purposes, and have it dynamically construct a runtime on request.
- Now we're just re-constructing both the system and the runtime in this call
- and returning it every time, because:
-
- 1. We no longer have slow, Blockstore-style definitions to cache, so the
- performance of this is perfectly acceptable.
- 2. Having a singleton increases complexity and the chance of bugs.
- 3. Creating the XBlockRuntimeSystem every time only takes about 10-30 µs.
-
- Given that, the extra XBlockRuntimeSystem class just adds confusion. But
- despite that, it's tested, working code, and so I'm putting off refactoring
- for now.
+ Each XBlockRuntime is bound to one user (and usually one request or one
+ celery task). It is typically used just to load and render a single block,
+ but the API _does_ allow a single runtime instance to load multiple blocks
+ (as long as they're for the same user).
"""
- params = get_xblock_app_config().get_runtime_system_params()
+ params = get_xblock_app_config().get_runtime_params()
params.update(
- runtime_class=LearningCoreXBlockRuntime,
handler_url=get_handler_url,
authored_data_store=LearningCoreFieldData(),
)
- runtime = _XBlockRuntimeSystem(**params)
+ runtime = LearningCoreXBlockRuntime(user, **params)
return runtime
@@ -121,7 +109,7 @@ def load_block(usage_key, user, *, check_permission: CheckPerm | None = CheckPer
# e.g. a course might specify that all 'problem' XBlocks have 'max_attempts'
# set to 3.
# field_overrides = context_impl.get_field_overrides(usage_key)
- runtime = get_runtime_system().get_runtime(user=user)
+ runtime = get_runtime(user=user)
try:
return runtime.get_block(usage_key)
diff --git a/openedx/core/djangoapps/xblock/apps.py b/openedx/core/djangoapps/xblock/apps.py
index 5ba2361322..848470a3a9 100644
--- a/openedx/core/djangoapps/xblock/apps.py
+++ b/openedx/core/djangoapps/xblock/apps.py
@@ -16,9 +16,9 @@ class XBlockAppConfig(AppConfig):
verbose_name = 'New XBlock Runtime'
label = 'xblock_new' # The name 'xblock' is already taken by ORA2's 'openassessment.xblock' app :/
- def get_runtime_system_params(self):
+ def get_runtime_params(self):
"""
- Get the XBlockRuntimeSystem parameters appropriate for viewing and/or
+ Get the LearningCoreXBlockRuntime parameters appropriate for viewing and/or
editing XBlock content.
"""
raise NotImplementedError
@@ -43,9 +43,9 @@ class LmsXBlockAppConfig(XBlockAppConfig):
LMS-specific configuration of the XBlock Runtime django app.
"""
- def get_runtime_system_params(self):
+ def get_runtime_params(self):
"""
- Get the XBlockRuntimeSystem parameters appropriate for viewing and/or
+ Get the LearningCoreXBlockRuntime parameters appropriate for viewing and/or
editing XBlock content in the LMS
"""
return dict(
@@ -65,9 +65,9 @@ class StudioXBlockAppConfig(XBlockAppConfig):
Studio-specific configuration of the XBlock Runtime django app.
"""
- def get_runtime_system_params(self):
+ def get_runtime_params(self):
"""
- Get the XBlockRuntimeSystem parameters appropriate for viewing and/or
+ Get the LearningCoreXBlockRuntime parameters appropriate for viewing and/or
editing XBlock content in Studio
"""
return dict(
diff --git a/openedx/core/djangoapps/xblock/runtime/learning_core_runtime.py b/openedx/core/djangoapps/xblock/runtime/learning_core_runtime.py
index 26aa7af60f..41fd79f5a0 100644
--- a/openedx/core/djangoapps/xblock/runtime/learning_core_runtime.py
+++ b/openedx/core/djangoapps/xblock/runtime/learning_core_runtime.py
@@ -211,7 +211,7 @@ class LearningCoreXBlockRuntime(XBlockRuntime):
# We've pre-loaded the fields for this block, so the FieldData shouldn't
# consider these values "changed" in its sense of "you have to persist
# these because we've altered the field values from what was stored".
- self.system.authored_data_store.mark_unchanged(block)
+ self.authored_data_store.mark_unchanged(block)
return block
@@ -221,7 +221,7 @@ class LearningCoreXBlockRuntime(XBlockRuntime):
This gets called by block.save() - do not call this directly.
"""
- if not self.system.authored_data_store.has_changes(block):
+ if not self.authored_data_store.has_changes(block):
return # No changes, so no action needed.
# Verify that the user has permission to write to authored data in this
@@ -254,7 +254,7 @@ class LearningCoreXBlockRuntime(XBlockRuntime):
},
created=now,
)
- self.system.authored_data_store.mark_unchanged(block)
+ self.authored_data_store.mark_unchanged(block)
def _get_component_from_usage_key(self, usage_key):
"""
diff --git a/openedx/core/djangoapps/xblock/runtime/runtime.py b/openedx/core/djangoapps/xblock/runtime/runtime.py
index 5746af491d..fe633f686f 100644
--- a/openedx/core/djangoapps/xblock/runtime/runtime.py
+++ b/openedx/core/djangoapps/xblock/runtime/runtime.py
@@ -95,18 +95,30 @@ class XBlockRuntime(RuntimeShim, Runtime):
# currently only used to track if we're in the studio_view (see below under service())
view_name: str | None
- def __init__(self, system: XBlockRuntimeSystem, user: UserType | None):
+ def __init__(
+ self,
+ user: UserType | None,
+ *,
+ handler_url: Callable[[UsageKey, str, UserType | None], str],
+ student_data_mode: StudentDataMode,
+ id_reader: Optional[IdReader] = None,
+ authored_data_store: Optional[FieldData] = None,
+ ):
super().__init__(
- id_reader=system.id_reader,
+ id_reader=id_reader or OpaqueKeyReader(),
mixins=(
LmsBlockMixin, # Adds Non-deprecated LMS/Studio functionality
XBlockShim, # Adds deprecated LMS/Studio functionality / backwards compatibility
),
default_class=None,
select=None,
- id_generator=system.id_generator,
+ id_generator=MemoryIdManager(), # We don't really use id_generator until we need to support asides
)
- self.system = system
+ assert student_data_mode in (StudentDataMode.Ephemeral, StudentDataMode.Persisted)
+ self.authored_data_store = authored_data_store
+ self.children_data_store = None
+ self.student_data_mode = student_data_mode
+ self.handler_url_fn = handler_url
self.user = user
# self.user_id must be set as a separate attribute since base class sets it:
if self.user is None:
@@ -126,7 +138,7 @@ class XBlockRuntime(RuntimeShim, Runtime):
if thirdparty:
log.warning("thirdparty handlers are not supported by this runtime for XBlock %s.", type(block))
- url = self.system.handler_url(block.scope_ids.usage_id, handler_name, self.user)
+ url = self.handler_url_fn(block.scope_ids.usage_id, handler_name, self.user)
if suffix:
if not url.endswith('/'):
url += '/'
@@ -275,7 +287,7 @@ class XBlockRuntime(RuntimeShim, Runtime):
# the preview engine, and 'main' otherwise.
# For backwards compatibility, we check the student_data_mode (Ephemeral indicates CMS) and the
# view_name for 'studio_view.' self.view_name is set by render() below.
- if self.system.student_data_mode == StudentDataMode.Ephemeral and self.view_name != 'studio_view':
+ if self.student_data_mode == StudentDataMode.Ephemeral and self.view_name != 'studio_view':
return MakoService(namespace_prefix='lms.')
return MakoService()
elif service_name == "i18n":
@@ -301,14 +313,12 @@ class XBlockRuntime(RuntimeShim, Runtime):
return EventPublishingService(self.user, context_key, make_track_function())
elif service_name == 'enrollments':
return EnrollmentsService()
+ elif service_name == 'error_tracker':
+ return make_error_tracker()
- # Check if the XBlockRuntimeSystem wants to handle this:
- service = self.system.get_service(block, service_name)
# Otherwise, fall back to the base implementation which loads services
# defined in the constructor:
- if service is None:
- service = super().service(block, service_name)
- return service
+ return super().service(block, service_name)
def _init_field_data_for_block(self, block: XBlock) -> FieldData:
"""
@@ -322,7 +332,7 @@ class XBlockRuntime(RuntimeShim, Runtime):
assert isinstance(self.user_id, str) and self.user_id.startswith("anon")
kvs = EphemeralKeyValueStore()
student_data_store = KvsFieldData(kvs)
- elif self.system.student_data_mode == StudentDataMode.Ephemeral:
+ elif self.student_data_mode == StudentDataMode.Ephemeral:
# We're in an environment like Studio where we want to let the
# author test blocks out but not permanently save their state.
kvs = EphemeralKeyValueStore()
@@ -341,10 +351,10 @@ class XBlockRuntime(RuntimeShim, Runtime):
student_data_store = KvsFieldData(kvs=DjangoKeyValueStore(field_data_cache))
return SplitFieldData({
- Scope.content: self.system.authored_data_store,
- Scope.settings: self.system.authored_data_store,
- Scope.parent: self.system.authored_data_store,
- Scope.children: self.system.children_data_store,
+ Scope.content: self.authored_data_store,
+ Scope.settings: self.authored_data_store,
+ Scope.parent: self.authored_data_store,
+ Scope.children: self.children_data_store,
Scope.user_state_summary: student_data_store,
Scope.user_state: student_data_store,
Scope.user_info: student_data_store,
@@ -407,62 +417,3 @@ class XBlockRuntime(RuntimeShim, Runtime):
"""
# Subclasses should override this
return None
-
-
-class XBlockRuntimeSystem:
- """
- This class is essentially a factory for XBlockRuntimes. This is a
- long-lived object which provides the behavior specific to the application
- that wants to use XBlocks. Unlike XBlockRuntime, a single instance of this
- class can be used with many different XBlocks, whereas each XBlock gets its
- own instance of XBlockRuntime.
- """
- def __init__(
- self,
- handler_url: Callable[[UsageKey, str, UserType | None], str],
- student_data_mode: StudentDataMode,
- runtime_class: type[XBlockRuntime],
- id_reader: Optional[IdReader] = None,
- authored_data_store: Optional[FieldData] = None,
- ):
- """
- args:
- handler_url: A method to get URLs to call XBlock handlers. It must
- implement this signature:
- handler_url(
- usage_key: UsageKey,
- handler_name: str,
- user: User | AnonymousUser | None
- ) -> str
- student_data_mode: Specifies whether student data should be kept
- in a temporary in-memory store (e.g. Studio) or persisted
- forever in the database.
- runtime_class: What runtime to use, e.g. LearningCoreXBlockRuntime
- """
- self.handler_url = handler_url
- self.id_reader = id_reader or OpaqueKeyReader()
- self.id_generator = MemoryIdManager() # We don't really use id_generator until we need to support asides
- self.runtime_class = runtime_class
- self.authored_data_store = authored_data_store
- self.children_data_store = None
- assert student_data_mode in (StudentDataMode.Ephemeral, StudentDataMode.Persisted)
- self.student_data_mode = student_data_mode
-
- def get_runtime(self, user: UserType | None) -> XBlockRuntime:
- """
- Get the XBlock runtime for the specified Django user. The user can be
- a regular user, an AnonymousUser, or None.
- """
- return self.runtime_class(self, user)
-
- def get_service(self, block, service_name: str):
- """
- Get a runtime service
-
- Runtime services may come from this XBlockRuntimeSystem,
- or if this method returns None, they may come from the
- XBlockRuntime.
- """
- if service_name == 'error_tracker':
- return make_error_tracker()
- return None # None means see if XBlockRuntime offers this service
From e4c69565f451a63d6796773099ccd42c57ab187f Mon Sep 17 00:00:00 2001
From: Dima Alipov
Date: Wed, 27 Mar 2024 17:02:51 +0200
Subject: [PATCH 16/32] feat: adaptive display of links
Do not display the 'Learn more' and 'Share feedback' links for
banner that is enabled by the context_course.discussions_settings
flag if the URLs for these links are not set in the settings.
---
cms/templates/course_outline.html | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/cms/templates/course_outline.html b/cms/templates/course_outline.html
index f44fdcfc80..61f524123b 100644
--- a/cms/templates/course_outline.html
+++ b/cms/templates/course_outline.html
@@ -76,14 +76,18 @@ from django.urls import reverse
${_("This course run is using an upgraded version of edx discussion forum. In order to display the discussions sidebar, discussions xBlocks will no longer be visible to learners.")}
From cf8ed149d4cb4c749c90fe8379edd8df88e0ad1d Mon Sep 17 00:00:00 2001
From: Attiya Ishaque
Date: Mon, 14 Oct 2024 16:45:04 +0500
Subject: [PATCH 18/32] fix: fix track selection (#35648)
---
common/djangoapps/course_modes/views.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/common/djangoapps/course_modes/views.py b/common/djangoapps/course_modes/views.py
index 046767a26b..759073a135 100644
--- a/common/djangoapps/course_modes/views.py
+++ b/common/djangoapps/course_modes/views.py
@@ -195,6 +195,7 @@ class ChooseModeView(View):
"content_gating_enabled": gated_content,
"course_duration_limit_enabled": CourseDurationLimitConfig.enabled_for_enrollment(request.user, course),
"search_courses_url": urljoin(settings.MKTG_URLS.get('ROOT'), '/search?tab=course'),
+ "course_run_key": course_id,
}
context.update(
get_experiment_user_metadata_context(
From af69cbe6714f21d70d77a27ad75d00bdfee3911d Mon Sep 17 00:00:00 2001
From: Fatima Sohail <68312464+sohailfatima@users.noreply.github.com>
Date: Mon, 14 Oct 2024 19:20:02 +0500
Subject: [PATCH 19/32] feat: waffle flag based switch to ses for goal reminder
email (#35615)
* feat: waffle based switch to ses for goal reminder email
* test: added test cases for ace message parameters
* feat: updated logic to specify channel name
* chore: update edx-ace version
---
.../commands/goal_reminder_email.py | 15 +++++++--
.../tests/test_goal_reminder_email.py | 33 ++++++++++++++++++-
lms/envs/common.py | 3 ++
.../features/course_experience/__init__.py | 10 ++++++
requirements/edx/base.txt | 2 +-
requirements/edx/development.txt | 2 +-
requirements/edx/doc.txt | 2 +-
requirements/edx/testing.txt | 2 +-
8 files changed, 62 insertions(+), 7 deletions(-)
diff --git a/lms/djangoapps/course_goals/management/commands/goal_reminder_email.py b/lms/djangoapps/course_goals/management/commands/goal_reminder_email.py
index 0f8227e320..abd1dc5375 100644
--- a/lms/djangoapps/course_goals/management/commands/goal_reminder_email.py
+++ b/lms/djangoapps/course_goals/management/commands/goal_reminder_email.py
@@ -23,7 +23,7 @@ from openedx.core.djangoapps.site_configuration import helpers as configuration_
from openedx.core.djangoapps.user_api.preferences.api import get_user_preference
from openedx.core.lib.celery.task_utils import emulate_http_request
from openedx.features.course_duration_limits.access import get_user_course_expiration_date
-from openedx.features.course_experience import ENABLE_COURSE_GOALS
+from openedx.features.course_experience import ENABLE_COURSE_GOALS, ENABLE_SES_FOR_GOALREMINDER
from openedx.features.course_experience.url_helpers import get_learning_mfe_home_url
log = logging.getLogger(__name__)
@@ -86,13 +86,24 @@ def send_ace_message(goal):
'programs_url': getattr(settings, 'ACE_EMAIL_PROGRAMS_URL', None),
})
+ options = {'transactional': True}
+
+ is_ses_enabled = ENABLE_SES_FOR_GOALREMINDER.is_enabled(goal.course_key)
+
+ if is_ses_enabled:
+ options = {
+ 'transactional': True,
+ 'from_address': settings.LMS_COMM_DEFAULT_FROM_EMAIL,
+ 'override_default_channel': 'django_email',
+ }
+
msg = Message(
name="goalreminder",
app_label="course_goals",
recipient=Recipient(user.id, user.email),
language=language,
context=message_context,
- options={'transactional': True},
+ options=options,
)
with emulate_http_request(site, user):
diff --git a/lms/djangoapps/course_goals/management/commands/tests/test_goal_reminder_email.py b/lms/djangoapps/course_goals/management/commands/tests/test_goal_reminder_email.py
index ad60420e0d..5b98b202d4 100644
--- a/lms/djangoapps/course_goals/management/commands/tests/test_goal_reminder_email.py
+++ b/lms/djangoapps/course_goals/management/commands/tests/test_goal_reminder_email.py
@@ -5,6 +5,7 @@ from pytz import UTC
from unittest import mock # lint-amnesty, pylint: disable=wrong-import-order
import ddt
+from django.conf import settings
from django.core.management import call_command
from django.test import TestCase
from edx_toggles.toggles.testutils import override_waffle_flag
@@ -20,7 +21,7 @@ from lms.djangoapps.course_goals.tests.factories import (
from lms.djangoapps.certificates.data import CertificateStatuses
from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFactory
from openedx.core.djangolib.testing.utils import skip_unless_lms
-from openedx.features.course_experience import ENABLE_COURSE_GOALS
+from openedx.features.course_experience import ENABLE_COURSE_GOALS, ENABLE_SES_FOR_GOALREMINDER
# Some constants just for clarity of tests (assuming week starts on a Monday, as March 2021 used below does)
MONDAY = 0
@@ -180,3 +181,33 @@ class TestGoalReminderEmailCommand(TestCase):
def test_old_course(self, end):
self.make_valid_goal(overview__end=end)
self.call_command(expect_sent=False)
+
+ @mock.patch('lms.djangoapps.course_goals.management.commands.goal_reminder_email.ace.send')
+ def test_params_with_ses(self, mock_ace):
+ """Test that the parameters of the msg passed to ace.send() are set correctly when SES is enabled"""
+ with override_waffle_flag(ENABLE_SES_FOR_GOALREMINDER, active=None):
+ goal = self.make_valid_goal()
+ flag = get_waffle_flag_model().get(ENABLE_SES_FOR_GOALREMINDER.name)
+ flag.users.add(goal.user)
+
+ with freeze_time('2021-03-02 10:00:00'):
+ call_command('goal_reminder_email')
+
+ assert mock_ace.call_count == 1
+ msg = mock_ace.call_args[0][0]
+ assert msg.options['override_default_channel'] == 'django_email'
+ assert msg.options['from_address'] == settings.LMS_COMM_DEFAULT_FROM_EMAIL
+
+ @mock.patch('lms.djangoapps.course_goals.management.commands.goal_reminder_email.ace.send')
+ def test_params_without_ses(self, mock_ace):
+ """Test that the parameters of the msg passed to ace.send() are set correctly when SES is not enabled"""
+ self.make_valid_goal()
+
+ with freeze_time('2021-03-02 10:00:00'):
+ call_command('goal_reminder_email')
+
+ assert mock_ace.call_count == 1
+ msg = mock_ace.call_args[0][0]
+ assert msg.options['transactional'] is True
+ assert 'override_default_channel' not in msg.options
+ assert 'from_address' not in msg.options
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 770c642d5f..b59d60c751 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -5534,3 +5534,6 @@ SURVEY_REPORT_EXTRA_DATA = {}
# .. for now it wil impact country listing in auth flow and user profile.
# .. eg ['US', 'CA']
DISABLED_COUNTRIES = []
+
+
+LMS_COMM_DEFAULT_FROM_EMAIL = "no-reply@example.com"
diff --git a/openedx/features/course_experience/__init__.py b/openedx/features/course_experience/__init__.py
index f8a662709e..a45d863e09 100644
--- a/openedx/features/course_experience/__init__.py
+++ b/openedx/features/course_experience/__init__.py
@@ -34,6 +34,16 @@ COURSE_PRE_START_ACCESS_FLAG = WaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.pre_start_ac
# .. toggle_warning: This temporary feature toggle does not have a target removal date.
ENABLE_COURSE_GOALS = CourseWaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.enable_course_goals', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
+# .. toggle_name: course_experience.enable_ses_for_goalreminder
+# .. toggle_implementation: CourseWaffleFlag
+# .. toggle_default: False
+# .. toggle_description: Used to determine whether or not to use AWS SES to send goal reminder emails for the course.
+# .. toggle_use_cases: opt_in, temporary
+# .. toggle_creation_date: 2024-10-06
+# .. toggle_target_removal_date: None
+# .. toggle_warning: This temporary feature toggle does not have a target removal date.
+ENABLE_SES_FOR_GOALREMINDER = CourseWaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.enable_ses_for_goalreminder', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
+
# Waffle flag to enable anonymous access to a course
SEO_WAFFLE_FLAG_NAMESPACE = 'seo'
COURSE_ENABLE_UNENROLLED_ACCESS_FLAG = CourseWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index b92ad29f8a..2bac3df49c 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -401,7 +401,7 @@ drf-yasg==1.21.7
# via
# django-user-tasks
# edx-api-doc-tools
-edx-ace==1.11.2
+edx-ace==1.11.3
# via -r requirements/edx/kernel.in
edx-api-doc-tools==2.0.0
# via
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index a5713bc3dd..d61db58ae4 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -657,7 +657,7 @@ drf-yasg==1.21.7
# -r requirements/edx/testing.txt
# django-user-tasks
# edx-api-doc-tools
-edx-ace==1.11.2
+edx-ace==1.11.3
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 57f36577a5..32ef31e8d3 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -481,7 +481,7 @@ drf-yasg==1.21.7
# -r requirements/edx/base.txt
# django-user-tasks
# edx-api-doc-tools
-edx-ace==1.11.2
+edx-ace==1.11.3
# via -r requirements/edx/base.txt
edx-api-doc-tools==2.0.0
# via
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 4f26caa958..77b3896967 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -505,7 +505,7 @@ drf-yasg==1.21.7
# -r requirements/edx/base.txt
# django-user-tasks
# edx-api-doc-tools
-edx-ace==1.11.2
+edx-ace==1.11.3
# via -r requirements/edx/base.txt
edx-api-doc-tools==2.0.0
# via
From 17122eb442c5c9ed1d5f00f81c68c514d927f617 Mon Sep 17 00:00:00 2001
From: Justin Hynes
Date: Tue, 15 Oct 2024 08:33:36 -0400
Subject: [PATCH 20/32] refactor: remove PII from log messages in programs
tasks (#35623)
* refactor: remove PII from log messages in programs tasks
[APER-3723]
Refactors a number of log statements from the Celery tasks in the Programs Django app. This removes username (considered PII) from the log statements and opts to use LMS User ID instead.
* fix: quality
---
openedx/core/djangoapps/programs/tasks.py | 58 ++++++++++---------
.../djangoapps/programs/tests/test_tasks.py | 12 ++--
2 files changed, 36 insertions(+), 34 deletions(-)
diff --git a/openedx/core/djangoapps/programs/tasks.py b/openedx/core/djangoapps/programs/tasks.py
index 42548fd494..d3b4b867e4 100644
--- a/openedx/core/djangoapps/programs/tasks.py
+++ b/openedx/core/djangoapps/programs/tasks.py
@@ -315,14 +315,16 @@ def award_program_certificates(self, username): # lint-amnesty, pylint: disable
if str(programs_without_certificates[0]).lower() == "all":
return
- LOGGER.info(f"Running task award_program_certificates for user {student}")
+ LOGGER.info(f"Running task award_program_certificates for user {student.id}")
try:
completed_programs = {}
for site in Site.objects.all():
completed_programs.update(get_completed_programs(site, student))
if not completed_programs:
- LOGGER.warning(f"Task award_program_certificates was called for user {student} with no completed programs")
+ LOGGER.warning(
+ f"Task award_program_certificates was called for user {student.id} with no completed programs"
+ )
return
# determine which program certificates have been awarded to the user
@@ -331,7 +333,7 @@ def award_program_certificates(self, username): # lint-amnesty, pylint: disable
# program is part of the "programs without certificates" list in our site configuration
awarded_and_skipped_program_uuids = list(set(existing_program_uuids + list(programs_without_certificates)))
except Exception as exc:
- error_msg = f"Failed to determine program certificates to be awarded for user {student}: {exc}"
+ error_msg = f"Failed to determine program certificates to be awarded for user {student.id}: {exc}"
LOGGER.exception(error_msg)
raise MaxRetriesExceededError(
f"Failed to award a program certificate to user {student.id}. Reason: {error_msg}"
@@ -360,18 +362,18 @@ def award_program_certificates(self, username): # lint-amnesty, pylint: disable
for program_uuid in new_program_uuids:
try:
award_program_certificate(credentials_client, student, program_uuid)
- LOGGER.info(f"Awarded program certificate to user {student} in program {program_uuid}")
+ LOGGER.info(f"Awarded program certificate to user {student.id} in program {program_uuid}")
except HTTPError as exc:
if exc.response.status_code == 404:
LOGGER.warning(
- f"Unable to award a program certificate to user {student} in program {program_uuid}. A "
+ f"Unable to award a program certificate to user {student.id} in program {program_uuid}. A "
f"certificate configuration for program {program_uuid} could not be found, the program might "
"not be configured correctly in Credentials"
)
elif exc.response.status_code == 429:
# Let celery handle retry attempts and backoff
error_msg = (
- f"Rate limited. Attempting to award certificate to user {student} in program {program_uuid}."
+ f"Rate limited. Attempting to award certificate to user {student.id} in program {program_uuid}."
)
LOGGER.warning(error_msg)
raise MaxRetriesExceededError(
@@ -379,33 +381,33 @@ def award_program_certificates(self, username): # lint-amnesty, pylint: disable
) from exc
else:
LOGGER.warning(
- f"Unable to award program certificate to user {student} in program {program_uuid}. The program "
- "might not be configured correctly in Credentials"
+ f"Unable to award program certificate to user {student.id} in program {program_uuid}. The "
+ "program might not be configured correctly in Credentials"
)
except Exception as exc: # pylint: disable=broad-except
# keep trying to award other certs, but let celery retry the whole task to fix any missing entries
LOGGER.exception(
- f"Failed to award program certificate to user {student} in program {program_uuid}: {exc}"
+ f"Failed to award program certificate to user {student.id} in program {program_uuid}: {exc}"
)
failed_program_certificate_award_attempts.append(program_uuid)
if failed_program_certificate_award_attempts:
# N.B. This logic assumes that this task is idempotent
- LOGGER.info(f"Retrying failed tasks to award program certificate(s) to user {student}")
+ LOGGER.info(f"Retrying failed tasks to award program certificate(s) to user {student.id}")
# The error message may change on each reattempt but will never be raised until the max number of retries
# have been exceeded. It is unlikely that this list will change by the time it reaches its maximimum number
# of attempts.
error_msg = (
- f"Failed to award program certificate(s) for user {student} in programs "
+ f"Failed to award program certificate(s) for user {student.id} in programs "
f"{failed_program_certificate_award_attempts}"
)
raise MaxRetriesExceededError(
f"Failed to award a program certificate to user {student.id}. Reason: {error_msg}"
)
else:
- LOGGER.warning(f"User {student} is not eligible for any new program certificates")
+ LOGGER.warning(f"User {student.id} is not eligible for any new program certificates")
- LOGGER.info(f"Successfully completed the task award_program_certificates for user {student}")
+ LOGGER.info(f"Successfully completed the task award_program_certificates for user {student.id}")
# pylint: disable=W0613
@@ -504,7 +506,7 @@ def award_course_certificate(self, username, course_run_key):
)
return
- LOGGER.info(f"Running task award_course_certificate for user {user}")
+ LOGGER.info(f"Running task award_course_certificate for user {user.id}")
try:
course_key = CourseKey.from_string(course_run_key)
except InvalidKeyError as exc:
@@ -574,7 +576,7 @@ def award_course_certificate(self, username, course_run_key):
org=course_key.org,
)
except Exception as exc:
- error_msg = f"Failed to post course certificate to be awarded for user {user}."
+ error_msg = f"Failed to post course certificate to be awarded for user {user.id}."
raise MaxRetriesExceededError(
f"Failed to award course certificate for user {user.id} for course {course_run_key}. Reason: {error_msg}"
) from exc
@@ -628,14 +630,14 @@ def revoke_program_certificates(self, username, course_key): # lint-amnesty, py
)
return
- LOGGER.info(f"Running task revoke_program_certificates for user {student}")
+ LOGGER.info(f"Running task revoke_program_certificates for user {student.id}")
try:
inverted_programs = get_inverted_programs(student)
course_specific_programs = inverted_programs.get(course_key)
if not course_specific_programs:
LOGGER.warning(
- f"Task revoke_program_certificates was called for user {student} and course run {course_key} with no "
- "engaged programs"
+ f"Task revoke_program_certificates was called for user {student.id} and course run {course_key} with "
+ "no engaged programs"
)
return
@@ -644,7 +646,7 @@ def revoke_program_certificates(self, username, course_key): # lint-amnesty, py
except Exception as exc:
error_msg = (
f"Failed to determine if any program certificates associated with course run {course_key} should be "
- f"revoked from user {student}"
+ f"revoked from user {student.id}"
)
LOGGER.exception(error_msg)
raise MaxRetriesExceededError(
@@ -668,17 +670,17 @@ def revoke_program_certificates(self, username, course_key): # lint-amnesty, py
for program_uuid in program_uuids_to_revoke:
try:
revoke_program_certificate(credentials_client, username, program_uuid)
- LOGGER.info(f"Revoked program certificate from user {student} in program {program_uuid}")
+ LOGGER.info(f"Revoked program certificate from user {student.id} in program {program_uuid}")
except HTTPError as exc:
if exc.response.status_code == 404:
LOGGER.warning(
- f"Unable to revoke program certificate from user {student} in program {program_uuid}, a "
+ f"Unable to revoke program certificate from user {student.id} in program {program_uuid}, a "
"program certificate could not be found"
)
elif exc.response.status_code == 429:
# Let celery handle retry attempts and backoff
error_msg = (
- f"Rate limited. Attempting to revoke a program certificate from user {student} in program "
+ f"Rate limited. Attempting to revoke a program certificate from user {student.id} in program "
f"{program_uuid}."
)
LOGGER.warning(error_msg)
@@ -687,23 +689,23 @@ def revoke_program_certificates(self, username, course_key): # lint-amnesty, py
) from exc
else:
LOGGER.warning(
- f"Unable to revoke program certificate from user {student} in program {program_uuid}"
+ f"Unable to revoke program certificate from user {student.id} in program {program_uuid}"
)
except Exception as exc: # pylint: disable=broad-except
# keep trying to revoke other certs, but let celery retry the whole task to fix any missing entries
LOGGER.exception(
- f"Failed to revoke program certificate from user {student} in program {program_uuid}: {exc}"
+ f"Failed to revoke program certificate from user {student.id} in program {program_uuid}: {exc}"
)
failed_program_certificate_revoke_attempts.append(program_uuid)
if failed_program_certificate_revoke_attempts:
# N.B. This logic assumes that this task is idempotent
- LOGGER.info(f"Failed task to revoke program certificate(s) from user {student}")
+ LOGGER.info(f"Failed task to revoke program certificate(s) from user {student .id}")
# The error message may change on each reattempt but will never be raised until the max number of retries
# have been exceeded. It is unlikely that this list will change by the time it reaches its maximimum number
# of attempts.
error_msg = (
- f"Failed to revoke program certificate(s) from user {student} for programs "
+ f"Failed to revoke program certificate(s) from user {student.id} for programs "
f"{failed_program_certificate_revoke_attempts}"
)
raise MaxRetriesExceededError(
@@ -711,9 +713,9 @@ def revoke_program_certificates(self, username, course_key): # lint-amnesty, py
f"Reason: {error_msg}"
)
else:
- LOGGER.info(f"No program certificates to revoke from user {student}")
+ LOGGER.info(f"No program certificates to revoke from user {student.id}")
- LOGGER.info(f"Successfully completed the task revoke_program_certificates for user {student}")
+ LOGGER.info(f"Successfully completed the task revoke_program_certificates for user {student.id}")
@shared_task(
diff --git a/openedx/core/djangoapps/programs/tests/test_tasks.py b/openedx/core/djangoapps/programs/tests/test_tasks.py
index d30cc4e868..e2b1c554c8 100644
--- a/openedx/core/djangoapps/programs/tests/test_tasks.py
+++ b/openedx/core/djangoapps/programs/tests/test_tasks.py
@@ -365,10 +365,10 @@ class AwardProgramCertificatesTestCase(CatalogIntegrationMixin, CredentialsApiCo
tasks.award_program_certificates.delay(self.student.username).get()
assert mock_award_program_certificate.call_count == 3
mock_warning.assert_called_once_with(
- f"Failed to award program certificate to user {self.student} in program 1: boom"
+ f"Failed to award program certificate to user {self.student.id} in program 1: boom"
)
- mock_info.assert_any_call(f"Awarded program certificate to user {self.student} in program 1")
- mock_info.assert_any_call(f"Awarded program certificate to user {self.student} in program 2")
+ mock_info.assert_any_call(f"Awarded program certificate to user {self.student.id} in program 1")
+ mock_info.assert_any_call(f"Awarded program certificate to user {self.student.id} in program 2")
def test_retry_on_programs_api_errors(self, mock_get_completed_programs, *_mock_helpers):
"""
@@ -835,10 +835,10 @@ class RevokeProgramCertificatesTestCase(CatalogIntegrationMixin, CredentialsApiC
assert mock_revoke_program_certificate.call_count == 3
mock_warning.assert_called_once_with(
- f"Failed to revoke program certificate from user {self.student} in program 1: boom"
+ f"Failed to revoke program certificate from user {self.student.id} in program 1: boom"
)
- mock_info.assert_any_call(f"Revoked program certificate from user {self.student} in program 1")
- mock_info.assert_any_call(f"Revoked program certificate from user {self.student} in program 2")
+ mock_info.assert_any_call(f"Revoked program certificate from user {self.student.id} in program 1")
+ mock_info.assert_any_call(f"Revoked program certificate from user {self.student.id} in program 2")
def test_retry_on_credentials_api_errors(
self,
From 70df3deea6491ead353fd518c4952792302ec8db Mon Sep 17 00:00:00 2001
From: Navin Karkera
Date: Tue, 15 Oct 2024 20:41:54 +0530
Subject: [PATCH 21/32] feat: set collections for a library component [FC-0062]
(#35600)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat: add & remove collections to component
Co-authored-by: Rômulo Penido
Co-authored-by: Chris Chávez
---
openedx/core/djangoapps/content/search/api.py | 1 +
.../djangoapps/content/search/documents.py | 15 ++--
.../djangoapps/content/search/handlers.py | 20 +++--
.../content/search/tests/test_api.py | 8 +-
.../core/djangoapps/content_libraries/api.py | 79 ++++++++++++++++++-
.../content_libraries/serializers.py | 18 +++++
.../content_libraries/signal_handlers.py | 35 ++++----
.../content_libraries/tests/test_api.py | 63 +++++++++++++++
.../core/djangoapps/content_libraries/urls.py | 2 +
.../djangoapps/content_libraries/views.py | 38 ++++++++-
requirements/constraints.txt | 2 +-
requirements/edx/base.txt | 4 +-
requirements/edx/development.txt | 4 +-
requirements/edx/doc.txt | 4 +-
requirements/edx/testing.txt | 4 +-
15 files changed, 248 insertions(+), 49 deletions(-)
diff --git a/openedx/core/djangoapps/content/search/api.py b/openedx/core/djangoapps/content/search/api.py
index b5ed1bde78..17338f20ab 100644
--- a/openedx/core/djangoapps/content/search/api.py
+++ b/openedx/core/djangoapps/content/search/api.py
@@ -320,6 +320,7 @@ def rebuild_index(status_cb: Callable[[str], None] | None = None) -> None:
Fields.block_id,
Fields.block_type,
Fields.context_key,
+ Fields.usage_key,
Fields.org,
Fields.tags,
Fields.tags + "." + Fields.tags_taxonomy,
diff --git a/openedx/core/djangoapps/content/search/documents.py b/openedx/core/djangoapps/content/search/documents.py
index eabeab9654..f520cd14e4 100644
--- a/openedx/core/djangoapps/content/search/documents.py
+++ b/openedx/core/djangoapps/content/search/documents.py
@@ -267,6 +267,13 @@ def _collections_for_content_object(object_id: UsageKey | LearningContextKey) ->
}
"""
+ result = {
+ Fields.collections: {
+ Fields.collections_display_name: [],
+ Fields.collections_key: [],
+ }
+ }
+
# Gather the collections associated with this object
collections = None
try:
@@ -279,14 +286,8 @@ def _collections_for_content_object(object_id: UsageKey | LearningContextKey) ->
log.warning(f"No component found for {object_id}")
if not collections:
- return {Fields.collections: {}}
+ return result
- result = {
- Fields.collections: {
- Fields.collections_display_name: [],
- Fields.collections_key: [],
- }
- }
for collection in collections:
result[Fields.collections][Fields.collections_display_name].append(collection.title)
result[Fields.collections][Fields.collections_key].append(collection.key)
diff --git a/openedx/core/djangoapps/content/search/handlers.py b/openedx/core/djangoapps/content/search/handlers.py
index 085387d336..24add6748d 100644
--- a/openedx/core/djangoapps/content/search/handlers.py
+++ b/openedx/core/djangoapps/content/search/handlers.py
@@ -179,13 +179,19 @@ def library_collection_updated_handler(**kwargs) -> None:
log.error("Received null or incorrect data for event")
return
- # Update collection index synchronously to make sure that search index is updated before
- # the frontend invalidates/refetches index.
- # See content_library_updated_handler for more details.
- update_library_collection_index_doc.apply(args=[
- str(library_collection.library_key),
- library_collection.collection_key,
- ])
+ if library_collection.background:
+ update_library_collection_index_doc.delay(
+ str(library_collection.library_key),
+ library_collection.collection_key,
+ )
+ else:
+ # Update collection index synchronously to make sure that search index is updated before
+ # the frontend invalidates/refetches index.
+ # See content_library_updated_handler for more details.
+ update_library_collection_index_doc.apply(args=[
+ str(library_collection.library_key),
+ library_collection.collection_key,
+ ])
@receiver(CONTENT_OBJECT_ASSOCIATIONS_CHANGED)
diff --git a/openedx/core/djangoapps/content/search/tests/test_api.py b/openedx/core/djangoapps/content/search/tests/test_api.py
index 4c6227af30..c89d84490e 100644
--- a/openedx/core/djangoapps/content/search/tests/test_api.py
+++ b/openedx/core/djangoapps/content/search/tests/test_api.py
@@ -219,10 +219,10 @@ class TestSearchApi(ModuleStoreTestCase):
doc_vertical["tags"] = {}
doc_problem1 = copy.deepcopy(self.doc_problem1)
doc_problem1["tags"] = {}
- doc_problem1["collections"] = {}
+ doc_problem1["collections"] = {'display_name': [], 'key': []}
doc_problem2 = copy.deepcopy(self.doc_problem2)
doc_problem2["tags"] = {}
- doc_problem2["collections"] = {}
+ doc_problem2["collections"] = {'display_name': [], 'key': []}
doc_collection = copy.deepcopy(self.collection_dict)
doc_collection["tags"] = {}
@@ -263,7 +263,7 @@ class TestSearchApi(ModuleStoreTestCase):
doc_vertical["tags"] = {}
doc_problem2 = copy.deepcopy(self.doc_problem2)
doc_problem2["tags"] = {}
- doc_problem2["collections"] = {}
+ doc_problem2["collections"] = {'display_name': [], 'key': []}
orig_from_component = library_api.LibraryXBlockMetadata.from_component
@@ -662,7 +662,7 @@ class TestSearchApi(ModuleStoreTestCase):
doc_problem_without_collection = {
"id": self.doc_problem1["id"],
- "collections": {},
+ "collections": {'display_name': [], 'key': []},
}
# Should delete the collection document
diff --git a/openedx/core/djangoapps/content_libraries/api.py b/openedx/core/djangoapps/content_libraries/api.py
index b9f3779af5..6f45a10daf 100644
--- a/openedx/core/djangoapps/content_libraries/api.py
+++ b/openedx/core/djangoapps/content_libraries/api.py
@@ -80,6 +80,7 @@ from opaque_keys import InvalidKeyError
from openedx_events.content_authoring.data import (
ContentLibraryData,
LibraryBlockData,
+ LibraryCollectionData,
)
from openedx_events.content_authoring.signals import (
CONTENT_LIBRARY_CREATED,
@@ -88,6 +89,7 @@ from openedx_events.content_authoring.signals import (
LIBRARY_BLOCK_CREATED,
LIBRARY_BLOCK_DELETED,
LIBRARY_BLOCK_UPDATED,
+ LIBRARY_COLLECTION_UPDATED,
)
from openedx_learning.api import authoring as authoring_api
from openedx_learning.api.authoring_models import Collection, Component, MediaType, LearningPackage, PublishableEntity
@@ -204,6 +206,15 @@ class ContentLibraryPermissionEntry:
access_level = attr.ib(AccessLevel.NO_ACCESS)
+@attr.s
+class CollectionMetadata:
+ """
+ Class to represent collection metadata in a content library.
+ """
+ key = attr.ib(type=str)
+ title = attr.ib(type=str)
+
+
@attr.s
class LibraryXBlockMetadata:
"""
@@ -219,9 +230,10 @@ class LibraryXBlockMetadata:
published_by = attr.ib("")
has_unpublished_changes = attr.ib(False)
created = attr.ib(default=None, type=datetime)
+ collections = attr.ib(type=list[CollectionMetadata], factory=list)
@classmethod
- def from_component(cls, library_key, component):
+ def from_component(cls, library_key, component, associated_collections=None):
"""
Construct a LibraryXBlockMetadata from a Component object.
"""
@@ -248,6 +260,7 @@ class LibraryXBlockMetadata:
last_draft_created=last_draft_created,
last_draft_created_by=last_draft_created_by,
has_unpublished_changes=component.versioning.has_unpublished_changes,
+ collections=associated_collections or [],
)
@@ -690,7 +703,7 @@ def get_library_components(library_key, text_search=None, block_types=None) -> Q
return components
-def get_library_block(usage_key) -> LibraryXBlockMetadata:
+def get_library_block(usage_key, include_collections=False) -> LibraryXBlockMetadata:
"""
Get metadata about (the draft version of) one specific XBlock in a library.
@@ -713,9 +726,17 @@ def get_library_block(usage_key) -> LibraryXBlockMetadata:
if not draft_version:
raise ContentLibraryBlockNotFound(usage_key)
+ if include_collections:
+ associated_collections = authoring_api.get_entity_collections(
+ component.learning_package_id,
+ component.key,
+ ).values('key', 'title')
+ else:
+ associated_collections = None
xblock_metadata = LibraryXBlockMetadata.from_component(
library_key=usage_key.context_key,
component=component,
+ associated_collections=associated_collections,
)
return xblock_metadata
@@ -1235,6 +1256,60 @@ def update_library_collection_components(
return collection
+def set_library_component_collections(
+ library_key: LibraryLocatorV2,
+ component: Component,
+ *,
+ collection_keys: list[str],
+ created_by: int | None = None,
+ # As an optimization, callers may pass in a pre-fetched ContentLibrary instance
+ content_library: ContentLibrary | None = None,
+) -> Component:
+ """
+ It Associates the component with collections for the given collection keys.
+
+ Only collections in queryset are associated with component, all previous component-collections
+ associations are removed.
+
+ If you've already fetched the ContentLibrary, pass it in to avoid refetching.
+
+ Raises:
+ * ContentLibraryCollectionNotFound if any of the given collection_keys don't match Collections in the given library.
+
+ Returns the updated Component.
+ """
+ if not content_library:
+ content_library = ContentLibrary.objects.get_by_key(library_key) # type: ignore[attr-defined]
+ assert content_library
+ assert content_library.learning_package_id
+ assert content_library.library_key == library_key
+
+ # Note: Component.key matches its PublishableEntity.key
+ collection_qs = authoring_api.get_collections(content_library.learning_package_id).filter(
+ key__in=collection_keys
+ )
+
+ affected_collections = authoring_api.set_collections(
+ content_library.learning_package_id,
+ component,
+ collection_qs,
+ created_by=created_by,
+ )
+
+ # For each collection, trigger LIBRARY_COLLECTION_UPDATED signal and set background=True to trigger
+ # collection indexing asynchronously.
+ for collection in affected_collections:
+ LIBRARY_COLLECTION_UPDATED.send_event(
+ library_collection=LibraryCollectionData(
+ library_key=library_key,
+ collection_key=collection.key,
+ background=True,
+ )
+ )
+
+ return component
+
+
def get_library_collection_usage_key(
library_key: LibraryLocatorV2,
collection_key: str,
diff --git a/openedx/core/djangoapps/content_libraries/serializers.py b/openedx/core/djangoapps/content_libraries/serializers.py
index 51ba55cd6b..8e9e5fc2a7 100644
--- a/openedx/core/djangoapps/content_libraries/serializers.py
+++ b/openedx/core/djangoapps/content_libraries/serializers.py
@@ -134,6 +134,14 @@ class ContentLibraryFilterSerializer(BaseFilterSerializer):
type = serializers.ChoiceField(choices=LIBRARY_TYPES, default=None, required=False)
+class CollectionMetadataSerializer(serializers.Serializer):
+ """
+ Serializer for CollectionMetadata
+ """
+ key = serializers.CharField()
+ title = serializers.CharField()
+
+
class LibraryXBlockMetadataSerializer(serializers.Serializer):
"""
Serializer for LibraryXBlockMetadata
@@ -161,6 +169,8 @@ class LibraryXBlockMetadataSerializer(serializers.Serializer):
slug = serializers.CharField(write_only=True)
tags_count = serializers.IntegerField(read_only=True)
+ collections = CollectionMetadataSerializer(many=True, required=False)
+
class LibraryXBlockTypeSerializer(serializers.Serializer):
"""
@@ -305,3 +315,11 @@ class ContentLibraryCollectionComponentsUpdateSerializer(serializers.Serializer)
"""
usage_keys = serializers.ListField(child=UsageKeyV2Serializer(), allow_empty=False)
+
+
+class ContentLibraryComponentCollectionsUpdateSerializer(serializers.Serializer):
+ """
+ Serializer for adding/removing Collections to/from a Component.
+ """
+
+ collection_keys = serializers.ListField(child=serializers.CharField(), allow_empty=True)
diff --git a/openedx/core/djangoapps/content_libraries/signal_handlers.py b/openedx/core/djangoapps/content_libraries/signal_handlers.py
index fedee045a9..58f45d218e 100644
--- a/openedx/core/djangoapps/content_libraries/signal_handlers.py
+++ b/openedx/core/djangoapps/content_libraries/signal_handlers.py
@@ -20,8 +20,8 @@ from openedx_events.content_authoring.signals import (
LIBRARY_COLLECTION_DELETED,
LIBRARY_COLLECTION_UPDATED,
)
-from openedx_learning.api.authoring import get_collection_components, get_component, get_components
-from openedx_learning.api.authoring_models import Collection, CollectionPublishableEntity, Component
+from openedx_learning.api.authoring import get_component, get_components
+from openedx_learning.api.authoring_models import Collection, CollectionPublishableEntity, Component, PublishableEntity
from lms.djangoapps.grades.api import signals as grades_signals
@@ -167,9 +167,11 @@ def library_collection_entity_deleted(sender, instance, **kwargs):
"""
Sends a CONTENT_OBJECT_ASSOCIATIONS_CHANGED event for components removed from a collection.
"""
- # Component.pk matches its entity.pk
- component = get_component(instance.entity_id)
- _library_collection_component_changed(component)
+ # Only trigger component updates if CollectionPublishableEntity was cascade deleted due to deletion of a collection.
+ if isinstance(kwargs.get('origin'), Collection):
+ # Component.pk matches its entity.pk
+ component = get_component(instance.entity_id)
+ _library_collection_component_changed(component)
@receiver(m2m_changed, sender=CollectionPublishableEntity, dispatch_uid="library_collection_entities_changed")
@@ -177,9 +179,6 @@ def library_collection_entities_changed(sender, instance, action, pk_set, **kwar
"""
Sends a CONTENT_OBJECT_ASSOCIATIONS_CHANGED event for components added/removed/cleared from a collection.
"""
- if not isinstance(instance, Collection):
- return
-
if action not in ["post_add", "post_remove", "post_clear"]:
return
@@ -191,18 +190,16 @@ def library_collection_entities_changed(sender, instance, action, pk_set, **kwar
log.error("{instance} is not associated with a content library.")
return
+ if isinstance(instance, PublishableEntity):
+ _library_collection_component_changed(instance.component, library.library_key)
+ return
+
+ # When action=="post_clear", pk_set==None
+ # Since the collection instance now has an empty entities set,
+ # we don't know which ones were removed, so we need to update associations for all library components.
+ components = get_components(instance.learning_package_id)
if pk_set:
- components = get_collection_components(
- instance.learning_package_id,
- instance.key,
- ).filter(pk__in=pk_set)
- else:
- # When action=="post_clear", pk_set==None
- # Since the collection instance now has an empty entities set,
- # we don't know which ones were removed, so we need to update associations for all library components.
- components = get_components(
- instance.learning_package_id,
- )
+ components = components.filter(pk__in=pk_set)
for component in components.all():
_library_collection_component_changed(component, library.library_key)
diff --git a/openedx/core/djangoapps/content_libraries/tests/test_api.py b/openedx/core/djangoapps/content_libraries/tests/test_api.py
index 8041c508dc..c526e7b1a1 100644
--- a/openedx/core/djangoapps/content_libraries/tests/test_api.py
+++ b/openedx/core/djangoapps/content_libraries/tests/test_api.py
@@ -308,6 +308,13 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest, OpenEdxEventsTe
description="Description for Collection 2",
created_by=self.user.id,
)
+ self.col3 = api.create_library_collection(
+ self.lib2.library_key,
+ collection_key="COL3",
+ title="Collection 3",
+ description="Description for Collection 3",
+ created_by=self.user.id,
+ )
# Create some library blocks in lib1
self.lib1_problem_block = self._add_block_to_library(
@@ -316,6 +323,10 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest, OpenEdxEventsTe
self.lib1_html_block = self._add_block_to_library(
self.lib1.library_key, "html", "html1",
)
+ # Create some library blocks in lib2
+ self.lib2_problem_block = self._add_block_to_library(
+ self.lib2.library_key, "problem", "problem2",
+ )
def test_create_library_collection(self):
event_receiver = mock.Mock()
@@ -498,3 +509,55 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest, OpenEdxEventsTe
],
)
assert self.lib1_problem_block["id"] in str(exc.exception)
+
+ def test_set_library_component_collections(self):
+ event_receiver = mock.Mock()
+ CONTENT_OBJECT_ASSOCIATIONS_CHANGED.connect(event_receiver)
+ collection_update_event_receiver = mock.Mock()
+ LIBRARY_COLLECTION_UPDATED.connect(collection_update_event_receiver)
+ assert not list(self.col2.entities.all())
+ component = api.get_component_from_usage_key(UsageKey.from_string(self.lib2_problem_block["id"]))
+
+ api.set_library_component_collections(
+ self.lib2.library_key,
+ component,
+ collection_keys=[self.col2.key, self.col3.key],
+ )
+
+ assert len(authoring_api.get_collection(self.lib2.learning_package_id, self.col2.key).entities.all()) == 1
+ assert len(authoring_api.get_collection(self.lib2.learning_package_id, self.col3.key).entities.all()) == 1
+ self.assertDictContainsSubset(
+ {
+ "signal": CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
+ "sender": None,
+ "content_object": ContentObjectChangedData(
+ object_id=self.lib2_problem_block["id"],
+ changes=["collections"],
+ ),
+ },
+ event_receiver.call_args_list[0].kwargs,
+ )
+ self.assertDictContainsSubset(
+ {
+ "signal": LIBRARY_COLLECTION_UPDATED,
+ "sender": None,
+ "library_collection": LibraryCollectionData(
+ self.lib2.library_key,
+ collection_key=self.col2.key,
+ background=True,
+ ),
+ },
+ collection_update_event_receiver.call_args_list[0].kwargs,
+ )
+ self.assertDictContainsSubset(
+ {
+ "signal": LIBRARY_COLLECTION_UPDATED,
+ "sender": None,
+ "library_collection": LibraryCollectionData(
+ self.lib2.library_key,
+ collection_key=self.col3.key,
+ background=True,
+ ),
+ },
+ collection_update_event_receiver.call_args_list[1].kwargs,
+ )
diff --git a/openedx/core/djangoapps/content_libraries/urls.py b/openedx/core/djangoapps/content_libraries/urls.py
index 9455f0de5e..e77c1b34d2 100644
--- a/openedx/core/djangoapps/content_libraries/urls.py
+++ b/openedx/core/djangoapps/content_libraries/urls.py
@@ -57,6 +57,8 @@ urlpatterns = [
path('blocks//', include([
# Get metadata about a specific XBlock in this library, or delete the block:
path('', views.LibraryBlockView.as_view()),
+ # Update collections for a given component
+ path('collections/', views.LibraryBlockCollectionsView.as_view(), name='update-collections'),
# Get the LTI URL of a specific XBlock
path('lti/', views.LibraryBlockLtiUrlView.as_view(), name='lti-url'),
# Get the OLX source code of the specified block:
diff --git a/openedx/core/djangoapps/content_libraries/views.py b/openedx/core/djangoapps/content_libraries/views.py
index 3712af6e59..6e50559f38 100644
--- a/openedx/core/djangoapps/content_libraries/views.py
+++ b/openedx/core/djangoapps/content_libraries/views.py
@@ -106,6 +106,7 @@ from openedx.core.djangoapps.content_libraries.serializers import (
ContentLibraryPermissionLevelSerializer,
ContentLibraryPermissionSerializer,
ContentLibraryUpdateSerializer,
+ ContentLibraryComponentCollectionsUpdateSerializer,
LibraryXBlockCreationSerializer,
LibraryXBlockMetadataSerializer,
LibraryXBlockTypeSerializer,
@@ -617,7 +618,7 @@ class LibraryBlockView(APIView):
"""
key = LibraryUsageLocatorV2.from_string(usage_key_str)
api.require_permission_for_library_key(key.lib_key, request.user, permissions.CAN_VIEW_THIS_CONTENT_LIBRARY)
- result = api.get_library_block(key)
+ result = api.get_library_block(key, include_collections=True)
return Response(LibraryXBlockMetadataSerializer(result).data)
@@ -640,6 +641,41 @@ class LibraryBlockView(APIView):
return Response({})
+@method_decorator(non_atomic_requests, name="dispatch")
+@view_auth_classes()
+class LibraryBlockCollectionsView(APIView):
+ """
+ View to set collections for a component.
+ """
+ @convert_exceptions
+ def patch(self, request, usage_key_str) -> Response:
+ """
+ Sets Collections for a Component.
+
+ Collection and Components must all be part of the given library/learning package.
+ """
+ key = LibraryUsageLocatorV2.from_string(usage_key_str)
+ content_library = api.require_permission_for_library_key(
+ key.lib_key,
+ request.user,
+ permissions.CAN_EDIT_THIS_CONTENT_LIBRARY
+ )
+ component = api.get_component_from_usage_key(key)
+ serializer = ContentLibraryComponentCollectionsUpdateSerializer(data=request.data)
+ serializer.is_valid(raise_exception=True)
+
+ collection_keys = serializer.validated_data['collection_keys']
+ api.set_library_component_collections(
+ library_key=key.lib_key,
+ component=component,
+ collection_keys=collection_keys,
+ created_by=self.request.user.id,
+ content_library=content_library,
+ )
+
+ return Response({'count': len(collection_keys)})
+
+
@method_decorator(non_atomic_requests, name="dispatch")
@view_auth_classes()
class LibraryBlockLtiUrlView(APIView):
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 6fb109d62f..d37ffb1bcd 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -140,7 +140,7 @@ optimizely-sdk<5.0
# Date: 2023-09-18
# pinning this version to avoid updates while the library is being developed
# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35269
-openedx-learning==0.13.1
+openedx-learning==0.15.0
# Date: 2023-11-29
# Open AI version 1.0.0 dropped support for openai.ChatCompletion which is currently in use in enterprise.
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 2bac3df49c..6ede5ed123 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -811,7 +811,7 @@ openedx-django-require==2.1.0
# via -r requirements/edx/kernel.in
openedx-django-wiki==2.1.0
# via -r requirements/edx/kernel.in
-openedx-events==9.14.1
+openedx-events==9.15.0
# via
# -r requirements/edx/kernel.in
# edx-enterprise
@@ -825,7 +825,7 @@ openedx-filters==1.10.0
# -r requirements/edx/kernel.in
# lti-consumer-xblock
# ora2
-openedx-learning==0.13.1
+openedx-learning==0.15.0
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index d61db58ae4..901a37c055 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -1358,7 +1358,7 @@ openedx-django-wiki==2.1.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-openedx-events==9.14.1
+openedx-events==9.15.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1374,7 +1374,7 @@ openedx-filters==1.10.0
# -r requirements/edx/testing.txt
# lti-consumer-xblock
# ora2
-openedx-learning==0.13.1
+openedx-learning==0.15.0
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index 32ef31e8d3..34e6275592 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -970,7 +970,7 @@ openedx-django-require==2.1.0
# via -r requirements/edx/base.txt
openedx-django-wiki==2.1.0
# via -r requirements/edx/base.txt
-openedx-events==9.14.1
+openedx-events==9.15.0
# via
# -r requirements/edx/base.txt
# edx-enterprise
@@ -984,7 +984,7 @@ openedx-filters==1.10.0
# -r requirements/edx/base.txt
# lti-consumer-xblock
# ora2
-openedx-learning==0.13.1
+openedx-learning==0.15.0
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 77b3896967..5658241be4 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -1021,7 +1021,7 @@ openedx-django-require==2.1.0
# via -r requirements/edx/base.txt
openedx-django-wiki==2.1.0
# via -r requirements/edx/base.txt
-openedx-events==9.14.1
+openedx-events==9.15.0
# via
# -r requirements/edx/base.txt
# edx-enterprise
@@ -1035,7 +1035,7 @@ openedx-filters==1.10.0
# -r requirements/edx/base.txt
# lti-consumer-xblock
# ora2
-openedx-learning==0.13.1
+openedx-learning==0.15.0
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
From 2bbd8ecd180fd299de2c4ee4bc1542ccb97b50bb Mon Sep 17 00:00:00 2001
From: Kyle McCormick
Date: Tue, 15 Oct 2024 11:32:01 -0400
Subject: [PATCH 22/32] feat!: Remove outdated Libraries Relaunch cruft
(#35644)
The V2 libraries project had a few past iterations which were never
launched. This commit cleans up pieces from those which we don't need
for the real Libraries Relaunch MVP in Sumac:
* Remove ENABLE_LIBRARY_AUTHORING_MICROFRONTEND,
LIBRARY_AUTHORING_FRONTEND_URL, and
REDIRECT_TO_LIBRARY_AUTHORING_MICROFRONTEND, all of which are obsolete
now that library authoring has been merged into
https://github.com/openedx/frontend-app-authoring.
More details on the new Content Libraries configuration settings are
here: https://github.com/openedx/frontend-app-authoring/issues/1334
* Remove dangling support for syncing V2 (learning core-backed) library
content using the LibraryContentBlock. This code was all based on an
older understanding of V2 Content Libraries, where the libraries were
smaller and versioned as a whole rather then versioned by-item.
Reference to V2 libraries will be done on a per-block basis using
the upstream/downstream system, described here:
https://github.com/openedx/edx-platform/blob/master/docs/decisions/0020-upstream-downstream.rst
It's important that we remove this support now so that OLX course
authors don't stuble upon it and use it, which would be buggy and
complicate future migrations.
* Remove the "mode" parameter from LibraryContentBlock. The only
supported mode was and is "random". We will not be adding any further
modes. Going forward for V2, we will have an ItemBank block for
randomizing items (regardless of source), which can be synthesized
with upstream referenced as described above. Existing
LibraryContentBlocks will be migrated.
* Finally, some renamings:
* LibraryContentBlock -> LegacyLibraryContentBlock
* LibraryToolsService -> LegacyLibraryToolsService
* LibrarySummary -> LegacyLibrarySummary
Module names and the old OLX tag (library_content) are unchanged.
Closes: https://github.com/openedx/frontend-app-authoring/issues/1115
---
cms/djangoapps/contentstore/config/waffle.py | 16 +-
.../rest_api/v1/serializers/home.py | 2 -
.../contentstore/rest_api/v1/views/home.py | 2 -
.../rest_api/v1/views/tests/test_home.py | 2 -
cms/djangoapps/contentstore/utils.py | 13 +-
cms/djangoapps/contentstore/views/library.py | 16 --
.../contentstore/views/tests/test_block.py | 2 +-
.../views/tests/test_clipboard_paste.py | 43 ++--
cms/envs/common.py | 15 +-
cms/envs/devstack.py | 3 -
cms/templates/index.html | 4 -
docs/docs_settings.py | 1 -
.../transformers/library_content.py | 8 +-
lms/djangoapps/courseware/block_render.py | 4 +-
lms/djangoapps/grades/rest_api/v1/views.py | 4 +-
.../core/djangoapps/content_libraries/api.py | 79 +------
.../djangoapps/content_libraries/tasks.py | 195 +++---------------
openedx/core/lib/xblock_utils/__init__.py | 2 +-
.../completion_integration/test_services.py | 4 +-
setup.py | 2 +-
webpack.builtinblocks.config.js | 9 +-
.../public/js/library_content_edit_helpers.js | 54 -----
xmodule/library_content_block.py | 77 ++-----
xmodule/library_tools.py | 75 +++----
xmodule/modulestore/mixed.py | 2 +-
.../split_mongo/caching_descriptor_system.py | 4 +-
xmodule/modulestore/split_mongo/split.py | 6 +-
xmodule/tests/test_library_content.py | 63 +++---
xmodule/tests/test_library_tools.py | 73 ++-----
xmodule/tests/test_randomize_block.py | 2 +-
xmodule/vertical_block.py | 2 +-
31 files changed, 180 insertions(+), 604 deletions(-)
delete mode 100644 xmodule/assets/library_content/public/js/library_content_edit_helpers.js
diff --git a/cms/djangoapps/contentstore/config/waffle.py b/cms/djangoapps/contentstore/config/waffle.py
index f84290ba83..ae0e6ea467 100644
--- a/cms/djangoapps/contentstore/config/waffle.py
+++ b/cms/djangoapps/contentstore/config/waffle.py
@@ -4,7 +4,7 @@ waffle switches for the contentstore app.
"""
-from edx_toggles.toggles import WaffleFlag, WaffleSwitch
+from edx_toggles.toggles import WaffleSwitch
from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
@@ -26,20 +26,6 @@ SHOW_REVIEW_RULES_FLAG = CourseWaffleFlag( # lint-amnesty, pylint: disable=togg
f'{WAFFLE_NAMESPACE}.show_review_rules', __name__, LOG_PREFIX
)
-# Waffle flag to redirect to the library authoring MFE.
-# .. toggle_name: contentstore.library_authoring_mfe
-# .. toggle_implementation: WaffleFlag
-# .. toggle_default: False
-# .. toggle_description: Toggles the new micro-frontend-based implementation of the library authoring experience.
-# .. toggle_use_cases: temporary, open_edx
-# .. toggle_creation_date: 2020-08-03
-# .. toggle_target_removal_date: 2020-12-31
-# .. toggle_warning: Also set settings.LIBRARY_AUTHORING_MICROFRONTEND_URL and ENABLE_LIBRARY_AUTHORING_MICROFRONTEND.
-# .. toggle_tickets: https://openedx.atlassian.net/wiki/spaces/OEPM/pages/4106944527/Libraries+Relaunch+Proposal+For+Product+Review
-REDIRECT_TO_LIBRARY_AUTHORING_MICROFRONTEND = WaffleFlag(
- f'{WAFFLE_NAMESPACE}.library_authoring_mfe', __name__, LOG_PREFIX
-)
-
# .. toggle_name: studio.custom_relative_dates
# .. toggle_implementation: CourseWaffleFlag
diff --git a/cms/djangoapps/contentstore/rest_api/v1/serializers/home.py b/cms/djangoapps/contentstore/rest_api/v1/serializers/home.py
index 0aa06d8b8d..4c3e2a4321 100644
--- a/cms/djangoapps/contentstore/rest_api/v1/serializers/home.py
+++ b/cms/djangoapps/contentstore/rest_api/v1/serializers/home.py
@@ -59,10 +59,8 @@ class CourseHomeSerializer(serializers.Serializer):
libraries = LibraryViewSerializer(many=True, required=False, allow_null=True)
libraries_enabled = serializers.BooleanField()
taxonomies_enabled = serializers.BooleanField()
- library_authoring_mfe_url = serializers.CharField()
taxonomy_list_mfe_url = serializers.CharField()
optimization_enabled = serializers.BooleanField()
- redirect_to_library_authoring_mfe = serializers.BooleanField()
request_course_creator_url = serializers.CharField()
rerun_creator_status = serializers.BooleanField()
show_new_library_button = serializers.BooleanField()
diff --git a/cms/djangoapps/contentstore/rest_api/v1/views/home.py b/cms/djangoapps/contentstore/rest_api/v1/views/home.py
index d72042cff6..ff476090ee 100644
--- a/cms/djangoapps/contentstore/rest_api/v1/views/home.py
+++ b/cms/djangoapps/contentstore/rest_api/v1/views/home.py
@@ -59,9 +59,7 @@ class HomePageView(APIView):
"in_process_course_actions": [],
"libraries": [],
"libraries_enabled": true,
- "library_authoring_mfe_url": "//localhost:3001/course/course-v1:edX+P315+2T2023",
"optimization_enabled": true,
- "redirect_to_library_authoring_mfe": false,
"request_course_creator_url": "/request_course_creator",
"rerun_creator_status": true,
"show_new_library_button": true,
diff --git a/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_home.py b/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_home.py
index a8b4cf5e39..c3c9652e5d 100644
--- a/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_home.py
+++ b/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_home.py
@@ -52,10 +52,8 @@ class HomePageViewTest(CourseTestCase):
"libraries": [],
"libraries_enabled": True,
"taxonomies_enabled": True,
- "library_authoring_mfe_url": settings.LIBRARY_AUTHORING_MICROFRONTEND_URL,
"taxonomy_list_mfe_url": 'http://course-authoring-mfe/taxonomies',
"optimization_enabled": False,
- "redirect_to_library_authoring_mfe": False,
"request_course_creator_url": "/request_course_creator",
"rerun_creator_status": True,
"show_new_library_button": True,
diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py
index 214193918e..035e44d5ce 100644
--- a/cms/djangoapps/contentstore/utils.py
+++ b/cms/djangoapps/contentstore/utils.py
@@ -98,7 +98,7 @@ from cms.djangoapps.contentstore.toggles import (
)
from cms.djangoapps.models.settings.course_grading import CourseGradingModel
from cms.djangoapps.models.settings.course_metadata import CourseMetadata
-from xmodule.library_tools import LibraryToolsService
+from xmodule.library_tools import LegacyLibraryToolsService
from xmodule.course_block import DEFAULT_START_DATE # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.data import CertificatesDisplayBehaviors
from xmodule.modulestore import ModuleStoreEnum # lint-amnesty, pylint: disable=wrong-import-order
@@ -1265,7 +1265,7 @@ def load_services_for_studio(runtime, user):
"settings": SettingsService(),
"lti-configuration": ConfigurationService(CourseAllowPIISharingInLTIFlag),
"teams_configuration": TeamsConfigurationService(),
- "library_tools": LibraryToolsService(modulestore(), user.id)
+ "library_tools": LegacyLibraryToolsService(modulestore(), user.id)
}
runtime._services.update(services) # lint-amnesty, pylint: disable=protected-access
@@ -1671,9 +1671,7 @@ def get_home_context(request, no_course=False):
ENABLE_GLOBAL_STAFF_OPTIMIZATION,
)
from cms.djangoapps.contentstore.views.library import (
- LIBRARY_AUTHORING_MICROFRONTEND_URL,
LIBRARIES_ENABLED,
- should_redirect_to_library_authoring_mfe,
user_can_view_create_library_button,
)
@@ -1699,12 +1697,9 @@ def get_home_context(request, no_course=False):
'in_process_course_actions': in_process_course_actions,
'libraries_enabled': LIBRARIES_ENABLED,
'taxonomies_enabled': not is_tagging_feature_disabled(),
- 'redirect_to_library_authoring_mfe': should_redirect_to_library_authoring_mfe(),
- 'library_authoring_mfe_url': LIBRARY_AUTHORING_MICROFRONTEND_URL,
'taxonomy_list_mfe_url': get_taxonomy_list_url(),
'libraries': libraries,
- 'show_new_library_button': user_can_view_create_library_button(user)
- and not should_redirect_to_library_authoring_mfe(),
+ 'show_new_library_button': user_can_view_create_library_button(user),
'user': user,
'request_course_creator_url': reverse('request_course_creator'),
'course_creator_status': _get_course_creator_status(user),
@@ -2202,7 +2197,7 @@ class StudioPermissionsService:
Deprecated. To be replaced by a more general authorization service.
- Only used by LibraryContentBlock (and library_tools.py).
+ Only used by LegacyLibraryContentBlock (and library_tools.py).
"""
def __init__(self, user):
diff --git a/cms/djangoapps/contentstore/views/library.py b/cms/djangoapps/contentstore/views/library.py
index 8c314caa66..340cadb4e2 100644
--- a/cms/djangoapps/contentstore/views/library.py
+++ b/cms/djangoapps/contentstore/views/library.py
@@ -41,7 +41,6 @@ from common.djangoapps.student.roles import (
)
from common.djangoapps.util.json_request import JsonResponse, JsonResponseBadRequest, expect_json
-from ..config.waffle import REDIRECT_TO_LIBRARY_AUTHORING_MICROFRONTEND
from ..utils import add_instructor, reverse_library_url
from .component import CONTAINER_TEMPLATES, get_component_templates
from cms.djangoapps.contentstore.xblock_storage_handlers.view_handlers import create_xblock_info
@@ -52,21 +51,6 @@ __all__ = ['library_handler', 'manage_library_users']
log = logging.getLogger(__name__)
LIBRARIES_ENABLED = settings.FEATURES.get('ENABLE_CONTENT_LIBRARIES', False)
-ENABLE_LIBRARY_AUTHORING_MICROFRONTEND = settings.FEATURES.get('ENABLE_LIBRARY_AUTHORING_MICROFRONTEND', False)
-LIBRARY_AUTHORING_MICROFRONTEND_URL = settings.LIBRARY_AUTHORING_MICROFRONTEND_URL
-
-
-def should_redirect_to_library_authoring_mfe():
- """
- Boolean helper method, returns whether or not to redirect to the Library
- Authoring MFE based on settings and flags.
- """
-
- return (
- ENABLE_LIBRARY_AUTHORING_MICROFRONTEND and
- LIBRARY_AUTHORING_MICROFRONTEND_URL and
- REDIRECT_TO_LIBRARY_AUTHORING_MICROFRONTEND.is_enabled()
- )
def _user_can_create_library_for_org(user, org=None):
diff --git a/cms/djangoapps/contentstore/views/tests/test_block.py b/cms/djangoapps/contentstore/views/tests/test_block.py
index 80a2535598..f3e20b45b2 100644
--- a/cms/djangoapps/contentstore/views/tests/test_block.py
+++ b/cms/djangoapps/contentstore/views/tests/test_block.py
@@ -982,7 +982,7 @@ class TestDuplicateItem(ItemTest, DuplicateHelper, OpenEdxEventsTestMixin):
def test_duplicate_library_content_block(self): # pylint: disable=too-many-statements
"""
- Test the LibraryContentBlock's special duplication process.
+ Test the LegacyLibraryContentBlock's special duplication process.
"""
store = modulestore()
diff --git a/cms/djangoapps/contentstore/views/tests/test_clipboard_paste.py b/cms/djangoapps/contentstore/views/tests/test_clipboard_paste.py
index 76dba57f1d..7979a422a3 100644
--- a/cms/djangoapps/contentstore/views/tests/test_clipboard_paste.py
+++ b/cms/djangoapps/contentstore/views/tests/test_clipboard_paste.py
@@ -7,13 +7,11 @@ import ddt
from opaque_keys.edx.keys import UsageKey
from rest_framework.test import APIClient
from openedx_tagging.core.tagging.models import Tag
-from organizations.models import Organization
from xmodule.modulestore.django import contentstore, modulestore
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, upload_file_to_course
-from xmodule.modulestore.tests.factories import BlockFactory, CourseFactory, ToyCourseFactory
+from xmodule.modulestore.tests.factories import BlockFactory, CourseFactory, ToyCourseFactory, LibraryFactory
from cms.djangoapps.contentstore.utils import reverse_usage_url
-from openedx.core.djangoapps.content_libraries import api as library_api
from openedx.core.djangoapps.content_tagging import api as tagging_api
CLIPBOARD_ENDPOINT = "/api/content-staging/v1/clipboard/"
@@ -165,12 +163,12 @@ class ClipboardPasteTestCase(ModuleStoreTestCase):
publish_item=True,
).location
- library = ClipboardLibraryContentPasteTestCase.setup_library()
+ library = ClipboardPasteFromV1LibraryTestCase.setup_library()
with self.store.bulk_operations(course_key):
library_content_block_key = BlockFactory.create(
parent=self.store.get_item(unit_key),
category="library_content",
- source_library_id=str(library.key),
+ source_library_id=str(library.context_key),
display_name="LC Block",
publish_item=True,
).location
@@ -393,27 +391,27 @@ class ClipboardPasteTestCase(ModuleStoreTestCase):
assert source_pic2_hash != dest_pic2_hash # Because there was a conflict, this file was unchanged.
-class ClipboardLibraryContentPasteTestCase(ModuleStoreTestCase):
+class ClipboardPasteFromV1LibraryTestCase(ModuleStoreTestCase):
"""
- Test Clipboard Paste functionality with library content
+ Test Clipboard Paste functionality with legacy (v1) library content
"""
def setUp(self):
"""
- Set up a v2 Content Library and a library content block
+ Set up a v1 Content Library and a library content block
"""
super().setUp()
self.client = APIClient()
self.client.login(username=self.user.username, password=self.user_password)
self.store = modulestore()
- library = self.setup_library()
+ self.library = self.setup_library()
# Create a library content block (lc), point it out our library, and sync it.
self.course = CourseFactory.create(display_name='Course')
self.orig_lc_block = BlockFactory.create(
parent=self.course,
category="library_content",
- source_library_id=str(library.key),
+ source_library_id=str(self.library.context_key),
display_name="LC Block",
publish_item=False,
)
@@ -426,18 +424,15 @@ class ClipboardLibraryContentPasteTestCase(ModuleStoreTestCase):
@classmethod
def setup_library(cls):
"""
- Creates and returns a content library.
+ Creates and returns a legacy content library with 1 problem
"""
- library = library_api.create_library(
- library_type=library_api.COMPLEX,
- org=Organization.objects.create(name="Test Org", short_name="CL-TEST"),
- slug="lib",
- title="Library",
- )
- # Populate it with a problem:
- problem_key = library_api.create_library_block(library.key, "problem", "p1").usage_key
- library_api.set_library_block_olx(problem_key, """
-
+ library = LibraryFactory.create(display_name='Library')
+ lib_block = BlockFactory.create(
+ parent_location=library.usage_key,
+ category="problem",
+ display_name="MCQ",
+ max_attempts=1,
+ data="""
@@ -445,9 +440,9 @@ class ClipboardLibraryContentPasteTestCase(ModuleStoreTestCase):
Right
-
- """)
- library_api.publish_changes(library.key)
+ """,
+ publish_item=False,
+ )
return library
def test_paste_library_content_block(self):
diff --git a/cms/envs/common.py b/cms/envs/common.py
index 7521cc21fa..63221ee0b0 100644
--- a/cms/envs/common.py
+++ b/cms/envs/common.py
@@ -434,18 +434,6 @@ FEATURES = {
# .. toggle_tickets: https://openedx.atlassian.net/browse/DEPR-58
'DEPRECATE_OLD_COURSE_KEYS_IN_STUDIO': True,
- # .. toggle_name: FEATURES['ENABLE_LIBRARY_AUTHORING_MICROFRONTEND']
- # .. toggle_implementation: DjangoSetting
- # .. toggle_default: False
- # .. toggle_description: Set to True to enable the Library Authoring MFE
- # .. toggle_use_cases: temporary
- # .. toggle_creation_date: 2020-06-20
- # .. toggle_target_removal_date: 2020-12-31
- # .. toggle_tickets: https://openedx.atlassian.net/wiki/spaces/OEPM/pages/4106944527/Libraries+Relaunch+Proposal+For+Product+Review
- # .. toggle_warning: Also set settings.LIBRARY_AUTHORING_MICROFRONTEND_URL and see
- # REDIRECT_TO_LIBRARY_AUTHORING_MICROFRONTEND for rollout.
- 'ENABLE_LIBRARY_AUTHORING_MICROFRONTEND': False,
-
# .. toggle_name: FEATURES['DISABLE_COURSE_CREATION']
# .. toggle_implementation: DjangoSetting
# .. toggle_default: False
@@ -601,7 +589,6 @@ IDA_LOGOUT_URI_LIST = []
COURSE_AUTHORING_MICROFRONTEND_URL = None
DISCUSSIONS_MICROFRONTEND_URL = None
DISCUSSIONS_MFE_FEEDBACK_URL = None
-LIBRARY_AUTHORING_MICROFRONTEND_URL = None
# .. toggle_name: ENABLE_AUTHN_RESET_PASSWORD_HIBP_POLICY
# .. toggle_implementation: DjangoSetting
# .. toggle_default: False
@@ -2779,6 +2766,7 @@ WIKI_HELP_URL = "https://edx.readthedocs.io/projects/open-edx-building-and-runni
CUSTOM_PAGES_HELP_URL = "https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_assets/pages.html#adding-custom-pages"
COURSE_LIVE_HELP_URL = "https://edx.readthedocs.io/projects/edx-partner-course-staff/en/latest/course_assets/course_live.html"
ORA_SETTINGS_HELP_URL = "https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_assets/pages.html#configuring-course-level-open-response-assessment-settings"
+# pylint: enable=line-too-long
# keys for big blue button live provider
COURSE_LIVE_GLOBAL_CREDENTIALS = {}
@@ -2810,6 +2798,7 @@ DISCUSSIONS_INCONTEXT_FEEDBACK_URL = ''
# Learn More link in upgraded discussion notification alert
# pylint: disable=line-too-long
DISCUSSIONS_INCONTEXT_LEARNMORE_URL = "https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/manage_discussions/discussions.html"
+# pylint: enable=line-too-long
#### django-simple-history##
# disable indexing on date field its coming django-simple-history.
diff --git a/cms/envs/devstack.py b/cms/envs/devstack.py
index 1d3a510cdc..1200a61b06 100644
--- a/cms/envs/devstack.py
+++ b/cms/envs/devstack.py
@@ -174,9 +174,6 @@ FEATURES['ENABLE_ORGANIZATION_STAFF_ACCESS_FOR_CONTENT_LIBRARIES'] = True
################### FRONTEND APPLICATION PUBLISHER URL ###################
FEATURES['FRONTEND_APP_PUBLISHER_URL'] = 'http://localhost:18400'
-################### FRONTEND APPLICATION LIBRARY AUTHORING ###################
-LIBRARY_AUTHORING_MICROFRONTEND_URL = 'http://localhost:3001'
-
################### FRONTEND APPLICATION COURSE AUTHORING ###################
COURSE_AUTHORING_MICROFRONTEND_URL = 'http://localhost:2001'
diff --git a/cms/templates/index.html b/cms/templates/index.html
index 766d68da78..e558597307 100644
--- a/cms/templates/index.html
+++ b/cms/templates/index.html
@@ -348,9 +348,6 @@ from openedx.core.djangolib.js_utils import (
% endif
% if libraries_enabled:
- % if redirect_to_library_authoring_mfe:
-
diff --git a/docs/docs_settings.py b/docs/docs_settings.py
index 5bc9b15946..f12848876e 100644
--- a/docs/docs_settings.py
+++ b/docs/docs_settings.py
@@ -14,7 +14,6 @@ from cms.envs.common import ( # lint-amnesty, pylint: disable=unused-import
ADVANCED_PROBLEM_TYPES,
COURSE_IMPORT_EXPORT_STORAGE,
GIT_EXPORT_DEFAULT_IDENT,
- LIBRARY_AUTHORING_MICROFRONTEND_URL,
SCRAPE_YOUTUBE_THUMBNAILS_JOB_QUEUE,
VIDEO_TRANSCRIPT_MIGRATIONS_JOB_QUEUE,
UPDATE_SEARCH_INDEX_JOB_QUEUE,
diff --git a/lms/djangoapps/course_blocks/transformers/library_content.py b/lms/djangoapps/course_blocks/transformers/library_content.py
index 616cf68f4b..10ef8c2138 100644
--- a/lms/djangoapps/course_blocks/transformers/library_content.py
+++ b/lms/djangoapps/course_blocks/transformers/library_content.py
@@ -14,7 +14,7 @@ from openedx.core.djangoapps.content.block_structure.transformer import (
BlockStructureTransformer,
FilteringTransformerMixin
)
-from xmodule.library_content_block import LibraryContentBlock # lint-amnesty, pylint: disable=wrong-import-order
+from xmodule.library_content_block import LegacyLibraryContentBlock # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order
from ..utils import get_student_module_as_dict
@@ -47,7 +47,6 @@ class ContentLibraryTransformer(FilteringTransformerMixin, BlockStructureTransfo
Collects any information that's necessary to execute this
transformer's transform method.
"""
- block_structure.request_xblock_fields('mode')
block_structure.request_xblock_fields('max_count')
block_structure.request_xblock_fields('category')
store = modulestore()
@@ -83,7 +82,6 @@ class ContentLibraryTransformer(FilteringTransformerMixin, BlockStructureTransfo
if library_children:
all_library_children.update(library_children)
selected = []
- mode = block_structure.get_xblock_field(block_key, 'mode')
max_count = block_structure.get_xblock_field(block_key, 'max_count')
if max_count < 0:
max_count = len(library_children)
@@ -100,7 +98,7 @@ class ContentLibraryTransformer(FilteringTransformerMixin, BlockStructureTransfo
# Update selected
previous_count = len(selected)
- block_keys = LibraryContentBlock.make_selection(selected, library_children, max_count, mode)
+ block_keys = LegacyLibraryContentBlock.make_selection(selected, library_children, max_count)
selected = block_keys['selected']
# Save back any changes
@@ -176,7 +174,7 @@ class ContentLibraryTransformer(FilteringTransformerMixin, BlockStructureTransfo
with tracker.get_tracker().context(full_event_name, context):
tracker.emit(full_event_name, event_data)
- LibraryContentBlock.publish_selected_children_events(
+ LegacyLibraryContentBlock.publish_selected_children_events(
block_keys,
format_block_keys,
publish_event,
diff --git a/lms/djangoapps/courseware/block_render.py b/lms/djangoapps/courseware/block_render.py
index 1bae903224..de92692ce4 100644
--- a/lms/djangoapps/courseware/block_render.py
+++ b/lms/djangoapps/courseware/block_render.py
@@ -45,7 +45,7 @@ from lms.djangoapps.teams.services import TeamsService
from openedx.core.lib.xblock_services.call_to_action import CallToActionService
from xmodule.contentstore.django import contentstore
from xmodule.exceptions import NotFoundError, ProcessingError
-from xmodule.library_tools import LibraryToolsService
+from xmodule.library_tools import LegacyLibraryToolsService
from xmodule.modulestore.django import XBlockI18nService, modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.partitions.partitions_service import PartitionService
@@ -626,7 +626,7 @@ def prepare_runtime_for_user(
),
'completion': CompletionService(user=user, context_key=course_id) if user and user.is_authenticated else None,
'i18n': XBlockI18nService,
- 'library_tools': LibraryToolsService(store, user_id=user.id if user else None),
+ 'library_tools': LegacyLibraryToolsService(store, user_id=user.id if user else None),
'partitions': PartitionService(course_id=course_id, cache=DEFAULT_REQUEST_CACHE.data),
'settings': SettingsService(),
'user_tags': UserTagsService(user=user, course_id=course_id),
diff --git a/lms/djangoapps/grades/rest_api/v1/views.py b/lms/djangoapps/grades/rest_api/v1/views.py
index 5f49f2299a..b6835fd61e 100644
--- a/lms/djangoapps/grades/rest_api/v1/views.py
+++ b/lms/djangoapps/grades/rest_api/v1/views.py
@@ -378,7 +378,7 @@ class SubmissionHistoryView(GradeViewMixin, PaginatedAPIView):
def get(self, request, course_id=None):
"""
Get submission history details. This submission history is related to only
- ProblemBlock and it doesn't support LibraryContentBlock or ContentLibraries
+ ProblemBlock and it doesn't support LegacyLibraryContentBlock or ContentLibraries
as of now.
**Usecases**:
@@ -463,7 +463,7 @@ class SubmissionHistoryView(GradeViewMixin, PaginatedAPIView):
@staticmethod
def get_problem_blocks(course):
""" Get a list of problem xblock for the course.
- This doesn't support LibraryContentBlock or ContentLibraries
+ This doesn't support LegacyLibraryContentBlock or ContentLibraries
as of now
"""
blocks = []
diff --git a/openedx/core/djangoapps/content_libraries/api.py b/openedx/core/djangoapps/content_libraries/api.py
index 6f45a10daf..f30d5f0477 100644
--- a/openedx/core/djangoapps/content_libraries/api.py
+++ b/openedx/core/djangoapps/content_libraries/api.py
@@ -76,7 +76,6 @@ from opaque_keys.edx.locator import (
LibraryLocator as LibraryLocatorV1,
LibraryCollectionLocator,
)
-from opaque_keys import InvalidKeyError
from openedx_events.content_authoring.data import (
ContentLibraryData,
LibraryBlockData,
@@ -99,10 +98,7 @@ from xblock.exceptions import XBlockNotFoundError
from openedx.core.djangoapps.xblock.api import get_component_from_usage_key, xblock_type_display_name
from openedx.core.lib.xblock_serializer.api import serialize_modulestore_block_for_learning_core
-from xmodule.library_root_xblock import LibraryRoot as LibraryRootV1
-from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
-from xmodule.modulestore.exceptions import ItemNotFoundError
from . import permissions, tasks
from .constants import ALL_RIGHTS_RESERVED, COMPLEX
@@ -421,8 +417,8 @@ def get_library(library_key):
# updated version of content that a course could pull in. But more recently,
# we've decided to do those version references at the level of the
# individual blocks being used, since a Learning Core backed library is
- # intended to be used for many LibraryContentBlocks and not 1:1 like v1
- # libraries. The top level version stays for now because LibraryContentBlock
+ # intended to be referenced in multiple course locations and not 1:1 like v1
+ # libraries. The top level version stays for now because LegacyLibraryContentBlock
# uses it, but that should hopefully change before the Redwood release.
version = 0 if last_publish_log is None else last_publish_log.pk
published_by = None
@@ -1340,77 +1336,6 @@ def get_library_collection_from_usage_key(
raise ContentLibraryCollectionNotFound from exc
-# V1/V2 Compatibility Helpers
-# (Should be removed as part of
-# https://github.com/openedx/edx-platform/issues/32457)
-# ======================================================
-
-def get_v1_or_v2_library(
- library_id: str | LibraryLocatorV1 | LibraryLocatorV2,
- version: str | int | None,
-) -> LibraryRootV1 | ContentLibraryMetadata | None:
- """
- Fetch either a V1 or V2 content library from a V1/V2 key (or key string) and version.
-
- V1 library versions are Mongo ObjectID strings.
- V2 library versions can be positive ints, or strings of positive ints.
- Passing version=None will return the latest version the library.
-
- Returns None if not found.
- If key is invalid, raises InvalidKeyError.
- For V1, if key has a version, it is ignored in favor of `version`.
- For V2, if version is provided but it isn't an int or parseable to one, we raise a ValueError.
-
- Examples:
- * get_v1_or_v2_library("library-v1:ProblemX+PR0B", None) ->
- * get_v1_or_v2_library("library-v1:ProblemX+PR0B", "65ff...") ->
- * get_v1_or_v2_library("lib:RG:rg-1", None) ->
- * get_v1_or_v2_library("lib:RG:rg-1", "36") ->
- * get_v1_or_v2_library("lib:RG:rg-1", "xyz") ->
- * get_v1_or_v2_library("notakey", "xyz") ->
-
- If you just want to get a V2 library, use `get_library` instead.
- """
- library_key: LibraryLocatorV1 | LibraryLocatorV2
- if isinstance(library_id, str):
- try:
- library_key = LibraryLocatorV1.from_string(library_id)
- except InvalidKeyError:
- library_key = LibraryLocatorV2.from_string(library_id)
- else:
- library_key = library_id
- if isinstance(library_key, LibraryLocatorV2):
- v2_version: int | None
- if version:
- v2_version = int(version)
- else:
- v2_version = None
- try:
- library = get_library(library_key)
- if v2_version is not None and library.version != v2_version:
- raise NotImplementedError(
- f"Tried to load version {v2_version} of learning_core-based library {library_key}. "
- f"Currently, only the latest version ({library.version}) may be loaded. "
- "This is a known issue. "
- "It will be fixed before the production release of learning_core-based (V2) content libraries. "
- )
- return library
- except ContentLibrary.DoesNotExist:
- return None
- elif isinstance(library_key, LibraryLocatorV1):
- v1_version: str | None
- if version:
- v1_version = str(version)
- else:
- v1_version = None
- store = modulestore()
- library_key = library_key.for_branch(ModuleStoreEnum.BranchName.library).for_version(v1_version)
- try:
- return store.get_library(library_key, remove_version=False, remove_branch=False, head_validation=False)
- except ItemNotFoundError:
- return None
-
-
# Import from Courseware
# ======================
diff --git a/openedx/core/djangoapps/content_libraries/tasks.py b/openedx/core/djangoapps/content_libraries/tasks.py
index 9f4f7aaaf7..f56b4adfe3 100644
--- a/openedx/core/djangoapps/content_libraries/tasks.py
+++ b/openedx/core/djangoapps/content_libraries/tasks.py
@@ -21,33 +21,20 @@ import logging
from celery import shared_task
from celery_utils.logged_task import LoggedTask
from celery.utils.log import get_task_logger
-from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
-from django.core.exceptions import PermissionDenied
from edx_django_utils.monitoring import set_code_owner_attribute, set_code_owner_attribute_from_module
-from opaque_keys.edx.keys import UsageKey
-from opaque_keys.edx.locator import (
- BlockUsageLocator,
- LibraryLocatorV2,
- LibraryUsageLocatorV2,
- LibraryLocator as LibraryLocatorV1
-)
from user_tasks.tasks import UserTask, UserTaskStatus
from xblock.fields import Scope
-from common.djangoapps.student.auth import has_studio_write_access
from opaque_keys.edx.keys import CourseKey
-from openedx.core.djangoapps.content_libraries import api as library_api
-from openedx.core.djangoapps.xblock.api import load_block
+from opaque_keys.edx.locator import BlockUsageLocator
from openedx.core.lib import ensure_cms
from xmodule.capa_block import ProblemBlock
-from xmodule.library_content_block import ANY_CAPA_TYPE_VALUE, LibraryContentBlock
-from xmodule.library_root_xblock import LibraryRoot as LibraryRootV1
+from xmodule.library_content_block import ANY_CAPA_TYPE_VALUE, LegacyLibraryContentBlock
+from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.modulestore.mixed import MixedModuleStore
-from xmodule.modulestore.split_mongo import BlockKey
-from xmodule.util.keys import derive_key
from . import api
from .models import ContentLibraryBlockImportTask
@@ -84,77 +71,6 @@ def import_blocks_from_course(import_task_id, course_key_str, use_course_key_as_
)
-def _import_block(store, user_id, source_block, dest_parent_key):
- """
- Recursively import a learning core block and its children.`
- """
- def generate_block_key(source_key, dest_parent_key):
- """
- Deterministically generate an ID for the new block and return the key.
- Keys are generated such that they appear identical to a v1 library with
- the same input block_id, library name, library organization, and parent block using derive_key
- """
- if not isinstance(source_key.lib_key, LibraryLocatorV2):
- raise TypeError(f"Expected source library key of type LibraryLocatorV2, got {source_key.lib_key} instead.")
- source_key_as_v1_course_key = LibraryLocatorV1(
- org=source_key.lib_key.org,
- library=source_key.lib_key.slug,
- branch='library'
- )
- derived_block_key = derive_key(
- source=source_key_as_v1_course_key.make_usage_key(source_key.block_type, source_key.block_id),
- dest_parent=BlockKey(dest_parent_key.block_type, dest_parent_key.block_id),
- )
- return dest_parent_key.context_key.make_usage_key(*derived_block_key)
-
- source_key = source_block.scope_ids.usage_id
- new_block_key = generate_block_key(source_key, dest_parent_key)
- try:
- new_block = store.get_item(new_block_key)
- if new_block.parent.block_id != dest_parent_key.block_id:
- raise ValueError(
- "Expected existing block {} to be a child of {} but instead it's a child of {}".format(
- new_block_key, dest_parent_key, new_block.parent,
- )
- )
- except ItemNotFoundError:
- new_block = store.create_child(
- user_id,
- dest_parent_key,
- source_key.block_type,
- block_id=new_block_key.block_id,
- )
-
- # Prepare a list of this block's static assets; any assets that are referenced as /static/{path} (the
- # recommended way for referencing them) will stop working, and so we rewrite the url when importing.
- # Copying assets not advised because modulestore doesn't namespace assets to each block like learning core, which
- # might cause conflicts when the same filename is used across imported blocks.
- if isinstance(source_key, LibraryUsageLocatorV2):
- all_assets = library_api.get_library_block_static_asset_files(source_key)
- else:
- all_assets = []
-
- for field_name, field in source_block.fields.items():
- if field.scope not in (Scope.settings, Scope.content):
- continue # Only copy authored field data
- if field.is_set_on(source_block) or field.is_set_on(new_block):
- field_value = getattr(source_block, field_name)
- setattr(new_block, field_name, field_value)
- new_block.save()
- store.update_item(new_block, user_id)
-
- if new_block.has_children:
- # Delete existing children in the new block, which can be reimported again if they still exist in the
- # source library
- for existing_child_key in new_block.children:
- store.delete_item(existing_child_key, user_id)
- # Now import the children
- for child in source_block.get_children():
- _import_block(store, user_id, child, new_block_key)
-
- return new_block_key
-
-
def _filter_child(store, usage_key, capa_type):
"""
Return whether this block is both a problem and has a `capa_type` which is included in the filter.
@@ -172,49 +88,6 @@ def _problem_type_filter(store, library, capa_type):
return [key for key in library.children if _filter_child(store, key, capa_type)]
-def _import_from_learning_core(user_id, store, dest_block, source_block_ids):
- """
- Imports a block from a learning-core-based learning context (usually a
- content library) into modulestore, as a new child of dest_block.
- Any existing children of dest_block are replaced.
- """
- dest_key = dest_block.scope_ids.usage_id
- if not isinstance(dest_key, BlockUsageLocator):
- raise TypeError(f"Destination {dest_key} should be a modulestore course.")
- if user_id is None:
- raise ValueError("Cannot check user permissions - LibraryTools user_id is None")
-
- if len(set(source_block_ids)) != len(source_block_ids):
- # We don't support importing the exact same block twice because it would break the way we generate new IDs
- # for each block and then overwrite existing copies of blocks when re-importing the same blocks.
- raise ValueError("One or more library component IDs is a duplicate.")
-
- dest_course_key = dest_key.context_key
- user = User.objects.get(id=user_id)
- if not has_studio_write_access(user, dest_course_key):
- raise PermissionDenied()
-
- # Read the source block; this will also confirm that user has permission to read it.
- # (This could be slow and use lots of memory, except for the fact that LibraryContentBlock which calls this
- # should be limiting the number of blocks to a reasonable limit. We load them all now instead of one at a
- # time in order to raise any errors before we start actually copying blocks over.)
- orig_blocks = [load_block(UsageKey.from_string(key), user) for key in source_block_ids]
-
- with store.bulk_operations(dest_course_key):
- child_ids_updated = set()
-
- for block in orig_blocks:
- new_block_id = _import_block(store, user_id, block, dest_key)
- child_ids_updated.add(new_block_id)
-
- # Remove any existing children that are no longer used
- for old_child_id in set(dest_block.children) - child_ids_updated:
- store.delete_item(old_child_id, user_id)
- # If this was called from a handler, it will save dest_block at the end, so we must update
- # dest_block.children to avoid it saving the old value of children and deleting the new ones.
- dest_block.children = store.get_item(dest_key).children
-
-
class LibrarySyncChildrenTask(UserTask): # pylint: disable=abstract-method
"""
Base class for tasks which operate upon library_content children.
@@ -244,7 +117,7 @@ def sync_from_library(
self: LibrarySyncChildrenTask,
user_id: int,
dest_block_id: str,
- library_version: str | int | None,
+ library_version: str | None,
) -> None:
"""
Celery task to update the children of the library_content block at `dest_block_id`.
@@ -300,8 +173,8 @@ def _sync_children(
task: LibrarySyncChildrenTask,
store: MixedModuleStore,
user_id: int,
- dest_block: LibraryContentBlock,
- library_version: int | str | None,
+ dest_block: LegacyLibraryContentBlock,
+ library_version: str | None,
) -> None:
"""
Implementation helper for `sync_from_library` and `duplicate_children` Celery tasks.
@@ -309,41 +182,29 @@ def _sync_children(
Can update children with a specific library `library_version`, or latest (`library_version=None`).
"""
source_blocks = []
- library_key = dest_block.source_library_key
- filter_children = (dest_block.capa_type != ANY_CAPA_TYPE_VALUE)
- library = library_api.get_v1_or_v2_library(library_key, version=library_version)
- if not library:
+ library_key = dest_block.source_library_key.for_branch(
+ ModuleStoreEnum.BranchName.library
+ ).for_version(library_version)
+ try:
+ library = store.get_library(library_key, remove_version=False, remove_branch=False, head_validation=False)
+ except ItemNotFoundError:
task.status.fail(f"Requested library {library_key} not found.")
- elif isinstance(library, LibraryRootV1):
- if filter_children:
- # Apply simple filtering based on CAPA problem types:
- source_blocks.extend(_problem_type_filter(store, library, dest_block.capa_type))
- else:
- source_blocks.extend(library.children)
- with store.bulk_operations(dest_block.scope_ids.usage_id.context_key):
- try:
- dest_block.source_library_version = str(library.location.library_key.version_guid)
- store.update_item(dest_block, user_id)
- dest_block.children = store.copy_from_template(
- source_blocks, dest_block.location, user_id, head_validation=True
- )
- # ^-- copy_from_template updates the children in the DB
- # but we must also set .children here to avoid overwriting the DB again
- except Exception as exception: # pylint: disable=broad-except
- TASK_LOGGER.exception('Error importing children for %s', dest_block.scope_ids.usage_id, exc_info=True)
- if task.status.state != UserTaskStatus.FAILED:
- task.status.fail({'raw_error_msg': str(exception)})
- raise
- elif isinstance(library, library_api.ContentLibraryMetadata):
- # TODO: add filtering by capa_type when V2 library will support different problem types
+ return
+ filter_children = (dest_block.capa_type != ANY_CAPA_TYPE_VALUE)
+ if filter_children:
+ # Apply simple filtering based on CAPA problem types:
+ source_blocks.extend(_problem_type_filter(store, library, dest_block.capa_type))
+ else:
+ source_blocks.extend(library.children)
+ with store.bulk_operations(dest_block.scope_ids.usage_id.context_key):
try:
- source_block_ids = [
- str(library_api.LibraryXBlockMetadata.from_component(library_key, component).usage_key)
- for component in library_api.get_library_components(library_key)
- ]
- _import_from_learning_core(user_id, store, dest_block, source_block_ids)
- dest_block.source_library_version = str(library.version)
+ dest_block.source_library_version = str(library.location.library_key.version_guid)
store.update_item(dest_block, user_id)
+ dest_block.children = store.copy_from_template(
+ source_blocks, dest_block.location, user_id, head_validation=True
+ )
+ # ^-- copy_from_template updates the children in the DB
+ # but we must also set .children here to avoid overwriting the DB again
except Exception as exception: # pylint: disable=broad-except
TASK_LOGGER.exception('Error importing children for %s', dest_block.scope_ids.usage_id, exc_info=True)
if task.status.state != UserTaskStatus.FAILED:
@@ -354,8 +215,8 @@ def _sync_children(
def _copy_overrides(
store: MixedModuleStore,
user_id: int,
- source_block: LibraryContentBlock,
- dest_block: LibraryContentBlock
+ source_block: LegacyLibraryContentBlock,
+ dest_block: LegacyLibraryContentBlock
) -> None:
"""
Copy any overrides the user has made on children of `source` over to the children of `dest_block`, recursively.
diff --git a/openedx/core/lib/xblock_utils/__init__.py b/openedx/core/lib/xblock_utils/__init__.py
index 26127dbfb3..a8b76541b6 100644
--- a/openedx/core/lib/xblock_utils/__init__.py
+++ b/openedx/core/lib/xblock_utils/__init__.py
@@ -452,7 +452,7 @@ def xblock_resource_pkg(block):
ProblemBlock, and most other built-in blocks currently. Handling for these
assets does not interact with this function.
2. The (preferred) standard XBlock runtime resource loading system, used by
- LibraryContentBlock. Handling for these assets *does* interact with this
+ LegacyLibraryContentBlock. Handling for these assets *does* interact with this
function.
We hope to migrate to (2) eventually, tracked by:
diff --git a/openedx/tests/completion_integration/test_services.py b/openedx/tests/completion_integration/test_services.py
index f4088678d9..7a6fad8ece 100644
--- a/openedx/tests/completion_integration/test_services.py
+++ b/openedx/tests/completion_integration/test_services.py
@@ -10,7 +10,7 @@ from completion.test_utils import CompletionWaffleTestMixin
from django.conf import settings
from django.test import override_settings
from opaque_keys.edx.keys import CourseKey
-from xmodule.library_tools import LibraryToolsService
+from xmodule.library_tools import LegacyLibraryToolsService
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, BlockFactory, LibraryFactory
from xmodule.tests import prepare_block_runtime
@@ -122,7 +122,7 @@ class CompletionServiceTestCase(CompletionWaffleTestMixin, SharedModuleStoreTest
Bind a block (part of self.course) so we can access student-specific data.
"""
prepare_block_runtime(block.runtime, course_id=block.location.course_key)
- block.runtime._services.update({'library_tools': LibraryToolsService(self.store, self.user.id)}) # lint-amnesty, pylint: disable=protected-access
+ block.runtime._services.update({'library_tools': LegacyLibraryToolsService(self.store, self.user.id)}) # lint-amnesty, pylint: disable=protected-access
def get_block(descriptor):
"""Mocks module_system get_block_for_descriptor function"""
diff --git a/setup.py b/setup.py
index 28a25cc914..21c8e537c9 100644
--- a/setup.py
+++ b/setup.py
@@ -21,7 +21,7 @@ XBLOCKS = [
"html = xmodule.html_block:HtmlBlock",
"image = xmodule.template_block:TranslateCustomTagBlock",
"library = xmodule.library_root_xblock:LibraryRoot",
- "library_content = xmodule.library_content_block:LibraryContentBlock",
+ "library_content = xmodule.library_content_block:LegacyLibraryContentBlock",
"lti = xmodule.lti_block:LTIBlock",
"poll_question = xmodule.poll_block:PollBlock",
"problem = xmodule.capa_block:ProblemBlock",
diff --git a/webpack.builtinblocks.config.js b/webpack.builtinblocks.config.js
index d86f891dc6..1c5a9b1e0e 100644
--- a/webpack.builtinblocks.config.js
+++ b/webpack.builtinblocks.config.js
@@ -38,6 +38,10 @@ module.exports = {
'./xmodule/js/src/xmodule.js',
'./xmodule/js/src/html/edit.js'
],
+ LibraryContentBlockEditor: [
+ './xmodule/js/src/xmodule.js',
+ './xmodule/js/src/vertical/edit.js'
+ ],
LTIBlockDisplay: [
'./xmodule/js/src/xmodule.js',
'./xmodule/js/src/lti/lti.js'
@@ -46,11 +50,6 @@ module.exports = {
'./xmodule/js/src/xmodule.js',
'./xmodule/js/src/raw/edit/metadata-only.js'
],
- LibraryContentBlockDisplay: './xmodule/js/src/xmodule.js',
- LibraryContentBlockEditor: [
- './xmodule/js/src/xmodule.js',
- './xmodule/js/src/vertical/edit.js'
- ],
PollBlockDisplay: [
'./xmodule/js/src/xmodule.js',
'./xmodule/js/src/javascript_loader.js',
diff --git a/xmodule/assets/library_content/public/js/library_content_edit_helpers.js b/xmodule/assets/library_content/public/js/library_content_edit_helpers.js
deleted file mode 100644
index 564cc5fb0f..0000000000
--- a/xmodule/assets/library_content/public/js/library_content_edit_helpers.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/* JavaScript for special editing operations that can be done on LibraryContentXBlock */
-// This is a temporary UI improvements that will be removed when V2 content libraries became
-// fully functional
-
-/**
- * Toggle the "Problem Type" settings section depending on selected library type.
- * As for now, the V2 libraries don't support different problem types, so they can't be
- * filtered by it. We're hiding the Problem Type field for them.
- */
-function checkProblemTypeShouldBeVisible(editor) {
- var libraries = editor.find('.wrapper-comp-settings.metadata_edit.is-active')
- .data().metadata.source_library_id.options;
- var selectedIndex = $("select[name='Library']", editor)[0].selectedIndex;
- var libraryKey = libraries[selectedIndex].value;
- var url = URI('/xblock')
- .segment(editor.find('.xblock.xblock-studio_view.xblock-studio_view-library_content.xblock-initialized')
- .data('usage-id'))
- .segment('handler')
- .segment('is_v2_library');
-
- $.ajax({
- type: 'POST',
- url: url,
- data: JSON.stringify({'library_key': libraryKey}),
- success: function(data) {
- var problemTypeSelect = editor.find("select[name='Problem Type']")
- .parents("li.field.comp-setting-entry.metadata_entry");
- data.is_v2 ? problemTypeSelect.hide() : problemTypeSelect.show();
- }
- });
-}
-
-/**
- * Waits untill editor html loaded, than calls checks for Program Type field toggling.
- */
-function waitForEditorLoading() {
- var checkContent = setInterval(function() {
- var $modal = $('.xblock-editor');
- var content = $modal.html();
- if (content) {
- clearInterval(checkContent);
- checkProblemTypeShouldBeVisible($modal);
- }
- }, 10);
-}
-// Initial call
-waitForEditorLoading();
-
-var $librarySelect = $("select[name='Library']");
-$(document).on('change', $librarySelect, waitForEditorLoading)
-
-var $libraryContentEditors = $('.xblock-header.xblock-header-library_content');
-var $editBtns = $libraryContentEditors.find('.action-item.action-edit');
-$(document).on('click', $editBtns, waitForEditorLoading)
diff --git a/xmodule/library_content_block.py b/xmodule/library_content_block.py
index 09a5d1dee1..adb07101d0 100644
--- a/xmodule/library_content_block.py
+++ b/xmodule/library_content_block.py
@@ -1,5 +1,12 @@
"""
-LibraryContent: The XBlock used to include blocks from a library in a course.
+LegacyLibraryContent: The XBlock used to randomly select a subset of blocks from a "v1" (modulestore-backed) library.
+
+In Studio, it's called the "Randomized Content Module".
+
+In the long-term, this block is deprecated in favor of "v2" (learning core-backed) library references:
+https://github.com/openedx/edx-platform/issues/32457
+
+We need to retain backwards-compatibility, but please do not build any new features into this.
"""
from __future__ import annotations
@@ -15,8 +22,7 @@ from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.utils.functional import classproperty
from lxml import etree
from lxml.etree import XMLSyntaxError
-from opaque_keys import InvalidKeyError
-from opaque_keys.edx.locator import LibraryLocator, LibraryLocatorV2
+from opaque_keys.edx.locator import LibraryLocator
from rest_framework import status
from web_fragments.fragment import Fragment
from webob import Response
@@ -78,7 +84,7 @@ class LibraryToolsUnavailable(ValueError):
@XBlock.wants('studio_user_permissions') # Only available in CMS.
@XBlock.wants('user')
@XBlock.needs('mako')
-class LibraryContentBlock(
+class LegacyLibraryContentBlock(
MakoTemplateBlockBase,
XmlMixin,
XModuleToXBlockMixin,
@@ -87,7 +93,7 @@ class LibraryContentBlock(
StudioEditableBlock,
):
"""
- An XBlock whose children are chosen dynamically from a content library.
+ An XBlock whose children are chosen dynamically from a legacy (v1) content library.
Can be used to create randomized assessments among other things.
Note: technically, all matching blocks from the content library are added
@@ -135,17 +141,6 @@ class LibraryContentBlock(
display_name=_("Library Version"),
scope=Scope.settings,
)
- mode = String(
- display_name=_("Mode"),
- help=_("Determines how content is drawn from the library"),
- default="random",
- values=[
- {"display_name": _("Choose n at random"), "value": "random"}
- # Future addition: Choose a new random set of n every time the student refreshes the block, for self tests
- # Future addition: manually selected blocks
- ],
- scope=Scope.settings,
- )
max_count = Integer(
display_name=_("Count"),
help=_("Enter the number of components to display to each student. Set it to -1 to display all components."),
@@ -179,15 +174,12 @@ class LibraryContentBlock(
"""
Convenience method to get the library ID as a LibraryLocator and not just a string.
- Supports either library v1 or library v2 locators.
+ Supports only v1 libraries.
"""
- try:
- return LibraryLocator.from_string(self.source_library_id)
- except InvalidKeyError:
- return LibraryLocatorV2.from_string(self.source_library_id)
+ return LibraryLocator.from_string(self.source_library_id)
@classmethod
- def make_selection(cls, selected, children, max_count, mode):
+ def make_selection(cls, selected, children, max_count):
"""
Dynamically selects block_ids indicating which of the possible children are displayed to the current user.
@@ -195,7 +187,6 @@ class LibraryContentBlock(
selected - list of (block_type, block_id) tuples assigned to this student
children - children of this block
max_count - number of components to display to each student
- mode - how content is drawn from the library
Returns:
A dict containing the following keys:
@@ -231,12 +222,9 @@ class LibraryContentBlock(
if num_to_add > 0:
# We need to select [more] blocks to display to this user:
pool = valid_block_keys - selected_keys
- if mode == "random":
- num_to_add = min(len(pool), num_to_add)
- added_block_keys = set(rand.sample(list(pool), num_to_add))
- # We now have the correct n random children to show for this user.
- else:
- raise NotImplementedError("Unsupported mode.")
+ num_to_add = min(len(pool), num_to_add)
+ added_block_keys = set(rand.sample(list(pool), num_to_add))
+ # We now have the correct n random children to show for this user.
selected_keys |= added_block_keys
if any((invalid_block_keys, overlimit_block_keys, added_block_keys)):
@@ -334,7 +322,7 @@ class LibraryContentBlock(
if max_count < 0:
max_count = len(self.children)
- block_keys = self.make_selection(self.selected, self.children, max_count, "random") # pylint: disable=no-member
+ block_keys = self.make_selection(self.selected, self.children, max_count) # pylint: disable=no-member
# Publish events for analytics purposes:
lib_tools = self.get_tools()
@@ -467,7 +455,6 @@ class LibraryContentBlock(
fragment = Fragment(
self.runtime.service(self, 'mako').render_cms_template(self.mako_template, self.get_context())
)
- fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/library_content_edit_helpers.js'))
add_webpack_js_to_fragment(fragment, 'LibraryContentBlockEditor')
shim_xmodule_js(fragment, self.studio_js_module_name)
return fragment
@@ -481,16 +468,12 @@ class LibraryContentBlock(
@property
def non_editable_metadata_fields(self):
non_editable_fields = super().non_editable_metadata_fields
- # The only supported mode is currently 'random'.
- # Add the mode field to non_editable_metadata_fields so that it doesn't
- # render in the edit form.
non_editable_fields.extend([
- LibraryContentBlock.mode,
- LibraryContentBlock.source_library_version,
+ LegacyLibraryContentBlock.source_library_version,
])
return non_editable_fields
- def get_tools(self, to_read_library_content: bool = False) -> 'LibraryToolsService':
+ def get_tools(self, to_read_library_content: bool = False) -> 'LegacyLibraryToolsService':
"""
Grab the library tools service and confirm that it'll work for us. Else, raise LibraryToolsUnavailable.
"""
@@ -564,22 +547,6 @@ class LibraryContentBlock(
library_version=(None if upgrade_to_latest else self.source_library_version),
)
- @XBlock.json_handler
- def is_v2_library(self, data, suffix=''): # pylint: disable=unused-argument
- """
- Check the library version by library_id.
-
- This is a temporary handler needed for hiding the Problem Type xblock editor field for V2 libraries.
- """
- lib_key = data.get('library_key')
- try:
- LibraryLocatorV2.from_string(lib_key)
- except InvalidKeyError:
- is_v2 = False
- else:
- is_v2 = True
- return {'is_v2': is_v2}
-
@XBlock.handler
def children_are_syncing(self, request, suffix=''): # pylint: disable=unused-argument
"""
@@ -809,14 +776,14 @@ class LibraryContentBlock(
return xml_object
-class LibrarySummary:
+class LegacyLibrarySummary:
"""
A library summary object which contains the fields required for library listing on studio.
"""
def __init__(self, library_locator, display_name):
"""
- Initialize LibrarySummary
+ Initialize LegacyLibrarySummary
Arguments:
library_locator (LibraryLocator): LibraryLocator object of the library.
diff --git a/xmodule/library_tools.py b/xmodule/library_tools.py
index 2c077a8884..7f9e83a937 100644
--- a/xmodule/library_tools.py
+++ b/xmodule/library_tools.py
@@ -1,18 +1,17 @@
"""
-XBlock runtime services for LibraryContentBlock
+XBlock runtime services for LegacyLibraryContentBlock
"""
from __future__ import annotations
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
-from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
+from opaque_keys.edx.locator import LibraryLocator
from user_tasks.models import UserTaskStatus
from openedx.core.lib import ensure_cms
-from openedx.core.djangoapps.content_libraries import api as library_api
from openedx.core.djangoapps.content_libraries import tasks as library_tasks
-from xmodule.library_content_block import LibraryContentBlock
-from xmodule.library_root_xblock import LibraryRoot as LibraryRootV1
+from xmodule.library_content_block import LegacyLibraryContentBlock
+from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.exceptions import ItemNotFoundError
@@ -21,9 +20,9 @@ def normalize_key_for_search(library_key):
return library_key.replace(version_guid=None, branch=None)
-class LibraryToolsService:
+class LegacyLibraryToolsService:
"""
- Service for LibraryContentBlock.
+ Service for LegacyLibraryContentBlock.
Allows to interact with libraries in the modulestore and learning core.
@@ -33,24 +32,31 @@ class LibraryToolsService:
self.store = modulestore
self.user_id = user_id
- def get_latest_library_version(self, lib_key) -> str | None:
+ def get_latest_library_version(self, library_id: str | LibraryLocator) -> str | None:
"""
Get the version of the given library as string.
The return value (library version) could be:
str() - for V1 library;
- str() - for V2 library.
None - if the library does not exist.
"""
- library = library_api.get_v1_or_v2_library(lib_key, version=None)
+ library_key: LibraryLocator
+ if isinstance(library_id, str):
+ library_key = LibraryLocator.from_string(library_id)
+ else:
+ library_key = library_id
+ library_key = library_key.for_branch(ModuleStoreEnum.BranchName.library).for_version(None)
+ try:
+ library = self.store.get_library(
+ library_key, remove_version=False, remove_branch=False, head_validation=False
+ )
+ except ItemNotFoundError:
+ return None
if not library:
return None
- elif isinstance(library, LibraryRootV1):
- # We need to know the library's version so ensure it's set in library.location.library_key.version_guid
- assert library.location.library_key.version_guid is not None
- return str(library.location.library_key.version_guid)
- elif isinstance(library, library_api.ContentLibraryMetadata):
- return str(library.version)
+ # We need to know the library's version so ensure it's set in library.location.library_key.version_guid
+ assert library.location.library_key.version_guid is not None
+ return str(library.location.library_key.version_guid)
def create_block_analytics_summary(self, course_key, block_keys):
"""
@@ -96,7 +102,7 @@ class LibraryToolsService:
"""
return self.store.check_supports(block.location.course_key, 'copy_from_template')
- def trigger_library_sync(self, dest_block: LibraryContentBlock, library_version: str | int | None) -> None:
+ def trigger_library_sync(self, dest_block: LegacyLibraryContentBlock, library_version: str | None) -> None:
"""
Queue task to synchronize the children of `dest_block` with it source library (at `library_version` or latest).
@@ -118,16 +124,20 @@ class LibraryToolsService:
`dest_block.children`.
"""
ensure_cms("library_content block children may only be synced in a CMS context")
- if not isinstance(dest_block, LibraryContentBlock):
+ if not isinstance(dest_block, LegacyLibraryContentBlock):
raise ValueError(f"Can only sync children for library_content blocks, not {dest_block.tag} blocks.")
if not dest_block.source_library_id:
dest_block.source_library_version = ""
return
- library_key = dest_block.source_library_key
- if not library_api.get_v1_or_v2_library(library_key, version=library_version):
+ library_key = dest_block.source_library_key.for_branch(
+ ModuleStoreEnum.BranchName.library
+ ).for_version(library_version)
+ try:
+ self.store.get_library(library_key, remove_version=False, remove_branch=False, head_validation=False)
+ except ItemNotFoundError as exc:
if library_version:
- raise ObjectDoesNotExist(f"Version {library_version} of library {library_key} not found.")
- raise ObjectDoesNotExist(f"Library {library_key} not found.")
+ raise ObjectDoesNotExist(f"Version {library_version} of library {library_key} not found.") from exc
+ raise ObjectDoesNotExist(f"Library {library_key} not found.") from exc
# TODO: This task is synchronous until we can figure out race conditions with import.
# These race conditions lead to failed imports of library content from course import.
@@ -140,12 +150,14 @@ class LibraryToolsService:
),
)
- def trigger_duplication(self, source_block: LibraryContentBlock, dest_block: LibraryContentBlock) -> None:
+ def trigger_duplication(
+ self, source_block: LegacyLibraryContentBlock, dest_block: LegacyLibraryContentBlock
+ ) -> None:
"""
Queue a task to duplicate the children of `source_block` to `dest_block`.
"""
ensure_cms("library_content block children may only be duplicated in a CMS context")
- if not isinstance(dest_block, LibraryContentBlock):
+ if not isinstance(dest_block, LegacyLibraryContentBlock):
raise ValueError(f"Can only duplicate children for library_content blocks, not {dest_block.tag} blocks.")
if source_block.scope_ids.usage_id.context_key != source_block.scope_ids.usage_id.context_key:
raise ValueError(
@@ -163,7 +175,7 @@ class LibraryToolsService:
dest_block_id=str(dest_block.scope_ids.usage_id),
)
- def are_children_syncing(self, library_content_block: LibraryContentBlock) -> bool:
+ def are_children_syncing(self, library_content_block: LegacyLibraryContentBlock) -> bool:
"""
Is a task currently running to sync the children of `library_content_block`?
@@ -179,21 +191,12 @@ class LibraryToolsService:
def list_available_libraries(self):
"""
- List all known libraries.
+ List all known legacy libraries.
- Collects Only V2 Libaries if the FEATURES[ENABLE_LIBRARY_AUTHORING_MICROFRONTEND] setting is True.
- Otherwise, return all v1 and v2 libraries.
Returns tuples of (library key, display_name).
"""
user = User.objects.get(id=self.user_id)
- v1_libs = [
+ return [
(lib.location.library_key.replace(version_guid=None, branch=None), lib.display_name)
for lib in self.store.get_library_summaries()
]
- v2_query = library_api.get_libraries_for_user(user)
- v2_libs_with_meta = library_api.get_metadata(v2_query)
- v2_libs = [(lib.key, lib.title) for lib in v2_libs_with_meta]
-
- if settings.FEATURES.get('ENABLE_LIBRARY_AUTHORING_MICROFRONTEND'):
- return v2_libs
- return v1_libs + v2_libs
diff --git a/xmodule/modulestore/mixed.py b/xmodule/modulestore/mixed.py
index e1ea6640ac..fb5be17041 100644
--- a/xmodule/modulestore/mixed.py
+++ b/xmodule/modulestore/mixed.py
@@ -343,7 +343,7 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase):
@strip_key
def get_library_summaries(self, **kwargs):
"""
- Returns a list of LibrarySummary objects.
+ Returns a list of LegacyLibrarySummary objects.
Information contains `location`, `display_name`, `locator` of the libraries in this modulestore.
"""
library_summaries = {}
diff --git a/xmodule/modulestore/split_mongo/caching_descriptor_system.py b/xmodule/modulestore/split_mongo/caching_descriptor_system.py
index b0965d63fe..a83fec32ba 100644
--- a/xmodule/modulestore/split_mongo/caching_descriptor_system.py
+++ b/xmodule/modulestore/split_mongo/caching_descriptor_system.py
@@ -13,7 +13,7 @@ from xblock.runtime import KeyValueStore, KvsFieldData
from xmodule.error_block import ErrorBlock
from xmodule.errortracker import exc_info_to_str
-from xmodule.library_tools import LibraryToolsService
+from xmodule.library_tools import LegacyLibraryToolsService
from xmodule.mako_block import MakoDescriptorSystem
from xmodule.modulestore import BlockData
from xmodule.modulestore.edit_info import EditInfoRuntimeMixin
@@ -78,7 +78,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li
user = get_current_user()
user_id = user.id if user else None
- self._services['library_tools'] = LibraryToolsService(modulestore, user_id=user_id)
+ self._services['library_tools'] = LegacyLibraryToolsService(modulestore, user_id=user_id)
# Cache of block field datas, keyed by the XBlock instance (since the ScopeId changes!)
self.block_field_datas = weakref.WeakKeyDictionary()
diff --git a/xmodule/modulestore/split_mongo/split.py b/xmodule/modulestore/split_mongo/split.py
index 64e19420a1..e69a2ca0e5 100644
--- a/xmodule/modulestore/split_mongo/split.py
+++ b/xmodule/modulestore/split_mongo/split.py
@@ -81,7 +81,7 @@ from xmodule.assetstore import AssetMetadata
from xmodule.course_block import CourseSummary
from xmodule.error_block import ErrorBlock
from xmodule.errortracker import null_error_tracker
-from xmodule.library_content_block import LibrarySummary
+from xmodule.library_content_block import LegacyLibrarySummary
from xmodule.modulestore import (
BlockData,
BulkOperationsMixin,
@@ -1029,7 +1029,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
@autoretry_read()
def get_library_summaries(self, **kwargs):
"""
- Returns a list of `LibrarySummary` objects.
+ Returns a list of `LegacyLibrarySummary` objects.
kwargs can be valid db fields to match against active_versions
collection e.g org='example_org'.
"""
@@ -1057,7 +1057,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
display_name = library_block_fields['display_name']
libraries_summaries.append(
- LibrarySummary(library_locator, display_name)
+ LegacyLibrarySummary(library_locator, display_name)
)
return libraries_summaries
diff --git a/xmodule/tests/test_library_content.py b/xmodule/tests/test_library_content.py
index e30e19922b..9914ef2d90 100644
--- a/xmodule/tests/test_library_content.py
+++ b/xmodule/tests/test_library_content.py
@@ -1,7 +1,5 @@
"""
-Basic unit tests for LibraryContentBlock
-
-Higher-level tests are in `cms/djangoapps/contentstore/tests/test_libraries.py`.
+Basic unit tests for LegacyLibraryContentBlock
"""
from unittest.mock import MagicMock, Mock, patch
@@ -9,15 +7,15 @@ import ddt
from bson.objectid import ObjectId
from fs.memoryfs import MemoryFS
from lxml import etree
-from opaque_keys.edx.locator import LibraryLocator, LibraryLocatorV2
+from opaque_keys.edx.locator import LibraryLocator
from rest_framework import status
from search.search_engine_base import SearchEngine
from web_fragments.fragment import Fragment
from xblock.runtime import Runtime as VanillaRuntime
from openedx.core.djangolib.testing.utils import skip_unless_cms
-from xmodule.library_content_block import ANY_CAPA_TYPE_VALUE, LibraryContentBlock
-from xmodule.library_tools import LibraryToolsService
+from xmodule.library_content_block import ANY_CAPA_TYPE_VALUE, LegacyLibraryContentBlock
+from xmodule.library_tools import LegacyLibraryToolsService
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.tests.factories import CourseFactory, LibraryFactory
from xmodule.modulestore.tests.utils import MixedSplitTestCase
@@ -33,15 +31,15 @@ dummy_render = lambda block, _: Fragment(block.data) # pylint: disable=invalid-
@skip_unless_cms
-class LibraryContentTest(MixedSplitTestCase):
+class LegacyLibraryContentTest(MixedSplitTestCase):
"""
- Base class for tests of LibraryContentBlock (library_content_block.py)
+ Base class for tests of LegacyLibraryContentBlock (library_content_block.py)
"""
def setUp(self):
super().setUp()
self.user_id = UserFactory().id
- self.tools = LibraryToolsService(self.store, self.user_id)
+ self.tools = LegacyLibraryToolsService(self.store, self.user_id)
self.library = LibraryFactory.create(modulestore=self.store)
self.lib_blocks = [
self.make_block("html", self.library, data=f"Hello world from block {i}")
@@ -88,29 +86,22 @@ class LibraryContentTest(MixedSplitTestCase):
@ddt.ddt
-class LibraryContentGeneralTest(LibraryContentTest):
+class LegacyLibraryContentGeneralTest(LegacyLibraryContentTest):
"""
- Test the base functionality of the LibraryContentBlock.
+ Test the base functionality of the LegacyLibraryContentBlock.
"""
- @ddt.data(
- ('library-v1:ProblemX+PR0B', LibraryLocator),
- ('lib:ORG:test-1', LibraryLocatorV2)
- )
- @ddt.unpack
- def test_source_library_key(self, library_key, expected_locator_type):
+ def test_source_library_key(self):
"""
Test the source_library_key property of the xblock.
-
- The method should correctly work either with V1 or V2 libraries.
"""
library = self.make_block(
"library_content",
self.vertical,
max_count=1,
- source_library_id=library_key
+ source_library_id='library-v1:ProblemX+PR0B',
)
- assert isinstance(library.source_library_key, expected_locator_type)
+ assert isinstance(library.source_library_key, LibraryLocator)
def test_initial_sync_from_library(self):
"""
@@ -133,9 +124,9 @@ class LibraryContentGeneralTest(LibraryContentTest):
assert len(self.lc_block.children) == len(self.lib_blocks)
-class TestLibraryContentExportImport(LibraryContentTest):
+class TestLibraryContentExportImport(LegacyLibraryContentTest):
"""
- Export and import tests for LibraryContentBlock
+ Export and import tests for LegacyLibraryContentBlock
"""
def setUp(self):
super().setUp()
@@ -173,7 +164,6 @@ class TestLibraryContentExportImport(LibraryContentTest):
assert imported_lc_block.display_name == self.lc_block.display_name
assert imported_lc_block.source_library_id == self.lc_block.source_library_id
assert imported_lc_block.source_library_version == self.lc_block.source_library_version
- assert imported_lc_block.mode == self.lc_block.mode
assert imported_lc_block.max_count == self.lc_block.max_count
assert imported_lc_block.capa_type == self.lc_block.capa_type
assert len(imported_lc_block.children) == len(self.lc_block.children)
@@ -195,13 +185,13 @@ class TestLibraryContentExportImport(LibraryContentTest):
# Now import it.
olx_element = etree.fromstring(exported_olx)
- imported_lc_block = LibraryContentBlock.parse_xml(olx_element, self.runtime, None)
+ imported_lc_block = LegacyLibraryContentBlock.parse_xml(olx_element, self.runtime, None)
self._verify_xblock_properties(imported_lc_block)
def test_xml_import_with_comments(self):
"""
- Test that XML comments within LibraryContentBlock are ignored during the import.
+ Test that XML comments within LegacyLibraryContentBlock are ignored during the import.
"""
olx_with_comments = (
'\n'
@@ -219,15 +209,15 @@ class TestLibraryContentExportImport(LibraryContentTest):
# Import the olx.
olx_element = etree.fromstring(olx_with_comments)
- imported_lc_block = LibraryContentBlock.parse_xml(olx_element, self.runtime, None)
+ imported_lc_block = LegacyLibraryContentBlock.parse_xml(olx_element, self.runtime, None)
self._verify_xblock_properties(imported_lc_block)
@ddt.ddt
-class LibraryContentBlockTestMixin:
+class LegacyLibraryContentBlockTestMixin:
"""
- Basic unit tests for LibraryContentBlock
+ Basic unit tests for LegacyLibraryContentBlock
"""
problem_types = [
["multiplechoiceresponse"], ["optionresponse"], ["optionresponse", "coderesponse"],
@@ -424,8 +414,7 @@ class LibraryContentBlockTestMixin:
Test the settings that are marked as "non-editable".
"""
non_editable_metadata_fields = self.lc_block.non_editable_metadata_fields
- assert LibraryContentBlock.mode in non_editable_metadata_fields
- assert LibraryContentBlock.display_name not in non_editable_metadata_fields
+ assert LegacyLibraryContentBlock.display_name not in non_editable_metadata_fields
def test_overlimit_blocks_chosen_randomly(self):
"""
@@ -503,7 +492,7 @@ search_index_mock = Mock(spec=SearchEngine) # pylint: disable=invalid-name
@patch.object(SearchEngine, 'get_search_engine', Mock(return_value=None, autospec=True))
-class TestLibraryContentBlockWithSearchIndex(LibraryContentBlockTestMixin, LibraryContentTest):
+class TestLegacyLibraryContentBlockWithSearchIndex(LegacyLibraryContentBlockTestMixin, LegacyLibraryContentTest):
"""
Tests for library container with mocked search engine response.
"""
@@ -532,9 +521,9 @@ class TestLibraryContentBlockWithSearchIndex(LibraryContentBlockTestMixin, Libra
)
@patch('xmodule.html_block.HtmlBlock.author_view', dummy_render, create=True)
@patch('xmodule.x_module.DescriptorSystem.applicable_aside_types', lambda self, block: [])
-class TestLibraryContentRender(LibraryContentTest):
+class TestLibraryContentRender(LegacyLibraryContentTest):
"""
- Rendering unit tests for LibraryContentBlock
+ Rendering unit tests for LegacyLibraryContentBlock
"""
def setUp(self):
@@ -559,9 +548,9 @@ class TestLibraryContentRender(LibraryContentTest):
# but some js initialization should happen
-class TestLibraryContentAnalytics(LibraryContentTest):
+class TestLibraryContentAnalytics(LegacyLibraryContentTest):
"""
- Test analytics features of LibraryContentBlock
+ Test analytics features of LegacyLibraryContentBlock
"""
def setUp(self):
@@ -573,7 +562,7 @@ class TestLibraryContentAnalytics(LibraryContentTest):
def _assert_event_was_published(self, event_type):
"""
- Check that a LibraryContentBlock analytics event was published by self.lc_block.
+ Check that a LegacyLibraryContentBlock analytics event was published by self.lc_block.
"""
assert self.publisher.called
assert len(self.publisher.call_args[0]) == 3 # pylint:disable=unsubscriptable-object
diff --git a/xmodule/tests/test_library_tools.py b/xmodule/tests/test_library_tools.py
index f93066cd5c..30b007c3d9 100644
--- a/xmodule/tests/test_library_tools.py
+++ b/xmodule/tests/test_library_tools.py
@@ -1,23 +1,20 @@
"""
-Tests for library tools service (only used by CMS)
+Tests for legacy library tools service (only used by CMS)
-Currently, the only known user of the LibraryToolsService is the
-LibraryContentBlock, so these tests are all written with only that
+The only known user of the LegacyLibraryToolsService is the
+LegacyLibraryContentBlock, so these tests are all written with only that
block type in mind.
"""
from unittest import mock
import ddt
-from django.conf import settings
-from django.test import override_settings
-from opaque_keys.edx.locator import LibraryLocator, LibraryLocatorV2
+from opaque_keys.edx.locator import LibraryLocator
-from common.djangoapps.student.roles import CourseInstructorRole
from common.djangoapps.student.tests.factories import UserFactory
from openedx.core.djangolib.testing.utils import skip_unless_cms
from openedx.core.djangoapps.content_libraries.tests.base import ContentLibrariesRestApiTest
-from xmodule.library_tools import LibraryToolsService
+from xmodule.library_tools import LegacyLibraryToolsService
from xmodule.modulestore.tests.factories import CourseFactory, LibraryFactory
from xmodule.modulestore.tests.utils import MixedSplitTestCase
@@ -26,34 +23,23 @@ from xmodule.modulestore.tests.utils import MixedSplitTestCase
@ddt.ddt
class ContentLibraryToolsTest(MixedSplitTestCase, ContentLibrariesRestApiTest):
"""
- Tests for LibraryToolsService.
-
- Tests interaction with learning-core-based (V2) and mongo-based (V1) content libraries.
+ Tests for LegacyLibraryToolsService.
"""
def setUp(self):
super().setUp()
UserFactory(is_staff=True, id=self.user_id)
- self.tools = LibraryToolsService(self.store, self.user_id)
+ self.tools = LegacyLibraryToolsService(self.store, self.user_id)
def test_list_available_libraries(self):
"""
- Test listing of libraries.
-
- Collects Only V2 Libaries if the FEATURES[ENABLE_LIBRARY_AUTHORING_MICROFRONTEND] setting is True.
- Otherwise, return all v1 and v2 libraries.
+ Test listing of v1 libraries.
"""
# create V1 library
_ = LibraryFactory.create(modulestore=self.store)
- # create V2 library
+ # create V2 library (should not be included in this list)
self._create_library(slug="testlib1_preview", title="Test Library 1", description="Testing XBlocks")
all_libraries = self.tools.list_available_libraries()
- assert all_libraries
- assert len(all_libraries) == 2
-
- with override_settings(FEATURES={**settings.FEATURES, "ENABLE_LIBRARY_AUTHORING_MICROFRONTEND": True}):
- all_libraries = self.tools.list_available_libraries()
- assert all_libraries
- assert len(all_libraries) == 1
+ assert len(all_libraries) == 1
@mock.patch('xmodule.modulestore.split_mongo.split.SplitMongoModuleStore.get_library_summaries')
def test_list_available_libraries_fetch(self, mock_get_library_summaries):
@@ -63,7 +49,7 @@ class ContentLibraryToolsTest(MixedSplitTestCase, ContentLibrariesRestApiTest):
_ = self.tools.list_available_libraries()
assert mock_get_library_summaries.called
- def test_get_latest_v1_library_version(self):
+ def test_get_latest_library_version(self):
"""
Test get_v1_library_version for V1 libraries.
@@ -84,49 +70,16 @@ class ContentLibraryToolsTest(MixedSplitTestCase, ContentLibrariesRestApiTest):
assert result == str(lib.location.library_key.version_guid)
@ddt.data(
- 'library-v1:Fake+Key', # V1 library key
- 'lib:Fake:V-2', # V2 library key
+ 'library-v1:Fake+Key',
LibraryLocator.from_string('library-v1:Fake+Key'),
- LibraryLocatorV2.from_string('lib:Fake:V-2'),
)
def test_get_latest_library_version_no_library(self, lib_key):
"""
Test get_latest_library_version result when the library does not exist.
-
- Provided lib_key's are valid V1 or V2 keys.
"""
assert self.tools.get_latest_library_version(lib_key) is None
- def test_update_children_for_v2_lib(self):
- """
- Test update_children with V2 library as a source.
- """
- library = self._create_library(
- slug="cool-v2-lib", title="The best Library", description="Spectacular description"
- )
- self._add_block_to_library(library["id"], "unit", "unit1_id")
-
- course = CourseFactory.create(modulestore=self.store, user_id=self.user.id)
- CourseInstructorRole(course.id).add_users(self.user)
-
- content_block = self.make_block(
- "library_content",
- course,
- max_count=1,
- source_library_id=library['id']
- )
- assert len(content_block.children) == 0
-
- # Populate children from library
- self.tools.trigger_library_sync(content_block, library_version=None)
-
- # The updates happen in a Celery task, so this particular content_block instance is no updated.
- # We must re-instantiate it from modulstore in order to see the updated children list.
- content_block = self.store.get_item(content_block.location)
-
- assert len(content_block.children) == 1
-
- def test_update_children_for_v1_lib(self):
+ def test_update_children(self):
"""
Test update_children with V1 library as a source.
diff --git a/xmodule/tests/test_randomize_block.py b/xmodule/tests/test_randomize_block.py
index deebdfe4f1..52413faad3 100644
--- a/xmodule/tests/test_randomize_block.py
+++ b/xmodule/tests/test_randomize_block.py
@@ -16,7 +16,7 @@ from .test_course_block import DummySystem as TestImportSystem
class RandomizeBlockTest(MixedSplitTestCase):
"""
- Base class for tests of LibraryContentBlock (library_content_block.py)
+ Base class for tests of RandomizeBlock (randomize_block.py)
"""
maxDiff = None
diff --git a/xmodule/vertical_block.py b/xmodule/vertical_block.py
index 2a10ae4449..81621a7a7b 100644
--- a/xmodule/vertical_block.py
+++ b/xmodule/vertical_block.py
@@ -188,7 +188,7 @@ class VerticalBlock(
if has_access_error:
return True
- # Check child nodes if they exist (e.g. randomized library question aka LibraryContentBlock)
+ # Check child nodes if they exist (e.g. randomized library question aka LegacyLibraryContentBlock)
for child in block.get_children():
has_access_error = getattr(child, 'has_access_error', False)
if has_access_error:
From e28a01e2bf78d5750c649edf5e8d48776cc38d8d Mon Sep 17 00:00:00 2001
From: Muhammad Adeel Tajamul
<77053848+muhammadadeeltajamul@users.noreply.github.com>
Date: Wed, 16 Oct 2024 11:13:06 +0500
Subject: [PATCH 23/32] temp: logs for unsubscribe event (#35652)
---
openedx/core/djangoapps/notifications/email/utils.py | 4 ++++
openedx/core/djangoapps/notifications/events.py | 5 ++---
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/openedx/core/djangoapps/notifications/email/utils.py b/openedx/core/djangoapps/notifications/email/utils.py
index 582e867d62..81a245b2cf 100644
--- a/openedx/core/djangoapps/notifications/email/utils.py
+++ b/openedx/core/djangoapps/notifications/email/utils.py
@@ -3,6 +3,7 @@ Email Notifications Utils
"""
import datetime
import json
+import logging
from bs4 import BeautifulSoup
from django.conf import settings
@@ -31,6 +32,7 @@ from .notification_icons import NotificationTypeIcons
User = get_user_model()
+log = logging.getLogger(__name__)
def is_email_notification_flag_enabled(user=None):
@@ -411,4 +413,6 @@ def update_user_preferences_from_patch(encrypted_username, encrypted_patch):
if pref_value else EmailCadence.NEVER
type_prefs['email_cadence'] = cadence_value
preference.save()
+ if not user.id:
+ log.info(f" - user.id is null - {encrypted_username} ")
notification_preference_unsubscribe_event(user)
diff --git a/openedx/core/djangoapps/notifications/events.py b/openedx/core/djangoapps/notifications/events.py
index 91b12075a8..74e6e56e41 100644
--- a/openedx/core/djangoapps/notifications/events.py
+++ b/openedx/core/djangoapps/notifications/events.py
@@ -167,6 +167,5 @@ def notification_preference_unsubscribe_event(user):
'username': user.username,
'event_type': 'email_digest_unsubscribe'
}
- with tracker.get_tracker().context(NOTIFICATION_PREFERENCE_UNSUBSCRIBE, event_data):
- tracker.emit(NOTIFICATION_PREFERENCE_UNSUBSCRIBE, event_data)
- segment.track(user.id, NOTIFICATION_PREFERENCE_UNSUBSCRIBE, event_data)
+ tracker.emit(NOTIFICATION_PREFERENCE_UNSUBSCRIBE, event_data)
+ segment.track(user.id, NOTIFICATION_PREFERENCE_UNSUBSCRIBE, event_data)
From d72e87d3f33a2939c4e10825421069f3284029f0 Mon Sep 17 00:00:00 2001
From: Muhammad Adeel Tajamul
<77053848+muhammadadeeltajamul@users.noreply.github.com>
Date: Wed, 16 Oct 2024 13:58:40 +0500
Subject: [PATCH 24/32] feat: removed extra spaces from start and end of
content (#35647)
---
.../rest_api/discussions_notifications.py | 39 +++++++++++++++++--
.../tests/test_discussions_notifications.py | 22 +++++++++--
2 files changed, 54 insertions(+), 7 deletions(-)
diff --git a/lms/djangoapps/discussion/rest_api/discussions_notifications.py b/lms/djangoapps/discussion/rest_api/discussions_notifications.py
index 498a05fdb9..b0eb7c89dc 100644
--- a/lms/djangoapps/discussion/rest_api/discussions_notifications.py
+++ b/lms/djangoapps/discussion/rest_api/discussions_notifications.py
@@ -3,7 +3,7 @@ Discussion notifications sender util.
"""
import re
-from bs4 import BeautifulSoup
+from bs4 import BeautifulSoup, Tag
from django.conf import settings
from django.utils.text import Truncator
@@ -380,6 +380,30 @@ def remove_html_tags(text):
return re.sub(clean, '', text)
+def strip_empty_tags(soup):
+ """
+ Strip starting and ending empty tags from the soup object
+ """
+ def strip_tag(element, reverse=False):
+ """
+ Checks if element is empty and removes it
+ """
+ if not element.get_text(strip=True):
+ element.extract()
+ return True
+ if isinstance(element, Tag):
+ child_list = element.contents[::-1] if reverse else element.contents
+ for child in child_list:
+ if not strip_tag(child):
+ break
+ return False
+
+ while soup.contents:
+ if not (strip_tag(soup.contents[0]) or strip_tag(soup.contents[-1], reverse=True)):
+ break
+ return soup
+
+
def clean_thread_html_body(html_body):
"""
Get post body with tags removed and limited to 500 characters
@@ -401,6 +425,9 @@ def clean_thread_html_body(html_body):
for match in html_body.find_all(tag):
match.unwrap()
+ if not html_body.find():
+ return str(html_body)
+
# Replace tags that are not allowed in email
tags_to_update = [
{"source": "button", "target": "span"},
@@ -412,11 +439,15 @@ def clean_thread_html_body(html_body):
for tag_dict in tags_to_update:
for source_tag in html_body.find_all(tag_dict['source']):
target_tag = html_body.new_tag(tag_dict['target'], **source_tag.attrs)
- if source_tag.string:
- target_tag.string = source_tag.string
- source_tag.replace_with(target_tag)
+ if source_tag.contents:
+ for content in list(source_tag.contents):
+ target_tag.append(content)
+ source_tag.insert_before(target_tag)
+ source_tag.extract()
for tag in html_body.find_all(True):
tag.attrs = {}
tag['style'] = 'margin: 0'
+
+ html_body = strip_empty_tags(html_body)
return str(html_body)
diff --git a/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py b/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py
index 0a8d750416..9e4a76aa40 100644
--- a/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py
+++ b/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py
@@ -179,15 +179,23 @@ class TestCleanThreadHtmlBody(unittest.TestCase):
"""
Tests that the clean_thread_html_body function replaces the button tag with span tag
"""
- # Tests for button replacement tag with text
html_body = ''
expected_output = 'Button'
result = clean_thread_html_body(html_body)
self.assertEqual(result, expected_output)
- # Tests button tag replacement without text
+ html_body = '
abc
abc
'
+ expected_output = '
abc
'\
+ '
abc
'
+ result = clean_thread_html_body(html_body)
+ self.assertEqual(result, expected_output)
+
+ def test_button_tag_removal(self):
+ """
+ Tests button tag with no text is removed if at start or end
+ """
html_body = ''
- expected_output = ''
+ expected_output = ''
result = clean_thread_html_body(html_body)
self.assertEqual(result, expected_output)
@@ -196,3 +204,11 @@ class TestCleanThreadHtmlBody(unittest.TestCase):
html_body = '
Paragraph
'
result = clean_thread_html_body(html_body)
self.assertEqual(result, '
Paragraph
')
+
+ def test_strip_empty_tags(self):
+ """
+ Tests if the clean_thread_html_body function removes starting and ending empty tags
+ """
+ html_body = '
content
'
+ result = clean_thread_html_body(html_body)
+ self.assertEqual(result, '
content
')
From d8303a176cafea277dfe9418f625c3bf083110f3 Mon Sep 17 00:00:00 2001
From: ayesha waris <73840786+ayesha-waris@users.noreply.github.com>
Date: Wed, 16 Oct 2024 17:27:04 +0500
Subject: [PATCH 25/32] feat: added waffle flag for new notification view
(#35569)
---
openedx/core/djangoapps/notifications/config/waffle.py | 10 ++++++++++
openedx/core/djangoapps/notifications/utils.py | 9 ++++++++-
openedx/core/djangoapps/notifications/views.py | 6 ++++--
3 files changed, 22 insertions(+), 3 deletions(-)
diff --git a/openedx/core/djangoapps/notifications/config/waffle.py b/openedx/core/djangoapps/notifications/config/waffle.py
index 4a1481f6f5..b74cd84dca 100644
--- a/openedx/core/djangoapps/notifications/config/waffle.py
+++ b/openedx/core/djangoapps/notifications/config/waffle.py
@@ -49,3 +49,13 @@ ENABLE_ORA_GRADE_NOTIFICATION = CourseWaffleFlag(f"{WAFFLE_NAMESPACE}.enable_ora
# .. toggle_warning: When the flag is ON, Notifications Grouping feature is enabled.
# .. toggle_tickets: INF-1472
ENABLE_NOTIFICATION_GROUPING = CourseWaffleFlag(f'{WAFFLE_NAMESPACE}.enable_notification_grouping', __name__)
+
+# .. toggle_name: notifications.enable_new_notification_view
+# .. toggle_implementation: WaffleFlag
+# .. toggle_default: False
+# .. toggle_description: Waffle flag to enable new notification view
+# .. toggle_use_cases: temporary, open_edx
+# .. toggle_creation_date: 2024-09-30
+# .. toggle_target_removal_date: 2025-10-10
+# .. toggle_tickets: INF-1603
+ENABLE_NEW_NOTIFICATION_VIEW = WaffleFlag(f"{WAFFLE_NAMESPACE}.enable_new_notification_view", __name__)
diff --git a/openedx/core/djangoapps/notifications/utils.py b/openedx/core/djangoapps/notifications/utils.py
index 249eaf8749..fa948dcf42 100644
--- a/openedx/core/djangoapps/notifications/utils.py
+++ b/openedx/core/djangoapps/notifications/utils.py
@@ -5,7 +5,7 @@ from typing import Dict, List
from common.djangoapps.student.models import CourseAccessRole, CourseEnrollment
from openedx.core.djangoapps.django_comment_common.models import Role
-from openedx.core.djangoapps.notifications.config.waffle import ENABLE_NOTIFICATIONS
+from openedx.core.djangoapps.notifications.config.waffle import ENABLE_NOTIFICATIONS, ENABLE_NEW_NOTIFICATION_VIEW
from openedx.core.lib.cache_utils import request_cached
@@ -47,6 +47,13 @@ def get_show_notifications_tray(user):
return show_notifications_tray
+def get_is_new_notification_view_enabled():
+ """
+ Returns True if the waffle flag for the new notification view is enabled, False otherwise.
+ """
+ return ENABLE_NEW_NOTIFICATION_VIEW.is_enabled()
+
+
def get_list_in_batches(input_list, batch_size):
"""
Divides the list of objects into list of list of objects each of length batch_size.
diff --git a/openedx/core/djangoapps/notifications/views.py b/openedx/core/djangoapps/notifications/views.py
index e6798c7c5c..e87274088f 100644
--- a/openedx/core/djangoapps/notifications/views.py
+++ b/openedx/core/djangoapps/notifications/views.py
@@ -39,7 +39,7 @@ from .serializers import (
UserCourseNotificationPreferenceSerializer,
UserNotificationPreferenceUpdateSerializer,
)
-from .utils import get_show_notifications_tray
+from .utils import get_show_notifications_tray, get_is_new_notification_view_enabled
@allow_any_authenticated_user()
@@ -329,6 +329,7 @@ class NotificationCountView(APIView):
)
count_total = 0
show_notifications_tray = get_show_notifications_tray(self.request.user)
+ is_new_notification_view_enabled = get_is_new_notification_view_enabled()
count_by_app_name_dict = {
app_name: 0
for app_name in COURSE_NOTIFICATION_APPS
@@ -344,7 +345,8 @@ class NotificationCountView(APIView):
"show_notifications_tray": show_notifications_tray,
"count": count_total,
"count_by_app_name": count_by_app_name_dict,
- "notification_expiry_days": settings.NOTIFICATIONS_EXPIRY
+ "notification_expiry_days": settings.NOTIFICATIONS_EXPIRY,
+ "is_new_notification_view_enabled": is_new_notification_view_enabled
})
From bf862d89e72aab9097166b88729ef4e4e3d46f9d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9gis=20Behmo?=
Date: Mon, 14 Oct 2024 07:53:41 +0200
Subject: [PATCH 26/32] feat!: hide courses in /courses based on catalog
visibility
Previously, courses were always displayed on the LMS /courses page,
independently of their catalog visibility attribute. This meant that
even with visibility="none" courses were being displayed. This was very
counter-intuitive.
With this change, courses are displayed only when their visibility is
set to "both".
This change is flagged as breaking because it has the potential to
affect course catalogs in existing platforms.
To test this change, go to http(s)://LMS/courses as an anonymous user.
Pick a visible course and go to its "advanced settings" in the studio.
Set "Course visibility in catalog" to "about" or "none". Then, clear the
cache with the following command:
./manage.py lms shell -c "from django.core.cache import cache; cache.clear()"
Open the /courses page again: the course should no longer be visible.
Close https://github.com/openedx/wg-build-test-release/issues/330
---
lms/djangoapps/branding/tests/test_page.py | 24 ++++++++++++++++++++++
lms/djangoapps/courseware/views/views.py | 11 ++++++++--
2 files changed, 33 insertions(+), 2 deletions(-)
diff --git a/lms/djangoapps/branding/tests/test_page.py b/lms/djangoapps/branding/tests/test_page.py
index 9904f79f64..46f10bd692 100644
--- a/lms/djangoapps/branding/tests/test_page.py
+++ b/lms/djangoapps/branding/tests/test_page.py
@@ -22,6 +22,7 @@ from lms.djangoapps.courseware.tests.helpers import LoginEnrollmentTestCase
from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order
+from xmodule.course_block import CATALOG_VISIBILITY_ABOUT, CATALOG_VISIBILITY_NONE
FEATURES_WITH_STARTDATE = settings.FEATURES.copy()
FEATURES_WITH_STARTDATE['DISABLE_START_DATES'] = False
@@ -201,6 +202,20 @@ class IndexPageCourseCardsSortingTests(ModuleStoreTestCase):
display_name='Tech Beta Course',
emit_signals=True,
)
+ self.course_with_none_visibility = CourseFactory.create(
+ org='MITx',
+ number='1003',
+ catalog_visibility=CATALOG_VISIBILITY_NONE,
+ display_name='Course with "none" catalog visibility',
+ emit_signals=True,
+ )
+ self.course_with_about_visibility = CourseFactory.create(
+ org='MITx',
+ number='1003',
+ catalog_visibility=CATALOG_VISIBILITY_ABOUT,
+ display_name='Course with "about" catalog visibility',
+ emit_signals=True,
+ )
self.factory = RequestFactory()
@patch('common.djangoapps.student.views.management.render_to_response', RENDER_MOCK)
@@ -300,6 +315,15 @@ class IndexPageCourseCardsSortingTests(ModuleStoreTestCase):
assert context['courses'][1].id == self.starting_earlier.id
assert context['courses'][2].id == self.course_with_default_start_date.id
+ @patch('lms.djangoapps.courseware.views.views.render_to_response', RENDER_MOCK)
+ def test_invisible_courses_are_not_displayed(self):
+ response = self.client.get(reverse('courses'))
+ ((_template, context), _) = RENDER_MOCK.call_args # pylint: disable=unpacking-non-sequence
+
+ rendered_ids = [course.id for course in context["courses"]]
+ assert self.course_with_none_visibility.id not in rendered_ids
+ assert self.course_with_about_visibility.id not in rendered_ids
+
class IndexPageProgramsTests(SiteMixin, ModuleStoreTestCase):
"""
diff --git a/lms/djangoapps/courseware/views/views.py b/lms/djangoapps/courseware/views/views.py
index 79a52db8a0..1c57b23d9b 100644
--- a/lms/djangoapps/courseware/views/views.py
+++ b/lms/djangoapps/courseware/views/views.py
@@ -46,7 +46,11 @@ from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
from token_utils.api import unpack_token_for
from web_fragments.fragment import Fragment
-from xmodule.course_block import COURSE_VISIBILITY_PUBLIC, COURSE_VISIBILITY_PUBLIC_OUTLINE
+from xmodule.course_block import (
+ COURSE_VISIBILITY_PUBLIC,
+ COURSE_VISIBILITY_PUBLIC_OUTLINE,
+ CATALOG_VISIBILITY_CATALOG_AND_ABOUT,
+)
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError, NoPathToItem
from xmodule.tabs import CourseTabList
@@ -288,7 +292,10 @@ def courses(request):
course_discovery_meanings = getattr(settings, 'COURSE_DISCOVERY_MEANINGS', {})
set_default_filter = ENABLE_COURSE_DISCOVERY_DEFAULT_LANGUAGE_FILTER.is_enabled()
if not settings.FEATURES.get('ENABLE_COURSE_DISCOVERY'):
- courses_list = get_courses(request.user)
+ courses_list = get_courses(
+ request.user,
+ filter_={"catalog_visibility": CATALOG_VISIBILITY_CATALOG_AND_ABOUT},
+ )
if configuration_helpers.get_value("ENABLE_COURSE_SORTING_BY_START_DATE",
settings.FEATURES["ENABLE_COURSE_SORTING_BY_START_DATE"]):
From 77e683d5b6b7164b934611806ce956f3ea246329 Mon Sep 17 00:00:00 2001
From: David Ormsbee
Date: Wed, 16 Oct 2024 11:12:11 -0400
Subject: [PATCH 27/32] feat: new view & API calls to serve content library
assets (#35639)
This commit adds a new view to serve static assets for content
libraries, along with Content Library API calls to add, delete, and get
metadata about these assets. These assets come from Learning Core and
should ONLY BE ACCESSED FROM STUDIO. Users must have read access to the
library in order to see an asset in that library.
This also re-implements video transcript support for content libraries
and re-enables some previously disabled tests around it.
---
cms/envs/test.py | 10 ++
.../core/djangoapps/content_libraries/api.py | 126 ++++++++++++++++--
.../tests/test_content_libraries.py | 20 ++-
.../tests/test_static_assets.py | 92 +++++++++++--
.../core/djangoapps/content_libraries/urls.py | 5 +
.../djangoapps/content_libraries/views.py | 80 ++++++++++-
xmodule/video_block/transcripts_utils.py | 109 ++++++++++++---
7 files changed, 391 insertions(+), 51 deletions(-)
diff --git a/cms/envs/test.py b/cms/envs/test.py
index 38b7c78171..49db506088 100644
--- a/cms/envs/test.py
+++ b/cms/envs/test.py
@@ -333,3 +333,13 @@ COURSE_LIVE_GLOBAL_CREDENTIALS["BIG_BLUE_BUTTON"] = {
"SECRET": "***",
"URL": "***",
}
+
+############## openedx-learning (Learning Core) config ##############
+OPENEDX_LEARNING = {
+ 'MEDIA': {
+ 'BACKEND': 'django.core.files.storage.InMemoryStorage',
+ 'OPTIONS': {
+ 'location': MEDIA_ROOT + "_private"
+ }
+ }
+}
diff --git a/openedx/core/djangoapps/content_libraries/api.py b/openedx/core/djangoapps/content_libraries/api.py
index f30d5f0477..4d09eb4549 100644
--- a/openedx/core/djangoapps/content_libraries/api.py
+++ b/openedx/core/djangoapps/content_libraries/api.py
@@ -56,6 +56,7 @@ from datetime import datetime, timezone
import base64
import hashlib
import logging
+import mimetypes
import attr
import requests
@@ -68,6 +69,7 @@ from django.db import IntegrityError, transaction
from django.db.models import Q, QuerySet
from django.utils.translation import gettext as _
from edx_rest_api_client.client import OAuthAPIClient
+from django.urls import reverse
from lxml import etree
from opaque_keys.edx.keys import BlockTypeKey, UsageKey, UsageKeyV2
from opaque_keys.edx.locator import (
@@ -96,7 +98,11 @@ from organizations.models import Organization
from xblock.core import XBlock
from xblock.exceptions import XBlockNotFoundError
-from openedx.core.djangoapps.xblock.api import get_component_from_usage_key, xblock_type_display_name
+from openedx.core.djangoapps.xblock.api import (
+ get_component_from_usage_key,
+ get_xblock_app_config,
+ xblock_type_display_name,
+)
from openedx.core.lib.xblock_serializer.api import serialize_modulestore_block_for_learning_core
from xmodule.modulestore.django import modulestore
@@ -1018,18 +1024,48 @@ def get_library_block_static_asset_files(usage_key) -> list[LibraryXBlockStaticF
Returns a list of LibraryXBlockStaticFile objects, sorted by path.
- TODO: This is not yet implemented for Learning Core backed libraries.
TODO: Should this be in the general XBlock API rather than the libraries API?
"""
- return []
+ component = get_component_from_usage_key(usage_key)
+ component_version = component.versioning.draft
+
+ # If there is no Draft version, then this was soft-deleted
+ if component_version is None:
+ return []
+
+ # cvc = the ComponentVersionContent through table
+ cvc_set = (
+ component_version
+ .componentversioncontent_set
+ .filter(content__has_file=True)
+ .order_by('key')
+ .select_related('content')
+ )
+
+ site_root_url = get_xblock_app_config().get_site_root_url()
+
+ return [
+ LibraryXBlockStaticFile(
+ path=cvc.key,
+ size=cvc.content.size,
+ url=site_root_url + reverse(
+ 'content_libraries:library-assets',
+ kwargs={
+ 'component_version_uuid': component_version.uuid,
+ 'asset_path': cvc.key,
+ }
+ ),
+ )
+ for cvc in cvc_set
+ ]
-def add_library_block_static_asset_file(usage_key, file_name, file_content) -> LibraryXBlockStaticFile:
+def add_library_block_static_asset_file(usage_key, file_path, file_content, user=None) -> LibraryXBlockStaticFile:
"""
Upload a static asset file into the library, to be associated with the
specified XBlock. Will silently overwrite an existing file of the same name.
- file_name should be a name like "doc.pdf". It may optionally contain slashes
+ file_path should be a name like "doc.pdf". It may optionally contain slashes
like 'en/doc.pdf'
file_content should be a binary string.
@@ -1041,10 +1077,67 @@ def add_library_block_static_asset_file(usage_key, file_name, file_content) -> L
video_block = UsageKey.from_string("lb:VideoTeam:python-intro:video:1")
add_library_block_static_asset_file(video_block, "subtitles-en.srt", subtitles.encode('utf-8'))
"""
- raise NotImplementedError("Static assets not yet implemented for Learning Core")
+ # File path validations copied over from v1 library logic. This can't really
+ # hurt us inside our system because we never use these paths in an actual
+ # file system–they're just string keys that point to hash-named data files
+ # in a common library (learning package) level directory. But it might
+ # become a security issue during import/export serialization.
+ if file_path != file_path.strip().strip('/'):
+ raise InvalidNameError("file_path cannot start/end with / or whitespace.")
+ if '//' in file_path or '..' in file_path:
+ raise InvalidNameError("Invalid sequence (// or ..) in file_path.")
+
+ component = get_component_from_usage_key(usage_key)
+
+ media_type_str, _encoding = mimetypes.guess_type(file_path)
+ # We use "application/octet-stream" as a generic fallback media type, per
+ # RFC 2046: https://datatracker.ietf.org/doc/html/rfc2046
+ # TODO: This probably makes sense to push down to openedx-learning?
+ media_type_str = media_type_str or "application/octet-stream"
+
+ now = datetime.now(tz=timezone.utc)
+
+ with transaction.atomic():
+ media_type = authoring_api.get_or_create_media_type(media_type_str)
+ content = authoring_api.get_or_create_file_content(
+ component.publishable_entity.learning_package.id,
+ media_type.id,
+ data=file_content,
+ created=now,
+ )
+ component_version = authoring_api.create_next_component_version(
+ component.pk,
+ content_to_replace={file_path: content.id},
+ created=now,
+ created_by=user.id if user else None,
+ )
+ transaction.on_commit(
+ lambda: LIBRARY_BLOCK_UPDATED.send_event(
+ library_block=LibraryBlockData(
+ library_key=usage_key.context_key,
+ usage_key=usage_key,
+ )
+ )
+ )
+
+ # Now figure out the URL for the newly created asset...
+ site_root_url = get_xblock_app_config().get_site_root_url()
+ local_path = reverse(
+ 'content_libraries:library-assets',
+ kwargs={
+ 'component_version_uuid': component_version.uuid,
+ 'asset_path': file_path,
+ }
+ )
+
+ return LibraryXBlockStaticFile(
+ path=file_path,
+ url=site_root_url + local_path,
+ size=content.size,
+ )
-def delete_library_block_static_asset_file(usage_key, file_name):
+def delete_library_block_static_asset_file(usage_key, file_path, user=None):
"""
Delete a static asset file from the library.
@@ -1054,7 +1147,24 @@ def delete_library_block_static_asset_file(usage_key, file_name):
video_block = UsageKey.from_string("lb:VideoTeam:python-intro:video:1")
delete_library_block_static_asset_file(video_block, "subtitles-en.srt")
"""
- raise NotImplementedError("Static assets not yet implemented for Learning Core")
+ component = get_component_from_usage_key(usage_key)
+ now = datetime.now(tz=timezone.utc)
+
+ with transaction.atomic():
+ component_version = authoring_api.create_next_component_version(
+ component.pk,
+ content_to_replace={file_path: None},
+ created=now,
+ created_by=user.id if user else None,
+ )
+ transaction.on_commit(
+ lambda: LIBRARY_BLOCK_UPDATED.send_event(
+ library_block=LibraryBlockData(
+ library_key=usage_key.context_key,
+ usage_key=usage_key,
+ )
+ )
+ )
def get_allowed_block_types(library_key): # pylint: disable=unused-argument
diff --git a/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py b/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py
index 56e258f8a1..03b32e08ad 100644
--- a/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py
+++ b/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py
@@ -661,13 +661,13 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix
self._get_library_block_olx(block3_key, expect_response=403)
self._get_library_block_fields(block3_key, expect_response=403)
self._get_library_block_assets(block3_key, expect_response=403)
- self._get_library_block_asset(block3_key, file_name="whatever.png", expect_response=403)
+ self._get_library_block_asset(block3_key, file_name="static/whatever.png", expect_response=403)
# Nor can they preview the block:
self._render_block_view(block3_key, view_name="student_view", expect_response=403)
# But if we grant allow_public_read, then they can:
with self.as_user(admin):
self._update_library(lib_id, allow_public_read=True)
- # self._set_library_block_asset(block3_key, "whatever.png", b"data")
+ self._set_library_block_asset(block3_key, "static/whatever.png", b"data")
with self.as_user(random_user):
self._get_library_block_olx(block3_key)
self._render_block_view(block3_key, view_name="student_view")
@@ -680,7 +680,7 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix
with self.as_user(user):
self._set_library_block_olx(block3_key, "", expect_response=403)
self._set_library_block_fields(block3_key, {"data": "", "metadata": {}}, expect_response=403)
- # self._set_library_block_asset(block3_key, "test.txt", b"data", expect_response=403)
+ self._set_library_block_asset(block3_key, "static/test.txt", b"data", expect_response=403)
self._delete_library_block(block3_key, expect_response=403)
self._commit_library_changes(lib_id, expect_response=403)
self._revert_library_changes(lib_id, expect_response=403)
@@ -690,9 +690,9 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix
olx = self._get_library_block_olx(block3_key)
self._set_library_block_olx(block3_key, olx)
self._set_library_block_fields(block3_key, {"data": olx, "metadata": {}})
- # self._get_library_block_assets(block3_key)
- # self._set_library_block_asset(block3_key, "test.txt", b"data")
- # self._get_library_block_asset(block3_key, file_name="test.txt")
+ self._get_library_block_assets(block3_key)
+ self._set_library_block_asset(block3_key, "static/test.txt", b"data")
+ self._get_library_block_asset(block3_key, file_name="static/test.txt")
self._delete_library_block(block3_key)
self._commit_library_changes(lib_id)
self._revert_library_changes(lib_id) # This is a no-op after the commit, but should still have 200 response
@@ -915,7 +915,6 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix
event_receiver.call_args.kwargs
)
- @skip("We still need to re-implement static asset handling.")
def test_library_block_add_asset_update_event(self):
"""
Check that LIBRARY_BLOCK_CREATED event is sent when a static asset is
@@ -934,7 +933,7 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix
block = self._add_block_to_library(lib_id, "unit", "u1")
block_id = block["id"]
- self._set_library_block_asset(block_id, "test.txt", b"data")
+ self._set_library_block_asset(block_id, "static/test.txt", b"data")
usage_key = LibraryUsageLocatorV2(
lib_key=library_key,
@@ -955,7 +954,6 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix
event_receiver.call_args.kwargs
)
- @skip("We still need to re-implement static asset handling.")
def test_library_block_del_asset_update_event(self):
"""
Check that LIBRARY_BLOCK_CREATED event is sent when a static asset is
@@ -974,9 +972,9 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix
block = self._add_block_to_library(lib_id, "unit", "u1")
block_id = block["id"]
- self._set_library_block_asset(block_id, "test.txt", b"data")
+ self._set_library_block_asset(block_id, "static/test.txt", b"data")
- self._delete_library_block_asset(block_id, 'text.txt')
+ self._delete_library_block_asset(block_id, 'static/text.txt')
usage_key = LibraryUsageLocatorV2(
lib_key=library_key,
diff --git a/openedx/core/djangoapps/content_libraries/tests/test_static_assets.py b/openedx/core/djangoapps/content_libraries/tests/test_static_assets.py
index 92ff4c1767..a5f69f94b1 100644
--- a/openedx/core/djangoapps/content_libraries/tests/test_static_assets.py
+++ b/openedx/core/djangoapps/content_libraries/tests/test_static_assets.py
@@ -1,11 +1,16 @@
"""
Tests for static asset files in Learning-Core-based Content Libraries
"""
-from unittest import skip
+from uuid import UUID
+from opaque_keys.edx.keys import UsageKey
+
+from common.djangoapps.student.tests.factories import UserFactory
from openedx.core.djangoapps.content_libraries.tests.base import (
ContentLibrariesRestApiTest,
)
+from openedx.core.djangoapps.xblock.api import get_component_from_usage_key
+from openedx.core.djangolib.testing.utils import skip_unless_cms
# Binary data representing an SVG image file
SVG_DATA = """