diff --git a/cms/djangoapps/contentstore/rest_api/v1/serializers/__init__.py b/cms/djangoapps/contentstore/rest_api/v1/serializers/__init__.py
index c60d33bc44..a5c3497998 100644
--- a/cms/djangoapps/contentstore/rest_api/v1/serializers/__init__.py
+++ b/cms/djangoapps/contentstore/rest_api/v1/serializers/__init__.py
@@ -13,3 +13,7 @@ from .proctoring import (
ProctoringErrorsSerializer
)
from .settings import CourseSettingsSerializer
+from .xblock import XblockSerializer
+from .videos import VideoUploadSerializer, VideoImageSerializer
+from .transcripts import TranscriptSerializer
+from .assets import AssetSerializer
diff --git a/cms/djangoapps/contentstore/rest_api/v1/serializers/assets.py b/cms/djangoapps/contentstore/rest_api/v1/serializers/assets.py
new file mode 100644
index 0000000000..e44c31b511
--- /dev/null
+++ b/cms/djangoapps/contentstore/rest_api/v1/serializers/assets.py
@@ -0,0 +1,13 @@
+"""
+API Serializers for assets
+"""
+from rest_framework import serializers
+from .common import StrictSerializer
+
+
+class AssetSerializer(StrictSerializer):
+ """
+ Strict Serializer for file assets.
+ """
+ file = serializers.FileField(required=False, allow_null=True)
+ locked = serializers.BooleanField(required=False, allow_null=True)
diff --git a/cms/djangoapps/contentstore/rest_api/v1/serializers/common.py b/cms/djangoapps/contentstore/rest_api/v1/serializers/common.py
index bc2f8d2da6..362504ccb0 100644
--- a/cms/djangoapps/contentstore/rest_api/v1/serializers/common.py
+++ b/cms/djangoapps/contentstore/rest_api/v1/serializers/common.py
@@ -17,3 +17,36 @@ class CourseCommonSerializer(serializers.Serializer):
rerun_link = serializers.CharField()
run = serializers.CharField()
url = serializers.CharField()
+
+
+class StrictSerializer(serializers.Serializer):
+ """
+ Serializers that validates strong parameters, i.e. that no extra fields are passed in.
+ The serializer inheriting from this may throw a ValidationError and can be called in a try/catch
+ block that will return a 400 response on ValidationError.
+ """
+ def to_internal_value(self, data):
+ """
+ raise validation error if there are any unexpected fields.
+ """
+ # Transform and validate the expected fields
+ ret = super().to_internal_value(data)
+
+ # Get the list of valid fields from the serializer
+ valid_fields = set(self.fields.keys())
+
+ # Check for unexpected fields
+ extra_fields = set(data.keys()) - valid_fields
+ if extra_fields:
+ # Check if these unexpected fields are due to nested serializers
+ for field_name in list(extra_fields):
+ if isinstance(self.fields.get(field_name), serializers.BaseSerializer):
+ extra_fields.remove(field_name)
+
+ # If there are still unexpected fields left, raise an error
+ if extra_fields:
+ raise serializers.ValidationError(
+ {field: ["This field is not expected."] for field in extra_fields}
+ )
+
+ return ret
diff --git a/cms/djangoapps/contentstore/rest_api/v1/serializers/transcripts.py b/cms/djangoapps/contentstore/rest_api/v1/serializers/transcripts.py
new file mode 100644
index 0000000000..2b72f1ff44
--- /dev/null
+++ b/cms/djangoapps/contentstore/rest_api/v1/serializers/transcripts.py
@@ -0,0 +1,15 @@
+"""
+API Serializers for transcripts
+"""
+from rest_framework import serializers
+from .common import StrictSerializer
+
+
+class TranscriptSerializer(StrictSerializer):
+ """
+ Strict Serializer for video transcripts.
+ """
+ file = serializers.FileField()
+ edx_video_id = serializers.CharField()
+ language_code = serializers.CharField(required=False, allow_null=True)
+ new_language_code = serializers.CharField(required=False, allow_null=True)
diff --git a/cms/djangoapps/contentstore/rest_api/v1/serializers/videos.py b/cms/djangoapps/contentstore/rest_api/v1/serializers/videos.py
new file mode 100644
index 0000000000..c08856d1b5
--- /dev/null
+++ b/cms/djangoapps/contentstore/rest_api/v1/serializers/videos.py
@@ -0,0 +1,29 @@
+"""
+API Serializers for videos
+"""
+from rest_framework import serializers
+from .common import StrictSerializer
+
+
+class FileSpecSerializer(StrictSerializer):
+ """ Strict Serializer for file specs """
+ file_name = serializers.CharField()
+ content_type = serializers.ChoiceField(choices=['video/mp4', 'video/webm', 'video/ogg'])
+
+
+class VideoUploadSerializer(StrictSerializer):
+ """
+ Strict Serializer for video upload urls.
+ Note that these are not actually video uploads but endpoints to generate an upload url for AWS
+ and generating a video placeholder without performing an actual upload.
+ """
+ files = serializers.ListField(
+ child=FileSpecSerializer()
+ )
+
+
+class VideoImageSerializer(StrictSerializer):
+ """
+ Strict Serializer for video imgage files.
+ """
+ file = serializers.ImageField()
diff --git a/cms/djangoapps/contentstore/rest_api/v1/serializers/xblock.py b/cms/djangoapps/contentstore/rest_api/v1/serializers/xblock.py
index 68d44af0ff..522a34e077 100644
--- a/cms/djangoapps/contentstore/rest_api/v1/serializers/xblock.py
+++ b/cms/djangoapps/contentstore/rest_api/v1/serializers/xblock.py
@@ -1,15 +1,79 @@
"""
API Serializers for xblocks
"""
+from rest_framework import serializers
+from .common import StrictSerializer
+
+# The XblockSerializer is designed to be scalable and generic. As such, its structure
+# should remain as general as possible. Avoid indiscriminately adding fields to it,
+# especially those that are xblock-specific. In the future, we aim to develop a solution
+# that can generate serializer fields dynamically based on the xblock definitions.
-# TODO: implement and use serializer
-# from rest_framework import serializers
+class XblockSerializer(StrictSerializer):
+ """
+ A serializer for xblocks that enforces strict validation.
-# class XblockSerializer(serializers.Serializer):
-# """Serializer for xblocks"""
-# id=serializers.CharField(required=False)
-# display_name=serializers.CharField(required=False)
-# category=serializers.CharField(required=False)
-# data=serializers.CharField(required=False)
-# metadata=serializers.DictField(required=False)
+ The serializer ensures:
+ 1. All top-level fields have the expected data types.
+ 2. No unexpected fields are passed in.
+
+ Note: The current list of fields is not exhaustive. It is primarily designed
+ to support the CMS API demo. While optional fields have been added, they were
+ chosen based on ease of discovery, not comprehensiveness.
+ """
+ id = serializers.CharField(required=False, allow_null=True)
+ parent_locator = serializers.CharField(required=False, allow_null=True)
+ display_name = serializers.CharField(required=False, allow_null=True)
+ category = serializers.CharField(required=False, allow_null=True)
+ data = serializers.CharField(required=False, allow_null=True)
+ metadata = serializers.DictField(required=False, allow_null=True)
+ has_changes = serializers.BooleanField(required=False, allow_null=True)
+ children = serializers.ListField(required=False, allow_null=True)
+ fields = serializers.DictField(required=False, allow_null=True)
+ has_children = serializers.BooleanField(required=False, allow_null=True)
+ video_sharing_enabled = serializers.BooleanField(required=False, allow_null=True)
+ video_sharing_options = serializers.CharField(required=False, allow_null=True)
+ video_sharing_doc_url = serializers.CharField(required=False, allow_null=True)
+ edited_on = serializers.CharField(required=False, allow_null=True)
+ published = serializers.BooleanField(required=False, allow_null=True)
+ published_on = serializers.JSONField(required=False, allow_null=True)
+ studio_url = serializers.CharField(required=False, allow_null=True)
+ released_to_students = serializers.BooleanField(required=False, allow_null=True)
+ release_date = serializers.JSONField(required=False, allow_null=True)
+ nullout = serializers.JSONField(required=False, allow_null=True)
+ graderType = serializers.JSONField(required=False, allow_null=True)
+ visibility_state = serializers.CharField(required=False, allow_null=True)
+ has_explicit_staff_lock = serializers.BooleanField(
+ required=False, allow_null=True
+ )
+ start = serializers.CharField(required=False, allow_null=True)
+ graded = serializers.BooleanField(required=False, allow_null=True)
+ due_date = serializers.CharField(required=False, allow_null=True)
+ due = serializers.JSONField(required=False, allow_null=True)
+ relative_weeks_due = serializers.JSONField(required=False, allow_null=True)
+ format = serializers.JSONField(required=False, allow_null=True)
+ course_graders = serializers.ListField(required=False, allow_null=True)
+ actions = serializers.DictField(required=False, allow_null=True)
+ explanatory_message = serializers.Field(required=False, allow_null=True)
+ group_access = serializers.DictField(required=False, allow_null=True)
+ user_partitions = serializers.ListField(required=False, allow_null=True)
+ show_correctness = serializers.CharField(required=False, allow_null=True)
+ discussion_enabled = serializers.BooleanField(required=False, allow_null=True)
+ ancestor_has_staff_lock = serializers.BooleanField(required=False, allow_null=True)
+ user_partition_info = serializers.DictField(required=False, allow_null=True)
+ summary_configuration_enabled = serializers.JSONField(required=False, allow_null=True)
+ isPrereq = serializers.BooleanField(required=False, allow_null=True)
+ prereqUsageKey = serializers.CharField(required=False, allow_null=True)
+ prereqMinScore = serializers.IntegerField(required=False, allow_null=True)
+ prereqMinCompletion = serializers.IntegerField(required=False, allow_null=True)
+ publish = serializers.ChoiceField(
+ required=False,
+ allow_null=True,
+ choices=['make_public', 'republish', 'discard_changes']
+ )
+ duplicate_source_locator = serializers.CharField(required=False, allow_null=True)
+ move_source_locator = serializers.CharField(required=False, allow_null=True)
+ target_index = serializers.IntegerField(required=False, allow_null=True)
+ boilerplate = serializers.JSONField(required=False, allow_null=True)
+ staged_content = serializers.CharField(required=False, allow_null=True)
diff --git a/cms/djangoapps/contentstore/rest_api/v1/urls.py b/cms/djangoapps/contentstore/rest_api/v1/urls.py
index 286424f5e1..9c1bef0c90 100644
--- a/cms/djangoapps/contentstore/rest_api/v1/urls.py
+++ b/cms/djangoapps/contentstore/rest_api/v1/urls.py
@@ -74,7 +74,7 @@ urlpatterns = [
videos.VideosView.as_view(), name='studio_content_videos_uploads'
),
re_path(
- fr'^videos/images/{settings.COURSE_ID_PATTERN}/{VIDEO_ID_PATTERN}?$',
+ fr'^videos/images/{settings.COURSE_ID_PATTERN}/{VIDEO_ID_PATTERN}$',
videos.VideoImagesView.as_view(), name='studio_content_videos_images'
),
re_path(
diff --git a/cms/djangoapps/contentstore/rest_api/v1/views/assets.py b/cms/djangoapps/contentstore/rest_api/v1/views/assets.py
index dc95087a12..481d850ca8 100644
--- a/cms/djangoapps/contentstore/rest_api/v1/views/assets.py
+++ b/cms/djangoapps/contentstore/rest_api/v1/views/assets.py
@@ -14,6 +14,11 @@ from ....api import course_author_access_required
from cms.djangoapps.contentstore.asset_storage_handlers import handle_assets
import cms.djangoapps.contentstore.toggles as contentstore_toggles
+from cms.djangoapps.contentstore.rest_api.v1.serializers import AssetSerializer
+from .utils import validate_request_with_serializer
+from rest_framework.parsers import (MultiPartParser, FormParser, JSONParser)
+from openedx.core.lib.api.parsers import TypedFileUploadParser
+
log = logging.getLogger(__name__)
toggles = contentstore_toggles
@@ -25,6 +30,8 @@ class AssetsView(DeveloperErrorViewMixin, RetrieveUpdateDestroyAPIView, CreateAP
course_key: required argument, needed to authorize course authors and identify the asset.
asset_key_string: required argument, needed to identify the asset.
"""
+ serializer_class = AssetSerializer
+ parser_classes = (JSONParser, MultiPartParser, FormParser, TypedFileUploadParser)
def dispatch(self, request, *args, **kwargs):
# TODO: probably want to refactor this to a decorator.
@@ -44,11 +51,13 @@ class AssetsView(DeveloperErrorViewMixin, RetrieveUpdateDestroyAPIView, CreateAP
@csrf_exempt
@course_author_access_required
+ @validate_request_with_serializer
def create(self, request, course_key): # pylint: disable=arguments-differ
return handle_assets(request, course_key.html_id())
@course_author_access_required
@expect_json_in_class_view
+ @validate_request_with_serializer
def update(self, request, course_key, asset_key_string): # pylint: disable=arguments-differ
return handle_assets(request, course_key.html_id(), asset_key_string)
diff --git a/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_assets.py b/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_assets.py
index 908af1d1bc..61fa331d6c 100644
--- a/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_assets.py
+++ b/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_assets.py
@@ -6,9 +6,11 @@ not the underlying Xblock service.
It checks that the assets_handler method of the Xblock service is called with the expected parameters.
"""
from unittest.mock import patch
+from django.core.files import File
from django.http import JsonResponse
from django.urls import reverse
+from mock import MagicMock
from rest_framework import status
from rest_framework.test import APITestCase
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
@@ -17,6 +19,8 @@ from cms.djangoapps.contentstore.tests.test_utils import AuthorizeStaffTestCase
ASSET_KEY_STRING = "asset-v1:dede+aba+weagi+type@asset+block@_0e37192a-42c4-441e-a3e1-8e40ec304e2e.jpg"
+mock_image = MagicMock(file=File)
+mock_image.name = "test.jpg"
class AssetsViewTestCase(AuthorizeStaffTestCase):
@@ -146,7 +150,7 @@ class AssetsViewPostTest(AssetsViewTestCase, ModuleStoreTestCase, APITestCase):
def get_test_data(self):
return {
- "file": ASSET_KEY_STRING,
+ "file": mock_image,
}
def assert_assets_handler_called(self, *, mock_handle_assets, response):
@@ -159,7 +163,7 @@ class AssetsViewPostTest(AssetsViewTestCase, ModuleStoreTestCase, APITestCase):
course_id = self.get_course_key_string()
- assert passed_args.data.get("file") == ASSET_KEY_STRING
+ assert passed_args.data.get("file").name == mock_image.name
assert passed_args.method == "POST"
assert passed_args.path == self.get_url()
diff --git a/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_xblock.py b/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_xblock.py
index f8d871a5be..64462a6ee2 100644
--- a/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_xblock.py
+++ b/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_xblock.py
@@ -50,7 +50,7 @@ class XBlockViewTestCase(AuthorizeStaffTestCase):
return_value=JsonResponse(
{
"locator": TEST_LOCATOR,
- "courseKey": AuthorizeStaffTestCase.get_course_key_string(),
+ "course_key": AuthorizeStaffTestCase.get_course_key_string(),
}
),
)
@@ -128,7 +128,6 @@ class XBlockViewGetTest(XBlockViewTestCase, ModuleStoreTestCase, APITestCase):
assert response.status_code == status.HTTP_200_OK
data = response.json()
assert data["locator"] == TEST_LOCATOR
- assert data["courseKey"] == self.get_course_key_string()
class XBlockViewPostTest(XBlockViewTestCase, ModuleStoreTestCase, APITestCase):
@@ -150,7 +149,6 @@ class XBlockViewPostTest(XBlockViewTestCase, ModuleStoreTestCase, APITestCase):
return {
"parent_locator": course_id,
"category": "html",
- "courseKey": course_id,
}
def assert_xblock_handler_called(self, *, mock_handle_xblock, response):
@@ -161,9 +159,6 @@ class XBlockViewPostTest(XBlockViewTestCase, ModuleStoreTestCase, APITestCase):
mock_handle_xblock.assert_called_once()
passed_args = mock_handle_xblock.call_args[0][0]
- course_id = self.get_course_key_string()
-
- assert passed_args.data.get("courseKey") == course_id
assert passed_args.method == "POST"
assert passed_args.path == self.get_url()
@@ -187,7 +182,6 @@ class XBlockViewPostTest(XBlockViewTestCase, ModuleStoreTestCase, APITestCase):
assert response.status_code == status.HTTP_200_OK
data = response.json()
assert data["locator"] == TEST_LOCATOR
- assert data["courseKey"] == self.get_course_key_string()
class XBlockViewPutTest(XBlockViewTestCase, ModuleStoreTestCase, APITestCase):
@@ -196,10 +190,8 @@ class XBlockViewPutTest(XBlockViewTestCase, ModuleStoreTestCase, APITestCase):
"""
def get_test_data(self):
- course_id = self.get_course_key_string()
return {
"category": "html",
- "courseKey": course_id,
"data": "
Updated block!
",
"has_changes": True,
"id": TEST_LOCATOR,
@@ -216,9 +208,6 @@ class XBlockViewPutTest(XBlockViewTestCase, ModuleStoreTestCase, APITestCase):
mock_handle_xblock.assert_called_once()
passed_args = mock_handle_xblock.call_args[0][0]
- course_id = self.get_course_key_string()
-
- assert passed_args.data.get("courseKey") == course_id
assert passed_args.data.get("data") == "Updated block!
"
assert passed_args.data.get("id") == TEST_LOCATOR
assert passed_args.method == "PUT"
@@ -244,7 +233,6 @@ class XBlockViewPutTest(XBlockViewTestCase, ModuleStoreTestCase, APITestCase):
assert response.status_code == status.HTTP_200_OK
data = response.json()
assert data["locator"] == TEST_LOCATOR
- assert data["courseKey"] == self.get_course_key_string()
class XBlockViewPatchTest(XBlockViewTestCase, ModuleStoreTestCase, APITestCase):
@@ -253,10 +241,8 @@ class XBlockViewPatchTest(XBlockViewTestCase, ModuleStoreTestCase, APITestCase):
"""
def get_test_data(self):
- course_id = self.get_course_key_string()
return {
"category": "html",
- "courseKey": course_id,
"data": "Patched block!
",
"has_changes": True,
"id": TEST_LOCATOR,
@@ -273,9 +259,6 @@ class XBlockViewPatchTest(XBlockViewTestCase, ModuleStoreTestCase, APITestCase):
mock_handle_xblock.assert_called_once()
passed_args = mock_handle_xblock.call_args[0][0]
- course_id = self.get_course_key_string()
-
- assert passed_args.data.get("courseKey") == course_id
assert passed_args.data.get("data") == "Patched block!
"
assert passed_args.data.get("id") == TEST_LOCATOR
assert passed_args.method == "PATCH"
@@ -301,7 +284,6 @@ class XBlockViewPatchTest(XBlockViewTestCase, ModuleStoreTestCase, APITestCase):
assert response.status_code == status.HTTP_200_OK
data = response.json()
assert data["locator"] == TEST_LOCATOR
- assert data["courseKey"] == self.get_course_key_string()
class XBlockViewDeleteTest(XBlockViewTestCase, ModuleStoreTestCase, APITestCase):
@@ -343,4 +325,3 @@ class XBlockViewDeleteTest(XBlockViewTestCase, ModuleStoreTestCase, APITestCase)
assert response.status_code == status.HTTP_200_OK
data = response.json()
assert data["locator"] == TEST_LOCATOR
- assert data["courseKey"] == self.get_course_key_string()
diff --git a/cms/djangoapps/contentstore/rest_api/v1/views/transcripts.py b/cms/djangoapps/contentstore/rest_api/v1/views/transcripts.py
index e4e8ff8667..f621929f9c 100644
--- a/cms/djangoapps/contentstore/rest_api/v1/views/transcripts.py
+++ b/cms/djangoapps/contentstore/rest_api/v1/views/transcripts.py
@@ -21,6 +21,11 @@ from cms.djangoapps.contentstore.transcript_storage_handlers import (
handle_transcript_download,
)
import cms.djangoapps.contentstore.toggles as contentstore_toggles
+from cms.djangoapps.contentstore.rest_api.v1.serializers import TranscriptSerializer
+from rest_framework.parsers import (MultiPartParser, FormParser)
+from openedx.core.lib.api.parsers import TypedFileUploadParser
+
+from .utils import validate_request_with_serializer
log = logging.getLogger(__name__)
toggles = contentstore_toggles
@@ -34,6 +39,8 @@ class TranscriptView(DeveloperErrorViewMixin, CreateAPIView, RetrieveAPIView, De
edx_video_id: optional query parameter, needed to identify the transcript.
language_code: optional query parameter, needed to identify the transcript.
"""
+ serializer_class = TranscriptSerializer
+ parser_classes = (MultiPartParser, FormParser, TypedFileUploadParser)
def dispatch(self, request, *args, **kwargs):
if not toggles.use_studio_content_api():
@@ -43,6 +50,7 @@ class TranscriptView(DeveloperErrorViewMixin, CreateAPIView, RetrieveAPIView, De
@csrf_exempt
@course_author_access_required
@expect_json_in_class_view
+ @validate_request_with_serializer
def create(self, request, course_key_string): # pylint: disable=arguments-differ
return upload_transcript(request)
diff --git a/cms/djangoapps/contentstore/rest_api/v1/views/utils.py b/cms/djangoapps/contentstore/rest_api/v1/views/utils.py
new file mode 100644
index 0000000000..7175b76c7c
--- /dev/null
+++ b/cms/djangoapps/contentstore/rest_api/v1/views/utils.py
@@ -0,0 +1,23 @@
+"""
+Utilities for the REST API views.
+"""
+from functools import wraps
+from django.http import HttpResponseBadRequest
+
+
+def validate_request_with_serializer(view_func):
+ """
+ A decorator to validate request data using the view's serializer.
+
+ Usage:
+ @validate_request_with_serializer
+ def my_view_function(self, request, ...):
+ ...
+ """
+ @wraps(view_func)
+ def _wrapped_view(instance, request, *args, **kwargs):
+ serializer = instance.serializer_class(data=request.data)
+ if not serializer.is_valid():
+ return HttpResponseBadRequest(reason=serializer.errors)
+ return view_func(instance, request, *args, **kwargs)
+ return _wrapped_view
diff --git a/cms/djangoapps/contentstore/rest_api/v1/views/videos.py b/cms/djangoapps/contentstore/rest_api/v1/views/videos.py
index a6e776b77c..710dbb5df2 100644
--- a/cms/djangoapps/contentstore/rest_api/v1/views/videos.py
+++ b/cms/djangoapps/contentstore/rest_api/v1/views/videos.py
@@ -7,10 +7,12 @@ from rest_framework.generics import (
RetrieveAPIView,
DestroyAPIView
)
+from rest_framework.parsers import (MultiPartParser, FormParser)
from django.views.decorators.csrf import csrf_exempt
from django.http import Http404
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, view_auth_classes
+from openedx.core.lib.api.parsers import TypedFileUploadParser
from common.djangoapps.util.json_request import expect_json_in_class_view
from ....api import course_author_access_required
@@ -19,10 +21,12 @@ from cms.djangoapps.contentstore.video_storage_handlers import (
handle_videos,
get_video_encodings_download,
handle_video_images,
- enabled_video_features,
- handle_generate_video_upload_link
+ enabled_video_features
)
+from cms.djangoapps.contentstore.rest_api.v1.serializers import VideoUploadSerializer, VideoImageSerializer
import cms.djangoapps.contentstore.toggles as contentstore_toggles
+from .utils import validate_request_with_serializer
+
log = logging.getLogger(__name__)
toggles = contentstore_toggles
@@ -35,6 +39,7 @@ class VideosView(DeveloperErrorViewMixin, CreateAPIView, RetrieveAPIView, Destro
course_key: required argument, needed to authorize course authors and identify the video.
video_id: required argument, needed to identify the video.
"""
+ serializer_class = VideoUploadSerializer
def dispatch(self, request, *args, **kwargs):
# TODO: probably want to refactor this to a decorator.
@@ -50,7 +55,9 @@ class VideosView(DeveloperErrorViewMixin, CreateAPIView, RetrieveAPIView, Destro
@csrf_exempt
@course_author_access_required
@expect_json_in_class_view
+ @validate_request_with_serializer
def create(self, request, course_key): # pylint: disable=arguments-differ
+ """Deprecated. Use the upload_link endpoint instead."""
return handle_videos(request, course_key.html_id())
@course_author_access_required
@@ -70,6 +77,8 @@ class VideoImagesView(DeveloperErrorViewMixin, CreateAPIView):
course_key: required argument, needed to authorize course authors and identify the video.
video_id: required argument, needed to identify the video.
"""
+ serializer_class = VideoImageSerializer
+ parser_classes = (MultiPartParser, FormParser, TypedFileUploadParser)
def dispatch(self, request, *args, **kwargs):
# TODO: probably want to refactor this to a decorator.
@@ -85,6 +94,7 @@ class VideoImagesView(DeveloperErrorViewMixin, CreateAPIView):
@csrf_exempt
@course_author_access_required
@expect_json_in_class_view
+ @validate_request_with_serializer
def create(self, request, course_key, edx_video_id=None): # pylint: disable=arguments-differ
return handle_video_images(request, course_key.html_id(), edx_video_id)
@@ -140,6 +150,7 @@ class UploadLinkView(DeveloperErrorViewMixin, CreateAPIView):
"""
public rest API endpoint providing a list of enabled video features.
"""
+ serializer_class = VideoUploadSerializer
def dispatch(self, request, *args, **kwargs):
# TODO: probably want to refactor this to a decorator.
@@ -155,5 +166,6 @@ class UploadLinkView(DeveloperErrorViewMixin, CreateAPIView):
@csrf_exempt
@course_author_access_required
@expect_json_in_class_view
+ @validate_request_with_serializer
def create(self, request, course_key): # pylint: disable=arguments-differ
- return handle_generate_video_upload_link(request, course_key.html_id())
+ return handle_videos(request, course_key.html_id())
diff --git a/cms/djangoapps/contentstore/rest_api/v1/views/xblock.py b/cms/djangoapps/contentstore/rest_api/v1/views/xblock.py
index 13087c6cd3..0e5765d8a2 100644
--- a/cms/djangoapps/contentstore/rest_api/v1/views/xblock.py
+++ b/cms/djangoapps/contentstore/rest_api/v1/views/xblock.py
@@ -13,7 +13,8 @@ from cms.djangoapps.contentstore.api import course_author_access_required
from cms.djangoapps.contentstore.xblock_storage_handlers import view_handlers
import cms.djangoapps.contentstore.toggles as contentstore_toggles
-# from cms.djangoapps.contentstore.rest_api.v1.serializers import XblockSerializer
+from cms.djangoapps.contentstore.rest_api.v1.serializers import XblockSerializer
+from .utils import validate_request_with_serializer
log = logging.getLogger(__name__)
@@ -29,8 +30,7 @@ class XblockView(DeveloperErrorViewMixin, RetrieveUpdateDestroyAPIView, CreateAP
usage_key_string (optional):
xblock identifier, for example in the form of "block-v1:+type@+block@"
"""
- # TODO: uncomment next line after XblockSerializer is implemented
- # serializer_class = XblockSerializer
+ serializer_class = XblockSerializer
def dispatch(self, request, *args, **kwargs):
# TODO: probably want to refactor this to a decorator.
@@ -51,11 +51,13 @@ class XblockView(DeveloperErrorViewMixin, RetrieveUpdateDestroyAPIView, CreateAP
@course_author_access_required
@expect_json_in_class_view
+ @validate_request_with_serializer
def update(self, request, course_key, usage_key_string=None):
return handle_xblock(request, usage_key_string)
@course_author_access_required
@expect_json_in_class_view
+ @validate_request_with_serializer
def partial_update(self, request, course_key, usage_key_string=None):
return handle_xblock(request, usage_key_string)
@@ -67,5 +69,6 @@ class XblockView(DeveloperErrorViewMixin, RetrieveUpdateDestroyAPIView, CreateAP
@csrf_exempt
@course_author_access_required
@expect_json_in_class_view
+ @validate_request_with_serializer
def create(self, request, course_key, usage_key_string=None):
return handle_xblock(request, usage_key_string)
diff --git a/cms/djangoapps/contentstore/tests/test_video_utils.py b/cms/djangoapps/contentstore/tests/test_video_utils.py
index 80d55b92b8..c81761a283 100644
--- a/cms/djangoapps/contentstore/tests/test_video_utils.py
+++ b/cms/djangoapps/contentstore/tests/test_video_utils.py
@@ -5,7 +5,7 @@ Unit tests for video utils.
from datetime import datetime
from unittest import TestCase
-from unittest.mock import MagicMock, patch
+from unittest import mock
import ddt
import pytz
@@ -144,7 +144,7 @@ class ScrapeVideoThumbnailsTestCase(CourseTestCase):
return mocked_response
@override_settings(AWS_ACCESS_KEY_ID='test_key_id', AWS_SECRET_ACCESS_KEY='test_secret')
- @patch('requests.get')
+ @mock.patch('requests.get')
@ddt.data(
(
{
@@ -228,7 +228,7 @@ class ScrapeVideoThumbnailsTestCase(CourseTestCase):
self.assertEqual(thumbnail_content_type, 'image/jpeg')
@override_settings(AWS_ACCESS_KEY_ID='test_key_id', AWS_SECRET_ACCESS_KEY='test_secret')
- @patch('requests.get')
+ @mock.patch('requests.get')
def test_scrape_youtube_thumbnail(self, mocked_request):
"""
Test that youtube thumbnails are correctly scrapped.
@@ -273,8 +273,8 @@ class ScrapeVideoThumbnailsTestCase(CourseTestCase):
)
)
@override_settings(AWS_ACCESS_KEY_ID='test_key_id', AWS_SECRET_ACCESS_KEY='test_secret')
- @patch('cms.djangoapps.contentstore.video_utils.LOGGER')
- @patch('requests.get')
+ @mock.patch('cms.djangoapps.contentstore.video_utils.LOGGER')
+ @mock.patch('requests.get')
@ddt.unpack
def test_scrape_youtube_thumbnail_logging(
self,
@@ -333,8 +333,8 @@ class ScrapeVideoThumbnailsTestCase(CourseTestCase):
)
),
)
- @patch('cms.djangoapps.contentstore.video_utils.LOGGER')
- @patch('cms.djangoapps.contentstore.video_utils.download_youtube_video_thumbnail')
+ @mock.patch('cms.djangoapps.contentstore.video_utils.LOGGER')
+ @mock.patch('cms.djangoapps.contentstore.video_utils.download_youtube_video_thumbnail')
@ddt.unpack
def test_no_video_thumbnail_downloaded(
self,
@@ -376,7 +376,7 @@ class S3Boto3TestCase(TestCase):
def setUp(self):
self.storage = S3Boto3Storage()
- self.storage._connections.connection = MagicMock() # pylint: disable=protected-access
+ self.storage._connections.connection = mock.MagicMock() # pylint: disable=protected-access
def order_dict(self, dictionary):
"""
@@ -417,18 +417,18 @@ class S3Boto3TestCase(TestCase):
content = ContentFile('new content')
storage = S3Boto3Storage(**{'bucket_name': 'test'})
- storage._connections.connection = MagicMock() # pylint: disable=protected-access
+ storage._connections.connection = mock.MagicMock() # pylint: disable=protected-access
storage.save(name, content)
storage.bucket.Object.assert_called_once_with(name)
obj = storage.bucket.Object.return_value
obj.upload_fileobj.assert_called_with(
- content,
+ mock.ANY,
ExtraArgs=self.order_dict({
'ContentType': 'text/plain',
}),
- Config=storage._transfer_config # pylint: disable=protected-access
+ Config=storage.transfer_config # pylint: disable=protected-access
)
@override_settings(AWS_DEFAULT_ACL='public-read')
@@ -445,7 +445,7 @@ class S3Boto3TestCase(TestCase):
name = 'test_storage_save.txt'
content = ContentFile('new content')
storage = S3Boto3Storage(**{'bucket_name': 'test', 'default_acl': default_acl})
- storage._connections.connection = MagicMock() # pylint: disable=protected-access
+ storage._connections.connection = mock.MagicMock() # pylint: disable=protected-access
storage.save(name, content)
storage.bucket.Object.assert_called_once_with(name)
@@ -461,9 +461,9 @@ class S3Boto3TestCase(TestCase):
del ExtraArgs['ACL']
obj.upload_fileobj.assert_called_with(
- content,
+ mock.ANY,
ExtraArgs=self.order_dict(ExtraArgs),
- Config=storage._transfer_config # pylint: disable=protected-access
+ Config=storage.transfer_config # pylint: disable=protected-access
)
@ddt.data('public-read', 'private')
@@ -476,16 +476,16 @@ class S3Boto3TestCase(TestCase):
content = ContentFile('new content')
storage = S3Boto3Storage(**{'bucket_name': 'test', 'default_acl': None})
- storage._connections.connection = MagicMock() # pylint: disable=protected-access
+ storage._connections.connection = mock.MagicMock() # pylint: disable=protected-access
storage.save(name, content)
storage.bucket.Object.assert_called_once_with(name)
obj = storage.bucket.Object.return_value
obj.upload_fileobj.assert_called_with(
- content,
- ExtraArgs=self.order_dict({
+ mock.ANY,
+ Config=storage.transfer_config, # pylint: disable=protected-access
+ ExtraArgs={
'ContentType': 'text/plain',
- }),
- Config=storage._transfer_config # pylint: disable=protected-access
+ },
)
diff --git a/cms/djangoapps/contentstore/video_storage_handlers.py b/cms/djangoapps/contentstore/video_storage_handlers.py
index d1a7c55ac6..6e83a5b9e3 100644
--- a/cms/djangoapps/contentstore/video_storage_handlers.py
+++ b/cms/djangoapps/contentstore/video_storage_handlers.py
@@ -729,7 +729,9 @@ def videos_post(course, request):
"""
if use_mock_video_uploads():
- return {'files': [{'file_name': 'video.mp4', 'upload_url': 'http://example.com/put_video'}]}, 200
+ return {'files': [{
+ 'file_name': 'video.mp4', 'upload_url': 'http://example.com/put_video', 'edx_video_id': '1234'
+ }]}, 200
error = None
data = request.json
diff --git a/conf/locale/ca/LC_MESSAGES/django.po b/conf/locale/ca/LC_MESSAGES/django.po
index 2b43475907..f3e40b6a47 100644
--- a/conf/locale/ca/LC_MESSAGES/django.po
+++ b/conf/locale/ca/LC_MESSAGES/django.po
@@ -67,7 +67,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: Waheed Ahmed , 2019\n"
"Language-Team: Catalan (https://app.transifex.com/open-edx/teams/6205/ca/)\n"
diff --git a/conf/locale/el/LC_MESSAGES/django.po b/conf/locale/el/LC_MESSAGES/django.po
index 5797d4de82..6637c4de6f 100644
--- a/conf/locale/el/LC_MESSAGES/django.po
+++ b/conf/locale/el/LC_MESSAGES/django.po
@@ -98,7 +98,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: Waheed Ahmed , 2019\n"
"Language-Team: Greek (https://app.transifex.com/open-edx/teams/6205/el/)\n"
diff --git a/conf/locale/en/LC_MESSAGES/django.po b/conf/locale/en/LC_MESSAGES/django.po
index e21204ea11..b746467a6a 100644
--- a/conf/locale/en/LC_MESSAGES/django.po
+++ b/conf/locale/en/LC_MESSAGES/django.po
@@ -38,8 +38,8 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-09-20 12:47+0000\n"
-"PO-Revision-Date: 2023-09-20 12:47:19.650590\n"
+"POT-Creation-Date: 2023-09-24 20:36+0000\n"
+"PO-Revision-Date: 2023-09-24 20:36:20.303017\n"
"Last-Translator: \n"
"Language-Team: openedx-translation \n"
"Language: en\n"
@@ -6374,6 +6374,17 @@ msgstr ""
msgid "Original usage key/ID of the thing that is in the clipboard."
msgstr ""
+#: openedx/core/djangoapps/content_tagging/models/base.py
+#: wiki/models/article.py
+msgid "owner"
+msgstr ""
+
+#: openedx/core/djangoapps/content_tagging/models/base.py
+msgid ""
+"Organization that is related to this taxonomy.If None, then this taxonomy is"
+" related to all organizations."
+msgstr ""
+
#: openedx/core/djangoapps/cors_csrf/models.py
msgid ""
"List of domains that are allowed to make cross-domain requests to this site."
@@ -8169,16 +8180,6 @@ msgid ""
"to date."
msgstr ""
-#: openedx/features/content_tagging/models/base.py wiki/models/article.py
-msgid "owner"
-msgstr ""
-
-#: openedx/features/content_tagging/models/base.py
-msgid ""
-"Organization that is related to this taxonomy.If None, then this taxonomy is"
-" related to all organizations."
-msgstr ""
-
#: openedx/features/content_type_gating/models.py
#: openedx/features/course_duration_limits/models.py
#: lms/templates/support/feature_based_enrollments.html
diff --git a/conf/locale/en/LC_MESSAGES/djangojs.po b/conf/locale/en/LC_MESSAGES/djangojs.po
index 77cda03c1d..8c7acf64fb 100644
--- a/conf/locale/en/LC_MESSAGES/djangojs.po
+++ b/conf/locale/en/LC_MESSAGES/djangojs.po
@@ -32,8 +32,8 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-09-20 12:47+0000\n"
-"PO-Revision-Date: 2023-09-20 12:47:19.757119\n"
+"POT-Creation-Date: 2023-09-24 20:36+0000\n"
+"PO-Revision-Date: 2023-09-24 20:36:20.294880\n"
"Last-Translator: \n"
"Language-Team: openedx-translation \n"
"Language: en\n"
diff --git a/conf/locale/eo/LC_MESSAGES/django.mo b/conf/locale/eo/LC_MESSAGES/django.mo
index 3828753ce5..cd2f6b7303 100644
Binary files a/conf/locale/eo/LC_MESSAGES/django.mo and b/conf/locale/eo/LC_MESSAGES/django.mo differ
diff --git a/conf/locale/eo/LC_MESSAGES/django.po b/conf/locale/eo/LC_MESSAGES/django.po
index c092432ca6..459f71da15 100644
--- a/conf/locale/eo/LC_MESSAGES/django.po
+++ b/conf/locale/eo/LC_MESSAGES/django.po
@@ -38,8 +38,8 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-09-20 12:47+0000\n"
-"PO-Revision-Date: 2023-09-20 12:47:19.650590\n"
+"POT-Creation-Date: 2023-09-24 20:36+0000\n"
+"PO-Revision-Date: 2023-09-24 20:36:20.303017\n"
"Last-Translator: \n"
"Language-Team: openedx-translation \n"
"Language: eo\n"
@@ -8051,6 +8051,19 @@ msgstr ""
"Örïgïnäl üsägé kéý/ÌD öf thé thïng thät ïs ïn thé çlïpßöärd. Ⱡ'σяєм ιρѕυм "
"∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α#"
+#: openedx/core/djangoapps/content_tagging/models/base.py
+#: wiki/models/article.py
+msgid "owner"
+msgstr "öwnér Ⱡ'σяєм ιρѕ#"
+
+#: openedx/core/djangoapps/content_tagging/models/base.py
+msgid ""
+"Organization that is related to this taxonomy.If None, then this taxonomy is"
+" related to all organizations."
+msgstr ""
+"Örgänïzätïön thät ïs rélätéd tö thïs täxönömý.Ìf Nöné, thén thïs täxönömý ïs"
+" rélätéd tö äll örgänïzätïöns. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт #"
+
#: openedx/core/djangoapps/cors_csrf/models.py
msgid ""
"List of domains that are allowed to make cross-domain requests to this site."
@@ -10427,18 +10440,6 @@ msgstr ""
"Ýöü hävé süççéssfüllý shïftéd ýöür çöürsé sçhédülé änd ýöür çäléndär ïs üp "
"tö däté. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢т#"
-#: openedx/features/content_tagging/models/base.py wiki/models/article.py
-msgid "owner"
-msgstr "öwnér Ⱡ'σяєм ιρѕ#"
-
-#: openedx/features/content_tagging/models/base.py
-msgid ""
-"Organization that is related to this taxonomy.If None, then this taxonomy is"
-" related to all organizations."
-msgstr ""
-"Örgänïzätïön thät ïs rélätéd tö thïs täxönömý.Ìf Nöné, thén thïs täxönömý ïs"
-" rélätéd tö äll örgänïzätïöns. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт #"
-
#: openedx/features/content_type_gating/models.py
#: openedx/features/course_duration_limits/models.py
#: lms/templates/support/feature_based_enrollments.html
diff --git a/conf/locale/eo/LC_MESSAGES/djangojs.mo b/conf/locale/eo/LC_MESSAGES/djangojs.mo
index 52cf0799ae..a037d84d76 100644
Binary files a/conf/locale/eo/LC_MESSAGES/djangojs.mo and b/conf/locale/eo/LC_MESSAGES/djangojs.mo differ
diff --git a/conf/locale/eo/LC_MESSAGES/djangojs.po b/conf/locale/eo/LC_MESSAGES/djangojs.po
index 02a8aa07e8..981fb3c738 100644
--- a/conf/locale/eo/LC_MESSAGES/djangojs.po
+++ b/conf/locale/eo/LC_MESSAGES/djangojs.po
@@ -32,8 +32,8 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-09-20 12:47+0000\n"
-"PO-Revision-Date: 2023-09-20 12:47:19.757119\n"
+"POT-Creation-Date: 2023-09-24 20:36+0000\n"
+"PO-Revision-Date: 2023-09-24 20:36:20.294880\n"
"Last-Translator: \n"
"Language-Team: openedx-translation \n"
"Language: eo\n"
diff --git a/conf/locale/eu_ES/LC_MESSAGES/django.po b/conf/locale/eu_ES/LC_MESSAGES/django.po
index 47e35a42f4..022886794f 100644
--- a/conf/locale/eu_ES/LC_MESSAGES/django.po
+++ b/conf/locale/eu_ES/LC_MESSAGES/django.po
@@ -64,7 +64,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: Abel Camacho , 2019\n"
"Language-Team: Basque (Spain) (https://app.transifex.com/open-edx/teams/6205/eu_ES/)\n"
diff --git a/conf/locale/fa_IR/LC_MESSAGES/djangojs.po b/conf/locale/fa_IR/LC_MESSAGES/djangojs.po
index 9d884cfc63..24db4fbf75 100644
--- a/conf/locale/fa_IR/LC_MESSAGES/djangojs.po
+++ b/conf/locale/fa_IR/LC_MESSAGES/djangojs.po
@@ -88,7 +88,7 @@ msgid ""
msgstr ""
"Project-Id-Version: edx-platform\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:42+0000\n"
+"POT-Creation-Date: 2023-09-17 20:42+0000\n"
"PO-Revision-Date: 2014-06-11 15:18+0000\n"
"Last-Translator: SeyedMahdi Saeid , 2023\n"
"Language-Team: Persian (Iran) (http://app.transifex.com/open-edx/edx-platform/language/fa_IR/)\n"
diff --git a/conf/locale/fr/LC_MESSAGES/django.po b/conf/locale/fr/LC_MESSAGES/django.po
index 8db14d164e..4278151bbf 100644
--- a/conf/locale/fr/LC_MESSAGES/django.po
+++ b/conf/locale/fr/LC_MESSAGES/django.po
@@ -316,7 +316,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: Alexandre DS , 2020\n"
"Language-Team: French (https://app.transifex.com/open-edx/teams/6205/fr/)\n"
diff --git a/conf/locale/id/LC_MESSAGES/django.po b/conf/locale/id/LC_MESSAGES/django.po
index 8880340d3c..b83ecbae83 100644
--- a/conf/locale/id/LC_MESSAGES/django.po
+++ b/conf/locale/id/LC_MESSAGES/django.po
@@ -106,7 +106,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: Aprisa Chrysantina , 2019\n"
"Language-Team: Indonesian (https://app.transifex.com/open-edx/teams/6205/id/)\n"
diff --git a/conf/locale/ja_JP/LC_MESSAGES/django.po b/conf/locale/ja_JP/LC_MESSAGES/django.po
index 4fc3fc97fe..ed5767dc03 100644
--- a/conf/locale/ja_JP/LC_MESSAGES/django.po
+++ b/conf/locale/ja_JP/LC_MESSAGES/django.po
@@ -114,7 +114,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: Waheed Ahmed , 2019\n"
"Language-Team: Japanese (Japan) (https://app.transifex.com/open-edx/teams/6205/ja_JP/)\n"
diff --git a/conf/locale/ka/LC_MESSAGES/django.po b/conf/locale/ka/LC_MESSAGES/django.po
index aae8d161cc..30851f8529 100644
--- a/conf/locale/ka/LC_MESSAGES/django.po
+++ b/conf/locale/ka/LC_MESSAGES/django.po
@@ -60,7 +60,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: Waheed Ahmed , 2019\n"
"Language-Team: Georgian (https://app.transifex.com/open-edx/teams/6205/ka/)\n"
diff --git a/conf/locale/lt_LT/LC_MESSAGES/django.po b/conf/locale/lt_LT/LC_MESSAGES/django.po
index aa0f63df5f..feddb2b7f6 100644
--- a/conf/locale/lt_LT/LC_MESSAGES/django.po
+++ b/conf/locale/lt_LT/LC_MESSAGES/django.po
@@ -72,7 +72,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: Waheed Ahmed , 2019\n"
"Language-Team: Lithuanian (Lithuania) (https://app.transifex.com/open-edx/teams/6205/lt_LT/)\n"
diff --git a/conf/locale/lv/LC_MESSAGES/django.po b/conf/locale/lv/LC_MESSAGES/django.po
index 34a05acee9..7975d7c8d1 100644
--- a/conf/locale/lv/LC_MESSAGES/django.po
+++ b/conf/locale/lv/LC_MESSAGES/django.po
@@ -49,7 +49,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: Waheed Ahmed , 2019\n"
"Language-Team: Latvian (https://app.transifex.com/open-edx/teams/6205/lv/)\n"
diff --git a/conf/locale/mn/LC_MESSAGES/django.po b/conf/locale/mn/LC_MESSAGES/django.po
index caef90941c..e7ad5f189e 100644
--- a/conf/locale/mn/LC_MESSAGES/django.po
+++ b/conf/locale/mn/LC_MESSAGES/django.po
@@ -74,7 +74,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: Waheed Ahmed , 2019\n"
"Language-Team: Mongolian (https://app.transifex.com/open-edx/teams/6205/mn/)\n"
diff --git a/conf/locale/pl/LC_MESSAGES/django.po b/conf/locale/pl/LC_MESSAGES/django.po
index 9ab2c97f82..63d7c1340e 100644
--- a/conf/locale/pl/LC_MESSAGES/django.po
+++ b/conf/locale/pl/LC_MESSAGES/django.po
@@ -150,7 +150,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: Marcin Miłek, 2022\n"
"Language-Team: Polish (https://app.transifex.com/open-edx/teams/6205/pl/)\n"
diff --git a/conf/locale/rtl/LC_MESSAGES/django.mo b/conf/locale/rtl/LC_MESSAGES/django.mo
index 8c8aec060c..3933c65090 100644
Binary files a/conf/locale/rtl/LC_MESSAGES/django.mo and b/conf/locale/rtl/LC_MESSAGES/django.mo differ
diff --git a/conf/locale/rtl/LC_MESSAGES/django.po b/conf/locale/rtl/LC_MESSAGES/django.po
index a078710c1d..1a218f7dc4 100644
--- a/conf/locale/rtl/LC_MESSAGES/django.po
+++ b/conf/locale/rtl/LC_MESSAGES/django.po
@@ -38,8 +38,8 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-09-20 12:47+0000\n"
-"PO-Revision-Date: 2023-09-20 12:47:19.650590\n"
+"POT-Creation-Date: 2023-09-24 20:36+0000\n"
+"PO-Revision-Date: 2023-09-24 20:36:20.303017\n"
"Last-Translator: \n"
"Language-Team: openedx-translation \n"
"Language: rtl\n"
@@ -7007,6 +7007,19 @@ msgstr ""
msgid "Original usage key/ID of the thing that is in the clipboard."
msgstr "Øɹᴉƃᴉnɐl nsɐƃǝ ʞǝʎ/ƗĐ øɟ ʇɥǝ ʇɥᴉnƃ ʇɥɐʇ ᴉs ᴉn ʇɥǝ ɔlᴉdbøɐɹd."
+#: openedx/core/djangoapps/content_tagging/models/base.py
+#: wiki/models/article.py
+msgid "owner"
+msgstr "øʍnǝɹ"
+
+#: openedx/core/djangoapps/content_tagging/models/base.py
+msgid ""
+"Organization that is related to this taxonomy.If None, then this taxonomy is"
+" related to all organizations."
+msgstr ""
+"Øɹƃɐnᴉzɐʇᴉøn ʇɥɐʇ ᴉs ɹǝlɐʇǝd ʇø ʇɥᴉs ʇɐxønøɯʎ.Ɨɟ Nønǝ, ʇɥǝn ʇɥᴉs ʇɐxønøɯʎ ᴉs"
+" ɹǝlɐʇǝd ʇø ɐll øɹƃɐnᴉzɐʇᴉøns."
+
#: openedx/core/djangoapps/cors_csrf/models.py
msgid ""
"List of domains that are allowed to make cross-domain requests to this site."
@@ -9018,18 +9031,6 @@ msgstr ""
"Ɏøn ɥɐʌǝ snɔɔǝssɟnllʎ sɥᴉɟʇǝd ʎønɹ ɔønɹsǝ sɔɥǝdnlǝ ɐnd ʎønɹ ɔɐlǝndɐɹ ᴉs nd "
"ʇø dɐʇǝ."
-#: openedx/features/content_tagging/models/base.py wiki/models/article.py
-msgid "owner"
-msgstr "øʍnǝɹ"
-
-#: openedx/features/content_tagging/models/base.py
-msgid ""
-"Organization that is related to this taxonomy.If None, then this taxonomy is"
-" related to all organizations."
-msgstr ""
-"Øɹƃɐnᴉzɐʇᴉøn ʇɥɐʇ ᴉs ɹǝlɐʇǝd ʇø ʇɥᴉs ʇɐxønøɯʎ.Ɨɟ Nønǝ, ʇɥǝn ʇɥᴉs ʇɐxønøɯʎ ᴉs"
-" ɹǝlɐʇǝd ʇø ɐll øɹƃɐnᴉzɐʇᴉøns."
-
#: openedx/features/content_type_gating/models.py
#: openedx/features/course_duration_limits/models.py
#: lms/templates/support/feature_based_enrollments.html
diff --git a/conf/locale/rtl/LC_MESSAGES/djangojs.mo b/conf/locale/rtl/LC_MESSAGES/djangojs.mo
index 5492ae5b46..d85f0d6e36 100644
Binary files a/conf/locale/rtl/LC_MESSAGES/djangojs.mo and b/conf/locale/rtl/LC_MESSAGES/djangojs.mo differ
diff --git a/conf/locale/rtl/LC_MESSAGES/djangojs.po b/conf/locale/rtl/LC_MESSAGES/djangojs.po
index 008760a54a..76d9de1c11 100644
--- a/conf/locale/rtl/LC_MESSAGES/djangojs.po
+++ b/conf/locale/rtl/LC_MESSAGES/djangojs.po
@@ -32,8 +32,8 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-09-20 12:47+0000\n"
-"PO-Revision-Date: 2023-09-20 12:47:19.757119\n"
+"POT-Creation-Date: 2023-09-24 20:36+0000\n"
+"PO-Revision-Date: 2023-09-24 20:36:20.294880\n"
"Last-Translator: \n"
"Language-Team: openedx-translation \n"
"Language: rtl\n"
diff --git a/conf/locale/sk/LC_MESSAGES/django.po b/conf/locale/sk/LC_MESSAGES/django.po
index d444869067..b161a52878 100644
--- a/conf/locale/sk/LC_MESSAGES/django.po
+++ b/conf/locale/sk/LC_MESSAGES/django.po
@@ -55,7 +55,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: Waheed Ahmed , 2019\n"
"Language-Team: Slovak (https://app.transifex.com/open-edx/teams/6205/sk/)\n"
diff --git a/conf/locale/sw_KE/LC_MESSAGES/django.po b/conf/locale/sw_KE/LC_MESSAGES/django.po
index 86a8f9fb03..e3ca02f5c6 100644
--- a/conf/locale/sw_KE/LC_MESSAGES/django.po
+++ b/conf/locale/sw_KE/LC_MESSAGES/django.po
@@ -85,7 +85,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: Waheed Ahmed , 2019\n"
"Language-Team: Swahili (Kenya) (https://app.transifex.com/open-edx/teams/6205/sw_KE/)\n"
diff --git a/conf/locale/th/LC_MESSAGES/django.po b/conf/locale/th/LC_MESSAGES/django.po
index 6f2a74ede1..b33b18ec22 100644
--- a/conf/locale/th/LC_MESSAGES/django.po
+++ b/conf/locale/th/LC_MESSAGES/django.po
@@ -116,7 +116,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: Waheed Ahmed , 2019\n"
"Language-Team: Thai (https://app.transifex.com/open-edx/teams/6205/th/)\n"
diff --git a/conf/locale/uk/LC_MESSAGES/django.po b/conf/locale/uk/LC_MESSAGES/django.po
index 7e07fc1047..2c79148d22 100644
--- a/conf/locale/uk/LC_MESSAGES/django.po
+++ b/conf/locale/uk/LC_MESSAGES/django.po
@@ -125,7 +125,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: Danylo Shcherbak , 2020\n"
"Language-Team: Ukrainian (https://app.transifex.com/open-edx/teams/6205/uk/)\n"
diff --git a/conf/locale/vi/LC_MESSAGES/django.po b/conf/locale/vi/LC_MESSAGES/django.po
index c61d56c393..891d04238c 100644
--- a/conf/locale/vi/LC_MESSAGES/django.po
+++ b/conf/locale/vi/LC_MESSAGES/django.po
@@ -198,7 +198,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: Le Minh Tri , 2020\n"
"Language-Team: Vietnamese (https://app.transifex.com/open-edx/teams/6205/vi/)\n"
diff --git a/conf/locale/zh_CN/LC_MESSAGES/django.po b/conf/locale/zh_CN/LC_MESSAGES/django.po
index b128bb14b0..b13d26e137 100644
--- a/conf/locale/zh_CN/LC_MESSAGES/django.po
+++ b/conf/locale/zh_CN/LC_MESSAGES/django.po
@@ -403,7 +403,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: ifLab , 2019\n"
"Language-Team: Chinese (China) (https://app.transifex.com/open-edx/teams/6205/zh_CN/)\n"
diff --git a/conf/locale/zh_HANS/LC_MESSAGES/django.po b/conf/locale/zh_HANS/LC_MESSAGES/django.po
index b128bb14b0..b13d26e137 100644
--- a/conf/locale/zh_HANS/LC_MESSAGES/django.po
+++ b/conf/locale/zh_HANS/LC_MESSAGES/django.po
@@ -403,7 +403,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: ifLab , 2019\n"
"Language-Team: Chinese (China) (https://app.transifex.com/open-edx/teams/6205/zh_CN/)\n"
diff --git a/conf/locale/zh_TW/LC_MESSAGES/django.po b/conf/locale/zh_TW/LC_MESSAGES/django.po
index ac047e66ce..d2bee32b49 100644
--- a/conf/locale/zh_TW/LC_MESSAGES/django.po
+++ b/conf/locale/zh_TW/LC_MESSAGES/django.po
@@ -177,7 +177,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.1a\n"
"Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n"
-"POT-Creation-Date: 2023-08-27 20:43+0000\n"
+"POT-Creation-Date: 2023-09-17 20:43+0000\n"
"PO-Revision-Date: 2019-01-20 20:43+0000\n"
"Last-Translator: Waheed Ahmed , 2019\n"
"Language-Team: Chinese (Taiwan) (https://app.transifex.com/open-edx/teams/6205/zh_TW/)\n"
diff --git a/openedx/core/djangoapps/notifications/admin.py b/openedx/core/djangoapps/notifications/admin.py
index 9fd69af219..52a758b97b 100644
--- a/openedx/core/djangoapps/notifications/admin.py
+++ b/openedx/core/djangoapps/notifications/admin.py
@@ -3,17 +3,61 @@ Django Admin for Notifications
"""
from django.contrib import admin
+from django.utils.translation import gettext_lazy as _
+from .base_notification import COURSE_NOTIFICATION_APPS, COURSE_NOTIFICATION_TYPES
from .models import CourseNotificationPreference, Notification
+class NotificationAppNameListFilter(admin.SimpleListFilter):
+ """
+ Shows list filter in django admin of notification apps
+ """
+ title = _("Notification App")
+ parameter_name = "app_name"
+
+ def lookups(self, request, model_admin):
+ lookup_list = [
+ (app_name, app_name)
+ for app_name in COURSE_NOTIFICATION_APPS.keys()
+ ]
+ return lookup_list
+
+ def queryset(self, request, queryset):
+ app_name = self.value()
+ if app_name not in COURSE_NOTIFICATION_APPS.keys():
+ return queryset
+ return queryset.filter(app_name=app_name)
+
+
+class NotificationTypeListFilter(admin.SimpleListFilter):
+ """
+ Shows list filter in django admin of notification types
+ """
+ title = _("Notification Type")
+ parameter_name = "notification_type"
+
+ def lookups(self, request, model_admin):
+ lookup_list = [
+ (notification_type, notification_type)
+ for notification_type in COURSE_NOTIFICATION_TYPES.keys()
+ ]
+ return lookup_list
+
+ def queryset(self, request, queryset):
+ notification_type = self.value()
+ if notification_type not in COURSE_NOTIFICATION_TYPES.keys():
+ return queryset
+ return queryset.filter(notification_type=notification_type)
+
+
class NotificationAdmin(admin.ModelAdmin):
"""
Admin for Notifications
"""
raw_id_fields = ('user',)
- search_fields = ('course_id', 'user__username')
- list_filter = ('app_name',)
+ search_fields = ('course_id', 'app_name', 'notification_type', 'user__username')
+ list_filter = (NotificationAppNameListFilter, NotificationTypeListFilter)
class CourseNotificationPreferenceAdmin(admin.ModelAdmin):
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 58a103c543..50c78c4851 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -20,9 +20,8 @@ celery>=5.2.2,<6.0.0
# required for celery>=5.2.0;<5.3.0
click>=8.0,<9.0
-# django-storages version upgrade
-django-storages==1.13.2
-
+# each version upgrade need release notes review.
+django-storages==1.14
# 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
@@ -33,7 +32,7 @@ edx-enterprise==4.3.1
# mentioned on this issue https://github.com/openedx/edx-platform/issues/32884
# 2. Versions from 1.5.0 to 2.0.0 have some migrations related changes.
# so we're upgrading minor versions one by one.
-django-oauth-toolkit==1.5.0
+django-oauth-toolkit==1.6.2
# constrained in opaque_keys. migration guide here: https://pymongo.readthedocs.io/en/4.0/migrate-to-pymongo4.html
@@ -78,8 +77,7 @@ pylint<2.16.0 # greater version failing quality test. Fix them in seperate ticke
# Deprecated version of the AWS SDK;
# we should stop using this
boto==2.39.0
-boto3==1.7.0 # Amazon Web Services SDK for Python
-botocore==1.10.84 # via boto3, s3transfer
+
# adding these constraints to minimize boto3 and botocore changeset
social-auth-core==4.3.0
@@ -134,3 +132,4 @@ openedx-learning==0.1.6
# existing custom parameter configurations unusable.
# https://github.com/openedx/xblock-lti-consumer/issues/410 has been opened to track a fix
lti-consumer-xblock==9.6.1
+
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index f0a5c00b1e..10891a48df 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -77,16 +77,14 @@ boto==2.39.0
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
-boto3==1.7.0
+boto3==1.28.53
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
# django-ses
# fs-s3fs
# ora2
-botocore==1.10.84
+botocore==1.31.53
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
# boto3
# s3transfer
@@ -259,7 +257,7 @@ django-celery-results==2.5.1
# via -r requirements/edx/kernel.in
django-classy-tags==4.1.0
# via django-sekizai
-django-config-models==2.5.0
+django-config-models==2.5.1
# via
# -r requirements/edx/kernel.in
# edx-enterprise
@@ -325,7 +323,7 @@ django-multi-email-field==0.7.0
# via edx-enterprise
django-mysql==4.11.0
# via -r requirements/edx/kernel.in
-django-oauth-toolkit==1.5.0
+django-oauth-toolkit==1.6.2
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
@@ -357,7 +355,7 @@ django-statici18n==2.4.0
# -r requirements/edx/kernel.in
# lti-consumer-xblock
# xblock-drag-and-drop-v2
-django-storages==1.13.2
+django-storages==1.14
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
@@ -402,10 +400,6 @@ djangorestframework==3.14.0
# super-csv
djangorestframework-xml==2.0.0
# via edx-enterprise
-docutils==0.19
- # via
- # -c requirements/edx/../constraints.txt
- # botocore
done-xblock==2.1.0
# via -r requirements/edx/bundled.in
drf-jwt==1.19.2
@@ -490,7 +484,7 @@ edx-enterprise==4.3.1
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
-edx-event-bus-kafka==5.4.0
+edx-event-bus-kafka==5.5.0
# via -r requirements/edx/kernel.in
edx-event-bus-redis==0.3.1
# via -r requirements/edx/kernel.in
@@ -500,7 +494,7 @@ edx-milestones==0.5.0
# via -r requirements/edx/kernel.in
edx-name-affirmation==2.3.6
# via -r requirements/edx/kernel.in
-edx-opaque-keys[django]==2.5.0
+edx-opaque-keys[django]==2.5.1
# via
# -r requirements/edx/kernel.in
# -r requirements/edx/paver.txt
@@ -636,7 +630,7 @@ jinja2==3.1.2
# via
# code-annotations
# coreschema
-jmespath==0.10.0
+jmespath==1.0.1
# via
# boto3
# botocore
@@ -778,7 +772,7 @@ openedx-django-pyfs==3.4.0
# xblock
openedx-django-require==2.1.0
# via -r requirements/edx/kernel.in
-openedx-django-wiki==2.0.1
+openedx-django-wiki==2.0.3
# via -r requirements/edx/kernel.in
openedx-events==8.5.0
# via
@@ -1034,7 +1028,7 @@ rules==3.3
# edx-enterprise
# edx-proctoring
# openedx-learning
-s3transfer==0.1.13
+s3transfer==0.6.2
# via boto3
sailthru-client==2.2.3
# via edx-ace
@@ -1062,7 +1056,6 @@ six==1.16.0
# chem
# codejail-includes
# crowdsourcehinter-xblock
- # django-oauth-toolkit
# edx-ace
# edx-auth-backends
# edx-ccx-keys
@@ -1170,6 +1163,7 @@ urllib3==1.26.16
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/paver.txt
+ # botocore
# elasticsearch
# py2neo
# requests
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index d38dfac599..28e9b4f9f1 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -145,17 +145,15 @@ boto==2.39.0
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-boto3==1.7.0
+boto3==1.28.53
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# django-ses
# fs-s3fs
# ora2
-botocore==1.10.84
+botocore==1.31.53
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# boto3
@@ -431,7 +429,7 @@ django-classy-tags==4.1.0
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# django-sekizai
-django-config-models==2.5.0
+django-config-models==2.5.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -524,7 +522,7 @@ django-mysql==4.11.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-django-oauth-toolkit==1.5.0
+django-oauth-toolkit==1.6.2
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
@@ -571,7 +569,7 @@ django-statici18n==2.4.0
# -r requirements/edx/testing.txt
# lti-consumer-xblock
# xblock-drag-and-drop-v2
-django-storages==1.13.2
+django-storages==1.14
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
@@ -640,8 +638,6 @@ docutils==0.19
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
- # -r requirements/edx/testing.txt
- # botocore
# pydata-sphinx-theme
# sphinx
# sphinx-mdinclude
@@ -759,7 +755,7 @@ edx-enterprise==4.3.1
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-edx-event-bus-kafka==5.4.0
+edx-event-bus-kafka==5.5.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -782,7 +778,7 @@ edx-name-affirmation==2.3.6
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-edx-opaque-keys[django]==2.5.0
+edx-opaque-keys[django]==2.5.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -950,7 +946,7 @@ gitdb==4.0.10
# via
# -r requirements/edx/doc.txt
# gitpython
-gitpython==3.1.36
+gitpython==3.1.37
# via -r requirements/edx/doc.txt
glob2==0.7
# via
@@ -1064,7 +1060,7 @@ jinja2==3.1.2
# coreschema
# diff-cover
# sphinx
-jmespath==0.10.0
+jmespath==1.0.1
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1305,7 +1301,7 @@ openedx-django-require==2.1.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
-openedx-django-wiki==2.0.1
+openedx-django-wiki==2.0.3
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1792,7 +1788,7 @@ rules==3.3
# edx-enterprise
# edx-proctoring
# openedx-learning
-s3transfer==0.1.13
+s3transfer==0.6.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1841,7 +1837,6 @@ six==1.16.0
# chem
# codejail-includes
# crowdsourcehinter-xblock
- # django-oauth-toolkit
# edx-ace
# edx-auth-backends
# edx-ccx-keys
@@ -2118,6 +2113,7 @@ urllib3==1.26.16
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
+ # botocore
# elasticsearch
# pact-python
# py2neo
diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt
index b27cca0f46..c168388afa 100644
--- a/requirements/edx/doc.txt
+++ b/requirements/edx/doc.txt
@@ -103,16 +103,14 @@ boto==2.39.0
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
-boto3==1.7.0
+boto3==1.28.53
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# django-ses
# fs-s3fs
# ora2
-botocore==1.10.84
+botocore==1.31.53
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# boto3
# s3transfer
@@ -312,7 +310,7 @@ django-classy-tags==4.1.0
# via
# -r requirements/edx/base.txt
# django-sekizai
-django-config-models==2.5.0
+django-config-models==2.5.1
# via
# -r requirements/edx/base.txt
# edx-enterprise
@@ -386,7 +384,7 @@ django-multi-email-field==0.7.0
# edx-enterprise
django-mysql==4.11.0
# via -r requirements/edx/base.txt
-django-oauth-toolkit==1.5.0
+django-oauth-toolkit==1.6.2
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
@@ -420,7 +418,7 @@ django-statici18n==2.4.0
# -r requirements/edx/base.txt
# lti-consumer-xblock
# xblock-drag-and-drop-v2
-django-storages==1.13.2
+django-storages==1.14
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
@@ -470,8 +468,6 @@ djangorestframework-xml==2.0.0
docutils==0.19
# via
# -c requirements/edx/../constraints.txt
- # -r requirements/edx/base.txt
- # botocore
# pydata-sphinx-theme
# sphinx
# sphinx-mdinclude
@@ -564,7 +560,7 @@ edx-enterprise==4.3.1
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
-edx-event-bus-kafka==5.4.0
+edx-event-bus-kafka==5.5.0
# via -r requirements/edx/base.txt
edx-event-bus-redis==0.3.1
# via -r requirements/edx/base.txt
@@ -576,7 +572,7 @@ edx-milestones==0.5.0
# via -r requirements/edx/base.txt
edx-name-affirmation==2.3.6
# via -r requirements/edx/base.txt
-edx-opaque-keys[django]==2.5.0
+edx-opaque-keys[django]==2.5.1
# via
# -r requirements/edx/base.txt
# edx-bulk-grades
@@ -686,7 +682,7 @@ geoip2==4.7.0
# via -r requirements/edx/base.txt
gitdb==4.0.10
# via gitpython
-gitpython==3.1.36
+gitpython==3.1.37
# via -r requirements/edx/doc.in
glob2==0.7
# via -r requirements/edx/base.txt
@@ -744,7 +740,7 @@ jinja2==3.1.2
# code-annotations
# coreschema
# sphinx
-jmespath==0.10.0
+jmespath==1.0.1
# via
# -r requirements/edx/base.txt
# boto3
@@ -916,7 +912,7 @@ openedx-django-pyfs==3.4.0
# xblock
openedx-django-require==2.1.0
# via -r requirements/edx/base.txt
-openedx-django-wiki==2.0.1
+openedx-django-wiki==2.0.3
# via -r requirements/edx/base.txt
openedx-events==8.5.0
# via
@@ -1222,7 +1218,7 @@ rules==3.3
# edx-enterprise
# edx-proctoring
# openedx-learning
-s3transfer==0.1.13
+s3transfer==0.6.2
# via
# -r requirements/edx/base.txt
# boto3
@@ -1256,7 +1252,6 @@ six==1.16.0
# chem
# codejail-includes
# crowdsourcehinter-xblock
- # django-oauth-toolkit
# edx-ace
# edx-auth-backends
# edx-ccx-keys
@@ -1425,6 +1420,7 @@ urllib3==1.26.16
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
+ # botocore
# elasticsearch
# py2neo
# requests
diff --git a/requirements/edx/paver.txt b/requirements/edx/paver.txt
index 962fdec088..46af4d76cc 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
-edx-opaque-keys==2.5.0
+edx-opaque-keys==2.5.1
# via -r requirements/edx/paver.in
idna==3.4
# via requests
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 18134e0ea5..6dc45b85a7 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -110,16 +110,14 @@ boto==2.39.0
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
-boto3==1.7.0
+boto3==1.28.53
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# django-ses
# fs-s3fs
# ora2
-botocore==1.10.84
+botocore==1.31.53
# via
- # -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
# boto3
# s3transfer
@@ -345,7 +343,7 @@ django-classy-tags==4.1.0
# via
# -r requirements/edx/base.txt
# django-sekizai
-django-config-models==2.5.0
+django-config-models==2.5.1
# via
# -r requirements/edx/base.txt
# edx-enterprise
@@ -419,7 +417,7 @@ django-multi-email-field==0.7.0
# edx-enterprise
django-mysql==4.11.0
# via -r requirements/edx/base.txt
-django-oauth-toolkit==1.5.0
+django-oauth-toolkit==1.6.2
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
@@ -453,7 +451,7 @@ django-statici18n==2.4.0
# -r requirements/edx/base.txt
# lti-consumer-xblock
# xblock-drag-and-drop-v2
-django-storages==1.13.2
+django-storages==1.14
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
@@ -500,11 +498,6 @@ djangorestframework-xml==2.0.0
# via
# -r requirements/edx/base.txt
# edx-enterprise
-docutils==0.19
- # via
- # -c requirements/edx/../constraints.txt
- # -r requirements/edx/base.txt
- # botocore
done-xblock==2.1.0
# via -r requirements/edx/base.txt
drf-jwt==1.19.2
@@ -594,7 +587,7 @@ edx-enterprise==4.3.1
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
-edx-event-bus-kafka==5.4.0
+edx-event-bus-kafka==5.5.0
# via -r requirements/edx/base.txt
edx-event-bus-redis==0.3.1
# via -r requirements/edx/base.txt
@@ -609,7 +602,7 @@ edx-milestones==0.5.0
# via -r requirements/edx/base.txt
edx-name-affirmation==2.3.6
# via -r requirements/edx/base.txt
-edx-opaque-keys[django]==2.5.0
+edx-opaque-keys[django]==2.5.1
# via
# -r requirements/edx/base.txt
# edx-bulk-grades
@@ -810,7 +803,7 @@ jinja2==3.1.2
# code-annotations
# coreschema
# diff-cover
-jmespath==0.10.0
+jmespath==1.0.1
# via
# -r requirements/edx/base.txt
# boto3
@@ -986,7 +979,7 @@ openedx-django-pyfs==3.4.0
# xblock
openedx-django-require==2.1.0
# via -r requirements/edx/base.txt
-openedx-django-wiki==2.0.1
+openedx-django-wiki==2.0.3
# via -r requirements/edx/base.txt
openedx-events==8.5.0
# via
@@ -1359,7 +1352,7 @@ rules==3.3
# edx-enterprise
# edx-proctoring
# openedx-learning
-s3transfer==0.1.13
+s3transfer==0.6.2
# via
# -r requirements/edx/base.txt
# boto3
@@ -1399,7 +1392,6 @@ six==1.16.0
# chem
# codejail-includes
# crowdsourcehinter-xblock
- # django-oauth-toolkit
# edx-ace
# edx-auth-backends
# edx-ccx-keys
@@ -1565,6 +1557,7 @@ urllib3==1.26.16
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
+ # botocore
# elasticsearch
# pact-python
# py2neo