Repository navigation
fix: LP-1102 show all orgs to global staff and course admin orgs when creating a library - #509
Conversation
Since allow_to_create_new_org started requiring ORGANIZATIONS_AUTOCREATE (openedx#36094), the Authoring MFE picks the org list from allowed_organizations_for_libraries whenever autocreate is disabled. That list only includes orgs where the user has a course creator grant or a staff CourseAccessRole. Global staff therefore saw only a few orgs in the New Library dropdown, and an org typed by hand is rejected. The library create API (server side) lets global staff use any org. get_allowed_organizations_for_libraries() now returns every organization when user.is_staff is set (the same check as GlobalStaff and user_can_create_organizations). Course staff, course admins and course creators still get only their role-based orgs.
…raries Behind the contentstore.expanded_library_creation_orgs flag: - Global staff are offered only active organizations, since ensure_organization() rejects inactive ones when autocreate is off. - Course admins (instructors) are offered their courses' organizations, as course staff already are. Update the toggle description and add tests for both cases.
abhalsod-sonata
left a comment
There was a problem hiding this comment.
Code looks correct and CI is green. It reuses the existing role helpers (OrgInstructorRole.get_orgs_for_user next to the existing OrgStaffRole call) and keeps everything behind the flag. The inline comments are mostly about trimming the diff. One thing to fix in the description:
v2 library creation does change server-side
The description says "Server-side create permissions are unchanged; only the dropdown list changes." That holds for legacy libraries, but not for v2. LibraryRootView.post (openedx/core/djangoapps/content_libraries/rest_api/libraries.py) authorizes the org with the same helper:
if not (
user_can_create_organizations(request.user) or
org_name in get_allowed_organizations_for_libraries(request.user)
):
raise ValidationError(...)With edx.org settings (ENABLE_CREATOR_GROUP=True, ENABLE_ORGANIZATION_STAFF_ACCESS_FOR_CONTENT_LIBRARIES=True) and the flag on, take a course creator who is not global staff and who is a Course Admin (only the instructor row) on a course in org X. That user can now create a v2 library in X. With the flag off, they get 400 "User not allowed to create libraries in 'X'".
I think this is the right behavior. Admin is a superset of Staff, and the change keeps the dropdown and the v2 check in sync, so the dropdown never offers an org that v2 create then rejects. It just needs to be stated. Suggested replacement for that sentence:
Legacy library create permissions are unchanged, since
_user_can_create_library_for_orgalready allows these orgs. v2 library creation (LibraryRootView.post) authorizes the org with this same list. So with the flag on, a course creator who is not global staff can also create v2 libraries in orgs where they are a Course Admin. This keeps the dropdown and the v2 check in sync. With the flag off, behavior is unchanged.
Agreed. Updated the description to call out the v2 behavior. |
Batch 1 of the ulmo upgrade [LP-1148]: bring back upstream release/ulmo.1 (Django 5.2) by reverting the May 27 revert, on the current release-ulmo. Conflict resolutions (13 files): - .github/workflows/check-for-tutorial-prs.yml: keep deleted (#507). - create_user_gdpr_testing.py: keep deleted (fork removal, ENT-11576). - xmodule/js/src/video/02_html5_hls_video.js: accept upstream's move to xmodule/assets/video/public/js/; the fork's HLS retry change is ported in a separate commit. - contentstore/views/course.py: keep #509's LP-1102 staff branch and toggle import, then upstream's set-based org logic; drop the removed use_new_home_page / use_new_course_outline_page imports. - modulestore_migrator/models.py: keep the fork's `.. no_pii:` annotations (#352). - test_content_libraries.py: import both CourseKey (#509) and LibraryCollectionLocator (ulmo.1). - user_api accounts test_api.py: keep #517's timezone/pytz imports and ulmo.1's ZoneInfo. - course_duration_limits test_access.py: keep the fork's RequestCache clearing, with ulmo.1's ZoneInfo. - requirements/common_constraints.txt: keep the fork's pip<26.2.1 and social-auth constraints (supersede ulmo.1's pip<25.3). - requirements/edx/*.txt: lti-consumer-xblock 9.14.3 (ulmo.1), ora2 6.17.2 (fork; it no longer needs loremipsum). Co-Authored-By: Claude <noreply@anthropic.com>
Batch 1 of the ulmo upgrade [LP-1148]: bring back upstream release/ulmo.1 (Django 5.2) by reverting the May 27 revert, on the current release-ulmo. Conflict resolutions (13 files): - .github/workflows/check-for-tutorial-prs.yml: keep deleted (#507). - create_user_gdpr_testing.py: keep deleted (fork removal, ENT-11576). - xmodule/js/src/video/02_html5_hls_video.js: accept upstream's move to xmodule/assets/video/public/js/; the fork's HLS retry change is ported in a separate commit. - contentstore/views/course.py: keep #509's LP-1102 staff branch and toggle import, then upstream's set-based org logic; drop the removed use_new_home_page / use_new_course_outline_page imports. - modulestore_migrator/models.py: keep the fork's `.. no_pii:` annotations (#352). - test_content_libraries.py: import both CourseKey (#509) and LibraryCollectionLocator (ulmo.1). - user_api accounts test_api.py: keep #517's timezone/pytz imports and ulmo.1's ZoneInfo. - course_duration_limits test_access.py: keep the fork's RequestCache clearing, with ulmo.1's ZoneInfo. - requirements/common_constraints.txt: keep the fork's pip<26.2.1 and social-auth constraints (supersede ulmo.1's pip<25.3). - requirements/edx/*.txt: lti-consumer-xblock 9.14.3 (ulmo.1), ora2 6.17.2 (fork; it no longer needs loremipsum). Co-Authored-By: Claude <noreply@anthropic.com>
Description
Fixes the organization dropdown in Studio's "New library" form, which shows too few organizations when
ORGANIZATIONS_AUTOCREATEis disabled (as on edx.org stage and prod).Since openedx#36094,
allow_to_create_new_orgrequiresORGANIZATIONS_AUTOCREATE. When it is off, the Authoring MFE builds the dropdown fromallowed_organizations_for_libraries, which only lists organizations where the user has astaffrole. As a result:instructorrole) never see their course's organization, although_user_can_create_library_for_orgalready lets them create legacy libraries there.Changes, behind a new temporary waffle flag
contentstore.expanded_library_creation_orgs(off by default):get_allowed_organizations_for_libraries()returns every active organization for global staff. Inactive organizations are left out becauseensure_organization()rejects them when autocreate is off, matching Studio's/organizationslist.get_organizations_for_non_course_creators()also includes organizations where the user has theinstructorrole, so Course Admins get the same organizations as Course Staff.Legacy library create permissions are unchanged, since _user_can_create_library_for_org already allows these orgs. v2 library creation (LibraryRootView.post) authorizes the org with this same list. So with the flag on, a course creator who is not global staff can also create v2 libraries in orgs where they are a Course Admin. This keeps the dropdown and the v2 check in sync. With the flag off, behavior is unchanged.
Roles affected: Course Author (global staff and Course Admins in Studio).
Supporting information
Testing instructions
ORGANIZATIONS_AUTOCREATE = Falseand create a few organizations in Django admin, including one inactive./admin/waffle/flag/→contentstore.expanded_library_creation_orgs. Course Admins are usually not staff or superusers, so set Everyone = Yes or add them under Users.X:Xis not listed.Xis listed, and "Create legacy library" inXsucceeds.Xis listed with the flag on and off (unchanged).Unit tests (ddt cases for each role, flag on and off):
test_library.py:get_allowed_organizations_for_librariesper role, plustest_legacy_library_org_offered_and_created, which uses stage/prod settings to check that the org is in the home API dropdown and thatPOST /library/creates the legacy library.test_home.py: the home API returns all active organizations to global staff.Other information
temporary(target removal 2027-01-07); after that the flag-on behavior becomes the only path.CAN_CREATE_CONTENT_LIBRARY).ENABLE_ORGANIZATION_STAFF_ACCESS_FOR_CONTENT_LIBRARIESoff, the dropdown still comes only from the user's course-creator grant (unchanged). edx.org uses the default,True.HomePageLibrariesViewTesthard-coded theOrganizationFactorysequence name (name0). The new tests create organizations earlier in the module, so it now uses the created library key.🤖 Generated with Claude Code