Skip to content

Commit 696e7df

Browse files
authored
Merge branch 'openedx:master' into kdmccormick/upload-delete-transcript
2 parents 097c430 + 1253831 commit 696e7df

File tree

12 files changed

+87
-54
lines changed

12 files changed

+87
-54
lines changed

.github/workflows/ci-static-analysis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
run: sudo apt update && sudo apt install -y libxmlsec1-dev
2626

2727
- name: Install pip
28-
run: python -m pip install -r requirements/pip.txt
28+
run: make pre-requirements
2929

3030
- name: Get pip cache dir
3131
id: pip-cache-dir

.github/workflows/lint-imports.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
run: sudo apt update && sudo apt install -y libxmlsec1-dev
2626

2727
- name: Install pip
28-
run: python -m pip install -r requirements/pip.txt
28+
run: make pre-requirements
2929

3030
- name: Get pip cache dir
3131
id: pip-cache-dir
@@ -40,7 +40,7 @@ jobs:
4040
restore-keys: ${{ runner.os }}-pip-
4141

4242
- name: Install python dependencies
43-
run: pip install -r requirements/edx/development.txt
43+
run: make dev-requirements
4444

4545
# As long there are sub-projects[1] in edx-platform, we analyze each
4646
# project separately here, in order to make import-linting errors easier

Makefile

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ detect_changed_source_translations: ## check if translation files are up-to-date
6666
i18n_tool changed
6767

6868
pre-requirements: ## install Python requirements for running pip-tools
69-
pip install -r requirements/pip.txt
7069
pip install -r requirements/pip-tools.txt
7170

7271
local-requirements:
@@ -122,12 +121,10 @@ compile-requirements: pre-requirements $(COMMON_CONSTRAINTS_TXT) ## Re-compile *
122121
@# time someone tries to use the outputs.
123122
sed 's/Django<5.0//g' requirements/common_constraints.txt > requirements/common_constraints.tmp
124123
mv requirements/common_constraints.tmp requirements/common_constraints.txt
125-
sed 's/pip<24.3//g' requirements/common_constraints.txt > requirements/common_constraints.tmp
124+
sed 's/pip<25.3//g' requirements/common_constraints.txt > requirements/common_constraints.tmp
126125
mv requirements/common_constraints.tmp requirements/common_constraints.txt
127-
pip-compile -v --allow-unsafe ${COMPILE_OPTS} -o requirements/pip.txt requirements/pip.in
128-
pip install -r requirements/pip.txt
129126

130-
pip-compile -v ${COMPILE_OPTS} -o requirements/pip-tools.txt requirements/pip-tools.in
127+
pip-compile -v --allow-unsafe ${COMPILE_OPTS} -o requirements/pip-tools.txt requirements/pip-tools.in
131128
pip install -r requirements/pip-tools.txt
132129

133130
@ export REBUILD='--rebuild'; \

cms/djangoapps/contentstore/tasks.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -945,17 +945,6 @@ def _get_users_by_access_level(v1_library_key):
945945
v2contentlib_api.set_library_user_permissions(v2_library_key, user, access_level)
946946

947947

948-
def _create_copy_content_task(v2_library_key, v1_library_key):
949-
"""
950-
spin up a celery task to import the V1 Library's content into the V2 library.
951-
This utilizes the fact that course and v1 library content is stored almost identically.
952-
"""
953-
return v2contentlib_api.import_blocks_create_task(
954-
v2_library_key, v1_library_key,
955-
use_course_key_as_block_id_suffix=False
956-
)
957-
958-
959948
@shared_task(time_limit=30)
960949
@set_code_owner_attribute
961950
def delete_v1_library(v1_library_key_string):

lms/djangoapps/courseware/access_utils.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@
2323
StartDateError,
2424
)
2525
from lms.djangoapps.courseware.masquerade import get_course_masquerade, is_masquerading_as_student
26-
from openedx.features.course_experience import COURSE_ENABLE_UNENROLLED_ACCESS_FLAG, COURSE_PRE_START_ACCESS_FLAG
26+
from openedx.features.course_experience import (
27+
COURSE_ENABLE_UNENROLLED_ACCESS_FLAG,
28+
COURSE_PRE_START_ACCESS_FLAG,
29+
ENFORCE_MASQUERADE_START_DATES,
30+
)
2731
from xmodule.course_block import COURSE_VISIBILITY_PUBLIC # lint-amnesty, pylint: disable=wrong-import-order
2832

2933
DEBUG_ACCESS = False
@@ -137,7 +141,10 @@ def check_start_date(user, days_early_for_beta, start, course_key, display_error
137141
if start_dates_disabled and not masquerading_as_student:
138142
return ACCESS_GRANTED
139143
else:
140-
if start is None or get_course_masquerade(user, course_key):
144+
if start is None:
145+
return ACCESS_GRANTED
146+
147+
if not ENFORCE_MASQUERADE_START_DATES.is_enabled(course_key) and get_course_masquerade(user, course_key):
141148
return ACCESS_GRANTED
142149

143150
if now is None:

lms/djangoapps/courseware/tests/test_access.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from django.test.client import RequestFactory
1818
from django.test.utils import override_settings
1919
from django.urls import reverse
20+
from edx_toggles.toggles.testutils import override_waffle_flag
2021
from milestones.tests.utils import MilestonesTestCaseMixin
2122
from opaque_keys.edx.locator import CourseLocator
2223

@@ -30,6 +31,7 @@
3031
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
3132
from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES
3233
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
34+
from openedx.features.course_experience import ENFORCE_MASQUERADE_START_DATES
3335
from common.djangoapps.student.models import CourseEnrollment
3436
from common.djangoapps.student.roles import CourseCcxCoachRole, CourseStaffRole
3537
from common.djangoapps.student.tests.factories import (
@@ -465,6 +467,50 @@ def test__has_access_to_block_with_start_date(self, start, expected_error_type):
465467

466468
self.verify_access(mock_unit, expected_access, expected_error_type)
467469

470+
@ddt.data(
471+
# Flag inactive (default)
472+
(False, True, None), # Masquerading, no start date
473+
(False, True, YESTERDAY), # Masquerading, past start date
474+
(False, False, TOMORROW), # Not masquerading, future start date
475+
(False, True, TOMORROW), # Masquerading, future start date
476+
# Flag active
477+
(True, True, None), # Masquerading, no start date
478+
(True, True, YESTERDAY), # Masquerading, past start date
479+
(True, False, TOMORROW), # Not masquerading, future start date
480+
(True, True, TOMORROW, False), # Masquerading, future start date - no access
481+
)
482+
@ddt.unpack
483+
@patch.dict("django.conf.settings.FEATURES", {"DISABLE_START_DATES": False})
484+
def test_enforce_masquerade_start_dates_flag(self, flag_active, is_masquerading, start, expected_access=True):
485+
"""
486+
Test that the ENFORCE_MASQUERADE_START_DATES flag controls whether masquerading bypasses start date
487+
restrictions.
488+
489+
When the flag is disabled (default), masquerading users bypass start dates.
490+
When the flag is enabled, masquerading users see the same start date restrictions as regular students.
491+
"""
492+
mock_unit = Mock(
493+
location=self.course.location,
494+
user_partitions=[],
495+
_class_tags={},
496+
start=self.DATES[start],
497+
visible_to_staff_only=False,
498+
merged_group_access={},
499+
)
500+
501+
if is_masquerading:
502+
self.course_staff.masquerade_settings = {self.course.id: CourseMasquerade(self.course.id, role="student")}
503+
504+
with override_waffle_flag(ENFORCE_MASQUERADE_START_DATES, active=flag_active):
505+
response = access._has_access_to_block(self.course_staff, "load", mock_unit, course_key=self.course.id)
506+
507+
if expected_access:
508+
assert response == access.ACCESS_GRANTED
509+
else:
510+
assert isinstance(response, access_response.StartDateError)
511+
assert response.to_json()["error_code"] is not None
512+
assert str(self.DATES[start]) in response.developer_message
513+
468514
def test__has_access_course_can_enroll(self):
469515
yesterday = datetime.datetime.now(pytz.utc) - datetime.timedelta(days=1)
470516
tomorrow = datetime.datetime.now(pytz.utc) + datetime.timedelta(days=1)

lms/static/sass/course/instructor/_instructor_2.scss

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -708,16 +708,14 @@
708708
.hint {
709709
@extend %t-copy-sub2;
710710

711-
display: block;
711+
display: none;
712712
position: absolute;
713713
top: ($baseline/2);
714-
715-
@include left(-9999em);
716-
717714
padding: ($baseline/2);
718715
width: 50%;
719716
background-color: $light-gray3;
720717
box-shadow: 2px 2px 3px $shadow;
718+
z-index: 1;
721719

722720
.hint-caret {
723721
display: block;
@@ -736,11 +734,10 @@
736734
/* ***
737735
* Ideally we want to handle functionality with JS.
738736
* This functionality should eventually be moved into CS/JS, and out of here. */
739-
.has-hint:hover > .hint {
740-
@include left($baseline*10);
741-
}
742-
737+
.has-hint:hover > .hint,
743738
.has-hint input:focus ~ .hint {
739+
display: block;
740+
744741
@include left($baseline*10);
745742
}
746743

openedx/features/course_experience/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,23 @@
8383
# .. toggle_tickets: https://openedx.atlassian.net/browse/AA-36
8484
CALENDAR_SYNC_FLAG = CourseWaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.calendar_sync', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
8585

86+
# .. toggle_name: course_experience.enforce_masquerade_start_dates
87+
# .. toggle_implementation: CourseWaffleFlag
88+
# .. toggle_default: False
89+
# .. toggle_description: When enabled, staff masquerading as students will see the same start date
90+
# restrictions as actual students. This provides a more accurate preview experience by enforcing
91+
# section and subsection start dates even when viewing the course as a masqueraded user.
92+
# When disabled (default), masquerading continues to bypass start date restrictions as before.
93+
# .. toggle_use_cases: opt_in
94+
# .. toggle_creation_date: 2025-10-08
95+
# .. toggle_warning: Enabling this flag means staff members masquerading as students will not be able to access course
96+
# content before its start date, which may impact course testing workflows.
97+
# Also, when you masquerade as a student in a course that starts in the future, you will lock yourself out of the
98+
# course in the current Django session. To revert this, you need to log out and log back in.
99+
ENFORCE_MASQUERADE_START_DATES = CourseWaffleFlag(
100+
f'{WAFFLE_FLAG_NAMESPACE}.enforce_masquerade_start_dates', __name__
101+
)
102+
86103

87104
def course_home_page_title(_course):
88105
"""

requirements/common_constraints.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ elasticsearch<7.14.0
2828
# See issue https://github.com/openedx/public-engineering/issues/440 for details regarding the ongoing fix.
2929
# The constraint can be removed once a release (pip-tools > 7.5.1) is available with support for pip 25.3
3030
# Issue to track this dependency and unpin later on: https://github.com/openedx/edx-lint/issues/503
31-
pip<25.3
31+

requirements/pip-tools.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,7 @@ wheel==0.45.1
2020
# via pip-tools
2121

2222
# The following packages are considered to be unsafe in a requirements file:
23-
# pip
24-
# setuptools
23+
pip==25.3
24+
# via pip-tools
25+
setuptools==80.9.0
26+
# via pip-tools

0 commit comments

Comments
 (0)