Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
87f3155
docs: add ADR for standardizing serializer usage (#38139)
Faraz32123 Mar 17, 2026
3235cef
refactor: use video block utils from xblocks-contrib package (#38088)
farhan Mar 9, 2026
aa09b53
feat: Upgrade Python dependency enterprise-integrated-channels (#38128)
github-actions[bot] Mar 9, 2026
c93f8ca
chore: bumped edx-enterprise version to 6.6.7 (#38120)
pbitla-sonata Mar 9, 2026
e655e1c
feat: update openedx-core: new catalog models + backfill migration (#…
bradenmacdonald Mar 10, 2026
655aba8
feat: Upgrade Python dependency xblocks-contrib (#38134)
github-actions[bot] Mar 10, 2026
cb8699e
test: fix/update test cases for the extracted video block (#38123)
farhan Mar 10, 2026
98ec58d
refactor: Remove capa folders (#38045)
irtazaakram Mar 10, 2026
4c3c874
fix: removing enterprise specific code from platform (#38117)
kiram15 Mar 10, 2026
0a72ed1
chore: Upgrade Python requirements
edx-requirements-bot Mar 10, 2026
43d87cf
fix: authz compat layer was failing on libraries v2 keys (#38131)
rodmgwgu Mar 11, 2026
a6425fc
feat: Upgrade Python dependency edx-enterprise
iloveagent57 Mar 10, 2026
254be03
feat: make discovery service dependency optional for discussion notif…
asajjad2 Mar 13, 2026
e773f94
feat: enable extracted (xblocks-contrib) video block (#38151)
farhan Mar 16, 2026
4dfc0cc
feat: Upgrade Python dependency enterprise-integrated-channels
iloveagent57 Mar 13, 2026
fb37eae
refactor: move third_party_auth settings from app startup to static c…
feanil Jan 30, 2026
70d8cb7
fix: skip third_party_auth settings tests under CMS and guard enterpr…
feanil Jan 30, 2026
953ec65
fix: Apply suggestions from code review
feanil Mar 11, 2026
1a77748
feat: User agreements API for generic agreement records
xitij2000 Nov 20, 2024
efff56e
feat: New User Agreements API
xitij2000 Jan 27, 2026
8506f2e
chore: drop Python 3.11 support
feanil Mar 8, 2026
9685d31
style: fix pylint 4.x line-too-long violations
feanil Mar 12, 2026
d1217cd
chore: regenerate requirements with Python 3.12
feanil Mar 8, 2026
5fe596c
fix: pin setuptools<82 in static-assets-check workflow
feanil Mar 12, 2026
8fc09ff
fix: Update more mentions of Python 3.11
feanil Mar 16, 2026
1ca60d2
feat: Upgrade Python dependency edx-enterprise
iloveagent57 Mar 16, 2026
914ad20
fix: update problem block tests (#37136)
irtazaakram Mar 17, 2026
a639d8a
feat: add sort order to cohorts endpoint (#38149)
brianjbuck-wgu Mar 17, 2026
6c7bc40
chore: Upgrade Python requirements
edx-requirements-bot Mar 17, 2026
62dc746
chore: version bump (#38183)
kiram15 Mar 17, 2026
42b46d4
docs: add ADR for standardizing permissions usage
Mar 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/check-consistent-dependencies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
- uses: actions/setup-python@v6
if: ${{ env.RELEVANT == 'true' }}
with:
python-version: '3.11'
python-version: '3.12'

- name: "Recompile requirements"
if: ${{ env.RELEVANT == 'true' }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-static-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
python-version:
- "3.11"
- "3.12"
os: ["ubuntu-24.04"]

steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/compile-python-requirements.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- name: Set up Python environment
uses: actions/setup-python@v6
with:
python-version: "3.11"
python-version: "3.12"

- name: Run make compile-requirements
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/js-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
os: [ubuntu-latest]
node-version: [20]
python-version:
- "3.11"
- "3.12"

steps:
- uses: actions/checkout@v6
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint-imports.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
python-version: "3.12"

- name: Install system requirements
run: sudo apt update && sudo apt install -y libxmlsec1-dev
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/migrations-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
matrix:
os: [ubuntu-24.04]
python-version:
- "3.11"
- "3.12"
# 'pinned' is used to install the latest patch version of Django
# within the global constraint i.e. Django==4.2.8 in current case
# because we have global constraint of Django<4.2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pylint-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: 3.11
python-version: 3.12

- name: Get pip cache dir
id: pip-cache-dir
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/quality-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
matrix:
os: [ubuntu-24.04]
python-version:
- "3.11"
- "3.12"
node-version: [20]

steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/semgrep.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
matrix:
os: ["ubuntu-latest"]
python-version:
- "3.11"
- "3.12"

steps:
- uses: actions/checkout@v6
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/static-assets-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
matrix:
os: [ubuntu-24.04]
python-version:
- "3.11"
- "3.12"
node-version: [20]
npm-version: [10.7.x]
mongo-version:
Expand Down Expand Up @@ -71,6 +71,13 @@ jobs:

- name: Install Limited Python Deps for Build
run: |
# Install pip-tools.txt first to pin setuptools<82 before installing
# assets.txt. setuptools 82+ removed pkg_resources, which pyfilesystem2
# (fs) still uses for namespace package declarations. The constraints.txt
# pin covers full installs, but this step only installs assets.txt so we
# pre-install pip-tools.txt to ensure setuptools 81.x is in place.
# See: https://github.com/PyFilesystem/pyfilesystem2/issues/577
pip install -r requirements/pip-tools.txt
pip install -r requirements/edx/assets.txt

- name: Add node_modules bin to $Path
Expand Down
29 changes: 14 additions & 15 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ jobs:
strategy:
matrix:
python-version:
- "3.11"
- "3.12"
django-version:
- "pinned"
Expand Down Expand Up @@ -57,18 +56,18 @@ jobs:
#
# We're testing the older version of Ubuntu and running the xmodule tests since those rely on many
# dependent complex libraries and will hopefully catch most issues quickly.
include:
- shard_name: "xmodule-with-cms"
python-version: "3.11"
django-version: "pinned"
mongo-version: "7.0"
os-version: "ubuntu-22.04"
- shard_name: "xmodule-with-lms"
python-version: "3.11"
django-version: "pinned"
mongo-version: "7.0"
os-version: "ubuntu-22.04"

#
# include:
# - shard_name: "xmodule-with-cms"
# python-version: "3.12"
# django-version: "pinned"
# mongo-version: "7.0"
# os-version: "ubuntu-24.04"
# - shard_name: "xmodule-with-lms"
# python-version: "3.12"
# django-version: "pinned"
# mongo-version: "7.0"
# os-version: "ubuntu-24.04"
steps:
- name: checkout repo
uses: actions/checkout@v6
Expand Down Expand Up @@ -156,7 +155,7 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.11
python-version: 3.12

- name: install system requirements
run: |
Expand Down Expand Up @@ -278,7 +277,7 @@ jobs:
strategy:
matrix:
python-version:
- 3.11
- 3.12
steps:
- name: Checkout repo
uses: actions/checkout@v6
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/upgrade-one-python-dependency.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
- name: Set up Python environment
uses: actions/setup-python@v6
with:
python-version: "3.11"
python-version: "3.12"

- name: Update any pinned dependencies
env:
Expand Down
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ version: 2
build:
os: "ubuntu-lts-latest"
tools:
python: "3.11"
python: "3.12"

sphinx:
configuration: docs/conf.py
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ OS:

Interpreters/Tools:

* Python 3.11 or 3.12
* Python 3.12

* Node: See the ``.nvmrc`` file in this repository.

Expand Down
2 changes: 1 addition & 1 deletion cms/djangoapps/contentstore/rest_api/v2/views/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from rest_framework.generics import GenericAPIView
from rest_framework import permissions
from cms.djangoapps.contentstore.rest_api.v2.serializers.utils import NumericalInputValidationRequestSerializer
from xmodule.capa.inputtypes import preview_numeric_input
from xblocks_contrib.problem.capa.inputtypes import preview_numeric_input


class NumericalInputValidationView(GenericAPIView):
Expand Down
7 changes: 6 additions & 1 deletion cms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,6 @@

MAKO_TEMPLATE_DIRS_BASE.insert(3, COMMON_ROOT / 'static')
MAKO_TEMPLATE_DIRS_BASE.append(CMS_ROOT / 'djangoapps' / 'pipeline_js' / 'templates')
MAKO_TEMPLATE_DIRS_BASE.append(XMODULE_ROOT / 'capa' / 'templates')


def make_lms_template_path(settings):
Expand Down Expand Up @@ -787,6 +786,9 @@ def make_lms_template_path(settings):

'common.djangoapps.xblock_django',

# Agreements
'openedx.core.djangoapps.agreements',

# Catalog integration
'openedx.core.djangoapps.catalog',

Expand Down Expand Up @@ -907,6 +909,9 @@ def make_lms_template_path(settings):

'openedx_events',

# Core models to represent courses
"openedx_catalog",

# Core apps that power libraries
"openedx_content",
*openedx_content_backcompat_apps_to_install(),
Expand Down
6 changes: 4 additions & 2 deletions cms/envs/devstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,9 +344,11 @@ def should_show_debug_toolbar(request): # lint-amnesty, pylint: disable=missing
access token endpoint: `{LMS_ROOT_URL}/oauth2/access_token`.
Please see separately provided documentation.
\n - How to test: You must be logged in as course author for whatever course you want to test with.
You can use the [Swagger UI](https://{CMS_BASE}/authoring-api/ui/) to "Try out" the API with your test course. To do this, you must select the "Local" server.
You can use the [Swagger UI](https://{CMS_BASE}/authoring-api/ui/) to "Try out" the API
with your test course. To do this, you must select the "Local" server.
\n - Public vs. Local servers: The "Public" server is where you can reach the API externally. The "Local" server is
for development with a local edx-platform version, and for use via the [Swagger UI](https://{CMS_BASE}/authoring-api/ui/).
for development with a local edx-platform version, and for use via the
[Swagger UI](https://{CMS_BASE}/authoring-api/ui/).
\n - Swaggerfile: [Download link](https://{CMS_BASE}/authoring-api/schema/)''',
'VERSION': '0.1.0',
'SERVE_INCLUDE_SCHEMA': False,
Expand Down
6 changes: 4 additions & 2 deletions cms/envs/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,9 +409,11 @@ def get_env_setting(setting):
access token endpoint: `{LMS_ROOT_URL}/oauth2/access_token`.
Please see separately provided documentation.
\n - How to test: You must be logged in as course author for whatever course you want to test with.
You can use the [Swagger UI](https://{CMS_BASE}/authoring-api/ui/) to "Try out" the API with your test course. To do this, you must select the "Local" server.
You can use the [Swagger UI](https://{CMS_BASE}/authoring-api/ui/) to "Try out" the API
with your test course. To do this, you must select the "Local" server.
\n - Public vs. Local servers: The "Public" server is where you can reach the API externally. The "Local" server is
for development with a local edx-platform version, and for use via the [Swagger UI](https://{CMS_BASE}/authoring-api/ui/).
for development with a local edx-platform version, and for use via the
[Swagger UI](https://{CMS_BASE}/authoring-api/ui/).
\n - Swaggerfile: [Download link](https://{CMS_BASE}/authoring-api/schema/)''',
'VERSION': '0.1.0',
'SERVE_INCLUDE_SCHEMA': False,
Expand Down
1 change: 0 additions & 1 deletion common/djangoapps/edxmako/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ def render_lms_template(self, template_file, dictionary):

Templates which are in these dirs will only work with this function:
edx-platform/lms/templates/
edx-platform/xmodule/capa/templates/
openedx/features/course_experience/templates
"""
return render_to_string(template_file, dictionary, namespace=lms_mako_namespace)
Expand Down
39 changes: 26 additions & 13 deletions common/djangoapps/student/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import CourseLocator
from openedx_authz.api import users as authz_api
from openedx_authz.api.data import RoleAssignmentData, CourseOverviewData
from openedx_authz.constants import roles as authz_roles

from common.djangoapps.student.models import CourseAccessRole
Expand Down Expand Up @@ -73,6 +74,24 @@ def authz_add_role(user: User, authz_role: str, course_key: str):
legacy_role = get_legacy_role_from_authz_role(authz_role)
emit_course_access_role_added(user, course_locator, course_locator.org, legacy_role)

def authz_get_all_course_assignments_for_user(user: User) -> list[RoleAssignmentData]:
"""
Get all course assignments for a user.
"""
assignments = authz_api.get_user_role_assignments(user_external_key=user.username)
# filter courses only
filtered_assignments = [
assignment for assignment in assignments
if isinstance(assignment.scope, CourseOverviewData)
]
return filtered_assignments

def get_org_from_key(key: str) -> str:
"""
Get the org from a course key.
"""
parsed_key = CourseKey.from_string(key)
return parsed_key.org

def register_access_role(cls):
"""
Expand Down Expand Up @@ -136,13 +155,12 @@ def get_authz_compat_course_access_roles_for_user(user: User) -> set[AuthzCompat
Retrieve all CourseAccessRole objects for a given user and convert them to AuthzCompatCourseAccessRole objects.
"""
compat_role_assignments = set()
assignments = authz_api.get_user_role_assignments(user_external_key=user.username)
assignments = authz_get_all_course_assignments_for_user(user)
for assignment in assignments:
for role in assignment.roles:
legacy_role = get_legacy_role_from_authz_role(authz_role=role.external_key)
course_key = assignment.scope.external_key
parsed_key = CourseKey.from_string(course_key)
org = parsed_key.org
org = get_org_from_key(course_key)
compat_role = AuthzCompatCourseAccessRole(
user_id=user.id,
username=user.username,
Expand Down Expand Up @@ -825,9 +843,7 @@ def courses_with_role(self) -> set[AuthzCompatCourseAccessRole]:

# Get all assignments for a user to a role
new_authz_roles = [get_authz_role_from_legacy_role(role) for role in roles]
all_authz_user_assignments = authz_api.get_user_role_assignments(
user_external_key=self.user.username
)
all_authz_user_assignments = authz_get_all_course_assignments_for_user(self.user)

all_assignments = set()

Expand All @@ -847,8 +863,7 @@ def courses_with_role(self) -> set[AuthzCompatCourseAccessRole]:
continue
legacy_role = get_legacy_role_from_authz_role(authz_role=role.external_key)
course_key = assignment.scope.external_key
parsed_key = CourseKey.from_string(course_key)
org = parsed_key.org
org = get_org_from_key(course_key)
all_assignments.add(AuthzCompatCourseAccessRole(
user_id=self.user.id,
username=self.user.username,
Expand Down Expand Up @@ -880,9 +895,7 @@ def has_courses_with_role(self, org: str | None = None) -> bool:

# Then check for authz assignments
new_authz_roles = [get_authz_role_from_legacy_role(role) for role in roles]
all_authz_user_assignments = authz_api.get_user_role_assignments(
user_external_key=self.user.username
)
all_authz_user_assignments = authz_get_all_course_assignments_for_user(self.user)

for assignment in all_authz_user_assignments:
for role in assignment.roles:
Expand All @@ -892,7 +905,7 @@ def has_courses_with_role(self, org: str | None = None) -> bool:
# There is at least one assignment, short circuit
return True
course_key = assignment.scope.external_key
parsed_key = CourseKey.from_string(course_key)
if org == parsed_key.org:
parsed_org = get_org_from_key(course_key)
if org == parsed_org:
return True
return False
20 changes: 20 additions & 0 deletions common/djangoapps/student/tests/test_roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@


import ddt
from unittest.mock import patch
from django.contrib.auth.models import Permission
from django.test import TestCase
from edx_toggles.toggles.testutils import override_waffle_flag
from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import LibraryLocator

from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
from openedx_authz.api.data import ContentLibraryData, RoleAssignmentData, RoleData, UserData
from openedx_authz.engine.enforcer import AuthzEnforcer

from common.djangoapps.student.admin import CourseAccessRoleHistoryAdmin
Expand All @@ -32,6 +34,7 @@
OrgInstructorRole,
OrgStaffRole,
RoleCache,
get_authz_compat_course_access_roles_for_user,
get_role_cache_key_for_course,
ROLE_CACHE_UNGROUPED_ROLES__KEY
)
Expand Down Expand Up @@ -236,6 +239,23 @@ def test_get_orgs_for_user(self):
role_second_org.add_users(self.student)
assert len(role.get_orgs_for_user(self.student)) == 2

def test_get_authz_compat_course_access_roles_for_user(self):
"""
Thest that get_authz_compat_course_access_roles_for_user doesn't crash when the user
has Libraries V2 or other non-course roles in their assignments.
"""
lib_assignment = RoleAssignmentData(
subject=UserData(external_key=self.student.username),
roles=[RoleData(external_key='test-role')],
scope=ContentLibraryData(external_key='lib:edX:test-lib'),
)
with patch(
'openedx_authz.api.users.get_subject_role_assignments',
return_value=[lib_assignment],
):
result = get_authz_compat_course_access_roles_for_user(self.student)
assert result == set()


@ddt.ddt
class RoleCacheTestCase(TestCase): # lint-amnesty, pylint: disable=missing-class-docstring
Expand Down
Loading
Loading