Skip to content

Commit b2a0cd1

Browse files
committed
Merge branch 'master' into chris/FAL-4180-sections-subsections-publish
2 parents 195c73e + 9bf9237 commit b2a0cd1

145 files changed

Lines changed: 4513 additions & 3346 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.coveragerc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ omit =
2525
common/djangoapps/*/migrations/*
2626
openedx/core/djangoapps/*/migrations/*
2727
openedx/core/djangoapps/debug/*
28+
openedx/envs/*
2829
openedx/features/*/migrations/*
2930

3031
concurrency=multiprocessing

.coveragerc-local

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ omit =
2424
common/djangoapps/*/migrations/*
2525
openedx/core/djangoapps/*/migrations/*
2626
openedx/core/djangoapps/debug/*
27+
openedx/envs/*
2728
openedx/features/*/migrations/*
2829

2930
concurrency=multiprocessing

.github/workflows/pylint-checks.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
- module-name: openedx-1
2121
path: "openedx/core/types/ openedx/core/djangoapps/ace_common/ openedx/core/djangoapps/agreements/ openedx/core/djangoapps/api_admin/ openedx/core/djangoapps/auth_exchange/ openedx/core/djangoapps/bookmarks/ openedx/core/djangoapps/cache_toolbox/ openedx/core/djangoapps/catalog/ openedx/core/djangoapps/ccxcon/ openedx/core/djangoapps/commerce/ openedx/core/djangoapps/common_initialization/ openedx/core/djangoapps/common_views/ openedx/core/djangoapps/config_model_utils/ openedx/core/djangoapps/content/ openedx/core/djangoapps/content_libraries/ openedx/core/djangoapps/content_staging/ openedx/core/djangoapps/contentserver/ openedx/core/djangoapps/cookie_metadata/ openedx/core/djangoapps/cors_csrf/ openedx/core/djangoapps/course_apps/ openedx/core/djangoapps/course_date_signals/ openedx/core/djangoapps/course_groups/ openedx/core/djangoapps/courseware_api/ openedx/core/djangoapps/crawlers/ openedx/core/djangoapps/credentials/ openedx/core/djangoapps/credit/ openedx/core/djangoapps/dark_lang/ openedx/core/djangoapps/debug/ openedx/core/djangoapps/discussions/ openedx/core/djangoapps/django_comment_common/ openedx/core/djangoapps/embargo/ openedx/core/djangoapps/enrollments/ openedx/core/djangoapps/external_user_ids/ openedx/core/djangoapps/zendesk_proxy/ openedx/core/djangolib/ openedx/core/lib/ openedx/core/djangoapps/course_live/"
2222
- module-name: openedx-2
23-
path: "openedx/core/djangoapps/geoinfo/ openedx/core/djangoapps/header_control/ openedx/core/djangoapps/heartbeat/ openedx/core/djangoapps/lang_pref/ openedx/core/djangoapps/models/ openedx/core/djangoapps/monkey_patch/ openedx/core/djangoapps/oauth_dispatch/ openedx/core/djangoapps/olx_rest_api/ openedx/core/djangoapps/password_policy/ openedx/core/djangoapps/plugin_api/ openedx/core/djangoapps/plugins/ openedx/core/djangoapps/profile_images/ openedx/core/djangoapps/programs/ openedx/core/djangoapps/safe_sessions/ openedx/core/djangoapps/schedules/ openedx/core/djangoapps/service_status/ openedx/core/djangoapps/session_inactivity_timeout/ openedx/core/djangoapps/signals/ openedx/core/djangoapps/site_configuration/ openedx/core/djangoapps/system_wide_roles/ openedx/core/djangoapps/theming/ openedx/core/djangoapps/user_api/ openedx/core/djangoapps/user_authn/ openedx/core/djangoapps/util/ openedx/core/djangoapps/verified_track_content/ openedx/core/djangoapps/video_config/ openedx/core/djangoapps/video_pipeline/ openedx/core/djangoapps/waffle_utils/ openedx/core/djangoapps/xblock/ openedx/core/djangoapps/xmodule_django/ openedx/core/tests/ openedx/features/ openedx/testing/ openedx/tests/ openedx/core/djangoapps/notifications/ openedx/core/djangoapps/staticfiles/ openedx/core/djangoapps/content_tagging/"
23+
path: "openedx/core/djangoapps/geoinfo/ openedx/core/djangoapps/header_control/ openedx/core/djangoapps/heartbeat/ openedx/core/djangoapps/lang_pref/ openedx/core/djangoapps/models/ openedx/core/djangoapps/monkey_patch/ openedx/core/djangoapps/oauth_dispatch/ openedx/core/djangoapps/olx_rest_api/ openedx/core/djangoapps/password_policy/ openedx/core/djangoapps/plugin_api/ openedx/core/djangoapps/plugins/ openedx/core/djangoapps/profile_images/ openedx/core/djangoapps/programs/ openedx/core/djangoapps/safe_sessions/ openedx/core/djangoapps/schedules/ openedx/core/djangoapps/service_status/ openedx/core/djangoapps/session_inactivity_timeout/ openedx/core/djangoapps/signals/ openedx/core/djangoapps/site_configuration/ openedx/core/djangoapps/system_wide_roles/ openedx/core/djangoapps/theming/ openedx/core/djangoapps/user_api/ openedx/core/djangoapps/user_authn/ openedx/core/djangoapps/util/ openedx/core/djangoapps/verified_track_content/ openedx/core/djangoapps/video_config/ openedx/core/djangoapps/video_pipeline/ openedx/core/djangoapps/waffle_utils/ openedx/core/djangoapps/xblock/ openedx/core/djangoapps/xmodule_django/ openedx/core/tests/ openedx/features/ openedx/testing/ openedx/tests/ openedx/envs/ openedx/core/djangoapps/notifications/ openedx/core/djangoapps/staticfiles/ openedx/core/djangoapps/content_tagging/"
2424
- module-name: common
2525
path: "common"
2626
- module-name: cms

cms/djangoapps/contentstore/core/course_optimizer_provider.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ def _get_node_path(block):
251251
}
252252

253253

254-
def _create_dto_recursive(xblock_node, xblock_dictionary):
254+
def _create_dto_recursive(xblock_node, xblock_dictionary, parent_id=None):
255255
"""
256256
Recursively build the Data Transfer Object by using
257257
the structure from the node tree and data from the dictionary.
@@ -264,7 +264,7 @@ def _create_dto_recursive(xblock_node, xblock_dictionary):
264264
xblock_children = []
265265

266266
for xblock_id, node in xblock_node.items():
267-
child_blocks = _create_dto_recursive(node, xblock_dictionary)
267+
child_blocks = _create_dto_recursive(node, xblock_dictionary, parent_id=xblock_id)
268268
xblock_data = xblock_dictionary.get(xblock_id, {})
269269

270270
xblock_entry = {
@@ -281,6 +281,13 @@ def _create_dto_recursive(xblock_node, xblock_dictionary):
281281
})
282282
else: # Non-leaf node
283283
category = xblock_data.get('category', None)
284+
# If parent and child has same IDs and level is 'sections', change it to 'subsections'
285+
# And if parent and child has same IDs and level is 'subsections', change it to 'units'
286+
if xblock_id == parent_id:
287+
if category == "chapter":
288+
category = "sequential"
289+
elif category == "sequential":
290+
category = "vertical"
284291
level = CATEGORY_TO_LEVEL_MAP.get(category, None)
285292
xblock_entry.update(child_blocks)
286293

cms/djangoapps/contentstore/signals/handlers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ def emit_catalog_info_changed_signal(course_key: CourseKey):
131131
if SEND_CATALOG_INFO_SIGNAL.is_enabled():
132132
timestamp, catalog_info = _create_catalog_data_for_signal(course_key)
133133
if catalog_info is not None:
134+
# .. event_implemented_name: COURSE_CATALOG_INFO_CHANGED
135+
# .. event_type: org.openedx.content_authoring.course.catalog_info.changed.v1
134136
COURSE_CATALOG_INFO_CHANGED.send_event(time=timestamp, catalog_info=catalog_info)
135137

136138

cms/djangoapps/contentstore/tasks.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
from opaque_keys.edx.locator import LibraryLocator, LibraryContainerLocator
3838
from organizations.api import add_organization_course, ensure_organization
3939
from organizations.exceptions import InvalidOrganizationException
40-
from organizations.models import Organization, OrganizationCourse
40+
from organizations.models import Organization
4141
from path import Path as path
4242
from pytz import UTC
4343
from user_tasks.models import UserTaskArtifact, UserTaskStatus
@@ -98,6 +98,15 @@
9898
ALL_ALLOWED_XBLOCKS = frozenset(
9999
[entry_point.name for entry_point in entry_points(group="xblock.v1")]
100100
)
101+
DEFAULT_HEADERS = {
102+
"User-Agent": (
103+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
104+
"AppleWebKit/537.36 (KHTML, like Gecko) "
105+
"Chrome/115.0.0.0 Safari/537.36"
106+
),
107+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
108+
"Connection": "keep-alive",
109+
}
101110

102111

103112
class LinkState:
@@ -163,12 +172,6 @@ def rerun_course(source_course_key_string, destination_course_key_string, user_i
163172
# call edxval to attach videos to the rerun
164173
copy_course_videos(source_course_key, destination_course_key)
165174

166-
# Copy OrganizationCourse
167-
organization_course = OrganizationCourse.objects.filter(course_id=source_course_key_string).first()
168-
169-
if organization_course:
170-
clone_instance(organization_course, {'course_id': destination_course_key_string})
171-
172175
# Copy RestrictedCourse
173176
restricted_course = RestrictedCourse.objects.filter(course_key=source_course_key).first()
174177

@@ -178,7 +181,7 @@ def rerun_course(source_course_key_string, destination_course_key_string, user_i
178181
for country_access_rule in country_access_rules:
179182
clone_instance(country_access_rule, {'restricted_course': new_restricted_course})
180183

181-
org_data = ensure_organization(source_course_key.org)
184+
org_data = ensure_organization(destination_course_key.org)
182185
add_organization_course(org_data, destination_course_key)
183186
return "succeeded"
184187

@@ -473,12 +476,12 @@ def sync_discussion_settings(course_key, user):
473476

474477
if (
475478
ENABLE_NEW_STRUCTURE_DISCUSSIONS.is_enabled()
476-
and not course.discussions_settings['provider_type'] == Provider.OPEN_EDX
479+
and not course.discussions_settings.get('provider_type', None) == Provider.OPEN_EDX
480+
and not course.discussions_settings.get('provider', None) == Provider.OPEN_EDX
477481
):
478482
LOGGER.info(f"New structure is enabled, also updating {course_key} to use new provider")
479483
course.discussions_settings['enable_graded_units'] = False
480484
course.discussions_settings['unit_level_visibility'] = True
481-
course.discussions_settings['provider'] = Provider.OPEN_EDX
482485
course.discussions_settings['provider_type'] = Provider.OPEN_EDX
483486
modulestore().update_item(course, user.id)
484487

@@ -1261,7 +1264,7 @@ async def _validate_urls_access_in_batches(url_list, course_key, batch_size=100)
12611264

12621265
async def _validate_batch(batch, course_key):
12631266
"""Validate a batch of URLs"""
1264-
async with aiohttp.ClientSession() as session:
1267+
async with aiohttp.ClientSession(headers=DEFAULT_HEADERS) as session:
12651268
tasks = [_validate_url_access(session, url_data, course_key) for url_data in batch]
12661269
batch_results = await asyncio.gather(*tasks)
12671270
return batch_results
@@ -1286,6 +1289,7 @@ async def _validate_url_access(session, url_data, course_key):
12861289
}
12871290
"""
12881291
block_id, url = url_data
1292+
url = url.strip() # Trim leading/trailing whitespace
12891293
result = {'block_id': block_id, 'url': url}
12901294
standardized_url = _convert_to_standard_url(url, course_key)
12911295
try:

cms/djangoapps/contentstore/tests/test_tasks.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,26 @@ def test_success(self):
189189
country=restricted_country
190190
)
191191

192+
def test_success_different_org(self):
193+
"""
194+
The task should clone the OrganizationCourse with a different org.
195+
"""
196+
old_course_key = self.course.id
197+
new_course_key = CourseLocator(org='neworg', course=old_course_key.course, run='rerun')
198+
199+
old_course_id = str(old_course_key)
200+
new_course_id = str(new_course_key)
201+
202+
organization = OrganizationFactory(short_name=old_course_key.org)
203+
OrganizationCourse.objects.create(course_id=old_course_id, organization=organization)
204+
205+
# Run the task!
206+
self._rerun_course(old_course_key, new_course_key)
207+
208+
# Verify the OrganizationCourse is cloned with a different org
209+
self.assertEqual(OrganizationCourse.objects.count(), 2)
210+
OrganizationCourse.objects.get(course_id=new_course_id, organization__short_name='neworg')
211+
192212

193213
@override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE)
194214
class RegisterExamsTaskTestCase(CourseTestCase): # pylint: disable=missing-class-docstring

cms/djangoapps/contentstore/utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2314,6 +2314,8 @@ def send_course_update_notification(course_key, content, user):
23142314
app_name="updates",
23152315
audience_filters={},
23162316
)
2317+
# .. event_implemented_name: COURSE_NOTIFICATION_REQUESTED
2318+
# .. event_type: org.openedx.learning.course.notification.requested.v1
23172319
COURSE_NOTIFICATION_REQUESTED.send_event(course_notification_data=notification_data)
23182320

23192321

cms/djangoapps/contentstore/video_storage_handlers.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,9 @@ def get_all_transcript_languages():
724724
third_party_transcription_languages.update(cielo_fidelity['PREMIUM']['languages'])
725725
third_party_transcription_languages.update(cielo_fidelity['PROFESSIONAL']['languages'])
726726

727-
all_languages_dict = dict(settings.ALL_LANGUAGES, **third_party_transcription_languages)
727+
# combines ALL_LANGUAGES with additional languages that should be supported for transcripts
728+
extended_all_languages = settings.ALL_LANGUAGES + settings.EXTENDED_VIDEO_TRANSCRIPT_LANGUAGES
729+
all_languages_dict = dict(extended_all_languages, **third_party_transcription_languages)
728730
# Return combined system settings and 3rd party transcript languages.
729731
all_languages = []
730732
for key, value in sorted(all_languages_dict.items(), key=lambda k_v: k_v[1]):

cms/djangoapps/contentstore/views/component.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,11 @@ def create_support_legend_dict():
494494
course_advanced_keys
495495
)
496496
if advanced_component_templates['templates']:
497+
# Advanced component templates should be sorted alphabetically by display name.
498+
advanced_component_templates['templates'] = sorted(
499+
advanced_component_templates['templates'],
500+
key=lambda x: x.get('display_name')
501+
)
497502
component_templates.append(advanced_component_templates)
498503

499504
return component_templates

0 commit comments

Comments
 (0)