Skip to content

Commit 6ef4473

Browse files
authored
Merge branch 'master' into pwnage101/ENT-11569
2 parents 795cdf8 + 1d10f9e commit 6ef4473

37 files changed

Lines changed: 2132 additions & 421 deletions

File tree

cms/djangoapps/contentstore/helpers.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -509,8 +509,18 @@ def _fetch_and_set_upstream_link(
509509
# temp_xblock.display_name == temp_xblock.upstream_display_name
510510
# temp_xblock.data == temp_xblock.upstream_data # for html blocks
511511
# Even then we want to set `downstream_customized` value to avoid overriding user customisations on sync
512-
downstream_customized = temp_xblock.xml_attributes.get("downstream_customized", '[]')
513-
temp_xblock.downstream_customized = json.loads(downstream_customized)
512+
downstream_customized = getattr(temp_xblock, "downstream_customized", [])
513+
# XmlMixin blocks expose raw XML attrs on `xml_attributes`; other blocks (e.g. DnD)
514+
# may not have this attribute, but still have parsed downstream_customized field.
515+
xml_attributes = getattr(temp_xblock, "xml_attributes", None)
516+
if isinstance(xml_attributes, dict):
517+
raw_downstream_customized = xml_attributes.get("downstream_customized")
518+
if isinstance(raw_downstream_customized, str):
519+
downstream_customized = json.loads(raw_downstream_customized)
520+
elif isinstance(raw_downstream_customized, list):
521+
downstream_customized = raw_downstream_customized
522+
if hasattr(temp_xblock, "downstream_customized"):
523+
temp_xblock.downstream_customized = downstream_customized
514524

515525

516526
def _import_xml_node_to_parent(

cms/djangoapps/modulestore_migrator/tasks.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -959,16 +959,18 @@ def _migrate_component(
959959
return component.versioning.draft.publishable_entity_version, None
960960

961961
# If component existed and was deleted or we have to replace the current version
962-
# Create the new component version for it
963-
component_version = libraries_api.set_library_block_olx(target_key, new_olx_str=olx)
962+
paths_to_media_ids = {}
964963
for filename, media_pk in context.content_by_filename.items():
965964
filename_no_ext, _ = os.path.splitext(filename)
966965
if filename_no_ext not in olx:
967966
continue
968967
new_path = f"static/{filename}"
969-
content_api.create_component_version_media(
970-
component_version.pk, media_pk, path=new_path
971-
)
968+
paths_to_media_ids[new_path] = media_pk
969+
970+
# Create the new component version for it
971+
component_version = libraries_api.set_library_block_olx(
972+
target_key, new_olx_str=olx, paths_to_media=paths_to_media_ids,
973+
)
972974

973975
# Publish the component
974976
libraries_api.publish_component_changes(target_key, context.created_by)

cms/envs/help_tokens.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ video = course_author:references/course_development/guide_to_video.html
2727
certificates = course_author:concepts/open_edx_platform/about_certificates.html
2828
content_highlights = course_author:how-tos/course_development/manage_course_highlight_emails.html
2929
social_sharing = course_author:how-tos/course_development/social_sharing.html
30+
sync_library_updates = course_author:how-tos/course_development/sync_a_library_update_to_your_course.html
3031

3132
# below are the language directory names for the different locales
3233
[locales]

cms/static/sass/elements/_modal-window.scss

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -406,43 +406,43 @@
406406
display: flex;
407407
flex-direction: column;
408408

409-
410-
// Set the components to flex so that they can grow
411-
div,
412-
form {
409+
div.edit-xblock-modal {
413410
display: flex;
414411
flex-direction: column;
415412
flex-grow: 1;
416413

417-
// Set the header to not grow
418-
header,
419-
&.modal-header {
420-
flex-grow: 0;
421-
}
414+
div.modal-content {
415+
display: flex;
416+
flex-direction: column;
417+
flex-grow: 1;
418+
padding: 0;
422419

423-
}
420+
div.xblock-editor {
421+
display: flex;
422+
flex-direction: column;
423+
flex-grow: 1;
424424

425-
ul {
426-
flex-grow: 1;
425+
div.xblock-studio_view {
426+
display: flex;
427+
flex-direction: column;
428+
flex-grow: 1;
429+
}
427430

428-
// Reset the divs inside the ul to preserve previous styling
429-
div,
430-
form {
431-
display: unset;
432-
flex-direction: unset;
433-
flex-grow: unset;
434-
}
435-
}
431+
div.editor-with-buttons {
432+
display: flex;
433+
flex-direction: column;
434+
flex-grow: 1;
435+
margin-bottom: 0;
436436

437-
&.modal-editor {
438-
.modal-content {
439-
padding: 0px;
437+
ul.settings-list {
438+
flex-grow: 1;
439+
}
440+
}
441+
}
440442
}
441443
}
442444
}
443445

444-
445-
446446
// specific modal overrides
447447
// ------------------------
448448

common/djangoapps/student/views/dashboard.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -614,8 +614,6 @@ def student_dashboard(request): # lint-amnesty, pylint: disable=too-many-statem
614614
link_end=HTML("</a>"),
615615
)
616616

617-
enterprise_message = ''
618-
619617
recovery_email_message = recovery_email_activation_message = None
620618
if is_secondary_email_feature_enabled():
621619
try:
@@ -792,7 +790,6 @@ def student_dashboard(request): # lint-amnesty, pylint: disable=too-many-statem
792790
context = {
793791
'urls': urls,
794792
'programs_data': programs_data,
795-
'enterprise_message': enterprise_message,
796793
'consent_required_courses': set(),
797794
'enrollment_message': enrollment_message,
798795
'redirect_message': Text(redirect_message),

common/djangoapps/student/views/management.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
from django.template.context_processors import csrf
2626
from django.urls import reverse
2727
from django.utils.translation import gettext as _
28-
from django.views.decorators.csrf import ensure_csrf_cookie # lint-amnesty, pylint: disable=unused-import # noqa: F401
28+
from django.views.decorators.csrf import ( # lint-amnesty, pylint: disable=unused-import # noqa: F401
29+
csrf_exempt,
30+
ensure_csrf_cookie,
31+
)
2932
from django.views.decorators.http import ( # lint-amnesty, pylint: disable=unused-import
3033
require_GET,
3134
require_POST,

lms/djangoapps/course_api/blocks/tests/pacts/verify_pact.py

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,15 @@
1717
class ProviderVerificationServer(LiveServerTestCase):
1818
""" Django Test Live Server for Pact Verification """
1919

20-
@classmethod
21-
def setUpClass(cls):
22-
super().setUpClass()
23-
24-
cls.verifier = Verifier(
25-
provider='lms',
26-
provider_base_url=cls.live_server_url,
27-
)
28-
29-
@classmethod
30-
def tearDownClass(cls):
31-
super().tearDownClass()
32-
3320
def test_verify_pact(self):
34-
output, _ = self.verifier.verify_pacts(
35-
os.path.join(PACT_DIR, PACT_FILE),
36-
headers=['Pact-Authentication: Allow', ],
37-
provider_states_setup_url=f"{self.live_server_url}{reverse('provider-state-view')}",
21+
(
22+
Verifier(name='lms')
23+
.add_transport(url=self.live_server_url)
24+
.add_source(os.path.join(PACT_DIR, PACT_FILE))
25+
.add_custom_header('Pact-Authentication', 'Allow')
26+
.state_handler(
27+
f"{self.live_server_url}{reverse('provider-state-view')}",
28+
body=True,
29+
)
30+
.verify()
3831
)
39-
40-
assert output == 0

lms/djangoapps/courseware/tests/pacts/verify_pact.py

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,15 @@
1717
class ProviderVerificationServer(LiveServerTestCase):
1818
""" Django Test Live Server for Pact Verification """
1919

20-
@classmethod
21-
def setUpClass(cls):
22-
super().setUpClass()
23-
24-
cls.verifier = Verifier(
25-
provider='lms',
26-
provider_base_url=cls.live_server_url,
27-
)
28-
29-
@classmethod
30-
def tearDownClass(cls):
31-
super().tearDownClass()
32-
3320
def test_verify_pact(self):
34-
output, _ = self.verifier.verify_pacts(
35-
os.path.join(PACT_DIR, PACT_FILE),
36-
headers=['Pact-Authentication: Allow', ],
37-
provider_states_setup_url=f"{self.live_server_url}{reverse('courseware_xblock_handler_provider_state')}",
21+
(
22+
Verifier(name='lms')
23+
.add_transport(url=self.live_server_url)
24+
.add_source(os.path.join(PACT_DIR, PACT_FILE))
25+
.add_custom_header('Pact-Authentication', 'Allow')
26+
.state_handler(
27+
f"{self.live_server_url}{reverse('courseware_xblock_handler_provider_state')}",
28+
body=True,
29+
)
30+
.verify()
3831
)
39-
40-
assert output == 0

lms/djangoapps/discussion/rest_api/tasks.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from django.contrib.auth import get_user_model
88
from edx_django_utils.monitoring import set_code_owner_attribute
99
from eventtracking import tracker
10+
from forum import api as forum_api
1011
from opaque_keys.edx.locator import CourseKey
1112

1213
from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole
@@ -99,8 +100,12 @@ def delete_course_post_for_user(user_id, username, course_ids, event_data=None):
99100
"""
100101
event_data = event_data or {}
101102
log.info(f"<<Bulk Delete>> Deleting all posts for {username} in course {course_ids}")
102-
threads_deleted = Thread.delete_user_threads(user_id, course_ids)
103-
comments_deleted = Comment.delete_user_comments(user_id, course_ids)
103+
threads_deleted = 0
104+
comments_deleted = 0
105+
for course_id in course_ids:
106+
result = forum_api.delete_user_posts(user_id=str(user_id), course_id=course_id)
107+
threads_deleted += result.get("thread_count", 0)
108+
comments_deleted += result.get("comment_count", 0)
104109
log.info(f"<<Bulk Delete>> Deleted {threads_deleted} posts and {comments_deleted} comments for {username} "
105110
f"in course {course_ids}")
106111
event_data.update({

lms/djangoapps/discussion/rest_api/views.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from drf_yasg import openapi
1212
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
1313
from edx_rest_framework_extensions.auth.session.authentication import SessionAuthenticationAllowInactiveUser
14+
from forum import api as forum_api
1415
from opaque_keys.edx.keys import CourseKey
1516
from rest_framework import permissions, status
1617
from rest_framework.authentication import SessionAuthentication
@@ -35,8 +36,6 @@
3536
from openedx.core.djangoapps.discussions.models import DiscussionsConfiguration, Provider
3637
from openedx.core.djangoapps.discussions.serializers import DiscussionSettingsSerializer
3738
from openedx.core.djangoapps.django_comment_common import comment_client
38-
from openedx.core.djangoapps.django_comment_common.comment_client.comment import Comment
39-
from openedx.core.djangoapps.django_comment_common.comment_client.thread import Thread
4039
from openedx.core.djangoapps.django_comment_common.models import CourseDiscussionSettings, Role
4140
from openedx.core.djangoapps.user_api.accounts.permissions import CanReplaceUsername, CanRetireUser
4241
from openedx.core.djangoapps.user_api.models import UserRetirementStatus
@@ -1571,7 +1570,6 @@ class BulkDeleteUserPosts(DeveloperErrorViewMixin, APIView):
15711570
def post(self, request, course_id):
15721571
"""
15731572
Implements the delete user posts endpoint.
1574-
TODO: Add support for MySQLBackend as well
15751573
"""
15761574
username = request.GET.get("username", None)
15771575
execute_task = request.GET.get("execute", "false").lower() == "true"
@@ -1595,8 +1593,12 @@ def post(self, request, course_id):
15951593
log.info(f"<<Bulk Delete>> {username} enrolled in {enrollments}")
15961594
log.info(f"<<Bulk Delete>> Posts for {username} in {course_ids} - for {course_or_org} {course_id}")
15971595

1598-
comment_count = Comment.get_user_comment_count(user.id, course_ids)
1599-
thread_count = Thread.get_user_threads_count(user.id, course_ids)
1596+
thread_count = 0
1597+
comment_count = 0
1598+
for cid in course_ids:
1599+
counts = forum_api.get_user_post_counts(user_id=str(user.id), course_id=cid)
1600+
thread_count += counts.get("thread_count", 0)
1601+
comment_count += counts.get("comment_count", 0)
16001602
log.info(f"<<Bulk Delete>> {username} in {course_ids} - Count thread {thread_count}, comment {comment_count}")
16011603

16021604
if execute_task:

0 commit comments

Comments
 (0)