diff --git a/.github/workflows/migrations-check.yml b/.github/workflows/migrations-check.yml index bd0a07680ad7..fc4a48876a93 100644 --- a/.github/workflows/migrations-check.yml +++ b/.github/workflows/migrations-check.yml @@ -61,10 +61,6 @@ jobs: - name: Install Python dependencies run: | make dev-requirements - pip uninstall -y mysqlclient - pip install --no-binary mysqlclient mysqlclient - pip uninstall -y xmlsec - pip install --no-binary xmlsec xmlsec - name: Initiate Services run: | @@ -78,6 +74,11 @@ jobs: UPDATE mysql.user SET authentication_string = null WHERE user = 'root'; FLUSH PRIVILEGES; EOF + + - name: Install mysqlclient-dev binary + run: | + sudo apt-get update + sudo apt-get install -y libmysqlclient-dev - name: Run Tests env: diff --git a/.github/workflows/unit-tests-gh-hosted.yml b/.github/workflows/unit-tests-gh-hosted.yml index 894f61048e90..80a6a4e53a08 100644 --- a/.github/workflows/unit-tests-gh-hosted.yml +++ b/.github/workflows/unit-tests-gh-hosted.yml @@ -9,7 +9,7 @@ on: jobs: run-test: - if: github.repository != 'openedx/edx-platform' && github.repository != 'edx/edx-platform-private' + if: (github.repository != 'openedx/edx-platform' && github.repository != 'edx/edx-platform-private') || (github.repository == 'openedx/edx-platform' && (startsWith(github.base_ref, 'open-release') == true)) runs-on: ubuntu-20.04 strategy: fail-fast: false @@ -17,23 +17,24 @@ jobs: python-version: [ '3.8' ] django-version: - "pinned" - shard_name: [ - "lms-1", - "lms-2", - "lms-3", - "lms-4", - "lms-5", - "lms-6", - "openedx-1", - "openedx-2", - "openedx-3", - "openedx-4", - "cms-1", - "cms-2", - "common-1", - "common-2", - "common-3", - ] + # When updating the shards, remember to make the same changes in + # .github/workflows/unit-tests.yml + shard_name: + - "lms-1" + - "lms-2" + - "lms-3" + - "lms-4" + - "lms-5" + - "lms-6" + - "openedx-1" + - "openedx-2" + - "openedx-3" + - "openedx-4" + - "cms-1" + - "cms-2" + - "common-1" + - "common-2" + - "xmodule-1" name: gh-hosted-python-${{ matrix.python-version }},django-${{ matrix.django-version }},${{ matrix.shard_name }} steps: - uses: actions/checkout@v2 @@ -78,7 +79,7 @@ jobs: uses: ./.github/actions/unit-tests collect-and-verify: - if: github.repository != 'openedx/edx-platform' && github.repository != 'edx/edx-platform-private' + if: (github.repository != 'openedx/edx-platform' && github.repository != 'edx/edx-platform-private') || (github.repository == 'openedx/edx-platform' && (startsWith(github.base_ref, 'open-release') == true)) runs-on: ubuntu-20.04 strategy: matrix: diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 5b4b8ed94355..c66c30d7be6f 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -9,7 +9,7 @@ on: jobs: run-tests: name: python-${{ matrix.python-version }},django-${{ matrix.django-version }},${{ matrix.shard_name }} - if: github.repository == 'edx/edx-platform-private' || github.repository == 'openedx/edx-platform' + if: (github.repository == 'edx/edx-platform-private') || (github.repository == 'openedx/edx-platform' && (startsWith(github.base_ref, 'open-release') == false)) runs-on: [ edx-platform-runner ] strategy: matrix: @@ -18,6 +18,8 @@ jobs: django-version: - "pinned" #- "4.0" + # When updating the shards, remember to make the same changes in + # .github/workflows/unit-tests-gh-hosted.yml shard_name: - "lms-1" - "lms-2" @@ -80,7 +82,7 @@ jobs: # https://github.com/orgs/community/discussions/33579 success: name: Tests successful - if: always() + if: (github.repository == 'edx/edx-platform-private') || (github.repository == 'openedx/edx-platform' && (startsWith(github.base_ref, 'open-release') == false)) needs: - run-tests runs-on: ubuntu-latest diff --git a/.github/workflows/verify-gha-unit-tests-count.yml b/.github/workflows/verify-gha-unit-tests-count.yml index a519c62fa0a0..c68092942d70 100644 --- a/.github/workflows/verify-gha-unit-tests-count.yml +++ b/.github/workflows/verify-gha-unit-tests-count.yml @@ -8,7 +8,7 @@ on: jobs: collect-and-verify: - if: github.repository == 'edx/edx-platform-private' || github.repository == 'openedx/edx-platform' + if: (github.repository == 'edx/edx-platform-private') || (github.repository == 'openedx/edx-platform' && (startsWith(github.base_ref, 'open-release') == false)) runs-on: [ edx-platform-runner ] steps: - name: sync directory owner diff --git a/Makefile b/Makefile index 7aa7334fdfac..fec1c2caf2e7 100644 --- a/Makefile +++ b/Makefile @@ -64,8 +64,8 @@ pull: ## update the Docker image used by "make shell" docker pull edxops/edxapp:latest pre-requirements: ## install Python requirements for running pip-tools - pip install -qr requirements/pip.txt - pip install -qr requirements/edx/pip-tools.txt + pip install -r requirements/pip.txt + pip install -r requirements/pip-tools.txt local-requirements: # edx-platform installs some Python projects from within the edx-platform repo itself. @@ -74,7 +74,7 @@ local-requirements: dev-requirements: pre-requirements @# The "$(wildcard..)" is to include private.txt if it exists, and make no mention @# of it if it does not. Shell wildcarding can't do that with default options. - pip-sync -q requirements/edx/development.txt $(wildcard requirements/edx/private.txt) + pip-sync requirements/edx/development.txt $(wildcard requirements/edx/private.txt) make local-requirements base-requirements: pre-requirements @@ -96,7 +96,6 @@ shell: ## launch a bash shell in a Docker container with all edx-platform depend # Order is very important in this list: files must appear after everything they include! REQ_FILES = \ - requirements/edx/pip-tools \ requirements/edx/coverage \ requirements/edx/doc \ requirements/edx/paver \ @@ -117,23 +116,26 @@ $(COMMON_CONSTRAINTS_TXT): echo "$(COMMON_CONSTRAINTS_TEMP_COMMENT)" | cat - $(@) > temp && mv temp $(@) compile-requirements: export CUSTOM_COMPILE_COMMAND=make upgrade -compile-requirements: $(COMMON_CONSTRAINTS_TXT) ## Re-compile *.in requirements to *.txt - pip install -q pip-tools - pip-compile --allow-unsafe --upgrade -o requirements/edx/pip.txt requirements/edx/pip.in +compile-requirements: pre-requirements $(COMMON_CONSTRAINTS_TXT) ## Re-compile *.in requirements to *.txt + @# Bootstrapping: Rebuild pip and pip-tools first, and then install them + @# so that if there are any failures we'll know now, rather than the next + @# time someone tries to use the outputs. + pip-compile -v --allow-unsafe ${COMPILE_OPTS} -o requirements/pip.txt requirements/pip.in + pip install -r requirements/pip.txt + + pip-compile -v ${COMPILE_OPTS} -o requirements/pip-tools.txt requirements/pip-tools.in + pip install -r requirements/pip-tools.txt @ export REBUILD='--rebuild'; \ for f in $(REQ_FILES); do \ echo ; \ echo "== $$f ===============================" ; \ - echo "pip-compile -v --no-emit-trusted-host --no-emit-index-url $$REBUILD ${COMPILE_OPTS} -o $$f.txt $$f.in"; \ - pip-compile -v --no-emit-trusted-host --no-emit-index-url $$REBUILD ${COMPILE_OPTS} -o $$f.txt $$f.in || exit 1; \ + echo "pip-compile -v $$REBUILD ${COMPILE_OPTS} -o $$f.txt $$f.in"; \ + pip-compile -v $$REBUILD ${COMPILE_OPTS} -o $$f.txt $$f.in || exit 1; \ export REBUILD=''; \ done - pip install -qr requirements/edx/pip.txt - pip install -qr requirements/edx/pip-tools.txt - -upgrade: pre-requirements ## update the pip requirements files to use the latest releases satisfying our constraints +upgrade: ## update the pip requirements files to use the latest releases satisfying our constraints $(MAKE) compile-requirements COMPILE_OPTS="--upgrade" check-types: ## run static type-checking tests diff --git a/cms/djangoapps/contentstore/tests/test_course_settings.py b/cms/djangoapps/contentstore/tests/test_course_settings.py index a135faf06712..be7691ee7e45 100644 --- a/cms/djangoapps/contentstore/tests/test_course_settings.py +++ b/cms/djangoapps/contentstore/tests/test_course_settings.py @@ -106,7 +106,12 @@ def setUp(self): super().setUp() self.fullcourse = CourseFactory.create() self.course_setting_url = get_url(self.course.id, 'advanced_settings_handler') - self.non_staff_client, _ = self.create_non_staff_authed_user_client() + + self.non_staff_client, self.nonstaff = self.create_non_staff_authed_user_client() + # "nonstaff" means "non Django staff" here. We assign this user to course staff + # role to check that even so they won't have advanced settings access when explicitly + # restricted. + CourseStaffRole(self.course.id).add_users(self.nonstaff) @override_settings(FEATURES={'DISABLE_MOBILE_COURSE_AVAILABLE': True}) def test_mobile_field_available(self): @@ -145,16 +150,50 @@ def test_discussion_fields_available(self, is_pages_and_resources_enabled, self.assertEqual('discussion_blackouts' in response, fields_visible) self.assertEqual('discussion_topics' in response, fields_visible) - @override_settings(FEATURES={'DISABLE_ADVANCED_SETTINGS': True}) - def test_disable_advanced_settings_feature(self): - """ - If this feature is enabled, only staff should be able to access the advanced settings page. - """ - response = self.non_staff_client.get_html(self.course_setting_url) - self.assertEqual(response.status_code, 403) + @ddt.data(False, True) + def test_disable_advanced_settings_feature(self, disable_advanced_settings): + """ + If this feature is enabled, only Django Staff/Superuser should be able to access the "Advanced Settings" page. + For non-staff users the "Advanced Settings" tab link should not be visible. + """ + advanced_settings_link_html = f"Advanced Settings".encode('utf-8') + + with override_settings(FEATURES={'DISABLE_ADVANCED_SETTINGS': disable_advanced_settings}): + for handler in ( + 'import_handler', + 'export_handler', + 'course_team_handler', + 'course_info_handler', + 'assets_handler', + 'tabs_handler', + 'settings_handler', + 'grading_handler', + 'textbooks_list_handler', + ): + # Test that non-staff users don't see the "Advanced Settings" tab link. + response = self.non_staff_client.get_html( + get_url(self.course.id, handler) + ) + self.assertEqual(response.status_code, 200) + if disable_advanced_settings: + self.assertNotIn(advanced_settings_link_html, response.content) + else: + self.assertIn(advanced_settings_link_html, response.content) + + # Test that staff users see the "Advanced Settings" tab link. + response = self.client.get_html( + get_url(self.course.id, handler) + ) + self.assertEqual(response.status_code, 200) + self.assertIn(advanced_settings_link_html, response.content) - response = self.client.get_html(self.course_setting_url) - self.assertEqual(response.status_code, 200) + # Test that non-staff users can't access the "Advanced Settings" page. + response = self.non_staff_client.get_html(self.course_setting_url) + self.assertEqual(response.status_code, 403 if disable_advanced_settings else 200) + + # Test that staff users can access the "Advanced Settings" page. + response = self.client.get_html(self.course_setting_url) + self.assertEqual(response.status_code, 200) @ddt.ddt diff --git a/cms/djangoapps/contentstore/views/component.py b/cms/djangoapps/contentstore/views/component.py index 457f84793efc..b7dd09d5c7cb 100644 --- a/cms/djangoapps/contentstore/views/component.py +++ b/cms/djangoapps/contentstore/views/component.py @@ -292,8 +292,8 @@ def create_support_legend_dict(): # by the components in the order listed in COMPONENT_TYPES. component_types = COMPONENT_TYPES[:] - # Libraries do not support discussions and openassessment and other libraries - component_not_supported_by_library = ['discussion', 'library', 'openassessment'] + # Libraries do not support discussions, drag-and-drop, and openassessment and other libraries + component_not_supported_by_library = ['discussion', 'library', 'openassessment', 'drag-and-drop-v2'] if library: component_types = [component for component in component_types if component not in set(component_not_supported_by_library)] diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py index 3bf9ade016e4..023cb07199fe 100644 --- a/cms/djangoapps/contentstore/views/course.py +++ b/cms/djangoapps/contentstore/views/course.py @@ -43,7 +43,12 @@ from common.djangoapps.course_action_state.models import CourseRerunState, CourseRerunUIStateManager from common.djangoapps.course_modes.models import CourseMode from common.djangoapps.edxmako.shortcuts import render_to_response -from common.djangoapps.student.auth import has_course_author_access, has_studio_read_access, has_studio_write_access +from common.djangoapps.student.auth import ( + has_course_author_access, + has_studio_read_access, + has_studio_write_access, + has_studio_advanced_settings_access +) from common.djangoapps.student.roles import ( CourseInstructorRole, CourseStaffRole, @@ -147,17 +152,6 @@ class AccessListFallback(Exception): pass # lint-amnesty, pylint: disable=unnecessary-pass -def has_advanced_settings_access(user): - """ - If DISABLE_ADVANCED_SETTINGS feature is enabled, only global staff can access "Advanced Settings". - """ - return ( - not settings.FEATURES.get('DISABLE_ADVANCED_SETTINGS', False) - or user.is_staff - or user.is_superuser - ) - - def get_course_and_check_access(course_key, user, depth=0): """ Function used to calculate and return the locator and course block @@ -763,7 +757,6 @@ def course_index(request, course_key): 'frontend_app_publisher_url': frontend_app_publisher_url, 'mfe_proctored_exam_settings_url': get_proctored_exam_settings_url(course_block.id), 'advance_settings_url': reverse_course_url('advanced_settings_handler', course_block.id), - 'advance_settings_access': has_advanced_settings_access(request.user), 'proctoring_errors': proctoring_errors, }) @@ -1432,7 +1425,7 @@ def advanced_settings_handler(request, course_key_string): json: update the Course's settings. The payload is a json rep of the metadata dicts. """ - if not has_advanced_settings_access(request.user): + if not has_studio_advanced_settings_access(request.user): raise PermissionDenied() course_key = CourseKey.from_string(course_key_string) diff --git a/cms/templates/settings.html b/cms/templates/settings.html index c3ed1afb9957..b7ef368a0911 100644 --- a/cms/templates/settings.html +++ b/cms/templates/settings.html @@ -7,6 +7,7 @@ <%namespace name='static' file='static_content.html'/> <%! from django.utils.translation import gettext as _ + from common.djangoapps.student.auth import has_studio_advanced_settings_access from cms.djangoapps.contentstore import utils from lms.djangoapps.certificates.api import can_show_certificate_available_date_field from openedx.core.djangolib.js_utils import ( @@ -721,7 +722,9 @@

${_("Other Course Settings")}

+ % if has_studio_advanced_settings_access(request.user): + % endif % if mfe_proctored_exam_settings_url: % endif diff --git a/cms/templates/settings_graders.html b/cms/templates/settings_graders.html index bde135122155..ac7119177978 100644 --- a/cms/templates/settings_graders.html +++ b/cms/templates/settings_graders.html @@ -11,6 +11,7 @@ import json from cms.djangoapps.contentstore import utils from django.utils.translation import gettext as _ + from common.djangoapps.student.auth import has_studio_advanced_settings_access from cms.djangoapps.models.settings.encoder import CourseSettingsEncoder from openedx.core.djangolib.js_utils import ( dump_js_escaped_json, js_escaped_string @@ -165,7 +166,9 @@

${_("Other Course Settings")}

+ % if has_studio_advanced_settings_access(request.user): + % endif % if mfe_proctored_exam_settings_url: % endif diff --git a/cms/templates/widgets/header.html b/cms/templates/widgets/header.html index 23678ec395ae..48b818da95a5 100644 --- a/cms/templates/widgets/header.html +++ b/cms/templates/widgets/header.html @@ -7,6 +7,7 @@ from django.urls import reverse from django.utils.translation import gettext as _ from urllib.parse import quote_plus + from common.djangoapps.student.auth import has_studio_advanced_settings_access from cms.djangoapps.contentstore import toggles from cms.djangoapps.contentstore.utils import get_pages_and_resources_url from openedx.core.djangoapps.discussions.config.waffle import ENABLE_PAGES_AND_RESOURCES_MICROFRONTEND @@ -120,7 +121,7 @@

${_("Course" ${_("Proctored Exam Settings")} % endif - % if advance_settings_access: + % if has_studio_advanced_settings_access(request.user): diff --git a/common/djangoapps/student/auth.py b/common/djangoapps/student/auth.py index 3091dbc45406..f8e45ce6ba6c 100644 --- a/common/djangoapps/student/auth.py +++ b/common/djangoapps/student/auth.py @@ -125,9 +125,23 @@ def has_course_author_access(user, course_key): return has_studio_write_access(user, course_key) +def has_studio_advanced_settings_access(user): + """ + If DISABLE_ADVANCED_SETTINGS feature is enabled, only Django Superuser + or Django Staff can access "Advanced Settings". + + By default, this feature is disabled. + """ + return ( + not settings.FEATURES.get('DISABLE_ADVANCED_SETTINGS', False) + or user.is_staff + or user.is_superuser + ) + + def has_studio_read_access(user, course_key): """ - Return True iff user is allowed to view this course/library in studio. + Return True if user is allowed to view this course/library in studio. Will also return True if user has write access in studio (has_course_author_access) There is currently no such thing as read-only course access in studio, but diff --git a/openedx/core/djangoapps/cache_toolbox/middleware.py b/openedx/core/djangoapps/cache_toolbox/middleware.py index 6284613d49e6..4ba2162fe35f 100644 --- a/openedx/core/djangoapps/cache_toolbox/middleware.py +++ b/openedx/core/djangoapps/cache_toolbox/middleware.py @@ -52,6 +52,14 @@ compounded by most projects wishing to avoid expiring session data as long as possible (in addition to storing sessions in persistent stores). +Dependency with SafeSessionMiddleware +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +CacheBackedAuthenticationMiddleware middleware logs out the user if the +session hash is changed due to password change. It flushes the session +and mark cookies for deletion in request which are then deleted in the +process_response of SafeSessionMiddleware. + Usage ~~~~~ @@ -88,7 +96,7 @@ from django.utils.crypto import constant_time_compare from django.utils.deprecation import MiddlewareMixin -from openedx.core.djangoapps.safe_sessions.middleware import SafeSessionMiddleware +from openedx.core.djangoapps.safe_sessions.middleware import SafeSessionMiddleware, _mark_cookie_for_deletion from .model import cache_model @@ -138,3 +146,4 @@ def _verify_session_auth(self, request): # change. Log the user out. request.session.flush() request.user = AnonymousUser() + _mark_cookie_for_deletion(request) diff --git a/openedx/core/djangoapps/cache_toolbox/tests/test_middleware.py b/openedx/core/djangoapps/cache_toolbox/tests/test_middleware.py index 68f0577968e2..21547b69ca1c 100644 --- a/openedx/core/djangoapps/cache_toolbox/tests/test_middleware.py +++ b/openedx/core/djangoapps/cache_toolbox/tests/test_middleware.py @@ -2,11 +2,15 @@ from unittest.mock import patch from django.conf import settings -from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user +from django.contrib.auth.models import User, AnonymousUser # lint-amnesty, pylint: disable=imported-auth-user from django.urls import reverse from django.test import TestCase +from django.contrib.auth import SESSION_KEY +from django.http import HttpResponse, SimpleCookie -from openedx.core.djangolib.testing.utils import skip_unless_cms, skip_unless_lms +from openedx.core.djangoapps.cache_toolbox.middleware import CacheBackedAuthenticationMiddleware +from openedx.core.djangoapps.safe_sessions.middleware import SafeCookieData, SafeSessionMiddleware +from openedx.core.djangolib.testing.utils import skip_unless_cms, skip_unless_lms, get_mock_request from common.djangoapps.student.tests.factories import UserFactory @@ -18,6 +22,9 @@ def setUp(self): password = 'test-password' self.user = UserFactory(password=password) self.client.login(username=self.user.username, password=password) + self.request = get_mock_request(self.user) + self.client.response = HttpResponse() + self.client.response.cookies = SimpleCookie() # preparing cookies def _test_change_session_hash(self, test_url, redirect_url, target_status_code=200): """ @@ -45,3 +52,36 @@ def test_session_change_cms(self): home_url = reverse('home') # Studio login redirects to LMS login self._test_change_session_hash(home_url, settings.LOGIN_URL + '?next=' + home_url, target_status_code=302) + + def test_user_logout_on_session_hash_change(self): + """ + Verify that if a user's session auth hash and the request's hash + differ, the user is logged out: + - session is flushed + - request user is changed to Anonymous user + - logged in cookies are deleted + """ + # preparing session and setting cookies + session_id = self.client.session.session_key + safe_cookie_data = SafeCookieData.create(session_id, self.user.id) + self.request.COOKIES[settings.SESSION_COOKIE_NAME] = str(safe_cookie_data) + self.client.response.cookies[settings.SESSION_COOKIE_NAME] = session_id + self.client.response.cookies['edx-jwt-cookie-header-payload'] = 'test-jwt-payload' + SafeSessionMiddleware().process_request(self.request) + + # asserts that user, session, and JWT cookies exist + assert self.request.session.get(SESSION_KEY) is not None + assert self.request.user != AnonymousUser() + assert self.client.response.cookies.get(settings.SESSION_COOKIE_NAME).value == session_id + assert self.client.response.cookies.get('edx-jwt-cookie-header-payload').value == 'test-jwt-payload' + + with patch.object(User, 'get_session_auth_hash', return_value='abc123'): + CacheBackedAuthenticationMiddleware().process_request(self.request) + SafeSessionMiddleware().process_response(self.request, self.client.response) + + # asserts that user, session, and JWT cookies do not exist + assert self.request.session.get(SESSION_KEY) is None + assert self.request.user == AnonymousUser() + assert self.client.response.cookies.get(settings.SESSION_COOKIE_NAME).value != session_id + assert self.client.response.cookies.get(settings.SESSION_COOKIE_NAME).value == "" + assert self.client.response.cookies.get('edx-jwt-cookie-header-payload').value == "" diff --git a/openedx/core/djangoapps/catalog/management/commands/cache_programs.py b/openedx/core/djangoapps/catalog/management/commands/cache_programs.py index b815f8b4e4e5..9e0664f4930f 100644 --- a/openedx/core/djangoapps/catalog/management/commands/cache_programs.py +++ b/openedx/core/djangoapps/catalog/management/commands/cache_programs.py @@ -45,8 +45,17 @@ class Command(BaseCommand): """ help = "Rebuild the LMS' cache of program data." + def add_arguments(self, parser): + parser.add_argument( + '--domain', + dest='domain', + type=str, + help='Help in caching the programs for one site' + ) + # lint-amnesty, pylint: disable=bad-option-value, unicode-format-string def handle(self, *args, **options): # lint-amnesty, pylint: disable=too-many-statements + domain = options.get('domain', '') failure = False logger.info('populate-multitenant-programs switch is ON') @@ -68,7 +77,9 @@ def handle(self, *args, **options): # lint-amnesty, pylint: disable=too-many-st programs_by_type = {} programs_by_type_slug = {} organizations = {} - for site in Site.objects.all(): + + sites = Site.objects.filter(domain=domain) if domain else Site.objects.all() + for site in sites: site_config = getattr(site, 'configuration', None) if site_config is None or not site_config.get_value('COURSE_CATALOG_API_URL'): logger.info(f'Skipping site {site.domain}. No configuration.') diff --git a/openedx/core/djangoapps/catalog/management/commands/tests/test_cache_programs.py b/openedx/core/djangoapps/catalog/management/commands/tests/test_cache_programs.py index 331d53a73042..4078b2d90d55 100644 --- a/openedx/core/djangoapps/catalog/management/commands/tests/test_cache_programs.py +++ b/openedx/core/djangoapps/catalog/management/commands/tests/test_cache_programs.py @@ -48,29 +48,53 @@ def setUp(self): } ) + self.site_domain2 = 'testsite2.com' + self.site2 = self.set_up_site( + self.site_domain2, + { + 'COURSE_CATALOG_API_URL': self.catalog_integration.get_internal_api_url().rstrip('/') + } + ) + self.list_url = self.catalog_integration.get_internal_api_url().rstrip('/') + '/programs/' self.detail_tpl = self.list_url.rstrip('/') + '/{uuid}/' self.pathway_url = self.catalog_integration.get_internal_api_url().rstrip('/') + '/pathways/' self.programs = ProgramFactory.create_batch(3) + self.programs2 = ProgramFactory.create_batch(3) self.pathways = PathwayFactory.create_batch(3) + self.pathways2 = PathwayFactory.create_batch(3) self.child_program = ProgramFactory.create() + self.child_program2 = ProgramFactory.create() self.programs[0]['curricula'][0]['programs'].append(self.child_program) self.programs.append(self.child_program) - self.programs[0]['authoring_organizations'] = OrganizationFactory.create_batch(2) + self.programs2[0]['curricula'][0]['programs'].append(self.child_program2) + self.programs2.append(self.child_program2) + self.programs2[0]['authoring_organizations'] = OrganizationFactory.create_batch(2) + for pathway in self.pathways: self.programs += pathway['programs'] - self.uuids = [program['uuid'] for program in self.programs] + for pathway in self.pathways2: + self.programs2 += pathway['programs'] + + self.uuids = { + f"{self.site_domain}": [program["uuid"] for program in self.programs], + f"{self.site_domain2}": [program["uuid"] for program in self.programs2], + } # add some of the previously created programs to some pathways self.pathways[0]['programs'].extend([self.programs[0], self.programs[1]]) self.pathways[1]['programs'].append(self.programs[0]) - def mock_list(self): + # add some of the previously created programs to some pathways + self.pathways2[0]['programs'].extend([self.programs2[0], self.programs2[1]]) + self.pathways2[1]['programs'].append(self.programs2[0]) + + def mock_list(self, site=""): """ Mock the data returned by the program listing API endpoint. """ # pylint: disable=unused-argument def list_callback(request, uri, headers): @@ -81,8 +105,8 @@ def list_callback(request, uri, headers): 'uuids_only': ['1'] } assert request.querystring == expected - - return (200, headers, json.dumps(self.uuids)) + uuids = self.uuids[self.site_domain2] if site else self.uuids[self.site_domain] + return (200, headers, json.dumps(uuids)) httpretty.register_uri( httpretty.GET, @@ -130,17 +154,36 @@ def pathways_callback(request, uri, headers): # pylint: disable=unused-argument return (200, headers, json.dumps(body)) - # NOTE: httpretty does not properly match against query strings here (using match_querystring arg) - # as such, it does not actually look at the query parameters (for page num), but returns items in a LIFO order. - # this means that for multiple pages, you must call this function starting from the last page. - # we do assert the page number query param above, however httpretty.register_uri( httpretty.GET, - self.pathway_url, + self.pathway_url + f'?exclude_utm=1&page={page_number}', body=pathways_callback, content_type='application/json', + match_querystring=True, ) + def test_handle_domain(self): + """ + Verify that the command argument is working correct or not. + """ + UserFactory(username=self.catalog_integration.service_username) + + programs = { + PROGRAM_CACHE_KEY_TPL.format(uuid=program['uuid']): program for program in self.programs2 + } + + self.mock_list(self.site2) + self.mock_pathways(self.pathways2) + + for uuid in self.uuids[self.site_domain2]: + program = programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)] + self.mock_detail(uuid, program) + + call_command('cache_programs', f'--domain={self.site_domain2}') + + cached_uuids = cache.get(SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=self.site_domain2)) + assert set(cached_uuids) == set(self.uuids[self.site_domain2]) + def test_handle_programs(self): """ Verify that the command requests and caches program UUIDs and details. @@ -158,14 +201,14 @@ def test_handle_programs(self): self.mock_list() self.mock_pathways(self.pathways) - for uuid in self.uuids: + for uuid in self.uuids[self.site_domain]: program = programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)] self.mock_detail(uuid, program) call_command('cache_programs') cached_uuids = cache.get(SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=self.site_domain)) - assert set(cached_uuids) == set(self.uuids) + assert set(cached_uuids) == set(self.uuids[self.site_domain]) program_keys = list(programs.keys()) cached_programs = cache.get_many(program_keys) @@ -228,7 +271,7 @@ def test_handle_pathways(self): self.mock_list() self.mock_pathways(self.pathways) - for uuid in self.uuids: + for uuid in self.uuids[self.site_domain]: program = programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)] self.mock_detail(uuid, program) @@ -267,7 +310,7 @@ def test_pathways_multiple_pages(self): } self.mock_list() - for uuid in self.uuids: + for uuid in self.uuids[self.site_domain]: program = programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)] self.mock_detail(uuid, program) @@ -337,7 +380,7 @@ def test_handle_missing_pathways(self): self.mock_list() - for uuid in self.uuids: + for uuid in self.uuids[self.site_domain]: program = programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)] self.mock_detail(uuid, program) @@ -365,7 +408,7 @@ def test_handle_missing_programs(self): self.mock_list() - for uuid in self.uuids[:2]: + for uuid in self.uuids[self.site_domain][:2]: program = partial_programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)] self.mock_detail(uuid, program) @@ -375,7 +418,7 @@ def test_handle_missing_programs(self): assert context.value.code == 1 cached_uuids = cache.get(SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=self.site_domain)) - assert set(cached_uuids) == set(self.uuids) + assert set(cached_uuids) == set(self.uuids[self.site_domain]) program_keys = list(all_programs.keys()) cached_programs = cache.get_many(program_keys) diff --git a/openedx/core/djangoapps/schedules/resolvers.py b/openedx/core/djangoapps/schedules/resolvers.py index 1b406a6d7150..3498042415ff 100644 --- a/openedx/core/djangoapps/schedules/resolvers.py +++ b/openedx/core/djangoapps/schedules/resolvers.py @@ -3,6 +3,7 @@ import datetime import logging from itertools import groupby +from urllib.parse import urljoin import attr from django.conf import settings @@ -322,7 +323,7 @@ def get_template_context(self, user, user_schedules): context = { 'course_links': course_links, 'first_course_name': first_schedule.enrollment.course.display_name, - 'cert_image': static('course_experience/images/verified-cert.png'), + 'cert_image': urljoin(settings.LMS_ROOT_URL, static('course_experience/images/verified-cert.png')), 'course_ids': course_id_strs, } context.update(first_valid_upsell_context) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index fd79c09c815d..61324782cc06 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -90,3 +90,7 @@ babel==2.11.0 social-auth-app-django==5.0.0 algoliasearch==2.6.3 django-ipware==4.0.2 + +# pytz>2022 has major changes which are causing test failures. +# Pinning this version for now so this could be fixed in a separate PR later on +pytz<2023 diff --git a/requirements/edx-sandbox/py38.txt b/requirements/edx-sandbox/py38.txt index 08f881603dad..3935028963eb 100644 --- a/requirements/edx-sandbox/py38.txt +++ b/requirements/edx-sandbox/py38.txt @@ -38,7 +38,7 @@ matplotlib==3.3.4 # -r requirements/edx-sandbox/py38.in mpmath==1.3.0 # via sympy -networkx==3.0 +networkx==3.1 # via -r requirements/edx-sandbox/py38.in nltk==3.8.1 # via @@ -52,7 +52,7 @@ numpy==1.22.4 # scipy openedx-calc==3.0.1 # via -r requirements/edx-sandbox/py38.in -pillow==9.4.0 +pillow==9.5.0 # via matplotlib pycparser==2.21 # via cffi @@ -66,7 +66,7 @@ python-dateutil==2.8.2 # via matplotlib random2==1.0.1 # via -r requirements/edx-sandbox/py38.in -regex==2022.10.31 +regex==2023.3.23 # via nltk scipy==1.7.3 # via diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index c29df9ff37d2..a3bbb45cf8ed 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -16,7 +16,7 @@ algoliasearch==2.6.3 # -r requirements/edx/base.in amqp==5.1.1 # via kombu -analytics-python==1.4.0 +analytics-python==1.4.post1 # via -r requirements/edx/base.in aniso8601==9.0.1 # via edx-tincan-py35 @@ -34,7 +34,7 @@ async-timeout==4.0.2 # via # aiohttp # redis -attrs==22.2.0 +attrs==23.1.0 # via # -r requirements/edx/base.in # aiohttp @@ -53,7 +53,7 @@ backoff==1.10.0 # via analytics-python backports-zoneinfo==0.2.1 # via icalendar -beautifulsoup4==4.11.2 +beautifulsoup4==4.12.2 # via pynliner billiard==3.6.4.0 # via celery @@ -177,7 +177,7 @@ defusedxml==0.7.1 # social-auth-core deprecated==1.2.13 # via jwcrypto -django==3.2.18 +django==3.2.19 # via # -c requirements/edx/../common_constraints.txt # -r requirements/edx/base.in @@ -261,7 +261,7 @@ django-config-models==2.3.0 # edx-enterprise # edx-name-affirmation # lti-consumer-xblock -django-cors-headers==3.13.0 +django-cors-headers==3.14.0 # via -r requirements/edx/base.in django-countries==7.5.1 # via @@ -282,7 +282,7 @@ django-fernet-fields==0.6 # via # -r requirements/edx/base.in # edx-enterprise -django-filter==22.1 +django-filter==23.1 # via # -r requirements/edx/base.in # edx-enterprise @@ -467,7 +467,7 @@ edx-django-utils==5.3.0 # ora2 # outcome-surveys # super-csv -edx-drf-extensions==8.4.1 +edx-drf-extensions==8.6.0 # via # -r requirements/edx/base.in # edx-completion @@ -484,7 +484,7 @@ edx-enterprise==3.61.11 # -c requirements/edx/../constraints.txt # -r requirements/edx/base.in # learner-pathway-progress -edx-event-bus-kafka==3.9.4 +edx-event-bus-kafka==3.9.6 # via -r requirements/edx/base.in edx-i18n-tools==0.9.2 # via ora2 @@ -542,6 +542,7 @@ edx-toggles==5.0.0 # edx-completion # edx-event-bus-kafka # edx-name-affirmation + # edx-search # edxval # learner-pathway-progress # ora2 @@ -570,7 +571,7 @@ event-tracking==2.1.0 # edx-search fastavro==1.7.3 # via openedx-events -filelock==3.10.0 +filelock==3.11.0 # via snowflake-connector-python frozenlist==1.3.3 # via @@ -600,7 +601,7 @@ html5lib==1.1 # via # -r requirements/edx/base.in # ora2 -icalendar==5.0.4 +icalendar==5.0.5 # via -r requirements/edx/base.in idna==3.4 # via @@ -609,7 +610,7 @@ idna==3.4 # requests # snowflake-connector-python # yarl -importlib-metadata==6.0.0 +importlib-metadata==6.4.1 # via markdown importlib-resources==5.12.0 # via jsonschema @@ -711,7 +712,7 @@ markupsafe==2.1.2 # xblock maxminddb==2.2.0 # via geoip2 -mock==5.0.1 +mock==5.0.2 # via -r requirements/edx/paver.txt mongodbproxy @ git+https://github.com/openedx/MongoDBProxy.git@d92bafe9888d2940f647a7b2b2383b29c752f35a # via -r requirements/edx/github.in @@ -731,7 +732,7 @@ mysqlclient==2.1.1 # via # -r requirements/edx/base.in # openedx-blockstore -newrelic==8.7.0 +newrelic==8.8.0 # via # -r requirements/edx/base.in # edx-django-utils @@ -767,7 +768,7 @@ openedx-django-require==2.0.0 # via -r requirements/edx/base.in openedx-django-wiki==2.0.0 # via -r requirements/edx/base.in -openedx-events==5.1.0 +openedx-events==7.0.0 # via # -r requirements/edx/base.in # edx-event-bus-kafka @@ -783,7 +784,7 @@ oscrypto==1.3.0 # via snowflake-connector-python outcome-surveys==2.4.0 # via -r requirements/edx/base.in -packaging==23.0 +packaging==23.1 # via # drf-yasg # py2neo @@ -811,7 +812,7 @@ pgpy==0.6.0 # via edx-enterprise piexif==1.1.3 # via -r requirements/edx/base.in -pillow==9.4.0 +pillow==9.5.0 # via # -r requirements/edx/base.in # edx-enterprise @@ -822,7 +823,7 @@ polib==1.2.0 # via edx-i18n-tools prompt-toolkit==3.0.38 # via click-repl -psutil==5.9.4 +psutil==5.9.5 # via # -r requirements/edx/paver.txt # edx-django-utils @@ -843,7 +844,7 @@ pycryptodomex==3.17 # lti-consumer-xblock # pyjwkest # snowflake-connector-python -pygments==2.14.0 +pygments==2.15.0 # via # -r requirements/edx/base.in # py2neo @@ -917,7 +918,7 @@ python-memcached==1.59 # via -r requirements/edx/paver.txt python-slugify==8.0.1 # via code-annotations -python-swiftclient==4.2.0 +python-swiftclient==4.3.0 # via ora2 python3-openid==3.2.0 ; python_version >= "3" # via @@ -929,6 +930,7 @@ python3-saml==1.9.0 # -r requirements/edx/base.in pytz==2022.7.1 # via + # -c requirements/edx/../constraints.txt # -r requirements/edx/base.in # babel # celery @@ -961,13 +963,13 @@ pyyaml==6.0 # xblock random2==1.0.1 # via -r requirements/edx/base.in -rapidfuzz==2.13.7 +rapidfuzz==2.15.1 # via levenshtein recommender-xblock==2.0.1 # via -r requirements/edx/base.in -redis==4.5.1 +redis==4.5.4 # via -r requirements/edx/base.in -regex==2022.10.31 +regex==2023.3.23 # via nltk requests==2.28.2 # via @@ -1019,7 +1021,7 @@ semantic-version==2.10.0 # via edx-drf-extensions shapely==2.0.1 # via -r requirements/edx/base.in -simplejson==3.18.4 +simplejson==3.19.1 # via # -r requirements/edx/base.in # sailthru-client @@ -1062,7 +1064,7 @@ slumber==0.7.1 # edx-bulk-grades # edx-enterprise # edx-rest-api-client -snowflake-connector-python==3.0.1 +snowflake-connector-python==3.0.2 # via edx-enterprise social-auth-app-django==5.0.0 # via @@ -1081,7 +1083,7 @@ sorl-thumbnail==12.9.0 # openedx-django-wiki sortedcontainers==2.4.0 # via -r requirements/edx/base.in -soupsieve==2.4 +soupsieve==2.4.1 # via beautifulsoup4 sqlparse==0.4.3 # via @@ -1143,7 +1145,7 @@ vine==5.0.0 # kombu voluptuous==0.13.1 # via ora2 -watchdog==2.3.1 +watchdog==3.0.0 # via -r requirements/edx/paver.txt wcwidth==0.2.6 # via prompt-toolkit diff --git a/requirements/edx/coverage.txt b/requirements/edx/coverage.txt index 0192aa373a2f..248e7daa1f3d 100644 --- a/requirements/edx/coverage.txt +++ b/requirements/edx/coverage.txt @@ -6,7 +6,7 @@ # chardet==5.1.0 # via diff-cover -coverage==7.2.1 +coverage==7.2.3 # via -r requirements/edx/coverage.in diff-cover==7.5.0 # via -r requirements/edx/coverage.in @@ -16,5 +16,5 @@ markupsafe==2.1.2 # via jinja2 pluggy==1.0.0 # via diff-cover -pygments==2.14.0 +pygments==2.15.0 # via diff-cover diff --git a/requirements/edx/development.in b/requirements/edx/development.in index 6b4cfa8317a6..1a857f291711 100644 --- a/requirements/edx/development.in +++ b/requirements/edx/development.in @@ -10,7 +10,7 @@ -c ../constraints.txt --r pip-tools.txt # pip-tools and its dependencies, for managing requirements files +-r ../pip-tools.txt # pip-tools and its dependencies, for managing requirements files -r testing.txt # Dependencies for running the various test suites click # Used for perf_tests utilities in modulestore diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 145905017d4b..58dc6727674c 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -24,7 +24,7 @@ amqp==5.1.1 # via # -r requirements/edx/testing.txt # kombu -analytics-python==1.4.0 +analytics-python==1.4.post1 # via -r requirements/edx/testing.txt aniso8601==9.0.1 # via @@ -59,7 +59,7 @@ async-timeout==4.0.2 # -r requirements/edx/testing.txt # aiohttp # redis -attrs==22.2.0 +attrs==23.1.0 # via # -r requirements/edx/testing.txt # aiohttp @@ -68,7 +68,6 @@ attrs==22.2.0 # lti-consumer-xblock # openedx-blockstore # openedx-events - # pytest babel==2.11.0 # via # -c requirements/edx/../constraints.txt @@ -84,7 +83,7 @@ backports-zoneinfo==0.2.1 # via # -r requirements/edx/testing.txt # icalendar -beautifulsoup4==4.11.2 +beautifulsoup4==4.12.2 # via # -r requirements/edx/testing.txt # pynliner @@ -125,7 +124,7 @@ bridgekeeper==0.9 # via -r requirements/edx/testing.txt build==0.10.0 # via - # -r requirements/edx/pip-tools.txt + # -r requirements/edx/../pip-tools.txt # pip-tools celery==5.2.7 # via @@ -168,8 +167,8 @@ chem==1.2.0 click==8.1.3 # via # -c requirements/edx/../constraints.txt + # -r requirements/edx/../pip-tools.txt # -r requirements/edx/development.in - # -r requirements/edx/pip-tools.txt # -r requirements/edx/testing.txt # celery # click-didyoumean @@ -220,7 +219,7 @@ coreschema==0.0.4 # -r requirements/edx/testing.txt # coreapi # drf-yasg -coverage[toml]==7.2.1 +coverage[toml]==7.2.3 # via # -r requirements/edx/testing.txt # pytest-cov @@ -274,7 +273,7 @@ distlib==0.3.6 # via # -r requirements/edx/testing.txt # virtualenv -django==3.2.18 +django==3.2.19 # via # -c requirements/edx/../common_constraints.txt # -r requirements/edx/testing.txt @@ -363,7 +362,7 @@ django-config-models==2.3.0 # edx-enterprise # edx-name-affirmation # lti-consumer-xblock -django-cors-headers==3.13.0 +django-cors-headers==3.14.0 # via -r requirements/edx/testing.txt django-countries==7.5.1 # via @@ -378,7 +377,7 @@ django-crum==0.7.9 # edx-rbac # edx-toggles # super-csv -django-debug-toolbar==3.8.1 +django-debug-toolbar==4.0.0 # via -r requirements/edx/development.in django-environ==0.10.0 # via @@ -388,7 +387,7 @@ django-fernet-fields==0.6 # via # -r requirements/edx/testing.txt # edx-enterprise -django-filter==22.1 +django-filter==23.1 # via # -r requirements/edx/testing.txt # edx-enterprise @@ -593,7 +592,7 @@ edx-django-utils==5.2.0 # ora2 # outcome-surveys # super-csv -edx-drf-extensions==8.4.1 +edx-drf-extensions==8.6.0 # via # -r requirements/edx/testing.txt # edx-completion @@ -610,7 +609,7 @@ edx-enterprise==3.61.11 # -c requirements/edx/../constraints.txt # -r requirements/edx/testing.txt # learner-pathway-progress -edx-event-bus-kafka==3.9.4 +edx-event-bus-kafka==3.9.6 # via -r requirements/edx/testing.txt edx-i18n-tools==0.9.2 # via @@ -677,6 +676,7 @@ edx-toggles==5.0.0 # edx-completion # edx-event-bus-kafka # edx-name-affirmation + # edx-search # edxval # learner-pathway-progress # ora2 @@ -716,11 +716,11 @@ execnet==1.9.0 # pytest-xdist factory-boy==3.2.1 # via -r requirements/edx/testing.txt -faker==17.6.0 +faker==18.4.0 # via # -r requirements/edx/testing.txt # factory-boy -fastapi==0.94.1 +fastapi==0.95.1 # via # -r requirements/edx/testing.txt # pact-python @@ -728,7 +728,7 @@ fastavro==1.7.3 # via # -r requirements/edx/testing.txt # openedx-events -filelock==3.10.0 +filelock==3.11.0 # via # -r requirements/edx/testing.txt # snowflake-connector-python @@ -786,7 +786,7 @@ httpx==0.23.1 # via # -r requirements/edx/testing.txt # pact-python -icalendar==5.0.4 +icalendar==5.0.5 # via -r requirements/edx/testing.txt idna==3.4 # via @@ -801,7 +801,7 @@ imagesize==1.4.1 # via sphinx import-linter==1.8.0 # via -r requirements/edx/testing.txt -importlib-metadata==6.0.0 +importlib-metadata==6.4.1 # via # -r requirements/edx/testing.txt # markdown @@ -960,7 +960,7 @@ mccabe==0.7.0 # pylint mistune==2.0.5 # via sphinx-mdinclude -mock==5.0.1 +mock==5.0.2 # via -r requirements/edx/testing.txt mongodbproxy @ git+https://github.com/openedx/MongoDBProxy.git@d92bafe9888d2940f647a7b2b2383b29c752f35a # via -r requirements/edx/testing.txt @@ -980,7 +980,7 @@ multidict==6.0.4 # -r requirements/edx/testing.txt # aiohttp # yarl -mypy==1.1.1 +mypy==1.2.0 # via -r requirements/edx/development.in mypy-extensions==1.0.0 # via mypy @@ -988,7 +988,7 @@ mysqlclient==2.1.1 # via # -r requirements/edx/testing.txt # openedx-blockstore -newrelic==8.7.0 +newrelic==8.8.0 # via # -r requirements/edx/testing.txt # edx-django-utils @@ -1027,7 +1027,7 @@ openedx-django-require==2.0.0 # via -r requirements/edx/testing.txt openedx-django-wiki==2.0.0 # via -r requirements/edx/testing.txt -openedx-events==5.1.0 +openedx-events==7.0.0 # via # -r requirements/edx/testing.txt # edx-event-bus-kafka @@ -1045,9 +1045,9 @@ oscrypto==1.3.0 # snowflake-connector-python outcome-surveys==2.4.0 # via -r requirements/edx/testing.txt -packaging==23.0 +packaging==23.1 # via - # -r requirements/edx/pip-tools.txt + # -r requirements/edx/../pip-tools.txt # -r requirements/edx/testing.txt # build # drf-yasg @@ -1083,22 +1083,22 @@ pgpy==0.6.0 # via # -r requirements/edx/testing.txt # edx-enterprise -picobox==2.2.0 +picobox==3.0.0 # via sphinxcontrib-openapi piexif==1.1.3 # via -r requirements/edx/testing.txt -pillow==9.4.0 +pillow==9.5.0 # via # -r requirements/edx/testing.txt # edx-enterprise # edx-organizations -pip-tools==6.12.3 - # via -r requirements/edx/pip-tools.txt +pip-tools==6.13.0 + # via -r requirements/edx/../pip-tools.txt pkgutil-resolve-name==1.3.10 # via # -r requirements/edx/testing.txt # jsonschema -platformdirs==3.1.1 +platformdirs==3.2.0 # via # -r requirements/edx/testing.txt # pylint @@ -1117,7 +1117,7 @@ prompt-toolkit==3.0.38 # via # -r requirements/edx/testing.txt # click-repl -psutil==5.9.4 +psutil==5.9.5 # via # -r requirements/edx/testing.txt # edx-django-utils @@ -1152,11 +1152,11 @@ pycryptodomex==3.17 # lti-consumer-xblock # pyjwkest # snowflake-connector-python -pydantic==1.10.6 +pydantic==1.10.7 # via # -r requirements/edx/testing.txt # fastapi -pygments==2.14.0 +pygments==2.15.0 # via # -r requirements/edx/testing.txt # diff-cover @@ -1237,7 +1237,7 @@ pyparsing==3.0.9 # openedx-calc pyproject-hooks==1.0.0 # via - # -r requirements/edx/pip-tools.txt + # -r requirements/edx/../pip-tools.txt # build pyquery==2.0.0 # via -r requirements/edx/testing.txt @@ -1250,7 +1250,7 @@ pysrt==1.1.2 # via # -r requirements/edx/testing.txt # edxval -pytest==7.2.2 +pytest==7.3.1 # via # -r requirements/edx/testing.txt # pylint-pytest @@ -1300,7 +1300,7 @@ python-slugify==8.0.1 # via # -r requirements/edx/testing.txt # code-annotations -python-swiftclient==4.2.0 +python-swiftclient==4.3.0 # via # -r requirements/edx/testing.txt # ora2 @@ -1314,6 +1314,7 @@ python3-saml==1.9.0 # -r requirements/edx/testing.txt pytz==2022.7.1 # via + # -c requirements/edx/../constraints.txt # -r requirements/edx/testing.txt # babel # celery @@ -1349,15 +1350,15 @@ pyyaml==6.0 # xblock random2==1.0.1 # via -r requirements/edx/testing.txt -rapidfuzz==2.13.7 +rapidfuzz==2.15.1 # via # -r requirements/edx/testing.txt # levenshtein recommender-xblock==2.0.1 # via -r requirements/edx/testing.txt -redis==4.5.1 +redis==4.5.4 # via -r requirements/edx/testing.txt -regex==2022.10.31 +regex==2023.3.23 # via # -r requirements/edx/testing.txt # nltk @@ -1432,7 +1433,7 @@ semantic-version==2.10.0 # edx-drf-extensions shapely==2.0.1 # via -r requirements/edx/testing.txt -simplejson==3.18.4 +simplejson==3.19.1 # via # -r requirements/edx/testing.txt # sailthru-client @@ -1491,7 +1492,7 @@ sniffio==1.3.0 # httpx snowballstemmer==2.2.0 # via sphinx -snowflake-connector-python==3.0.1 +snowflake-connector-python==3.0.2 # via # -r requirements/edx/testing.txt # edx-enterprise @@ -1512,7 +1513,7 @@ sorl-thumbnail==12.9.0 # openedx-django-wiki sortedcontainers==2.4.0 # via -r requirements/edx/testing.txt -soupsieve==2.4 +soupsieve==2.4.1 # via # -r requirements/edx/testing.txt # beautifulsoup4 @@ -1584,7 +1585,7 @@ toml==0.10.2 # via vulture tomli==2.0.1 # via - # -r requirements/edx/pip-tools.txt + # -r requirements/edx/../pip-tools.txt # -r requirements/edx/testing.txt # build # coverage @@ -1594,7 +1595,7 @@ tomli==2.0.1 # pyproject-hooks # pytest # tox -tomlkit==0.11.6 +tomlkit==0.11.7 # via # -r requirements/edx/testing.txt # pylint @@ -1665,7 +1666,7 @@ voluptuous==0.13.1 # ora2 vulture==2.7 # via -r requirements/edx/development.in -watchdog==2.3.1 +watchdog==3.0.0 # via -r requirements/edx/testing.txt wcwidth==0.2.6 # via @@ -1691,7 +1692,7 @@ webob==1.8.7 # xblock wheel==0.40.0 # via - # -r requirements/edx/pip-tools.txt + # -r requirements/edx/../pip-tools.txt # pip-tools wrapt==1.15.0 # via diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index 2c73cb516713..087cfd909599 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -34,7 +34,7 @@ idna==3.4 # via requests imagesize==1.4.1 # via sphinx -importlib-metadata==6.0.0 +importlib-metadata==6.4.1 # via sphinx jinja2==3.1.2 # via @@ -42,16 +42,18 @@ jinja2==3.1.2 # sphinx markupsafe==2.1.2 # via jinja2 -packaging==23.0 +packaging==23.1 # via sphinx pbr==5.11.1 # via stevedore -pygments==2.14.0 +pygments==2.15.0 # via sphinx python-slugify==8.0.1 # via code-annotations pytz==2022.7.1 - # via babel + # via + # -c requirements/edx/../constraints.txt + # babel pyyaml==6.0 # via code-annotations requests==2.28.2 diff --git a/requirements/edx/paver.txt b/requirements/edx/paver.txt index 28aff7e3ec5a..fe1b04e27b1c 100644 --- a/requirements/edx/paver.txt +++ b/requirements/edx/paver.txt @@ -20,7 +20,7 @@ libsass==0.10.0 # via -r requirements/edx/paver.in markupsafe==2.1.2 # via -r requirements/edx/paver.in -mock==5.0.1 +mock==5.0.2 # via -r requirements/edx/paver.in path==16.6.0 # via -r requirements/edx/paver.in @@ -28,7 +28,7 @@ paver==1.3.4 # via -r requirements/edx/paver.in pbr==5.11.1 # via stevedore -psutil==5.9.4 +psutil==5.9.5 # via -r requirements/edx/paver.in pymongo==3.13.0 # via @@ -50,7 +50,7 @@ stevedore==5.0.0 # edx-opaque-keys urllib3==1.26.15 # via requests -watchdog==2.3.1 +watchdog==3.0.0 # via -r requirements/edx/paver.in wrapt==1.15.0 # via -r requirements/edx/paver.in diff --git a/requirements/edx/pip.txt b/requirements/edx/pip.txt deleted file mode 100644 index f0e5e9397cae..000000000000 --- a/requirements/edx/pip.txt +++ /dev/null @@ -1,14 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.8 -# by the following command: -# -# make upgrade -# -wheel==0.40.0 - # via -r requirements/edx/pip.in - -# The following packages are considered to be unsafe in a requirements file: -pip==23.0.1 - # via -r requirements/edx/pip.in -setuptools==67.6.0 - # via -r requirements/edx/pip.in diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 5a94e1edf188..7bf33a176d50 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -22,7 +22,7 @@ amqp==5.1.1 # via # -r requirements/edx/base.txt # kombu -analytics-python==1.4.0 +analytics-python==1.4.post1 # via -r requirements/edx/base.txt aniso8601==9.0.1 # via @@ -55,7 +55,7 @@ async-timeout==4.0.2 # -r requirements/edx/base.txt # aiohttp # redis -attrs==22.2.0 +attrs==23.1.0 # via # -r requirements/edx/base.txt # aiohttp @@ -64,8 +64,6 @@ attrs==22.2.0 # lti-consumer-xblock # openedx-blockstore # openedx-events - # outcome - # pytest babel==2.11.0 # via # -c requirements/edx/../constraints.txt @@ -80,7 +78,7 @@ backports-zoneinfo==0.2.1 # via # -r requirements/edx/base.txt # icalendar -beautifulsoup4==4.11.2 +beautifulsoup4==4.12.2 # via # -r requirements/edx/base.txt # -r requirements/edx/testing.in @@ -210,7 +208,7 @@ coreschema==0.0.4 # -r requirements/edx/base.txt # coreapi # drf-yasg -coverage[toml]==7.2.1 +coverage[toml]==7.2.3 # via # -r requirements/edx/coverage.txt # pytest-cov @@ -258,7 +256,7 @@ dill==0.3.6 # via pylint distlib==0.3.6 # via virtualenv -django==3.2.18 +django==3.2.19 # via # -c requirements/edx/../common_constraints.txt # -r requirements/edx/base.txt @@ -346,7 +344,7 @@ django-config-models==2.3.0 # edx-enterprise # edx-name-affirmation # lti-consumer-xblock -django-cors-headers==3.13.0 +django-cors-headers==3.14.0 # via -r requirements/edx/base.txt django-countries==7.5.1 # via @@ -369,7 +367,7 @@ django-fernet-fields==0.6 # via # -r requirements/edx/base.txt # edx-enterprise -django-filter==22.1 +django-filter==23.1 # via # -r requirements/edx/base.txt # edx-enterprise @@ -572,7 +570,7 @@ edx-django-utils==5.2.0 # ora2 # outcome-surveys # super-csv -edx-drf-extensions==8.4.1 +edx-drf-extensions==8.6.0 # via # -r requirements/edx/base.txt # edx-completion @@ -589,7 +587,7 @@ edx-enterprise==3.61.11 # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt # learner-pathway-progress -edx-event-bus-kafka==3.9.4 +edx-event-bus-kafka==3.9.6 # via -r requirements/edx/base.txt edx-i18n-tools==0.9.2 # via @@ -655,6 +653,7 @@ edx-toggles==5.0.0 # edx-completion # edx-event-bus-kafka # edx-name-affirmation + # edx-search # edxval # learner-pathway-progress # ora2 @@ -690,15 +689,15 @@ execnet==1.9.0 # via pytest-xdist factory-boy==3.2.1 # via -r requirements/edx/testing.in -faker==17.6.0 +faker==18.4.0 # via factory-boy -fastapi==0.94.1 +fastapi==0.95.1 # via pact-python fastavro==1.7.3 # via # -r requirements/edx/base.txt # openedx-events -filelock==3.10.0 +filelock==3.11.0 # via # -r requirements/edx/base.txt # snowflake-connector-python @@ -749,7 +748,7 @@ httpretty==1.1.4 # via -r requirements/edx/testing.in httpx==0.23.1 # via pact-python -icalendar==5.0.4 +icalendar==5.0.5 # via -r requirements/edx/base.txt idna==3.4 # via @@ -762,7 +761,7 @@ idna==3.4 # yarl import-linter==1.8.0 # via -r requirements/edx/testing.in -importlib-metadata==6.0.0 +importlib-metadata==6.4.1 # via # -r requirements/edx/base.txt # markdown @@ -912,7 +911,7 @@ maxminddb==2.2.0 # geoip2 mccabe==0.7.0 # via pylint -mock==5.0.1 +mock==5.0.2 # via -r requirements/edx/base.txt mongodbproxy @ git+https://github.com/openedx/MongoDBProxy.git@d92bafe9888d2940f647a7b2b2383b29c752f35a # via -r requirements/edx/base.txt @@ -936,7 +935,7 @@ mysqlclient==2.1.1 # via # -r requirements/edx/base.txt # openedx-blockstore -newrelic==8.7.0 +newrelic==8.8.0 # via # -r requirements/edx/base.txt # edx-django-utils @@ -975,7 +974,7 @@ openedx-django-require==2.0.0 # via -r requirements/edx/base.txt openedx-django-wiki==2.0.0 # via -r requirements/edx/base.txt -openedx-events==5.1.0 +openedx-events==7.0.0 # via # -r requirements/edx/base.txt # edx-event-bus-kafka @@ -993,7 +992,7 @@ oscrypto==1.3.0 # snowflake-connector-python outcome-surveys==2.4.0 # via -r requirements/edx/base.txt -packaging==23.0 +packaging==23.1 # via # -r requirements/edx/base.txt # drf-yasg @@ -1030,7 +1029,7 @@ pgpy==0.6.0 # edx-enterprise piexif==1.1.3 # via -r requirements/edx/base.txt -pillow==9.4.0 +pillow==9.5.0 # via # -r requirements/edx/base.txt # edx-enterprise @@ -1039,7 +1038,7 @@ pkgutil-resolve-name==1.3.10 # via # -r requirements/edx/base.txt # jsonschema -platformdirs==3.1.1 +platformdirs==3.2.0 # via # pylint # virtualenv @@ -1058,7 +1057,7 @@ prompt-toolkit==3.0.38 # via # -r requirements/edx/base.txt # click-repl -psutil==5.9.4 +psutil==5.9.5 # via # -r requirements/edx/base.txt # edx-django-utils @@ -1091,9 +1090,9 @@ pycryptodomex==3.17 # lti-consumer-xblock # pyjwkest # snowflake-connector-python -pydantic==1.10.6 +pydantic==1.10.7 # via fastapi -pygments==2.14.0 +pygments==2.15.0 # via # -r requirements/edx/base.txt # -r requirements/edx/coverage.txt @@ -1176,7 +1175,7 @@ pysrt==1.1.2 # via # -r requirements/edx/base.txt # edxval -pytest==7.2.2 +pytest==7.3.1 # via # -r requirements/edx/testing.in # pylint-pytest @@ -1226,7 +1225,7 @@ python-slugify==8.0.1 # via # -r requirements/edx/base.txt # code-annotations -python-swiftclient==4.2.0 +python-swiftclient==4.3.0 # via # -r requirements/edx/base.txt # ora2 @@ -1240,6 +1239,7 @@ python3-saml==1.9.0 # -r requirements/edx/base.txt pytz==2022.7.1 # via + # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt # babel # celery @@ -1272,15 +1272,15 @@ pyyaml==6.0 # xblock random2==1.0.1 # via -r requirements/edx/base.txt -rapidfuzz==2.13.7 +rapidfuzz==2.15.1 # via # -r requirements/edx/base.txt # levenshtein recommender-xblock==2.0.1 # via -r requirements/edx/base.txt -redis==4.5.1 +redis==4.5.4 # via -r requirements/edx/base.txt -regex==2022.10.31 +regex==2023.3.23 # via # -r requirements/edx/base.txt # nltk @@ -1352,7 +1352,7 @@ semantic-version==2.10.0 # edx-drf-extensions shapely==2.0.1 # via -r requirements/edx/base.txt -simplejson==3.18.4 +simplejson==3.19.1 # via # -r requirements/edx/base.txt # sailthru-client @@ -1406,7 +1406,7 @@ sniffio==1.3.0 # anyio # httpcore # httpx -snowflake-connector-python==3.0.1 +snowflake-connector-python==3.0.2 # via # -r requirements/edx/base.txt # edx-enterprise @@ -1427,7 +1427,7 @@ sorl-thumbnail==12.9.0 # openedx-django-wiki sortedcontainers==2.4.0 # via -r requirements/edx/base.txt -soupsieve==2.4 +soupsieve==2.4.1 # via # -r requirements/edx/base.txt # beautifulsoup4 @@ -1476,7 +1476,7 @@ tomli==2.0.1 # pylint # pytest # tox -tomlkit==0.11.6 +tomlkit==0.11.7 # via pylint tox==3.28.0 # via @@ -1538,7 +1538,7 @@ voluptuous==0.13.1 # via # -r requirements/edx/base.txt # ora2 -watchdog==2.3.1 +watchdog==3.0.0 # via -r requirements/edx/base.txt wcwidth==0.2.6 # via diff --git a/requirements/edx/pip-tools.in b/requirements/pip-tools.in similarity index 95% rename from requirements/edx/pip-tools.in rename to requirements/pip-tools.in index 50f3de3e19d6..5d4419ea4bc0 100644 --- a/requirements/edx/pip-tools.in +++ b/requirements/pip-tools.in @@ -7,6 +7,6 @@ # * confirm that it has no system requirements beyond what we already install # * run "make upgrade" to update the detailed requirements files --c ../constraints.txt +-c constraints.txt pip-tools # Contains pip-compile, used to generate pip requirements files diff --git a/requirements/edx/pip-tools.txt b/requirements/pip-tools.txt similarity index 76% rename from requirements/edx/pip-tools.txt rename to requirements/pip-tools.txt index 15e167ee7153..cda5abf53760 100644 --- a/requirements/edx/pip-tools.txt +++ b/requirements/pip-tools.txt @@ -8,12 +8,12 @@ build==0.10.0 # via pip-tools click==8.1.3 # via - # -c requirements/edx/../constraints.txt + # -c requirements/constraints.txt # pip-tools -packaging==23.0 +packaging==23.1 # via build -pip-tools==6.12.3 - # via -r requirements/edx/pip-tools.in +pip-tools==6.13.0 + # via -r requirements/pip-tools.in pyproject-hooks==1.0.0 # via build tomli==2.0.1 diff --git a/requirements/edx/pip.in b/requirements/pip.in similarity index 77% rename from requirements/edx/pip.in rename to requirements/pip.in index 741969aac127..cc36db5a0831 100644 --- a/requirements/edx/pip.in +++ b/requirements/pip.in @@ -1,4 +1,4 @@ --c ../constraints.txt +-c constraints.txt # Core dependencies for installing other dependencies pip diff --git a/requirements/pip.txt b/requirements/pip.txt index a811cc82f187..4b902cbc3468 100644 --- a/requirements/pip.txt +++ b/requirements/pip.txt @@ -1,2 +1,14 @@ -pip==22.1 -wheel==0.37.1 +# +# This file is autogenerated by pip-compile with Python 3.8 +# by the following command: +# +# make upgrade +# +wheel==0.40.0 + # via -r requirements/pip.in + +# The following packages are considered to be unsafe in a requirements file: +pip==23.1 + # via -r requirements/pip.in +setuptools==67.6.1 + # via -r requirements/pip.in diff --git a/scripts/ci-runner.Dockerfile b/scripts/ci-runner.Dockerfile index 999b89941eff..291f1d33d32f 100644 --- a/scripts/ci-runner.Dockerfile +++ b/scripts/ci-runner.Dockerfile @@ -46,7 +46,7 @@ COPY openedx/core/lib openedx/core/lib COPY lms lms COPY cms cms COPY requirements/pip.txt requirements/pip.txt -COPY requirements/edx/pip-tools.txt requirements/edx/pip-tools.txt +COPY requirements/pip-tools.txt requirements/pip-tools.txt COPY requirements/edx/testing.txt requirements/edx/testing.txt COPY Makefile Makefile RUN make test-requirements diff --git a/xmodule/js/src/video/04_video_full_screen.js b/xmodule/js/src/video/04_video_full_screen.js index 0730300d105e..9da85d8dfb67 100644 --- a/xmodule/js/src/video/04_video_full_screen.js +++ b/xmodule/js/src/video/04_video_full_screen.js @@ -161,7 +161,21 @@ return this.videoFullScreen.height; } - /** + function notifyParent(fullscreenOpen) { + if (window !== window.parent) { + // This is used by the Learning MFE to know about changing fullscreen mode. + // The MFE is then able to respond appropriately and scroll window to the previous position. + window.parent.postMessage({ + type: 'plugin.videoFullScreen', + payload: { + open: fullscreenOpen + } + }, document.referrer + ); + } + } + + /** * Event handler to toggle fullscreen mode. * @param {jquery Event} event */ @@ -192,6 +206,8 @@ this.resizer.delta.reset().setMode('width'); } this.el.trigger('fullscreen', [this.isFullScreen]); + + this.videoFullScreen.notifyParent(false); } function handleEnter() { @@ -202,6 +218,8 @@ return; } + this.videoFullScreen.notifyParent(true); + this.videoFullScreen.fullScreenState = this.isFullScreen = true; fullScreenClassNameEl.addClass('video-fullscreen'); this.videoFullScreen.fullScreenEl @@ -267,7 +285,8 @@ handleFullscreenChange: handleFullscreenChange, toggle: toggle, toggleHandler: toggleHandler, - updateControlsHeight: updateControlsHeight + updateControlsHeight: updateControlsHeight, + notifyParent: notifyParent }; state.bindTo(methodsDict, state.videoFullScreen, state);