diff --git a/Makefile b/Makefile index b76479fbda..6c525a57b6 100644 --- a/Makefile +++ b/Makefile @@ -123,6 +123,8 @@ compile-requirements: pre-requirements $(COMMON_CONSTRAINTS_TXT) ## Re-compile * @# time someone tries to use the outputs. sed 's/Django<5.0//g' requirements/common_constraints.txt > requirements/common_constraints.tmp mv requirements/common_constraints.tmp requirements/common_constraints.txt + sed 's/pip<24.3//g' requirements/common_constraints.txt > requirements/common_constraints.tmp + mv requirements/common_constraints.tmp requirements/common_constraints.txt pip-compile -v --allow-unsafe ${COMPILE_OPTS} -o requirements/pip.txt requirements/pip.in pip install -r requirements/pip.txt diff --git a/README.rst b/README.rst index e07d0ec1f2..dcd6e32c49 100644 --- a/README.rst +++ b/README.rst @@ -168,9 +168,13 @@ Set up CMS SSO (for Production): * Set ``SOCIAL_AUTH_EDX_OAUTH2_KEY`` to the client ID (``studio-sso-id``). * Set ``SOCIAL_AUTH_EDX_OAUTH2_SECRET`` to the client secret (which you copied). + Run the Platform ---------------- +Startup the Platform +==================== + First, ensure MySQL, Mongo, and Memcached are running. Start the LMS:: @@ -183,16 +187,46 @@ Start the CMS:: This will give you a mostly-headless Open edX platform. Most frontends have been migrated to "Micro-Frontends (MFEs)" which need to be installed and run -separately. At a bare minimum, you will need to run the `Authentication MFE`_, +separately. At a bare minimum, you will need to run the `Authoring MFE`_, `Learner Home MFE`_, and `Learning MFE`_ in order meaningfully navigate the UI. +A full list of the MFEs expected to run by default are listed below. .. _Tutor: https://github.com/overhangio/tutor .. _Site Ops home on docs.openedx.org: https://docs.openedx.org/en/latest/site_ops/index.html .. _development mode: https://docs.tutor.edly.io/dev.html .. _building static assets: ./docs/references/static-assets.rst -.. _Authentication MFE: https://github.com/openedx/frontend-app-authn/ .. _Learner Home MFE: https://github.com/openedx/frontend-app-learner-dashboard .. _Learning MFE: https://github.com/openedx/frontend-app-learning/ +.. _Authoring MFE: https://github.com/openedx/frontend-app-authoring/ + +Expected MFEs and Default Ports +------------------------------- + +Currently the following MFEs are enabled by default, and are expected to be +running at the given ports. + +.. list-table:: + :header-rows: 1 + + + * - Service Name + - Expected Location + - Override Setting Name + * - frontend-app-authoring + - localhost:2001 + - COURSE_AUTHORING_MICROFRONTEND_URL + * - frontend-app-learning + - localhost:2000 + - LEARNING_MICROFRONTEND_URL + * - frontend-app-learner-dashboard + - localhost:1996 + - LEARNER_HOME_MICROFRONTEND_URL + * - frontend-app-profile + - localhost:1995 + - PROFILE_MICROFRONTEND_URL + * - frontend-app-account + - localhost:1997 + - ACCOUNT_MICROFRONTEND_URL License ******* diff --git a/cms/conftest.py b/cms/conftest.py index 80d971070a..131ea8e048 100644 --- a/cms/conftest.py +++ b/cms/conftest.py @@ -40,23 +40,3 @@ def _django_clear_site_cache(): with what has been working for us so far. """ pass # lint-amnesty, pylint: disable=unnecessary-pass - - -@pytest.fixture(autouse=True) -def no_webpack_loader(monkeypatch): - """ - Monkeypatch webpack_loader to make sure that webpack assets don't need to be - compiled before unit tests are run. - """ - monkeypatch.setattr( - "webpack_loader.templatetags.webpack_loader.render_bundle", - lambda entry, extension=None, config='DEFAULT', attrs='': '' - ) - monkeypatch.setattr( - "webpack_loader.utils.get_as_tags", - lambda entry, extension=None, config='DEFAULT', attrs='': [] - ) - monkeypatch.setattr( - "webpack_loader.utils.get_files", - lambda entry, extension=None, config='DEFAULT', attrs='': [] - ) diff --git a/cms/djangoapps/contentstore/core/course_optimizer_provider.py b/cms/djangoapps/contentstore/core/course_optimizer_provider.py index c2fa91c9e3..134329992c 100644 --- a/cms/djangoapps/contentstore/core/course_optimizer_provider.py +++ b/cms/djangoapps/contentstore/core/course_optimizer_provider.py @@ -491,17 +491,24 @@ def _generate_course_updates_content(course, updates_links, course_key=None): if not update_items: return course_updates + # Group links by update ID and categorize them + links_by_update = {} + for item in updates_links: + if len(item) >= 2: + update_id, link = item[0], item[1] + link_state = item[2] if len(item) >= 3 else LinkState.BROKEN + links_by_update.setdefault(update_id, _create_empty_links_data()) + _categorize_link_by_state(link, link_state, links_by_update[update_id], course_key) + for update in update_items: if update.get("status") != "deleted": - update_content = update.get("content", "") - update_link_data = _process_content_links(update_content, updates_links, course_key) - + update_id = update.get("id") course_updates.append( { - "id": str(update.get("id")), + "id": str(update_id), "displayName": update.get("date", "Unknown Date"), "url": f"/course/{str(course.id)}/course_info", - **update_link_data, + **links_by_update.get(update_id, _create_empty_links_data()), } ) @@ -522,14 +529,21 @@ def _generate_handouts_content(course, handouts_links, course_key=None): ): return course_handouts - links_data = _process_content_links(handouts_block.data, handouts_links, course_key) + # Group links by block_id and categorize them + links_by_handout = {} + for item in handouts_links: + if len(item) >= 2: + block_id, link = item[0], item[1] + link_state = item[2] if len(item) >= 3 else LinkState.BROKEN + links_by_handout.setdefault(block_id, _create_empty_links_data()) + _categorize_link_by_state(link, link_state, links_by_handout[block_id], course_key) course_handouts = [ { "id": str(usage_key), "displayName": "handouts", "url": f"/course/{str(course.id)}/course_info", - **links_data, + **links_by_handout.get(str(usage_key), _create_empty_links_data()), } ] return course_handouts diff --git a/cms/djangoapps/contentstore/core/tests/test_course_optimizer_provider.py b/cms/djangoapps/contentstore/core/tests/test_course_optimizer_provider.py index 26a657a98e..0780848dfd 100644 --- a/cms/djangoapps/contentstore/core/tests/test_course_optimizer_provider.py +++ b/cms/djangoapps/contentstore/core/tests/test_course_optimizer_provider.py @@ -15,7 +15,7 @@ from cms.djangoapps.contentstore.core.course_optimizer_provider import ( ) from cms.djangoapps.contentstore.tasks import LinkState, extract_content_URLs_from_course from cms.djangoapps.contentstore.tests.utils import CourseTestCase -from cms.djangoapps.contentstore.utils import contains_previous_course_reference +from cms.djangoapps.contentstore.utils import contains_course_reference from xmodule.tabs import StaticTab @@ -329,7 +329,7 @@ class TestLinkCheckProvider(CourseTestCase): for url, expected_match in test_cases: with self.subTest(url=url, expected=expected_match): - result = contains_previous_course_reference(url, previous_course_key) + result = contains_course_reference(url, previous_course_key) self.assertEqual( result, expected_match, diff --git a/cms/djangoapps/contentstore/rest_api/v1/serializers/__init__.py b/cms/djangoapps/contentstore/rest_api/v1/serializers/__init__.py index 616204ef59..f96cb9adea 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/serializers/__init__.py +++ b/cms/djangoapps/contentstore/rest_api/v1/serializers/__init__.py @@ -18,7 +18,7 @@ from .proctoring import ( ) from .settings import CourseSettingsSerializer from .textbooks import CourseTextbooksSerializer -from .vertical_block import ContainerHandlerSerializer, VerticalContainerSerializer +from .vertical_block import ContainerHandlerSerializer, ContainerChildrenSerializer from .videos import ( CourseVideosSerializer, VideoDownloadSerializer, diff --git a/cms/djangoapps/contentstore/rest_api/v1/serializers/vertical_block.py b/cms/djangoapps/contentstore/rest_api/v1/serializers/vertical_block.py index ba352fc7ae..84598fbcd9 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/serializers/vertical_block.py +++ b/cms/djangoapps/contentstore/rest_api/v1/serializers/vertical_block.py @@ -105,6 +105,15 @@ class ContainerHandlerSerializer(serializers.Serializer): return None +class UpstreamChildrenInfoSerializer(serializers.Serializer): + """ + Serializer holding the information about the children of an xblock that is syncing. + """ + name = serializers.CharField() + upstream = serializers.CharField(allow_null=True) + id = serializers.CharField() + + class UpstreamLinkSerializer(serializers.Serializer): """ Serializer holding info for syncing a block with its upstream (eg, a library block). @@ -115,9 +124,12 @@ class UpstreamLinkSerializer(serializers.Serializer): version_declined = serializers.IntegerField(allow_null=True) error_message = serializers.CharField(allow_null=True) ready_to_sync = serializers.BooleanField() + is_modified = serializers.BooleanField() + has_top_level_parent = serializers.BooleanField() + ready_to_sync_children = UpstreamChildrenInfoSerializer(many=True, required=False) -class ChildVerticalContainerSerializer(serializers.Serializer): +class ContainerChildSerializer(serializers.Serializer): """ Serializer for representing a xblock child of vertical container. """ @@ -160,11 +172,11 @@ class ChildVerticalContainerSerializer(serializers.Serializer): return actions -class VerticalContainerSerializer(serializers.Serializer): +class ContainerChildrenSerializer(serializers.Serializer): """ Serializer for representing a vertical container with state and children. """ - children = ChildVerticalContainerSerializer(many=True) + children = ContainerChildSerializer(many=True) is_published = serializers.BooleanField() can_paste_component = serializers.BooleanField() diff --git a/cms/djangoapps/contentstore/rest_api/v1/urls.py b/cms/djangoapps/contentstore/rest_api/v1/urls.py index 3492186797..685a81d778 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/urls.py +++ b/cms/djangoapps/contentstore/rest_api/v1/urls.py @@ -1,32 +1,33 @@ """ Contenstore API v1 URLs. """ from django.conf import settings -from django.urls import re_path, path +from django.urls import path, re_path from openedx.core.constants import COURSE_ID_PATTERN from .views import ( + ContainerChildrenView, ContainerHandlerView, CourseCertificatesView, CourseDetailsView, - CourseTeamView, - CourseTextbooksView, - CourseIndexView, CourseGradingView, CourseGroupConfigurationsView, + CourseIndexView, CourseRerunView, CourseSettingsView, + CourseTeamView, + CourseTextbooksView, CourseVideosView, CourseWaffleFlagsView, - HomePageView, + HelpUrlsView, HomePageCoursesView, HomePageLibrariesView, + HomePageView, ProctoredExamSettingsView, ProctoringErrorsView, - HelpUrlsView, - VideoUsageView, VideoDownloadView, - VerticalContainerView, + VideoUsageView, + vertical_container_children_redirect_view, ) app_name = 'v1' @@ -127,11 +128,17 @@ urlpatterns = [ ContainerHandlerView.as_view(), name="container_handler" ), + # Deprecated url, please use `container_children` url below re_path( fr'^container/vertical/{settings.USAGE_KEY_PATTERN}/children$', - VerticalContainerView.as_view(), + vertical_container_children_redirect_view, name="container_vertical" ), + re_path( + fr'^container/{settings.USAGE_KEY_PATTERN}/children$', + ContainerChildrenView.as_view(), + name="container_children" + ), re_path( fr'^course_waffle_flags(?:/{COURSE_ID_PATTERN})?$', CourseWaffleFlagsView.as_view(), diff --git a/cms/djangoapps/contentstore/rest_api/v1/views/__init__.py b/cms/djangoapps/contentstore/rest_api/v1/views/__init__.py index 89d8d56eaa..d4fcfd5f2e 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/views/__init__.py +++ b/cms/djangoapps/contentstore/rest_api/v1/views/__init__.py @@ -3,10 +3,10 @@ Views for v1 contentstore API. """ from .certificates import CourseCertificatesView from .course_details import CourseDetailsView -from .course_index import CourseIndexView +from .course_index import ContainerChildrenView, CourseIndexView from .course_rerun import CourseRerunView -from .course_waffle_flags import CourseWaffleFlagsView from .course_team import CourseTeamView +from .course_waffle_flags import CourseWaffleFlagsView from .grading import CourseGradingView from .group_configurations import CourseGroupConfigurationsView from .help_urls import HelpUrlsView @@ -14,7 +14,7 @@ from .home import HomePageCoursesView, HomePageLibrariesView, HomePageView from .proctoring import ProctoredExamSettingsView, ProctoringErrorsView from .settings import CourseSettingsView from .textbooks import CourseTextbooksView -from .vertical_block import ContainerHandlerView, VerticalContainerView +from .vertical_block import ContainerHandlerView, vertical_container_children_redirect_view from .videos import ( CourseVideosView, VideoDownloadView, diff --git a/cms/djangoapps/contentstore/rest_api/v1/views/course_index.py b/cms/djangoapps/contentstore/rest_api/v1/views/course_index.py index 2a6fb0f4bc..f79892658d 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/views/course_index.py +++ b/cms/djangoapps/contentstore/rest_api/v1/views/course_index.py @@ -1,5 +1,7 @@ """API Views for course index""" +import logging + import edx_api_doc_tools as apidocs from django.conf import settings from opaque_keys.edx.keys import CourseKey @@ -8,10 +10,24 @@ from rest_framework.response import Response from rest_framework.views import APIView from cms.djangoapps.contentstore.config.waffle import CUSTOM_RELATIVE_DATES -from cms.djangoapps.contentstore.rest_api.v1.serializers import CourseIndexSerializer -from cms.djangoapps.contentstore.utils import get_course_index_context +from cms.djangoapps.contentstore.rest_api.v1.mixins import ContainerHandlerMixin +from cms.djangoapps.contentstore.rest_api.v1.serializers import ( + CourseIndexSerializer, + ContainerChildrenSerializer, +) +from cms.djangoapps.contentstore.utils import ( + get_course_index_context, + get_user_partition_info, + get_visibility_partition_info, + get_xblock_render_error, + get_xblock_validation_messages, +) +from cms.djangoapps.contentstore.xblock_storage_handlers.view_handlers import get_xblock +from cms.lib.xblock.upstream_sync import UpstreamLink from common.djangoapps.student.auth import has_studio_read_access from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, verify_course_exists, view_auth_classes +from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order +from xmodule.modulestore.exceptions import ItemNotFoundError # lint-amnesty, pylint: disable=wrong-import-order @view_auth_classes(is_authenticated=True) @@ -98,3 +114,169 @@ class CourseIndexView(DeveloperErrorViewMixin, APIView): serializer = CourseIndexSerializer(course_index_context) return Response(serializer.data) + + +@view_auth_classes(is_authenticated=True) +class ContainerChildrenView(APIView, ContainerHandlerMixin): + """ + View for container xblock requests to get state and children data. + """ + + @apidocs.schema( + parameters=[ + apidocs.string_parameter( + "usage_key_string", + apidocs.ParameterLocation.PATH, + description="Container usage key", + ), + ], + responses={ + 200: ContainerChildrenSerializer, + 401: "The requester is not authenticated.", + 404: "The requested locator does not exist.", + }, + ) + def get(self, request: Request, usage_key_string: str): + """ + Get an object containing vertical state with children data. + + **Example Request** + + GET /api/contentstore/v1/container/{usage_key_string}/children + + **Response Values** + + If the request is successful, an HTTP 200 "OK" response is returned. + + The HTTP 200 response contains a single dict that contains keys that + are the vertical's container children data. + + **Example Response** + + ```json + { + "children": [ + { + "name": "Drag and Drop", + "block_id": "block-v1:org+101+101+type@drag-and-drop-v2+block@7599275ace6b46f5a482078a2954ca16", + "block_type": "drag-and-drop-v2", + "user_partition_info": {}, + "user_partitions": {} + "upstream_link": null, + "actions": { + "can_copy": true, + "can_duplicate": true, + "can_move": true, + "can_manage_access": true, + "can_delete": true, + "can_manage_tags": true, + }, + "has_validation_error": false, + "validation_errors": [], + }, + { + "name": "Video", + "block_id": "block-v1:org+101+101+type@video+block@0e3d39b12d7c4345981bda6b3511a9bf", + "block_type": "video", + "user_partition_info": {}, + "user_partitions": {} + "upstream_link": { + "upstream_ref": "lb:org:mylib:video:404", + "version_synced": 16 + "version_available": null, + "error_message": "Linked library item not found: lb:org:mylib:video:404", + "ready_to_sync": false, + }, + "actions": { + "can_copy": true, + "can_duplicate": true, + "can_move": true, + "can_manage_access": true, + "can_delete": true, + "can_manage_tags": true, + } + "validation_messages": [], + "render_error": "", + }, + { + "name": "Text", + "block_id": "block-v1:org+101+101+type@html+block@3e3fa1f88adb4a108cd14e9002143690", + "block_type": "html", + "user_partition_info": {}, + "user_partitions": {}, + "upstream_link": { + "upstream_ref": "lb:org:mylib:html:abcd", + "version_synced": 43, + "version_available": 49, + "error_message": null, + "ready_to_sync": true, + }, + "actions": { + "can_copy": true, + "can_duplicate": true, + "can_move": true, + "can_manage_access": true, + "can_delete": true, + "can_manage_tags": true, + }, + "validation_messages": [ + { + "text": "This component's access settings contradict its parent's access settings.", + "type": "error" + } + ], + "render_error": "Unterminated control keyword: 'if' in file '../problem.html'", + }, + ], + "is_published": false, + "can_paste_component": true, + } + ``` + """ + usage_key = self.get_object(usage_key_string) + current_xblock = get_xblock(usage_key, request.user) + is_course = current_xblock.scope_ids.usage_id.context_key.is_course + + with modulestore().bulk_operations(usage_key.course_key): + # load course once to reuse it for user_partitions query + course = modulestore().get_course(current_xblock.location.course_key) + children = [] + if current_xblock.has_children: + for child in current_xblock.children: + child_info = modulestore().get_item(child) + user_partition_info = get_visibility_partition_info(child_info, course=course) + user_partitions = get_user_partition_info(child_info, course=course) + upstream_link = UpstreamLink.try_get_for_block(child_info, log_error=False) + validation_messages = get_xblock_validation_messages(child_info) + render_error = get_xblock_render_error(request, child_info) + + children.append({ + "xblock": child_info, + "name": child_info.display_name_with_default, + "block_id": child_info.location, + "block_type": child_info.location.block_type, + "user_partition_info": user_partition_info, + "user_partitions": user_partitions, + "upstream_link": ( + # If the block isn't linked to an upstream (which is by far the most common case) then just + # make this field null, which communicates the same info, but with less noise. + upstream_link.to_json(include_child_info=True) if upstream_link.upstream_ref + else None + ), + "validation_messages": validation_messages, + "render_error": render_error, + }) + + is_published = False + try: + is_published = not modulestore().has_changes(current_xblock) + except ItemNotFoundError: + logging.error('Could not find any changes for block [%s]', usage_key) + + container_data = { + "children": children, + "is_published": is_published, + "can_paste_component": is_course, + } + serializer = ContainerChildrenSerializer(container_data) + return Response(serializer.data) diff --git a/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_vertical_block.py b/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_vertical_block.py index bcc9c031dc..16e6679833 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_vertical_block.py +++ b/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_vertical_block.py @@ -195,7 +195,7 @@ class ContainerVerticalViewTest(BaseXBlockContainer): Unit tests for the ContainerVerticalViewTest. """ - view_name = "container_vertical" + view_name = "container_children" def test_success_response(self): """ @@ -279,6 +279,8 @@ class ContainerVerticalViewTest(BaseXBlockContainer): "version_declined": None, "error_message": "Linked upstream library block was not found in the system", "ready_to_sync": False, + "has_top_level_parent": False, + "is_modified": False, }, "user_partition_info": expected_user_partition_info, "user_partitions": expected_user_partitions, diff --git a/cms/djangoapps/contentstore/rest_api/v1/views/vertical_block.py b/cms/djangoapps/contentstore/rest_api/v1/views/vertical_block.py index a71f718b70..0af99137e9 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/views/vertical_block.py +++ b/cms/djangoapps/contentstore/rest_api/v1/views/vertical_block.py @@ -1,33 +1,26 @@ """ API Views for unit page """ import logging + import edx_api_doc_tools as apidocs -from django.http import HttpResponseBadRequest +from django.http import HttpResponseBadRequest, HttpResponsePermanentRedirect +from django.urls import reverse from rest_framework.request import Request from rest_framework.response import Response from rest_framework.views import APIView -from cms.djangoapps.contentstore.utils import ( - get_container_handler_context, - get_user_partition_info, - get_visibility_partition_info, - get_xblock_validation_messages, - get_xblock_render_error, -) -from cms.djangoapps.contentstore.views.component import _get_item_in_course -from cms.djangoapps.contentstore.xblock_storage_handlers.view_handlers import get_xblock +from cms.djangoapps.contentstore.rest_api.v1.mixins import ContainerHandlerMixin from cms.djangoapps.contentstore.rest_api.v1.serializers import ( ContainerHandlerSerializer, - VerticalContainerSerializer, ) -from cms.lib.xblock.upstream_sync import UpstreamLink +from cms.djangoapps.contentstore.utils import ( + get_container_handler_context, +) +from cms.djangoapps.contentstore.views.component import _get_item_in_course from openedx.core.lib.api.view_utils import view_auth_classes from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.exceptions import ItemNotFoundError # lint-amnesty, pylint: disable=wrong-import-order -from cms.djangoapps.contentstore.rest_api.v1.mixins import ContainerHandlerMixin - - log = logging.getLogger(__name__) @@ -154,167 +147,16 @@ class ContainerHandlerView(APIView, ContainerHandlerMixin): return Response(serializer.data) -@view_auth_classes(is_authenticated=True) -class VerticalContainerView(APIView, ContainerHandlerMixin): - """ - View for container xblock requests to get vertical state and children data. +def vertical_container_children_redirect_view(request: Request, usage_key_string: str): """ + Redirects GET requests to container_children url. - @apidocs.schema( - parameters=[ - apidocs.string_parameter( - "usage_key_string", - apidocs.ParameterLocation.PATH, - description="Vertical usage key", - ), - ], - responses={ - 200: VerticalContainerSerializer, - 401: "The requester is not authenticated.", - 404: "The requested locator does not exist.", - }, + This view will return a 301 Moved Permanently response to the provided + usage_key string and automatically redirect the browser to the correct URL. + """ + redirect_location = reverse( + 'cms.djangoapps.contentstore:v1:container_children', + kwargs={'usage_key_string': usage_key_string}, ) - def get(self, request: Request, usage_key_string: str): - """ - Get an object containing vertical state with children data. - - **Example Request** - - GET /api/contentstore/v1/container/vertical/{usage_key_string}/children - - **Response Values** - - If the request is successful, an HTTP 200 "OK" response is returned. - - The HTTP 200 response contains a single dict that contains keys that - are the vertical's container children data. - - **Example Response** - - ```json - { - "children": [ - { - "name": "Drag and Drop", - "block_id": "block-v1:org+101+101+type@drag-and-drop-v2+block@7599275ace6b46f5a482078a2954ca16", - "block_type": "drag-and-drop-v2", - "user_partition_info": {}, - "user_partitions": {} - "upstream_link": null, - "actions": { - "can_copy": true, - "can_duplicate": true, - "can_move": true, - "can_manage_access": true, - "can_delete": true, - "can_manage_tags": true, - }, - "has_validation_error": false, - "validation_errors": [], - }, - { - "name": "Video", - "block_id": "block-v1:org+101+101+type@video+block@0e3d39b12d7c4345981bda6b3511a9bf", - "block_type": "video", - "user_partition_info": {}, - "user_partitions": {} - "upstream_link": { - "upstream_ref": "lb:org:mylib:video:404", - "version_synced": 16 - "version_available": null, - "error_message": "Linked library item not found: lb:org:mylib:video:404", - "ready_to_sync": false, - }, - "actions": { - "can_copy": true, - "can_duplicate": true, - "can_move": true, - "can_manage_access": true, - "can_delete": true, - "can_manage_tags": true, - } - "validation_messages": [], - "render_error": "", - }, - { - "name": "Text", - "block_id": "block-v1:org+101+101+type@html+block@3e3fa1f88adb4a108cd14e9002143690", - "block_type": "html", - "user_partition_info": {}, - "user_partitions": {}, - "upstream_link": { - "upstream_ref": "lb:org:mylib:html:abcd", - "version_synced": 43, - "version_available": 49, - "error_message": null, - "ready_to_sync": true, - }, - "actions": { - "can_copy": true, - "can_duplicate": true, - "can_move": true, - "can_manage_access": true, - "can_delete": true, - "can_manage_tags": true, - }, - "validation_messages": [ - { - "text": "This component's access settings contradict its parent's access settings.", - "type": "error" - } - ], - "render_error": "Unterminated control keyword: 'if' in file '../problem.html'", - }, - ], - "is_published": false, - "can_paste_component": true, - } - ``` - """ - usage_key = self.get_object(usage_key_string) - current_xblock = get_xblock(usage_key, request.user) - is_course = current_xblock.scope_ids.usage_id.context_key.is_course - - with modulestore().bulk_operations(usage_key.course_key): - # load course once to reuse it for user_partitions query - course = modulestore().get_course(current_xblock.location.course_key) - children = [] - if hasattr(current_xblock, "children"): - for child in current_xblock.children: - child_info = modulestore().get_item(child) - user_partition_info = get_visibility_partition_info(child_info, course=course) - user_partitions = get_user_partition_info(child_info, course=course) - upstream_link = UpstreamLink.try_get_for_block(child_info, log_error=False) - validation_messages = get_xblock_validation_messages(child_info) - render_error = get_xblock_render_error(request, child_info) - - children.append({ - "xblock": child_info, - "name": child_info.display_name_with_default, - "block_id": child_info.location, - "block_type": child_info.location.block_type, - "user_partition_info": user_partition_info, - "user_partitions": user_partitions, - "upstream_link": ( - # If the block isn't linked to an upstream (which is by far the most common case) then just - # make this field null, which communicates the same info, but with less noise. - upstream_link.to_json() if upstream_link.upstream_ref - else None - ), - "validation_messages": validation_messages, - "render_error": render_error, - }) - - is_published = False - try: - is_published = not modulestore().has_changes(current_xblock) - except ItemNotFoundError: - logging.error('Could not find any changes for block [%s]', usage_key) - - container_data = { - "children": children, - "is_published": is_published, - "can_paste_component": is_course, - } - serializer = VerticalContainerSerializer(container_data) - return Response(serializer.data) + http_response = HttpResponsePermanentRedirect(redirect_location) + return http_response diff --git a/cms/djangoapps/contentstore/tasks.py b/cms/djangoapps/contentstore/tasks.py index e9942ce3cf..71b86acca2 100644 --- a/cms/djangoapps/contentstore/tasks.py +++ b/cms/djangoapps/contentstore/tasks.py @@ -56,7 +56,7 @@ from cms.djangoapps.contentstore.storage import course_import_export_storage from cms.djangoapps.contentstore.toggles import enable_course_optimizer_check_prev_run_links from cms.djangoapps.contentstore.utils import ( IMPORTABLE_FILE_TYPES, - contains_previous_course_reference, + contains_course_reference, create_course_info_usage_key, create_or_update_xblock_upstream_link, delete_course, @@ -1190,7 +1190,7 @@ def _check_broken_links(task_instance, user_id, course_key_string, language): # Separate previous run links from regular links BEFORE validation urls_to_validate = [] for block_id, url in url_list: - if contains_previous_course_reference(url, previous_run_course_key): + if contains_course_reference(url, previous_run_course_key): previous_run_links.append([block_id, url, LinkState.PREVIOUS_RUN]) else: urls_to_validate.append([block_id, url]) @@ -1917,28 +1917,45 @@ def _course_link_update_required(url, course_key, prev_run_course_key): Args: url: The URL to check course_key: The current course key + prev_run_course_key: The previous course run key Returns: bool: True if the link needs updating """ - if not url or not course_key: + if not all((url, course_key, prev_run_course_key)): return False - course_id_match = contains_previous_course_reference(url, prev_run_course_key) - if not course_id_match: - return False - - # Check if it's the same org and course but different run - if ( - prev_run_course_key.org == course_key.org - and prev_run_course_key.course == course_key.course - and prev_run_course_key.run != course_key.run - ): + course_id_match = contains_course_reference(url, prev_run_course_key) + if course_id_match: return True + return False +def _replace_exact_course_reference(url, old_course_key, new_course_key): + """ + Replaces exact course key references in a URL, avoiding partial matches. + + Args: + url: The URL to update + old_course_key: The course key to replace + new_course_key: The course key to replace with + + Returns: + str: Updated URL with exact course key replacements + """ + if not old_course_key or not new_course_key or not url: + return url + + old_course_pattern = re.escape(str(old_course_key)) + + # Ensure the course key is followed by '/' or end of string + pattern = old_course_pattern + r'(?=/|$)' + + return re.sub(pattern, str(new_course_key), url, flags=re.IGNORECASE) + + def _determine_link_type(block_id): """ Determines the type of link based on block_id and URL. @@ -1987,22 +2004,13 @@ def _update_link_to_latest_rerun(link_data, course_key, prev_run_course_key, use if not original_url: return original_url - prev_run_course_org = prev_run_course_key.org if prev_run_course_key else None - prev_run_course_course = ( - prev_run_course_key.course if prev_run_course_key else None - ) - if prev_run_course_key == course_key: return original_url - # Validate url based on previous-run org - if ( - prev_run_course_org != course_key.org - or prev_run_course_course != course_key.course - ): - return original_url + new_url = _replace_exact_course_reference(original_url, prev_run_course_key, course_key) - new_url = original_url.replace(str(prev_run_course_key), str(course_key)) + if new_url == original_url: + return original_url # condition because we're showing handouts as updates if link_type == "course_updates" and "handouts" in str(block_id): diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index 775999ea60..c526111cf2 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -2477,18 +2477,24 @@ def get_previous_run_course_key(course_key): return rerun_state.source_course_key -def contains_previous_course_reference(url, previous_course_key): +def contains_course_reference(url, course_key): """ - Checks if a URL contains references to the previous course. + Checks if a URL contains an exact reference to the specified course key. + Uses specific delimiter matching to ensure exact matching and avoid partial matches. - Arguments: + Args: url: The URL to check - previous_course_key: The previous course key to look for + course_key: The course key to look for Returns: - bool: True if URL contains reference to previous course + bool: True if URL contains exact reference to the course """ - if not previous_course_key: + if not course_key or not url: return False - return str(previous_course_key).lower() in url.lower() + course_key_pattern = re.escape(str(course_key)) + + # Ensure the course key is followed by '/' or end of string + pattern = course_key_pattern + r'(?=/|$)' + + return bool(re.search(pattern, url, re.IGNORECASE)) diff --git a/cms/envs/common.py b/cms/envs/common.py index 00a2ba63b1..20db8ba750 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -66,43 +66,19 @@ from openedx.core.djangoapps.theming.helpers_dirs import ( from openedx.core.lib.license import LicenseMixin from openedx.core.lib.derived import Derived from openedx.core.release import doc_version +from openedx.core.lib.features_setting_proxy import FeaturesProxy + +# A proxy for feature flags stored in the settings namespace +FEATURES = FeaturesProxy(globals()) # pylint: enable=useless-suppression -################ Enable credit eligibility feature #################### -ENABLE_CREDIT_ELIGIBILITY = True - -################################ Block Structures ################################### -BLOCK_STRUCTURES_SETTINGS = dict( - # Delay, in seconds, after a new edit of a course is published - # before updating the block structures cache. This is needed - # for a better chance at getting the latest changes when there - # are secondary reads in sharded mongoDB clusters. See TNL-5041 - # for more info. - COURSE_PUBLISH_TASK_DELAY=30, - - # Delay, in seconds, between retry attempts if a task fails. - TASK_DEFAULT_RETRY_DELAY=30, - - # Maximum number of retries per task. - TASK_MAX_RETRIES=5, -) - ############################ FEATURE CONFIGURATION ############################# -PLATFORM_NAME = _('Your Platform Name Here') - CONTACT_MAILING_ADDRESS = _('Your Contact Mailing Address Here') -PLATFORM_DESCRIPTION = _('Your Platform Description Here') - -PLATFORM_FACEBOOK_ACCOUNT = "http://www.facebook.com/YourPlatformFacebookAccount" -PLATFORM_TWITTER_ACCOUNT = "@YourPlatformTwitterAccount" - # Dummy secret key for dev/test SECRET_KEY = 'dev key' -FAVICON_PATH = 'images/favicon.ico' - # .. setting_name: STUDIO_NAME # .. setting_default: Your Platform Studio @@ -110,387 +86,180 @@ FAVICON_PATH = 'images/favicon.ico' # templates. STUDIO_NAME = _("Your Platform Studio") STUDIO_SHORT_NAME = _("Studio") -FEATURES = { - 'GITHUB_PUSH': False, - # See annotations in lms/envs/common.py for details. - 'ENABLE_DISCUSSION_SERVICE': True, - # See annotations in lms/envs/common.py for details. - 'ENABLE_TEXTBOOK': True, +# FEATURES - # When True, all courses will be active, regardless of start date - # DO NOT SET TO True IN THIS FILE - # Doing so will cause all courses to be released on production - 'DISABLE_START_DATES': False, +GITHUB_PUSH = False - # email address for studio staff (eg to request course creation) - 'STUDIO_REQUEST_EMAIL': '', +# email address for studio staff (eg to request course creation) +STUDIO_REQUEST_EMAIL = '' - # Segment - must explicitly turn it on for production - 'CMS_SEGMENT_KEY': None, +# Segment - must explicitly turn it on for production +CMS_SEGMENT_KEY = None - # Enable URL that shows information about the status of various services - 'ENABLE_SERVICE_STATUS': False, +# If set to True, new Studio users won't be able to author courses unless +# an Open edX admin has added them to the course creator group. +ENABLE_CREATOR_GROUP = True - # Don't autoplay videos for course authors - 'AUTOPLAY_VIDEOS': False, +# If set to True, organization staff members can create libraries for their specific +# organization and no other organizations. They do not need to be course creators, +# even when ENABLE_CREATOR_GROUP is True. +ENABLE_ORGANIZATION_STAFF_ACCESS_FOR_CONTENT_LIBRARIES = True - # Move the course author to next page when a video finishes. Set to True to - # show an auto-advance button in videos. If False, videos never auto-advance. - 'ENABLE_AUTOADVANCE_VIDEOS': False, +# Turn off account locking if failed login attempts exceeds a limit +ENABLE_MAX_FAILED_LOGIN_ATTEMPTS = False - # If set to True, new Studio users won't be able to author courses unless - # an Open edX admin has added them to the course creator group. - 'ENABLE_CREATOR_GROUP': True, +# .. toggle_name: settings.EDITABLE_SHORT_DESCRIPTION +# .. toggle_implementation: DjangoSetting +# .. toggle_default: True +# .. toggle_description: This feature flag allows editing of short descriptions on the Schedule & Details page in +# Open edX Studio. Set to False if you want to disable the editing of the course short description. +# .. toggle_use_cases: open_edx +# .. toggle_creation_date: 2014-02-13 +# .. toggle_tickets: https://github.com/openedx/edx-platform/pull/2334 +EDITABLE_SHORT_DESCRIPTION = True - # If set to True, organization staff members can create libraries for their specific - # organization and no other organizations. They do not need to be course creators, - # even when ENABLE_CREATOR_GROUP is True. - 'ENABLE_ORGANIZATION_STAFF_ACCESS_FOR_CONTENT_LIBRARIES': True, +# Hide any Personally Identifiable Information from application logs +SQUELCH_PII_IN_LOGS = False - # Turn off account locking if failed login attempts exceeds a limit - 'ENABLE_MAX_FAILED_LOGIN_ATTEMPTS': False, +# Allow creating courses with non-ascii characters in the course id +ALLOW_UNICODE_COURSE_ID = False - # .. toggle_name: FEATURES['EDITABLE_SHORT_DESCRIPTION'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: True - # .. toggle_description: This feature flag allows editing of short descriptions on the Schedule & Details page in - # Open edX Studio. Set to False if you want to disable the editing of the course short description. - # .. toggle_use_cases: open_edx - # .. toggle_creation_date: 2014-02-13 - # .. toggle_tickets: https://github.com/openedx/edx-platform/pull/2334 - 'EDITABLE_SHORT_DESCRIPTION': True, +# Prevent concurrent logins per user +PREVENT_CONCURRENT_LOGINS = False - # Hide any Personally Identifiable Information from application logs - 'SQUELCH_PII_IN_LOGS': False, +# Turn off Video Upload Pipeline through Studio, by default +ENABLE_VIDEO_UPLOAD_PIPELINE = False - # Toggles the embargo functionality, which blocks users - # based on their location. - 'EMBARGO': False, +# Show a new field in "Advanced settings" that can store custom data about a +# course and that can be read from themes +ENABLE_OTHER_COURSE_SETTINGS = False - # Allow creating courses with non-ascii characters in the course id - 'ALLOW_UNICODE_COURSE_ID': False, +# Enable support for content libraries. Note that content libraries are +# only supported in courses using split mongo. +ENABLE_CONTENT_LIBRARIES = True - # Prevent concurrent logins per user - 'PREVENT_CONCURRENT_LOGINS': False, +# .. toggle_name: settings.ENABLE_CONTENT_LIBRARIES_LTI_TOOL +# .. toggle_implementation: DjangoSetting +# .. toggle_default: False +# .. toggle_description: When set to True, Content Libraries in +# Studio can be used as an LTI 1.3 tool by external LTI platforms. +# .. toggle_use_cases: open_edx +# .. toggle_creation_date: 2021-08-17 +# .. toggle_tickets: https://github.com/openedx/edx-platform/pull/27411 +ENABLE_CONTENT_LIBRARIES_LTI_TOOL = False - # Turn off Video Upload Pipeline through Studio, by default - 'ENABLE_VIDEO_UPLOAD_PIPELINE': False, +# Toggle course entrance exams feature +ENTRANCE_EXAMS = False - # See annotations in lms/envs/common.py for details. - 'ENABLE_EDXNOTES': False, +# Enable the courseware search functionality +ENABLE_COURSEWARE_INDEX = False - # Toggle to enable coordination with the Publisher tool (keep in sync with lms/envs/common.py) - 'ENABLE_PUBLISHER': False, +# Enable content libraries (modulestore) search functionality +ENABLE_LIBRARY_INDEX = False - # Show a new field in "Advanced settings" that can store custom data about a - # course and that can be read from themes - 'ENABLE_OTHER_COURSE_SETTINGS': False, +# .. toggle_name: settings.ALLOW_COURSE_RERUNS +# .. toggle_implementation: DjangoSetting +# .. toggle_default: True +# .. toggle_description: This will allow staff member to re-run the course from the studio home page and will +# always use the split modulestore. When this is set to False, the Re-run Course link will not be available on +# the studio home page. +# .. toggle_use_cases: open_edx +# .. toggle_creation_date: 2015-02-13 +# .. toggle_tickets: https://github.com/openedx/edx-platform/pull/6965 +ALLOW_COURSE_RERUNS = True - # Write new CSM history to the extended table. - # This will eventually default to True and may be - # removed since all installs should have the separate - # extended history table. This is needed in the LMS and CMS - # for migration consistency. - 'ENABLE_CSMH_EXTENDED': True, +# Whether archived courses (courses with end dates in the past) should be +# shown in Studio in a separate list. +ENABLE_SEPARATE_ARCHIVED_COURSES = True - # Enable support for content libraries. Note that content libraries are - # only supported in courses using split mongo. - 'ENABLE_CONTENT_LIBRARIES': True, +ENABLE_GRADE_DOWNLOADS = True - # .. toggle_name: FEATURES['ENABLE_CONTENT_LIBRARIES_LTI_TOOL'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: False - # .. toggle_description: When set to True, Content Libraries in - # Studio can be used as an LTI 1.3 tool by external LTI platforms. - # .. toggle_use_cases: open_edx - # .. toggle_creation_date: 2021-08-17 - # .. toggle_tickets: https://github.com/openedx/edx-platform/pull/27411 - 'ENABLE_CONTENT_LIBRARIES_LTI_TOOL': False, +ENABLE_DISCUSSION_HOME_PANEL = True - # Milestones application flag - 'MILESTONES_APP': False, +ENABLE_COUNTRY_ACCESS = False +ENABLE_CREDIT_API = False - # Prerequisite courses feature flag - 'ENABLE_PREREQUISITE_COURSES': False, +### ORA Feature Flags ### - # Toggle course entrance exams feature - 'ENTRANCE_EXAMS': False, +# .. toggle_name: settings.DEPRECATE_OLD_COURSE_KEYS_IN_STUDIO +# .. toggle_implementation: DjangoSetting +# .. toggle_default: True +# .. toggle_description: Warn about removing support for deprecated course keys. +# To enable, set to True. +# To disable, set to False. +# To enable with a custom support deadline, set to an ISO-8601 date string: +# eg: '2020-09-01' +# .. toggle_use_cases: temporary +# .. toggle_creation_date: 2020-06-12 +# .. toggle_target_removal_date: 2021-04-01 +# .. toggle_warning: This can be removed once support is removed for deprecated +# course keys. +# .. toggle_tickets: https://openedx.atlassian.net/browse/DEPR-58 +DEPRECATE_OLD_COURSE_KEYS_IN_STUDIO = True - # Toggle platform-wide course licensing - 'LICENSING': False, +# .. toggle_name: settings.DISABLE_COURSE_CREATION +# .. toggle_implementation: DjangoSetting +# .. toggle_default: False +# .. toggle_description: If set to True, it disables the course creation functionality and hides the "New Course" +# button in studio. +# It is important to note that the value of this flag only affects if the user doesn't have a staff role, +# otherwise the course creation functionality will work as it should. +# .. toggle_use_cases: open_edx +# .. toggle_creation_date: 2013-12-02 +# .. toggle_warning: Another toggle DISABLE_LIBRARY_CREATION overrides DISABLE_COURSE_CREATION, if present. +DISABLE_COURSE_CREATION = False - # Enable the courseware search functionality - 'ENABLE_COURSEWARE_INDEX': False, +# .. toggle_name: settings.ENABLE_LTI_PII_ACKNOWLEDGEMENT +# .. toggle_implementation: DjangoSetting +# .. toggle_default: False +# .. toggle_description: Enables the lti pii acknowledgement feature for a course +# .. toggle_use_cases: open_edx +# .. toggle_creation_date: 2023-10 +# .. toggle_target_removal_date: None +# .. toggle_tickets: 'https://2u-internal.atlassian.net/browse/MST-2055' +ENABLE_LTI_PII_ACKNOWLEDGEMENT = False - # Enable content libraries (modulestore) search functionality - 'ENABLE_LIBRARY_INDEX': False, +# .. toggle_name: settings.DISABLE_ADVANCED_SETTINGS +# .. toggle_implementation: DjangoSetting +# .. toggle_default: False +# .. toggle_description: Set to `True` to disable the advanced settings page in Studio for all users except those +# having `is_superuser` or `is_staff` set to `True`. +# .. toggle_use_cases: open_edx +# .. toggle_creation_date: 2023-03-31 +# .. toggle_tickets: https://github.com/openedx/edx-platform/pull/32015 +DISABLE_ADVANCED_SETTINGS = False - # .. toggle_name: FEATURES['ALLOW_COURSE_RERUNS'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: True - # .. toggle_description: This will allow staff member to re-run the course from the studio home page and will - # always use the split modulestore. When this is set to False, the Re-run Course link will not be available on - # the studio home page. - # .. toggle_use_cases: open_edx - # .. toggle_creation_date: 2015-02-13 - # .. toggle_tickets: https://github.com/openedx/edx-platform/pull/6965 - 'ALLOW_COURSE_RERUNS': True, +# .. toggle_name: settings.ENABLE_SEND_XBLOCK_LIFECYCLE_EVENTS_OVER_BUS +# .. toggle_implementation: DjangoSetting +# .. toggle_default: False +# .. toggle_description: Enables sending xblock lifecycle events over the event bus. Used to create the +# EVENT_BUS_PRODUCER_CONFIG setting +# .. toggle_use_cases: opt_in +# .. toggle_creation_date: 2023-10-10 +# .. toggle_target_removal_date: 2023-10-12 +# .. toggle_warning: The default may be changed in a later release. See +# https://github.com/openedx/openedx-events/issues/265 +# .. toggle_tickets: https://github.com/edx/edx-arch-experiments/issues/381 +ENABLE_SEND_XBLOCK_LIFECYCLE_EVENTS_OVER_BUS = False - # Certificates Web/HTML Views - 'CERTIFICATES_HTML_VIEW': False, +# .. toggle_name: settings.ENABLE_HIDE_FROM_TOC_UI +# .. toggle_implementation: DjangoSetting +# .. toggle_default: False +# .. toggle_description: When enabled, exposes hide_from_toc xblock attribute so course authors can configure it as +# a section visibility option in Studio. +# .. toggle_use_cases: open_edx +# .. toggle_creation_date: 2024-02-29 +# .. toggle_tickets: https://github.com/openedx/edx-platform/pull/33952 +ENABLE_HIDE_FROM_TOC_UI = False - # Teams feature - 'ENABLE_TEAMS': True, - - # Show video bumper in Studio - 'ENABLE_VIDEO_BUMPER': False, - - # How many seconds to show the bumper again, default is 7 days: - 'SHOW_BUMPER_PERIODICITY': 7 * 24 * 3600, - - # Enable credit eligibility feature - 'ENABLE_CREDIT_ELIGIBILITY': ENABLE_CREDIT_ELIGIBILITY, - - # Special Exams, aka Timed and Proctored Exams - 'ENABLE_SPECIAL_EXAMS': False, - - # Show the language selector in the header - 'SHOW_HEADER_LANGUAGE_SELECTOR': False, - - # At edX it's safe to assume that English transcripts are always available - # This is not the case for all installations. - # The default value in {lms,cms}/envs/common.py and xmodule/tests/test_video.py should be consistent. - 'FALLBACK_TO_ENGLISH_TRANSCRIPTS': True, - - # Set this to False to facilitate cleaning up invalid xml from your modulestore. - 'ENABLE_XBLOCK_XML_VALIDATION': True, - - # Allow public account creation - 'ALLOW_PUBLIC_ACCOUNT_CREATION': True, - - # Allow showing the registration links - 'SHOW_REGISTRATION_LINKS': True, - - # Whether or not the dynamic EnrollmentTrackUserPartition should be registered. - 'ENABLE_ENROLLMENT_TRACK_USER_PARTITION': True, - - 'ENABLE_PASSWORD_RESET_FAILURE_EMAIL': False, - - # Whether archived courses (courses with end dates in the past) should be - # shown in Studio in a separate list. - 'ENABLE_SEPARATE_ARCHIVED_COURSES': True, - - # For acceptance and load testing - 'AUTOMATIC_AUTH_FOR_TESTING': False, - - # Prevent auto auth from creating superusers or modifying existing users - 'RESTRICT_AUTOMATIC_AUTH': True, - - 'ENABLE_GRADE_DOWNLOADS': True, - 'ENABLE_MKTG_SITE': False, - 'ENABLE_DISCUSSION_HOME_PANEL': True, - 'ENABLE_CORS_HEADERS': False, - 'ENABLE_CROSS_DOMAIN_CSRF_COOKIE': False, - 'ENABLE_COUNTRY_ACCESS': False, - 'ENABLE_CREDIT_API': False, - 'ENABLE_OAUTH2_PROVIDER': False, - 'ENABLE_MOBILE_REST_API': False, - 'CUSTOM_COURSES_EDX': False, - 'ENABLE_READING_FROM_MULTIPLE_HISTORY_TABLES': True, - 'SHOW_FOOTER_LANGUAGE_SELECTOR': False, - 'ENABLE_ENROLLMENT_RESET': False, - # Settings for course import olx validation - 'ENABLE_COURSE_OLX_VALIDATION': False, - # .. toggle_name: FEATURES['DISABLE_MOBILE_COURSE_AVAILABLE'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: False - # .. toggle_description: Set to True to remove Mobile Course Available UI Flag from Studio's Advanced Settings - # page else Mobile Course Available UI Flag will be available on Studio side. - # .. toggle_use_cases: open_edx - # .. toggle_creation_date: 2020-02-14 - # .. toggle_tickets: https://github.com/openedx/edx-platform/pull/23073 - 'DISABLE_MOBILE_COURSE_AVAILABLE': False, - - # .. toggle_name: FEATURES['ENABLE_CHANGE_USER_PASSWORD_ADMIN'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: False - # .. toggle_description: Set to True to enable changing a user password through django admin. This is disabled by - # default because enabling allows a method to bypass password policy. - # .. toggle_use_cases: open_edx - # .. toggle_creation_date: 2020-02-21 - # .. toggle_tickets: 'https://github.com/openedx/edx-platform/pull/21616' - 'ENABLE_CHANGE_USER_PASSWORD_ADMIN': False, - - ### ORA Feature Flags ### - # .. toggle_name: FEATURES['ENABLE_ORA_ALL_FILE_URLS'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: False - # .. toggle_description: A "work-around" feature toggle meant to help in cases where some file uploads are not - # discoverable. If enabled, will iterate through all possible file key suffixes up to the max for displaying file - # metadata in staff assessments. - # .. toggle_use_cases: temporary - # .. toggle_creation_date: 2020-03-03 - # .. toggle_target_removal_date: None - # .. toggle_tickets: https://openedx.atlassian.net/browse/EDUCATOR-4951 - # .. toggle_warning: This temporary feature toggle does not have a target removal date. - 'ENABLE_ORA_ALL_FILE_URLS': False, - - # .. toggle_name: FEATURES['ENABLE_ORA_USER_STATE_UPLOAD_DATA'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: False - # .. toggle_description: A "work-around" feature toggle meant to help in cases where some file uploads are not - # discoverable. If enabled, will pull file metadata from StudentModule.state for display in staff assessments. - # .. toggle_use_cases: temporary - # .. toggle_creation_date: 2020-03-03 - # .. toggle_target_removal_date: None - # .. toggle_tickets: https://openedx.atlassian.net/browse/EDUCATOR-4951 - # .. toggle_warning: This temporary feature toggle does not have a target removal date. - 'ENABLE_ORA_USER_STATE_UPLOAD_DATA': False, - - # .. toggle_name: FEATURES['DEPRECATE_OLD_COURSE_KEYS_IN_STUDIO'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: True - # .. toggle_description: Warn about removing support for deprecated course keys. - # To enable, set to True. - # To disable, set to False. - # To enable with a custom support deadline, set to an ISO-8601 date string: - # eg: '2020-09-01' - # .. toggle_use_cases: temporary - # .. toggle_creation_date: 2020-06-12 - # .. toggle_target_removal_date: 2021-04-01 - # .. toggle_warning: This can be removed once support is removed for deprecated - # course keys. - # .. toggle_tickets: https://openedx.atlassian.net/browse/DEPR-58 - 'DEPRECATE_OLD_COURSE_KEYS_IN_STUDIO': True, - - # .. toggle_name: FEATURES['DISABLE_COURSE_CREATION'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: False - # .. toggle_description: If set to True, it disables the course creation functionality and hides the "New Course" - # button in studio. - # It is important to note that the value of this flag only affects if the user doesn't have a staff role, - # otherwise the course creation functionality will work as it should. - # .. toggle_use_cases: open_edx - # .. toggle_creation_date: 2013-12-02 - # .. toggle_warning: Another toggle DISABLE_LIBRARY_CREATION overrides DISABLE_COURSE_CREATION, if present. - 'DISABLE_COURSE_CREATION': False, - - # Can be turned off to disable the help link in the navbar - # .. toggle_name: FEATURES['ENABLE_HELP_LINK'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: True - # .. toggle_description: When True, a help link is displayed on the main navbar. Set False to hide it. - # .. toggle_use_cases: open_edx - # .. toggle_creation_date: 2021-03-05 - # .. toggle_tickets: https://github.com/openedx/edx-platform/pull/26106 - 'ENABLE_HELP_LINK': True, - - # .. toggle_name: FEATURES['ENABLE_INTEGRITY_SIGNATURE'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: False - # .. toggle_description: Whether to replace ID verification course/certificate requirement - # with an in-course Honor Code agreement - # (https://github.com/edx/edx-name-affirmation) - # .. toggle_use_cases: open_edx - # .. toggle_creation_date: 2022-02-15 - # .. toggle_target_removal_date: None - # .. toggle_tickets: 'https://openedx.atlassian.net/browse/MST-1348' - 'ENABLE_INTEGRITY_SIGNATURE': False, - - # .. toggle_name: FEATURES['ENABLE_LTI_PII_ACKNOWLEDGEMENT'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: False - # .. toggle_description: Enables the lti pii acknowledgement feature for a course - # .. toggle_use_cases: open_edx - # .. toggle_creation_date: 2023-10 - # .. toggle_target_removal_date: None - # .. toggle_tickets: 'https://2u-internal.atlassian.net/browse/MST-2055' - 'ENABLE_LTI_PII_ACKNOWLEDGEMENT': False, - - # .. toggle_name: MARK_LIBRARY_CONTENT_BLOCK_COMPLETE_ON_VIEW - # .. toggle_implementation: DjangoSetting - # .. toggle_default: False - # .. toggle_description: If enabled, the Library Content Block is marked as complete when users view it. - # Otherwise (by default), all children of this block must be completed. - # .. toggle_use_cases: open_edx - # .. toggle_creation_date: 2022-03-22 - # .. toggle_target_removal_date: None - # .. toggle_tickets: https://github.com/openedx/edx-platform/pull/28268 - # .. toggle_warning: For consistency in user-experience, keep the value in sync with the setting of the same name - # in the LMS and CMS. - 'MARK_LIBRARY_CONTENT_BLOCK_COMPLETE_ON_VIEW': False, - - # .. toggle_name: FEATURES['DISABLE_UNENROLLMENT'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: False - # .. toggle_description: Set to True to disable self-unenrollments via REST API. - # This also hides the "Unenroll" button on the Learner Dashboard. - # .. toggle_use_cases: open_edx - # .. toggle_creation_date: 2021-10-11 - # .. toggle_warning: For consistency in user experience, keep the value in sync with the setting of the same name - # in the LMS and CMS. - # .. toggle_tickets: 'https://github.com/open-craft/edx-platform/pull/429' - 'DISABLE_UNENROLLMENT': False, - - # .. toggle_name: FEATURES['DISABLE_ADVANCED_SETTINGS'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: False - # .. toggle_description: Set to `True` to disable the advanced settings page in Studio for all users except those - # having `is_superuser` or `is_staff` set to `True`. - # .. toggle_use_cases: open_edx - # .. toggle_creation_date: 2023-03-31 - # .. toggle_tickets: https://github.com/openedx/edx-platform/pull/32015 - 'DISABLE_ADVANCED_SETTINGS': False, - - # .. toggle_name: FEATURES['ENABLE_SEND_XBLOCK_LIFECYCLE_EVENTS_OVER_BUS'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: False - # .. toggle_description: Enables sending xblock lifecycle events over the event bus. Used to create the - # EVENT_BUS_PRODUCER_CONFIG setting - # .. toggle_use_cases: opt_in - # .. toggle_creation_date: 2023-10-10 - # .. toggle_target_removal_date: 2023-10-12 - # .. toggle_warning: The default may be changed in a later release. See - # https://github.com/openedx/openedx-events/issues/265 - # .. toggle_tickets: https://github.com/edx/edx-arch-experiments/issues/381 - 'ENABLE_SEND_XBLOCK_LIFECYCLE_EVENTS_OVER_BUS': False, - - # .. toggle_name: FEATURES['ENABLE_HIDE_FROM_TOC_UI'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: False - # .. toggle_description: When enabled, exposes hide_from_toc xblock attribute so course authors can configure it as - # a section visibility option in Studio. - # .. toggle_use_cases: open_edx - # .. toggle_creation_date: 2024-02-29 - # .. toggle_tickets: https://github.com/openedx/edx-platform/pull/33952 - 'ENABLE_HIDE_FROM_TOC_UI': False, - - # .. toggle_name: FEATURES['ENABLE_GRADING_METHOD_IN_PROBLEMS'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: False - # .. toggle_description: Enables the grading method feature in capa problems. - # .. toggle_use_cases: open_edx - # .. toggle_creation_date: 2024-03-22 - # .. toggle_tickets: https://github.com/openedx/edx-platform/pull/33911 - 'ENABLE_GRADING_METHOD_IN_PROBLEMS': False, - - # .. toggle_name: FEATURES['BADGES_ENABLED'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: False - # .. toggle_description: Set to True to enable the Badges feature. - # .. toggle_use_cases: open_edx - # .. toggle_creation_date: 2024-04-10 - 'BADGES_ENABLED': False, - - # .. toggle_name: FEATURES['IN_CONTEXT_DISCUSSION_ENABLED_DEFAULT'] - # .. toggle_implementation: DjangoSetting - # .. toggle_default: True - # .. toggle_description: Set to False to disable in-context discussion for units by default. - # .. toggle_use_cases: open_edx - # .. toggle_creation_date: 2024-09-02 - 'IN_CONTEXT_DISCUSSION_ENABLED_DEFAULT': True, -} +# .. toggle_name: settings.IN_CONTEXT_DISCUSSION_ENABLED_DEFAULT +# .. toggle_implementation: DjangoSetting +# .. toggle_default: True +# .. toggle_description: Set to False to disable in-context discussion for units by default. +# .. toggle_use_cases: open_edx +# .. toggle_creation_date: 2024-09-02 +IN_CONTEXT_DISCUSSION_ENABLED_DEFAULT = True # .. toggle_name: ENABLE_COPPA_COMPLIANCE # .. toggle_implementation: DjangoSetting @@ -508,22 +277,6 @@ MARKETING_EMAILS_OPT_IN = False ############################# MICROFRONTENDS ################################### COURSE_AUTHORING_MICROFRONTEND_URL = None -DISCUSSIONS_MICROFRONTEND_URL = None -DISCUSSIONS_MFE_FEEDBACK_URL = None -ACCOUNT_MICROFRONTEND_URL = None -LEARNING_MICROFRONTEND_URL = None - - -# .. toggle_name: ENABLE_DYNAMIC_REGISTRATION_FIELDS -# .. toggle_implementation: DjangoSetting -# .. toggle_default: False -# .. toggle_description: When enabled, this toggle adds fields configured in -# REGISTRATION_EXTRA_FIELDS to Authn MFE -# .. toggle_use_cases: temporary -# .. toggle_creation_date: 2022-04-21 -# .. toggle_target_removal_date: None -# .. toggle_tickets: https://openedx.atlassian.net/browse/VAN-838 -ENABLE_DYNAMIC_REGISTRATION_FIELDS = False ############################# SOCIAL MEDIA SHARING ############################# SOCIAL_SHARING_SETTINGS = { @@ -553,19 +306,9 @@ GEOIP_PATH = REPO_ROOT / "common/static/data/geoip/GeoLite2-Country.mmdb" DATA_DIR = COURSES_ROOT -DJFS = { - 'type': 'osfs', - 'directory_root': '/edx/var/edxapp/django-pyfs/static/django-pyfs', - 'url_root': '/static/django-pyfs', -} - ######################## BRANCH.IO ########################### BRANCH_IO_KEY = '' -######################## OPTIMIZELY ########################### -OPTIMIZELY_PROJECT_ID = None -OPTIMIZELY_FULLSTACK_SDK_KEY = None - ######################## GOOGLE ANALYTICS ########################### GOOGLE_ANALYTICS_ACCOUNT = None @@ -647,14 +390,10 @@ TEMPLATES = [ DEFAULT_TEMPLATE_ENGINE = TEMPLATES[0] #################################### AWS ####################################### -AWS_ACCESS_KEY_ID = None -AWS_SECRET_ACCESS_KEY = None AWS_SECURITY_TOKEN = None ############################################################################## -EDX_ROOT_URL = '' - # use the ratelimit backend to prevent brute force attacks AUTHENTICATION_BACKENDS = [ 'auth_backends.backends.EdXOAuth2', @@ -664,12 +403,6 @@ AUTHENTICATION_BACKENDS = [ 'bridgekeeper.backends.RulePermissionBackend', ] -# License for serving content in China -ICP_LICENSE = None -ICP_LICENSE_INFO = {} - -LOGGING_ENV = 'sandbox' - LMS_BASE = None # Use LMS SSO for login, once enabled by setting LOGIN_URL (see docs/guides/studio_oauth.rst) @@ -684,52 +417,17 @@ FRONTEND_REGISTER_URL = Derived(lambda settings: settings.LMS_ROOT_URL + '/regis ENTERPRISE_API_URL = Derived(lambda settings: settings.LMS_INTERNAL_ROOT_URL + '/enterprise/api/v1/') ENTERPRISE_CONSENT_API_URL = Derived(lambda settings: settings.LMS_INTERNAL_ROOT_URL + '/consent/api/v1/') -ENTERPRISE_MARKETING_FOOTER_QUERY_PARAMS = {} # Public domain name of Studio (should be resolvable from the end-user's browser) CMS_BASE = None CMS_ROOT_URL = None -LOG_DIR = '/edx/var/log/edx' - -LOCAL_LOGLEVEL = "INFO" - MAINTENANCE_BANNER_TEXT = 'Sample banner message' WIKI_ENABLED = True CERT_QUEUE = 'certificates' -ELASTIC_SEARCH_CONFIG = [ - { - 'use_ssl': False, - 'host': 'localhost', - 'port': 9200 - } -] - -######################### CSRF ######################################### - -# Forwards-compatibility with Django 1.7 -CSRF_COOKIE_AGE = 60 * 60 * 24 * 7 * 52 -# It is highly recommended that you override this in any environment accessed by -# end users -CSRF_COOKIE_SECURE = False - -CROSS_DOMAIN_CSRF_COOKIE_DOMAIN = '' -CROSS_DOMAIN_CSRF_COOKIE_NAME = '' - -#################### CAPA External Code Evaluation ############################# -XQUEUE_WAITTIME_BETWEEN_REQUESTS = 5 # seconds -XQUEUE_INTERFACE = { - 'url': 'http://localhost:18040', - 'basic_auth': ['edx', 'edx'], - 'django_auth': { - 'username': 'lms', - 'password': 'password' - } -} - ################################# Middleware ################################### MIDDLEWARE = [ @@ -818,12 +516,6 @@ MIDDLEWARE = [ EXTRA_MIDDLEWARE_CLASSES = [] -# Clickjacking protection can be disabled by setting this to 'ALLOW' -X_FRAME_OPTIONS = 'DENY' - -# Platform for Privacy Preferences header -P3P_HEADER = 'CP="Open EdX does not have a P3P policy."' - ############# XBlock Configuration ########## # Import after sys.path fixup @@ -848,30 +540,11 @@ XBLOCK_MIXINS = ( AuthoringMixin, ) -# .. setting_name: XBLOCK_EXTRA_MIXINS -# .. setting_default: () -# .. setting_description: Custom mixins that will be dynamically added to every XBlock and XBlockAside instance. -# These can be classes or dotted-path references to classes. -# For example: `XBLOCK_EXTRA_MIXINS = ('my_custom_package.my_module.MyCustomMixin',)` -XBLOCK_EXTRA_MIXINS = () - -# Paths to wrapper methods which should be applied to every XBlock's FieldData. -XBLOCK_FIELD_DATA_WRAPPERS = () - -# .. setting_name: XBLOCK_RUNTIME_V2_EPHEMERAL_DATA_CACHE -# .. setting_default: default -# .. setting_description: The django cache key of the cache to use for storing anonymous user state for XBlocks. -XBLOCK_RUNTIME_V2_EPHEMERAL_DATA_CACHE = 'default' - ############################ ORA 2 ############################################ # By default, don't use a file prefix ORA2_FILE_PREFIX = 'default_env-default_deployment/ora2' -# Default File Upload Storage bucket and prefix. Used by the FileUpload Service. -FILE_UPLOAD_STORAGE_BUCKET_NAME = 'SET-ME-PLEASE (ex. bucket-name)' -FILE_UPLOAD_STORAGE_PREFIX = 'submissions_attachments' - ############################ Modulestore Configuration ################################ DOC_STORE_CONFIG = { @@ -949,43 +622,6 @@ MODULESTORE = { # require student context. MODULESTORE_FIELD_OVERRIDE_PROVIDERS = () -DATABASES = { - # edxapp's edxapp-migrate scripts and the edxapp_migrate play - # will ensure that any DB not named read_replica will be migrated - # for both the lms and cms. - 'default': { - 'ATOMIC_REQUESTS': True, - 'CONN_MAX_AGE': 0, - 'ENGINE': 'django.db.backends.mysql', - 'HOST': '127.0.0.1', - 'NAME': 'edxapp', - 'OPTIONS': {}, - 'PASSWORD': 'password', - 'PORT': '3306', - 'USER': 'edxapp001' - }, - 'read_replica': { - 'CONN_MAX_AGE': 0, - 'ENGINE': 'django.db.backends.mysql', - 'HOST': '127.0.0.1', - 'NAME': 'edxapp', - 'OPTIONS': {}, - 'PASSWORD': 'password', - 'PORT': '3306', - 'USER': 'edxapp001' - }, - 'student_module_history': { - 'CONN_MAX_AGE': 0, - 'ENGINE': 'django.db.backends.mysql', - 'HOST': '127.0.0.1', - 'NAME': 'edxapp_csmh', - 'OPTIONS': {}, - 'PASSWORD': 'password', - 'PORT': '3306', - 'USER': 'edxapp001' - } -} - DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' DEFAULT_HASHING_ALGORITHM = 'sha256' @@ -1017,49 +653,8 @@ CODE_JAIL = { "limit_overrides": {}, } -# Some courses are allowed to run unsafe code. This is a list of regexes, one -# of them must match the course id for that course to run unsafe code. -# -# For example: -# -# COURSES_WITH_UNSAFE_CODE = [ -# r"Harvard/XY123.1/.*" -# ] - -COURSES_WITH_UNSAFE_CODE = [] - -# Cojail REST service -ENABLE_CODEJAIL_REST_SERVICE = False -# .. setting_name: CODE_JAIL_REST_SERVICE_REMOTE_EXEC -# .. setting_default: 'xmodule.capa.safe_exec.remote_exec.send_safe_exec_request_v0' -# .. setting_description: Set the python package.module.function that is reponsible of -# calling the remote service in charge of jailed code execution -CODE_JAIL_REST_SERVICE_REMOTE_EXEC = 'xmodule.capa.safe_exec.remote_exec.send_safe_exec_request_v0' -# .. setting_name: CODE_JAIL_REST_SERVICE_HOST -# .. setting_default: 'http://127.0.0.1:8550' -# .. setting_description: Set the codejail remote service host -CODE_JAIL_REST_SERVICE_HOST = 'http://127.0.0.1:8550' -# .. setting_name: CODE_JAIL_REST_SERVICE_CONNECT_TIMEOUT -# .. setting_default: 0.5 -# .. setting_description: Set the number of seconds CMS will wait to establish an internal -# connection to the codejail remote service. -CODE_JAIL_REST_SERVICE_CONNECT_TIMEOUT = 0.5 # time in seconds -# .. setting_name: CODE_JAIL_REST_SERVICE_READ_TIMEOUT -# .. setting_default: 3.5 -# .. setting_description: Set the number of seconds CMS will wait for a response from the -# codejail remote service endpoint. -CODE_JAIL_REST_SERVICE_READ_TIMEOUT = 3.5 # time in seconds - ############################ DJANGO_BUILTINS ################################ -# Change DEBUG in your environment settings files, not here -SESSION_COOKIE_SECURE = False -SESSION_SAVE_EVERY_REQUEST = False -SESSION_SERIALIZER = 'openedx.core.lib.session_serializers.PickleSerializer' -SESSION_COOKIE_NAME = 'sessionid' -# Site info -SITE_NAME = "localhost" -HTTPS = 'on' ROOT_URLCONF = 'cms.urls' COURSE_IMPORT_EXPORT_BUCKET = '' @@ -1067,12 +662,10 @@ COURSE_METADATA_EXPORT_BUCKET = '' ALTERNATE_WORKER_QUEUES = 'lms' -X_FRAME_OPTIONS = 'DENY' - # .. setting_name: GIT_REPO_EXPORT_DIR # .. setting_default: '/edx/var/edxapp/export_course_repos' # .. setting_description: When courses are exported to git, either with the export_git management command or the git -# export view from the studio (when FEATURES['ENABLE_EXPORT_GIT'] is True), they are stored in this directory, which +# export view from the studio (when settings.ENABLE_EXPORT_GIT is True), they are stored in this directory, which # must exist at the time of the export. GIT_REPO_EXPORT_DIR = '/edx/var/edxapp/export_course_repos' # .. setting_name: GIT_EXPORT_DEFAULT_IDENT @@ -1094,11 +687,6 @@ BUGS_EMAIL = 'bugs@example.com' SERVER_EMAIL = 'devops@example.com' UNIVERSITY_EMAIL = 'university@example.com' PRESS_EMAIL = 'press@example.com' -ADMINS = [] -MANAGERS = ADMINS - -# Initialize to 'release', but read from JSON in production.py -EDX_PLATFORM_REVISION = 'release' # Static content STATIC_URL = '/static/studio/' @@ -1113,32 +701,10 @@ STATICFILES_DIRS = [ XMODULE_ROOT / "static", ] -# Locale/Internationalization -CELERY_TIMEZONE = 'UTC' -TIME_ZONE = 'UTC' -LANGUAGE_CODE = 'en' # http://www.i18nguy.com/unicode/language-identifiers.html - -# Languages supported for custom course certificate templates -CERTIFICATE_TEMPLATE_LANGUAGES = { - 'en': 'English', - 'es': 'EspaƱol', -} - -USE_I18N = True -USE_L10N = True - -STATICI18N_FILENAME_FUNCTION = 'statici18n.utils.legacy_filename' -STATICI18N_ROOT = PROJECT_ROOT / "static" - -# Messages -MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage' - COURSE_IMPORT_EXPORT_STORAGE = 'django.core.files.storage.FileSystemStorage' COURSE_METADATA_EXPORT_STORAGE = 'django.core.files.storage.FileSystemStorage' - -##### EMBARGO ##### -EMBARGO_SITE_REDIRECT_URL = None +STATICI18N_ROOT = PROJECT_ROOT / "static" ##### custom vendor plugin variables ##### @@ -1293,10 +859,6 @@ STATICFILES_IGNORE_PATTERNS = ( ################################# DJANGO-REQUIRE ############################### - -# The baseUrl to pass to the r.js optimizer, relative to STATIC_ROOT. -REQUIRE_BASE_URL = "./" - # The name of a build profile to use for your project, relative to REQUIRE_BASE_URL. # A sensible value would be 'app.build.js'. Leave blank to use the built-in default build profile. # Set to False to disable running the default profile (e.g. if only using it to build Standalone @@ -1306,9 +868,6 @@ REQUIRE_BUILD_PROFILE = "cms/js/build.js" # The name of the require.js script used by your project, relative to REQUIRE_BASE_URL. REQUIRE_JS = "js/vendor/requiresjs/require.js" -# Whether to run django-require in debug mode. -REQUIRE_DEBUG = False - ########################## DJANGO WEBPACK LOADER ############################## WEBPACK_LOADER = { @@ -1322,7 +881,6 @@ WEBPACK_LOADER = { } } - ############################ SERVICE_VARIANT ################################## # SERVICE_VARIANT specifies name of the variant used, which decides what JSON @@ -1337,30 +895,6 @@ CONFIG_PREFIX = SERVICE_VARIANT + "." if SERVICE_VARIANT else "" ################################# CELERY ###################################### -# Message configuration - -CELERY_TASK_SERIALIZER = 'json' -CELERY_RESULT_SERIALIZER = 'json' - -CELERY_MESSAGE_COMPRESSION = 'gzip' - -# Results configuration - -CELERY_IGNORE_RESULT = False -CELERY_STORE_ERRORS_EVEN_IF_IGNORED = True - -# Events configuration - -CELERY_TRACK_STARTED = True - -CELERY_SEND_EVENTS = True -CELERY_SEND_TASK_SENT_EVENT = True - -# Exchange configuration - -CELERY_DEFAULT_EXCHANGE = 'edx.core' -CELERY_DEFAULT_EXCHANGE_TYPE = 'direct' - # Name the exchange and queues for each variant QUEUE_VARIANT = CONFIG_PREFIX.lower() @@ -1382,10 +916,6 @@ CELERY_QUEUES = { # Queues configuration -CELERY_QUEUE_HA_POLICY = 'all' - -CELERY_CREATE_MISSING_QUEUES = True - CLEAR_REQUEST_CACHE_ON_TASK_COMPLETION = True BROKER_USE_SSL = Derived(lambda settings: settings.CELERY_BROKER_USE_SSL) @@ -1398,27 +928,6 @@ HEARTBEAT_CELERY_ROUTING_KEY = HIGH_PRIORITY_QUEUE ############################## Video ########################################## -YOUTUBE = { - # YouTube JavaScript API - 'API': 'https://www.youtube.com/iframe_api', - - 'TEST_TIMEOUT': 1500, - - # URL to get YouTube metadata - 'METADATA_URL': 'https://www.googleapis.com/youtube/v3/videos', - - # Web page mechanism for scraping transcript information from youtube video pages - 'TRANSCRIPTS': { - 'CAPTION_TRACKS_REGEX': r"captionTracks\"\:\[(?P[^\]]+)", - 'YOUTUBE_URL_BASE': 'https://www.youtube.com/watch?v=', - 'ALLOWED_LANGUAGE_CODES': ["en", "en-US", "en-GB"], - }, - - 'IMAGE_API': 'http://img.youtube.com/vi/{youtube_id}/0.jpg', # /maxresdefault.jpg for 1920*1080 -} - -YOUTUBE_API_KEY = 'PUT_YOUR_API_KEY_HERE' - # Additional languages that should be supported for video transcripts, not included in ALL_LANGUAGES EXTENDED_VIDEO_TRANSCRIPT_LANGUAGES = [] @@ -1698,17 +1207,8 @@ INSTALLED_APPS = [ ################# EDX MARKETING SITE ################################## -EDXMKTG_LOGGED_IN_COOKIE_NAME = 'edxloggedin' -EDXMKTG_USER_INFO_COOKIE_NAME = 'edx-user-info' -EDXMKTG_USER_INFO_COOKIE_VERSION = 1 +MKTG_URL_LINK_MAP = {} -MKTG_URLS = {} -MKTG_URL_OVERRIDES = {} -MKTG_URL_LINK_MAP = { - -} - -SUPPORT_SITE_LINK = '' ID_VERIFICATION_SUPPORT_LINK = '' PASSWORD_RESET_SUPPORT_LINK = '' ACTIVATION_EMAIL_SUPPORT_LINK = '' @@ -1716,7 +1216,6 @@ LOGIN_ISSUE_SUPPORT_LINK = '' ############################## EVENT TRACKING ################################# -CMS_SEGMENT_KEY = None TRACK_MAX_EVENT = 50000 TRACKING_BACKENDS = { @@ -1776,11 +1275,6 @@ EVENT_TRACKING_PROCESSORS = [] EVENT_TRACKING_SEGMENTIO_EMIT_WHITELIST = [] -#### PASSWORD POLICY SETTINGS ##### -PASSWORD_POLICY_COMPLIANCE_ROLLOUT_CONFIG = { - 'ENFORCE_COMPLIANCE_ON_LOGIN': False -} - ##### ACCOUNT LOCKOUT DEFAULT PARAMETERS ##### MAX_FAILED_LOGIN_ATTEMPTS_ALLOWED = 6 MAX_FAILED_LOGIN_ATTEMPTS_LOCKOUT_PERIOD_SECS = 30 * 60 @@ -1835,15 +1329,6 @@ for app_name, insert_before in OPTIONAL_APPS: except (IndexError, ValueError): INSTALLED_APPS.append(app_name) - -### External auth usage -- prefixes for ENROLLMENT_DOMAIN -SHIBBOLETH_DOMAIN_PREFIX = 'shib:' - -# Set request limits for maximum size of a request body and maximum number of GET/POST parameters. (>=Django 1.10) -# Limits are currently disabled - but can be used for finer-grained denial-of-service protection. -DATA_UPLOAD_MAX_MEMORY_SIZE = None -DATA_UPLOAD_MAX_NUMBER_FIELDS = None - ### Size of chunks into which asset uploads will be divided UPLOAD_CHUNK_SIZE_IN_MB = 10 @@ -1854,9 +1339,6 @@ MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB = 20 # a file that exceeds the above size MAX_ASSET_UPLOAD_FILE_SIZE_URL = "" -### Default value for entrance exam minimum score -ENTRANCE_EXAM_MIN_SCORE_PCT = 50 - ### Default language for a new course DEFAULT_COURSE_LANGUAGE = "en" @@ -1886,12 +1368,6 @@ LIBRARY_BLOCK_TYPES = [ ############### Settings for Retirement ##################### # See annotations in lms/envs/common.py for details. -RETIRED_USERNAME_PREFIX = 'retired__user_' -# See annotations in lms/envs/common.py for details. -RETIRED_EMAIL_PREFIX = 'retired__user_' -# See annotations in lms/envs/common.py for details. -RETIRED_EMAIL_DOMAIN = 'retired.invalid' -# See annotations in lms/envs/common.py for details. RETIRED_USERNAME_FMT = Derived(lambda settings: settings.RETIRED_USERNAME_PREFIX + '{}') # See annotations in lms/envs/common.py for details. RETIRED_EMAIL_FMT = Derived(lambda settings: settings.RETIRED_EMAIL_PREFIX + '{}@' + settings.RETIRED_EMAIL_DOMAIN) @@ -1900,39 +1376,6 @@ RETIRED_USER_SALTS = ['abc', '123'] # See annotations in lms/envs/common.py for details. RETIREMENT_SERVICE_WORKER_USERNAME = 'RETIREMENT_SERVICE_USER' -# These states are the default, but are designed to be overridden in configuration. -# See annotations in lms/envs/common.py for details. -RETIREMENT_STATES = [ - 'PENDING', - - 'LOCKING_ACCOUNT', - 'LOCKING_COMPLETE', - - # Use these states only when ENABLE_DISCUSSION_SERVICE is True. - 'RETIRING_FORUMS', - 'FORUMS_COMPLETE', - - # TODO - Change these states to be the LMS-only email opt-out - PLAT-2189 - 'RETIRING_EMAIL_LISTS', - 'EMAIL_LISTS_COMPLETE', - - 'RETIRING_ENROLLMENTS', - 'ENROLLMENTS_COMPLETE', - - # Use these states only when ENABLE_STUDENT_NOTES is True. - 'RETIRING_NOTES', - 'NOTES_COMPLETE', - - 'RETIRING_LMS', - 'LMS_COMPLETE', - - 'ERRORED', - 'ABORTED', - 'COMPLETE', -] - -USERNAME_REPLACEMENT_WORKER = "REPLACE WITH VALID USERNAME" - # Files and Uploads type filter values FILES_AND_UPLOAD_TYPE_FILTERS = { @@ -1972,182 +1415,28 @@ FILES_AND_UPLOAD_TYPE_FILTERS = { 'text/coffeescript', 'text/x-matlab', 'application/sql', 'text/php', ] } -# Default to no Search Engine -SEARCH_ENGINE = None ELASTIC_FIELD_MAPPINGS = { "start_date": { "type": "date" } } -XBLOCK_SETTINGS = {} XBLOCK_FS_STORAGE_BUCKET = None XBLOCK_FS_STORAGE_PREFIX = None STUDIO_FRONTEND_CONTAINER_URL = None -################################ Settings for Credit Course Requirements ################################ -# Initial delay used for retrying tasks. -# Additional retries use longer delays. -# Value is in seconds. -CREDIT_TASK_DEFAULT_RETRY_DELAY = 30 - -# Maximum number of retries per task for errors that are not related -# to throttling. -CREDIT_TASK_MAX_RETRIES = 5 - -# Maximum age in seconds of timestamps we will accept -# when a credit provider notifies us that a student has been approved -# or denied for credit. -CREDIT_PROVIDER_TIMESTAMP_EXPIRATION = 15 * 60 - -CREDIT_PROVIDER_SECRET_KEYS = {} - -# .. setting_name: COMPREHENSIVE_THEME_DIRS -# .. setting_default: [] -# .. setting_description: A list of paths to directories, each of which will -# be searched for comprehensive themes. Do not override this Django setting directly. -# Instead, set the COMPREHENSIVE_THEME_DIRS environment variable, using colons (:) to -# separate paths. -COMPREHENSIVE_THEME_DIRS = os.environ.get("COMPREHENSIVE_THEME_DIRS", "").split(":") - -# .. setting_name: DEFAULT_SITE_THEME -# .. setting_default: None -# .. setting_description: See LMS annotation. -DEFAULT_SITE_THEME = None - -# .. toggle_name: ENABLE_COMPREHENSIVE_THEMING -# .. toggle_implementation: DjangoSetting -# .. toggle_default: False -# .. toggle_description: See LMS annotation. -# .. toggle_use_cases: open_edx -# .. toggle_creation_date: 2016-06-30 -ENABLE_COMPREHENSIVE_THEMING = False - -# .. setting_name: CUSTOM_RESOURCE_TEMPLATES_DIRECTORY -# .. setting_default: None -# .. setting_description: Path to an existing directory of YAML files containing -# html content to be used with the subclasses of xmodule.x_module.ResourceTemplates. -# Default example templates can be found in xmodule/templates/html. -# Note that the extension used is ".yaml" and not ".yml". -# See xmodule.x_module.ResourceTemplates for usage. -# "CUSTOM_RESOURCE_TEMPLATES_DIRECTORY" : null -CUSTOM_RESOURCE_TEMPLATES_DIRECTORY = None - ############################ Global Database Configuration ##################### DATABASE_ROUTERS = [ 'openedx.core.lib.django_courseware_routers.StudentModuleHistoryExtendedRouter', ] -############################ Cache Configuration ############################### - -CACHES = { - 'course_structure_cache': { - 'KEY_PREFIX': 'course_structure', - 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', - 'LOCATION': ['localhost:11211'], - 'TIMEOUT': '604800', # 1 week - 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', - 'OPTIONS': { - 'no_delay': True, - 'ignore_exc': True, - 'use_pooling': True, - 'connect_timeout': 0.5 - } - }, - 'celery': { - 'KEY_PREFIX': 'celery', - 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', - 'LOCATION': ['localhost:11211'], - 'TIMEOUT': '7200', - 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', - 'OPTIONS': { - 'no_delay': True, - 'ignore_exc': True, - 'use_pooling': True, - 'connect_timeout': 0.5 - } - }, - 'mongo_metadata_inheritance': { - 'KEY_PREFIX': 'mongo_metadata_inheritance', - 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', - 'LOCATION': ['localhost:11211'], - 'TIMEOUT': 300, - 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', - 'OPTIONS': { - 'no_delay': True, - 'ignore_exc': True, - 'use_pooling': True, - 'connect_timeout': 0.5 - } - }, - 'staticfiles': { - 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', - 'LOCATION': ['localhost:11211'], - 'KEY_PREFIX': 'staticfiles_general', - 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', - 'OPTIONS': { - 'no_delay': True, - 'ignore_exc': True, - 'use_pooling': True, - 'connect_timeout': 0.5 - } - }, - 'default': { - 'VERSION': '1', - 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', - 'LOCATION': ['localhost:11211'], - 'KEY_PREFIX': 'default', - 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', - 'OPTIONS': { - 'no_delay': True, - 'ignore_exc': True, - 'use_pooling': True, - 'connect_timeout': 0.5 - } - }, - 'configuration': { - 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', - 'LOCATION': ['localhost:11211'], - 'KEY_PREFIX': 'configuration', - 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', - 'OPTIONS': { - 'no_delay': True, - 'ignore_exc': True, - 'use_pooling': True, - 'connect_timeout': 0.5 - } - }, - 'general': { - 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', - 'LOCATION': ['localhost:11211'], - 'KEY_PREFIX': 'general', - 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', - 'OPTIONS': { - 'no_delay': True, - 'ignore_exc': True, - 'use_pooling': True, - 'connect_timeout': 0.5 - } - }, -} - ############################ OAUTH2 Provider ################################### # 5 minute expiration time for JWT id tokens issued for external API requests. OAUTH_ID_TOKEN_EXPIRATION = 5 * 60 -# Partner support link for CMS footer -PARTNER_SUPPORT_EMAIL = '' - -# Affiliate cookie tracking -AFFILIATE_COOKIE_NAME = 'dev_affiliate_id' - -# API access management -API_ACCESS_FROM_EMAIL = 'api-requests@example.com' -API_ACCESS_MANAGER_EMAIL = 'api-access@example.com' - EDX_DRF_EXTENSIONS = { # Set this value to an empty dict in order to prevent automatically updating # user data from values in (possibly stale) JWTs. @@ -2157,66 +1446,17 @@ EDX_DRF_EXTENSIONS = { ############## Settings for Studio Context Sensitive Help ############## HELP_TOKENS_INI_FILE = REPO_ROOT / "cms" / "envs" / "help_tokens.ini" -HELP_TOKENS_LANGUAGE_CODE = Derived(lambda settings: settings.LANGUAGE_CODE) -HELP_TOKENS_VERSION = Derived(lambda settings: doc_version()) -HELP_TOKENS_BOOKS = { - 'learner': 'https://edx.readthedocs.io/projects/open-edx-learner-guide', - 'course_author': 'https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course', -} - -# Used with Email sending -RETRY_ACTIVATION_EMAIL_MAX_ATTEMPTS = 5 -RETRY_ACTIVATION_EMAIL_TIMEOUT = 0.5 - -# Software Secure request retry settings -# Time in seconds before a retry of the task should be 60 mints. -SOFTWARE_SECURE_REQUEST_RETRY_DELAY = 60 * 60 -# Maximum of 6 retries before giving up. -SOFTWARE_SECURE_RETRY_MAX_ATTEMPTS = 6 ############## DJANGO-USER-TASKS ############## # How long until database records about the outcome of a task and its artifacts get deleted? USER_TASKS_MAX_AGE = timedelta(days=7) -############## Settings for the Enterprise App ###################### - -ENTERPRISE_SERVICE_WORKER_USERNAME = 'enterprise_worker' -ENTERPRISE_API_CACHE_TIMEOUT = 3600 # Value is in seconds -# The default value of this needs to be a 16 character string -ENTERPRISE_CUSTOMER_CATALOG_DEFAULT_CONTENT_FILTER = {} - -# The setting key maps to the channel code (e.g. 'SAP' for success factors), Channel code is defined as -# part of django model of each integrated channel in edx-enterprise. -# The absence of a key/value pair translates to NO LIMIT on the number of "chunks" transmitted per cycle. -INTEGRATED_CHANNELS_API_CHUNK_TRANSMISSION_LIMIT = {} - -BASE_COOKIE_DOMAIN = 'localhost' - -############## Settings for the Discovery App ###################### - -COURSE_CATALOG_URL_ROOT = 'http://localhost:8008' -COURSE_CATALOG_API_URL = f'{COURSE_CATALOG_URL_ROOT}/api/v1' - -# which access.py permission name to check in order to determine if a course is visible in -# the course catalog. We default this to the legacy permission 'see_exists'. -COURSE_CATALOG_VISIBILITY_PERMISSION = 'see_exists' - -# which access.py permission name to check in order to determine if a course about page is -# visible. We default this to the legacy permission 'see_exists'. -COURSE_ABOUT_VISIBILITY_PERMISSION = 'see_exists' - -DEFAULT_COURSE_VISIBILITY_IN_CATALOG = "both" -DEFAULT_MOBILE_AVAILABLE = False - ############################# Persistent Grades #################################### # Queue to use for updating persistent grades RECALCULATE_GRADES_ROUTING_KEY = DEFAULT_PRIORITY_QUEUE -# Rate limit for regrading tasks that a grading policy change can kick off -POLICY_CHANGE_TASK_RATE_LIMIT = '900/h' - # .. setting_name: DEFAULT_GRADE_DESIGNATIONS # .. setting_default: ['A', 'B', 'C', 'D'] # .. setting_description: The default 'pass' grade cutoff designations to be used. The failure grade @@ -2257,24 +1497,8 @@ VIDEO_IMAGE_ASPECT_RATIO_TEXT = '16:9' VIDEO_IMAGE_ASPECT_RATIO_ERROR_MARGIN = 0.1 ###################### ZENDESK ###################### -ZENDESK_URL = '' ZENDESK_USER = '' ZENDESK_API_KEY = '' -ZENDESK_CUSTOM_FIELDS = {} -ZENDESK_OAUTH_ACCESS_TOKEN = '' -# A mapping of string names to Zendesk Group IDs -# To get the IDs of your groups you can go to -# {zendesk_url}/api/v2/groups.json -ZENDESK_GROUP_ID_MAPPING = {} - -############## Settings for Completion API ######################### - -# Once a user has watched this percentage of a video, mark it as complete: -# (0.0 = 0%, 1.0 = 100%) -COMPLETION_VIDEO_COMPLETE_PERCENTAGE = 0.95 - -############### Settings for edx-rbac ############### -SYSTEM_WIDE_ROLE_CLASSES = [] ############## Installed Django Apps ######################### @@ -2289,38 +1513,13 @@ add_plugins(__name__, ProjectType.CMS, SettingsType.COMMON) # See: https://docs.python.org/2/library/wsgiref.html#wsgiref.util.FileWrapper COURSE_EXPORT_DOWNLOAD_CHUNK_SIZE = 8192 -# E-Commerce API Configuration -ECOMMERCE_PUBLIC_URL_ROOT = 'http://localhost:8002' -ECOMMERCE_API_URL = 'http://localhost:8002/api/v2' -ECOMMERCE_API_SIGNING_KEY = 'SET-ME-PLEASE' - -CREDENTIALS_INTERNAL_SERVICE_URL = 'http://localhost:8005' -CREDENTIALS_PUBLIC_SERVICE_URL = 'http://localhost:8005' -CREDENTIALS_SERVICE_USERNAME = 'credentials_service_user' -# time between scheduled runs, in seconds -NOTIFY_CREDENTIALS_FREQUENCY = 14400 - -ANALYTICS_DASHBOARD_URL = 'http://localhost:18110/courses' -ANALYTICS_DASHBOARD_NAME = 'Your Platform Name Here Insights' - COMMENTS_SERVICE_URL = 'http://localhost:18080' COMMENTS_SERVICE_KEY = 'password' EXAMS_SERVICE_URL = 'http://localhost:18740/api/v1' EXAMS_SERVICE_USERNAME = 'edx_exams_worker' -FINANCIAL_REPORTS = { - 'STORAGE_TYPE': 'localfs', - 'BUCKET': None, - 'ROOT_PATH': 'sandbox', -} - ############# CORS headers for cross-domain requests ################# -if FEATURES.get('ENABLE_CORS_HEADERS'): - CORS_ALLOW_CREDENTIALS = True - CORS_ORIGIN_WHITELIST = () - CORS_ORIGIN_ALLOW_ALL = False - CORS_ALLOW_INSECURE = False # Set CORS_ALLOW_HEADERS regardless of whether we've enabled ENABLE_CORS_HEADERS # because that decision might happen in a later config file. (The headers to @@ -2331,10 +1530,6 @@ CORS_ALLOW_HEADERS = corsheaders_default_headers + ( 'content-disposition', ) -LOGIN_REDIRECT_WHITELIST = [] - -DEPRECATED_ADVANCED_COMPONENT_TYPES = [] - ########################## VIDEO IMAGE STORAGE ############################ VIDEO_IMAGE_SETTINGS = dict( @@ -2353,19 +1548,6 @@ VIDEO_IMAGE_SETTINGS = dict( VIDEO_IMAGE_MAX_AGE = 31536000 ########################## VIDEO TRANSCRIPTS STORAGE ############################ -VIDEO_TRANSCRIPTS_SETTINGS = dict( - VIDEO_TRANSCRIPTS_MAX_BYTES=3 * 1024 * 1024, # 3 MB - # Backend storage - # STORAGE_CLASS='storages.backends.s3boto3.S3Boto3Storage', - # STORAGE_KWARGS=dict(bucket='video-transcripts-bucket'), - STORAGE_KWARGS=dict( - location=MEDIA_ROOT, - ), - DIRECTORY_PREFIX='video-transcripts/', - BASE_URL=MEDIA_URL, -) - -VIDEO_TRANSCRIPTS_MAX_AGE = 31536000 TRANSCRIPT_LANG_CACHE_TIMEOUT = 60 * 60 * 24 @@ -2384,17 +1566,6 @@ BULK_EMAIL_DEFAULT_FROM_EMAIL = 'no-reply@example.com' # a bulk email message. BULK_EMAIL_LOG_SENT_EMAILS = False -############### Settings for django file storage ################## -STORAGES = { - 'default': { - 'BACKEND': 'django.core.files.storage.FileSystemStorage' - }, - 'staticfiles': { - 'BACKEND': 'openedx.core.storage.ProductionStorage' - } -} - - ###################### Grade Downloads ###################### # These keys are used for all of our asynchronous downloadable files, including # the ones that contain information other than grades. @@ -2409,59 +1580,21 @@ GRADES_DOWNLOAD = { } ############### Settings swift ##################################### -SWIFT_USERNAME = None -SWIFT_KEY = None -SWIFT_TENANT_ID = None -SWIFT_TENANT_NAME = None -SWIFT_AUTH_URL = None -SWIFT_AUTH_VERSION = None -SWIFT_REGION_NAME = None SWIFT_USE_TEMP_URLS = False -SWIFT_TEMP_URL_KEY = None -SWIFT_TEMP_URL_DURATION = 1800 # seconds - -############### The SAML private/public key values ################ -SOCIAL_AUTH_SAML_SP_PRIVATE_KEY = "" -SOCIAL_AUTH_SAML_SP_PUBLIC_CERT = "" -SOCIAL_AUTH_SAML_SP_PRIVATE_KEY_DICT = {} -SOCIAL_AUTH_SAML_SP_PUBLIC_CERT_DICT = {} ############### Settings for facebook ############################## FACEBOOK_APP_ID = 'FACEBOOK_APP_ID' FACEBOOK_APP_SECRET = 'FACEBOOK_APP_SECRET' FACEBOOK_API_VERSION = 'v2.1' -############### Settings for django-fernet-fields ################## -FERNET_KEYS = [ - 'DUMMY KEY CHANGE BEFORE GOING TO PRODUCTION', -] - -### Proctoring configuration (redirct URLs and keys shared between systems) #### -PROCTORING_BACKENDS = { - 'DEFAULT': 'null', - # The null key needs to be quoted because - # null is a language independent type in YAML - 'null': {} -} - +###################### PROCTORING SETTINGS ########################## PROCTORING_SETTINGS = {} ###################### LEARNER PORTAL ################################ LEARNER_PORTAL_URL_ROOT = 'https://learner-portal-localhost:18000' ############################ JWT ################################# -JWT_ISSUER = 'http://127.0.0.1:8000/oauth2' -DEFAULT_JWT_ISSUER = { - 'ISSUER': 'http://127.0.0.1:8000/oauth2', - 'AUDIENCE': 'SET-ME-PLEASE', - 'SECRET_KEY': 'SET-ME-PLEASE' -} -JWT_EXPIRATION = 30 -JWT_PRIVATE_SIGNING_KEY = None - -SYSLOG_SERVER = '' -FEEDBACK_SUBMISSION_EMAIL = '' REGISTRATION_EXTRA_FIELDS = { 'confirm_email': 'hidden', 'level_of_education': 'optional', @@ -2478,9 +1611,6 @@ REGISTRATION_EXTRA_FIELDS = { EDXAPP_PARSE_KEYS = {} PARSE_KEYS = {} -############################ AI_TRANSLATIONS ################################## -AI_TRANSLATIONS_API_URL = 'http://localhost:18760/api/v1' - ###################### DEPRECATED URLS ########################## # .. toggle_name: DISABLE_DEPRECATED_SIGNIN_URL @@ -2505,33 +1635,12 @@ DISABLE_DEPRECATED_SIGNIN_URL = False # .. toggle_tickets: ARCH-1253 DISABLE_DEPRECATED_SIGNUP_URL = False -##### LOGISTRATION RATE LIMIT SETTINGS ##### -LOGISTRATION_RATELIMIT_RATE = '100/5m' -LOGISTRATION_PER_EMAIL_RATELIMIT_RATE = '30/5m' -LOGISTRATION_API_RATELIMIT = '20/m' -LOGIN_AND_REGISTER_FORM_RATELIMIT = '100/5m' -RESET_PASSWORD_TOKEN_VALIDATE_API_RATELIMIT = '30/7d' -RESET_PASSWORD_API_RATELIMIT = '30/7d' - ##### REGISTRATION RATE LIMIT SETTINGS ##### -REGISTRATION_VALIDATION_RATELIMIT = '30/7d' -REGISTRATION_RATELIMIT = '60/7d' OPTIONAL_FIELD_API_RATELIMIT = '10/h' -##### PASSWORD RESET RATE LIMIT SETTINGS ##### -PASSWORD_RESET_IP_RATE = '1/m' -PASSWORD_RESET_EMAIL_RATE = '2/h' - ######################## Setting for content libraries ######################## MAX_BLOCKS_PER_CONTENT_LIBRARY = 100_000 -################# Student Verification ################# -VERIFY_STUDENT = { - "DAYS_GOOD_FOR": 365, # How many days is a verficiation good for? - # The variable represents the window within which a verification is considered to be "expiring soon." - "EXPIRING_SOON_WINDOW": 28, -} - ######################## Organizations ######################## # .. toggle_name: ORGANIZATIONS_AUTOCREATE @@ -2550,39 +1659,9 @@ VERIFY_STUDENT = { # .. toggle_tickets: https://github.com/openedx/edx-organizations/blob/master/docs/decisions/0001-phase-in-db-backed-organizations-to-all.rst ORGANIZATIONS_AUTOCREATE = True -################# Settings for brand logos. ################# -LOGO_IMAGE_EXTRA_TEXT = '' -LOGO_URL = None -LOGO_URL_PNG = None -LOGO_TRADEMARK_URL = None -FAVICON_URL = None -DEFAULT_EMAIL_LOGO_URL = 'https://edx-cdn.org/v3/default/logo.png' - -############## Settings for course import olx validation ############################ -COURSE_OLX_VALIDATION_STAGE = 1 -COURSE_OLX_VALIDATION_IGNORE_LIST = None - -################# show account activate cta after register ######################## -SHOW_ACTIVATE_CTA_POPUP_COOKIE_NAME = 'show-account-activation-popup' -SHOW_ACCOUNT_ACTIVATION_CTA = False - ################# Documentation links for course apps ################# -# pylint: disable=line-too-long -CALCULATOR_HELP_URL = "https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_calculator.html" -DISCUSSIONS_HELP_URL = "https://docs.openedx.org/en/latest/educators/concepts/communication/about_course_discussions.html" -EDXNOTES_HELP_URL = "https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/enable_notes.html" -PROGRESS_HELP_URL = "https://docs.openedx.org/en/latest/educators/references/data/progress_page.html" -TEAMS_HELP_URL = "https://docs.openedx.org/en/latest/educators/navigation/advanced_features.html#use-teams-in-your-course" -TEXTBOOKS_HELP_URL = "https://docs.openedx.org/en/latest/educators/how-tos/course_development/manage_textbooks.html" -WIKI_HELP_URL = "https://docs.openedx.org/en/latest/educators/concepts/communication/about_course_wiki.html" -CUSTOM_PAGES_HELP_URL = "https://docs.openedx.org/en/latest/educators/how-tos/course_development/manage_custom_page.html" COURSE_LIVE_HELP_URL = "https://docs.openedx.org/en/latest/educators/how-tos/course_development/add_course_live.html" -ORA_SETTINGS_HELP_URL = "https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/Manage_ORA_Assignment.html" -# pylint: enable=line-too-long - -# keys for big blue button live provider -COURSE_LIVE_GLOBAL_CREDENTIALS = {} ######################## Registration ######################## @@ -2593,9 +1672,6 @@ INACTIVE_USER_LOGIN = True # Redirect URL for inactive user. If not set, user will be redirected to /login after the login itself (loop) INACTIVE_USER_URL = f'http://{CMS_BASE}' -# String length for the configurable part of the auto-generated username -AUTO_GENERATED_USERNAME_RANDOM_STRING_LENGTH = 4 - ######################## Discussion Forum settings ######################## # Feedback link in upgraded discussion notification alert @@ -2606,20 +1682,11 @@ DISCUSSIONS_INCONTEXT_FEEDBACK_URL = '' DISCUSSIONS_INCONTEXT_LEARNMORE_URL = "https://docs.openedx.org/en/latest/educators/concepts/communication/about_course_discussions.html" # pylint: enable=line-too-long -#### django-simple-history## -# disable indexing on date field its coming django-simple-history. -SIMPLE_HISTORY_DATE_INDEX = False - #### Event bus producing #### def _should_send_xblock_events(settings): - return settings.FEATURES['ENABLE_SEND_XBLOCK_LIFECYCLE_EVENTS_OVER_BUS'] - - -def _should_send_learning_badge_events(settings): - return settings.FEATURES['BADGES_ENABLED'] - + return settings.ENABLE_SEND_XBLOCK_LIFECYCLE_EVENTS_OVER_BUS # .. setting_name: EVENT_BUS_PRODUCER_CONFIG # .. setting_default: all events disabled @@ -2673,13 +1740,13 @@ EVENT_BUS_PRODUCER_CONFIG = { "org.openedx.learning.course.passing.status.updated.v1": { "learning-badges-lifecycle": { "event_key_field": "course_passing_status.course.course_key", - "enabled": Derived(_should_send_learning_badge_events), + "enabled": Derived(should_send_learning_badge_events), }, }, "org.openedx.learning.ccx.course.passing.status.updated.v1": { "learning-badges-lifecycle": { "event_key_field": "course_passing_status.course.ccx_course_key", - "enabled": Derived(_should_send_learning_badge_events), + "enabled": Derived(should_send_learning_badge_events), }, }, } @@ -2689,8 +1756,6 @@ EVENT_BUS_PRODUCER_CONFIG = { # This affects the Authoring API swagger docs but not the legacy swagger docs under /api-docs/. REST_FRAMEWORK['DEFAULT_SCHEMA_CLASS'] = 'drf_spectacular.openapi.AutoSchema' -BEAMER_PRODUCT_ID = "" - ################### Studio Search (beta), using Meilisearch ################### # Enable Studio search features (powered by Meilisearch) (beta, off by default) @@ -2733,28 +1798,6 @@ LIBRARY_ENABLED_BLOCKS = [ 'word_cloud', ] -############## NOTIFICATIONS EXPIRY ############## -NOTIFICATIONS_EXPIRY = 60 -EXPIRED_NOTIFICATIONS_DELETE_BATCH_SIZE = 10000 -NOTIFICATION_CREATION_BATCH_SIZE = 76 -NOTIFICATIONS_DEFAULT_FROM_EMAIL = "no-reply@example.com" -NOTIFICATION_DIGEST_LOGO = DEFAULT_EMAIL_LOGO_URL - - -SOCIAL_MEDIA_FOOTER_ACE_URLS = { - 'reddit': 'http://www.reddit.com/r/edx', - 'twitter': 'https://twitter.com/edXOnline', - 'linkedin': 'http://www.linkedin.com/company/edx', - 'facebook': 'http://www.facebook.com/EdxOnline', -} - -SOCIAL_MEDIA_LOGO_URLS = { - 'reddit': 'http://email-media.s3.amazonaws.com/edX/2021/social_5_reddit.png', - 'twitter': 'http://email-media.s3.amazonaws.com/edX/2021/social_2_twitter.png', - 'linkedin': 'http://email-media.s3.amazonaws.com/edX/2021/social_3_linkedin.png', - 'facebook': 'http://email-media.s3.amazonaws.com/edX/2021/social_1_fb.png', -} - # .. setting_name: DEFAULT_ORG_LOGO_URL # .. setting_default: Derived(lambda settings: settings.STATIC_URL + 'images/logo.png') # .. setting_description: The default logo url for organizations that do not have a logo set. diff --git a/cms/envs/devstack.py b/cms/envs/devstack.py index bda53a366c..d683f48708 100644 --- a/cms/envs/devstack.py +++ b/cms/envs/devstack.py @@ -50,7 +50,7 @@ FRONTEND_REGISTER_URL = LMS_ROOT_URL + '/register' ################################## Video Pipeline Settings ######################### -FEATURES['ENABLE_VIDEO_UPLOAD_PIPELINE'] = True +ENABLE_VIDEO_UPLOAD_PIPELINE = True ########################### PIPELINE ################################# @@ -126,7 +126,7 @@ def should_show_debug_toolbar(request): # lint-amnesty, pylint: disable=missing ################################ MILESTONES ################################ -FEATURES['MILESTONES_APP'] = True +MILESTONES_APP = True ########################### ORGANIZATIONS ################################# # Although production studio.edx.org disables `ORGANIZATIONS_AUTOCREATE`, @@ -136,16 +136,16 @@ FEATURES['MILESTONES_APP'] = True ORGANIZATIONS_AUTOCREATE = True ################################ ENTRANCE EXAMS ################################ -FEATURES['ENTRANCE_EXAMS'] = True +ENTRANCE_EXAMS = True ################################ COURSE LICENSES ################################ -FEATURES['LICENSING'] = True +LICENSING = True # Needed to enable licensing on video blocks XBLOCK_SETTINGS.update({'VideoBlock': {'licensing_enabled': True}}) ################################ SEARCH INDEX ################################ -FEATURES['ENABLE_COURSEWARE_INDEX'] = True -FEATURES['ENABLE_LIBRARY_INDEX'] = False +ENABLE_COURSEWARE_INDEX = True +ENABLE_LIBRARY_INDEX = False SEARCH_ENGINE = "search.elastic.ElasticSearchEngine" ELASTIC_SEARCH_CONFIG = [ @@ -157,22 +157,22 @@ ELASTIC_SEARCH_CONFIG = [ ] ################################ COURSE DISCUSSIONS ########################### -FEATURES['ENABLE_DISCUSSION_SERVICE'] = True +ENABLE_DISCUSSION_SERVICE = True ################################ CREDENTIALS ########################### CREDENTIALS_SERVICE_USERNAME = 'credentials_worker' ########################## Certificates Web/HTML View ####################### -FEATURES['CERTIFICATES_HTML_VIEW'] = True +CERTIFICATES_HTML_VIEW = True ########################## AUTHOR PERMISSION ####################### -FEATURES['ENABLE_CREATOR_GROUP'] = True +ENABLE_CREATOR_GROUP = True ########################## Library creation organizations restriction ####################### -FEATURES['ENABLE_ORGANIZATION_STAFF_ACCESS_FOR_CONTENT_LIBRARIES'] = True +ENABLE_ORGANIZATION_STAFF_ACCESS_FOR_CONTENT_LIBRARIES = True ################### FRONTEND APPLICATION PUBLISHER URL ################### -FEATURES['FRONTEND_APP_PUBLISHER_URL'] = 'http://localhost:18400' +FRONTEND_APP_PUBLISHER_URL = 'http://localhost:18400' ################### FRONTEND APPLICATION COURSE AUTHORING ################### COURSE_AUTHORING_MICROFRONTEND_URL = 'http://localhost:2001' @@ -252,13 +252,13 @@ MODULESTORE = convert_module_store_setting_if_needed(MODULESTORE) SECRET_KEY = '85920908f28904ed733fe576320db18cabd7b6cd' ############# CORS headers for cross-domain requests ################# -FEATURES['ENABLE_CORS_HEADERS'] = True +ENABLE_CORS_HEADERS = True CORS_ALLOW_CREDENTIALS = True CORS_ORIGIN_ALLOW_ALL = True ################### Special Exams (Proctoring) and Prereqs ################### -FEATURES['ENABLE_SPECIAL_EXAMS'] = True -FEATURES['ENABLE_PREREQUISITE_COURSES'] = True +ENABLE_SPECIAL_EXAMS = True +ENABLE_PREREQUISITE_COURSES = True # Used in edx-proctoring for ID generation in lieu of SECRET_KEY - dummy value # (ref MST-637) diff --git a/cms/envs/mock.yml b/cms/envs/mock.yml index b28ee60280..dd2d9cf79d 100644 --- a/cms/envs/mock.yml +++ b/cms/envs/mock.yml @@ -298,10 +298,6 @@ STORAGES: BACKEND: openedx.core.storage.ProductionStorage DEFAULT_FROM_EMAIL: no-reply@registration.localhost DEFAULT_HASHING_ALGORITHM: sha256 -DEFAULT_JWT_ISSUER: - AUDIENCE: test_password - ISSUER: https://courses.localhost/oauth2 - SECRET_KEY: test_secret_key DEFAULT_SITE_THEME: localhost DISABLED_COUNTRIES: - US @@ -513,7 +509,6 @@ JWT_AUTH: "kid": "lms001"}]}' JWT_SECRET_KEY: test_JWT_SECRET_KEY JWT_SIGNING_ALGORITHM: RS512 -JWT_ISSUER: https://courses.localhost/oauth2 LANGUAGE_COOKIE: language-preference LEARNER_PORTAL_URL_ROOT: https://masters.localhost LEARNER_RECORD_MICROFRONTEND_URL: https://records.localhost diff --git a/cms/envs/production.py b/cms/envs/production.py index 346a60da66..f2d7ab88e4 100644 --- a/cms/envs/production.py +++ b/cms/envs/production.py @@ -29,6 +29,11 @@ from openedx.core.lib.derived import derive_settings # lint-amnesty, pylint: di from openedx.core.lib.logsettings import get_logger_config # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.modulestore_settings import convert_module_store_setting_if_needed # lint-amnesty, pylint: disable=wrong-import-order +from openedx.core.lib.features_setting_proxy import FeaturesProxy + +# A proxy for feature flags stored in the settings namespace +FEATURES = FeaturesProxy(globals()) + def get_env_setting(setting): """ Get the environment setting or return exception """ @@ -286,7 +291,7 @@ EVENT_TRACKING_BACKENDS['segmentio']['OPTIONS']['processors'][0]['OPTIONS']['whi ) -if FEATURES['ENABLE_COURSEWARE_INDEX'] or FEATURES['ENABLE_LIBRARY_INDEX']: +if ENABLE_COURSEWARE_INDEX or ENABLE_LIBRARY_INDEX: # Use ElasticSearch for the search engine SEARCH_ENGINE = "search.elastic.ElasticSearchEngine" @@ -302,7 +307,7 @@ XBLOCK_SETTINGS.setdefault("VideoBlock", {})['YOUTUBE_API_KEY'] = YOUTUBE_API_KE JWT_AUTH.update(_YAML_TOKENS.get('JWT_AUTH', {})) ######################## CUSTOM COURSES for EDX CONNECTOR ###################### -if FEATURES['CUSTOM_COURSES_EDX']: +if CUSTOM_COURSES_EDX: INSTALLED_APPS.append('openedx.core.djangoapps.ccxcon.apps.CCXConnectorConfig') ########################## Extra middleware classes ####################### @@ -347,7 +352,7 @@ add_plugins(__name__, ProjectType.CMS, SettingsType.PRODUCTION) #### ############# CORS headers for cross-domain requests ################# -if FEATURES['ENABLE_CORS_HEADERS']: +if ENABLE_CORS_HEADERS: CORS_ALLOW_CREDENTIALS = True CORS_ORIGIN_WHITELIST = _YAML_TOKENS.get('CORS_ORIGIN_WHITELIST', ()) CORS_ORIGIN_ALLOW_ALL = _YAML_TOKENS.get('CORS_ORIGIN_ALLOW_ALL', False) diff --git a/cms/envs/test.py b/cms/envs/test.py index 23131c699f..9a294bf10b 100644 --- a/cms/envs/test.py +++ b/cms/envs/test.py @@ -23,6 +23,8 @@ from openedx.core.lib.derived import derive_settings from xmodule.modulestore.modulestore_settings import update_module_store_settings # pylint: disable=wrong-import-order +from openedx.core.lib.features_setting_proxy import FeaturesProxy + from .common import * # import settings from LMS for consistent behavior with CMS @@ -46,6 +48,8 @@ from lms.envs.test import ( # pylint: disable=wrong-import-order, disable=unuse XBLOCK_RUNTIME_V2_EPHEMERAL_DATA_CACHE, ) +# A proxy for feature flags stored in the settings namespace +FEATURES = FeaturesProxy(globals()) # Include a non-ascii character in STUDIO_NAME and STUDIO_SHORT_NAME to uncover possible # UnicodeEncodeErrors in tests. Also use lazy text to reveal possible json dumps errors @@ -66,13 +70,14 @@ TEST_ROOT = path("test_root") # Want static files in the same dir for running on jenkins. STATIC_ROOT = TEST_ROOT / "staticfiles" WEBPACK_LOADER["DEFAULT"]["STATS_FILE"] = STATIC_ROOT / "webpack-stats.json" +WEBPACK_LOADER['DEFAULT']['LOADER_CLASS'] = 'webpack_loader.loader.FakeWebpackLoader' GITHUB_REPO_ROOT = TEST_ROOT / "data" DATA_DIR = TEST_ROOT / "data" COMMON_TEST_DATA_ROOT = COMMON_ROOT / "test" / "data" # For testing "push to lms" -FEATURES["ENABLE_EXPORT_GIT"] = True +ENABLE_EXPORT_GIT = True GIT_REPO_EXPORT_DIR = TEST_ROOT / "export_course_repos" # TODO (cpennington): We need to figure out how envs/test.py can inject things into common.py so that we don't have to repeat this sort of thing # lint-amnesty, pylint: disable=line-too-long @@ -203,51 +208,50 @@ PASSWORD_HASHERS = [ # No segment key CMS_SEGMENT_KEY = None -FEATURES["DISABLE_SET_JWT_COOKIES_FOR_TESTS"] = True +DISABLE_SET_JWT_COOKIES_FOR_TESTS = True -FEATURES["ENABLE_SERVICE_STATUS"] = True +ENABLE_SERVICE_STATUS = True # Toggles embargo on for testing -FEATURES["EMBARGO"] = True +EMBARGO = True TEST_THEME = COMMON_ROOT / "test" / "test-theme" # For consistency in user-experience, keep the value of this setting in sync with # the one in lms/envs/test.py -FEATURES["ENABLE_DISCUSSION_SERVICE"] = False +ENABLE_DISCUSSION_SERVICE = False # Enable a parental consent age limit for testing PARENTAL_CONSENT_AGE_LIMIT = 13 # Enable certificates for the tests -FEATURES["CERTIFICATES_HTML_VIEW"] = True +CERTIFICATES_HTML_VIEW = True # Enable content libraries code for the tests -FEATURES["ENABLE_CONTENT_LIBRARIES"] = True +ENABLE_CONTENT_LIBRARIES = True -FEATURES["ENABLE_EDXNOTES"] = True +ENABLE_EDXNOTES = True # MILESTONES -FEATURES["MILESTONES_APP"] = True +MILESTONES_APP = True # ENTRANCE EXAMS -FEATURES["ENTRANCE_EXAMS"] = True -ENTRANCE_EXAM_MIN_SCORE_PCT = 50 +ENTRANCE_EXAMS = True VIDEO_CDN_URL = {"CN": "http://api.xuetangx.com/edx/video?s3_url="} # Courseware Search Index -FEATURES["ENABLE_COURSEWARE_INDEX"] = True -FEATURES["ENABLE_LIBRARY_INDEX"] = True +ENABLE_COURSEWARE_INDEX = True +ENABLE_LIBRARY_INDEX = True SEARCH_ENGINE = "search.tests.mock_search_engine.MockSearchEngine" -FEATURES["ENABLE_ENROLLMENT_TRACK_USER_PARTITION"] = True +ENABLE_ENROLLMENT_TRACK_USER_PARTITION = True ########################## AUTHOR PERMISSION ####################### -FEATURES["ENABLE_CREATOR_GROUP"] = False +ENABLE_CREATOR_GROUP = False # teams feature -FEATURES["ENABLE_TEAMS"] = True +ENABLE_TEAMS = True # Dummy secret key for dev/test SECRET_KEY = "85920908f28904ed733fe576320db18cabd7b6cd" @@ -257,7 +261,7 @@ INSTALLED_APPS += [ "openedx.core.djangoapps.ccxcon.apps.CCXConnectorConfig", "common.djangoapps.third_party_auth.apps.ThirdPartyAuthConfig", ] -FEATURES["CUSTOM_COURSES_EDX"] = True +CUSTOM_COURSES_EDX = True ########################## VIDEO IMAGE STORAGE ############################ VIDEO_IMAGE_SETTINGS = dict( diff --git a/cms/lib/xblock/upstream_sync.py b/cms/lib/xblock/upstream_sync.py index 5448a9b1cf..ca23f9eb5e 100644 --- a/cms/lib/xblock/upstream_sync.py +++ b/cms/lib/xblock/upstream_sync.py @@ -183,7 +183,6 @@ class UpstreamLink: } if ( include_child_info - and self.ready_to_sync and isinstance(self.upstream_key, LibraryContainerLocator) and self.downstream_key is not None ): diff --git a/common/djangoapps/pipeline_mako/templates/static_content.html b/common/djangoapps/pipeline_mako/templates/static_content.html index a5a2a575b0..fc80c77e9e 100644 --- a/common/djangoapps/pipeline_mako/templates/static_content.html +++ b/common/djangoapps/pipeline_mako/templates/static_content.html @@ -147,7 +147,7 @@ if not source: <% body = capture(caller.body) %> - ${HTML(render_bundle(entry, extension=None, config='DEFAULT', attrs=attrs))} + ${HTML(render_bundle(context, entry, extension=None, config='DEFAULT', attrs=attrs))} % if body: