Skip to content

Commit 631035b

Browse files
authored
Merge branch 'master' into renovate/edx-frontend-component-cookie-policy-banner-2.x
2 parents b13eff4 + 179e00a commit 631035b

File tree

62 files changed

+714
-1583
lines changed

Some content is hidden

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

62 files changed

+714
-1583
lines changed

.github/workflows/verify-dunder-init.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: CI
1+
name: Verify Dunder __init__.py Files
22

33
on:
44
pull_request:

.readthedocs.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ version: 2
33
build:
44
os: "ubuntu-22.04"
55
tools:
6-
python: "3.12"
6+
python: "3.11"
77

88
sphinx:
99
configuration: docs/conf.py

cms/static/js/views/pages/container_subviews.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -506,9 +506,13 @@ function($, _, gettext, BaseView, ViewUtils, XBlockViewUtils, MoveXBlockUtils, H
506506
},
507507

508508
renderTagElements: function(tags, depth, parentId) {
509+
/* This function displays the tags in the sidebar of the legacy Unit Outline Page.
510+
* It is not used when the Authoring MFE iframes a component in the Unit Outline. */
511+
const parentElement = document.querySelector(`.content-tags-${parentId}`);
512+
if (!parentElement) return;
513+
509514
const tagListElement = this;
510515
tags.forEach(function(tag) {
511-
const parentElement = document.querySelector(`.content-tags-${parentId}`);
512516
var tagContentElement = document.createElement('div'),
513517
tagValueElement = document.createElement('span');
514518

common/djangoapps/student/tests/test_linkedin.py

Lines changed: 22 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -38,28 +38,20 @@ class LinkedInAddToProfileUrlTests(TestCase):
3838
def test_linked_in_url(self, cert_mode, expected_cert_name):
3939
config = LinkedInAddToProfileConfigurationFactory()
4040

41-
# We can switch to this once edx-platform reaches Python 3.8
42-
# expected_url = (
43-
# 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&'
44-
# 'name={platform}+{cert_name}&certUrl={cert_url}&'
45-
# 'organizationId={company_identifier}'
46-
# ).format(
47-
# platform=quote(settings.PLATFORM_NAME.encode('utf-8')),
48-
# cert_name=expected_cert_name,
49-
# cert_url=quote(self.CERT_URL, safe=''),
50-
# company_identifier=config.company_identifier,
51-
# )
41+
expected_url = (
42+
'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&'
43+
'name={platform}+{cert_name}&certUrl={cert_url}&'
44+
'organizationId={company_identifier}'
45+
).format(
46+
platform=quote(settings.PLATFORM_NAME.encode('utf-8')),
47+
cert_name=expected_cert_name,
48+
cert_url=quote(self.CERT_URL, safe=''),
49+
company_identifier=config.company_identifier,
50+
)
5251

5352
actual_url = config.add_to_profile_url(self.COURSE_NAME, cert_mode, self.CERT_URL)
5453

55-
# We can switch to this instead of the assertIn once edx-platform reaches Python 3.8
56-
# There was a problem with dict ordering in the add_to_profile_url function that will go away then.
57-
# self.assertEqual(actual_url, expected_url)
58-
59-
assert 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME' in actual_url
60-
assert f'&name={quote(settings.PLATFORM_NAME.encode("utf-8"))}+{expected_cert_name}' in actual_url
61-
assert '&certUrl={cert_url}'.format(cert_url=quote(self.CERT_URL, safe='')) in actual_url
62-
assert f'&organizationId={config.company_identifier}' in actual_url
54+
self.assertEqual(actual_url, expected_url)
6355

6456
@ddt.data(
6557
('honor', 'Honor+Code+Credential+for+Test+Course+%E2%98%83'),
@@ -72,26 +64,18 @@ def test_linked_in_url(self, cert_mode, expected_cert_name):
7264
def test_linked_in_url_with_cert_name_override(self, cert_mode, expected_cert_name):
7365
config = LinkedInAddToProfileConfigurationFactory()
7466

75-
# We can switch to this once edx-platform reaches Python 3.8
76-
# expected_url = (
77-
# 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&'
78-
# 'name={platform}+{cert_name}&certUrl={cert_url}&'
79-
# 'organizationId={company_identifier}'
80-
# ).format(
81-
# platform=quote(settings.PLATFORM_NAME.encode('utf-8')),
82-
# cert_name=expected_cert_name,
83-
# cert_url=quote(self.CERT_URL, safe=''),
84-
# company_identifier=config.company_identifier,
85-
# )
67+
expected_url = (
68+
'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&'
69+
'name={platform}+{cert_name}&certUrl={cert_url}&'
70+
'organizationId={company_identifier}'
71+
).format(
72+
platform=quote(settings.PLATFORM_NAME.encode('utf-8')),
73+
cert_name=expected_cert_name,
74+
cert_url=quote(self.CERT_URL, safe=''),
75+
company_identifier=config.company_identifier,
76+
)
8677

8778
with with_site_configuration_context(configuration=self.SITE_CONFIGURATION):
8879
actual_url = config.add_to_profile_url(self.COURSE_NAME, cert_mode, self.CERT_URL)
8980

90-
# We can switch to this instead of the assertIn once edx-platform reaches Python 3.8
91-
# There was a problem with dict ordering in the add_to_profile_url function that will go away then.
92-
# self.assertEqual(actual_url, expected_url)
93-
94-
assert 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME' in actual_url
95-
assert f'&name={quote(settings.PLATFORM_NAME.encode("utf-8"))}+{expected_cert_name}' in actual_url
96-
assert '&certUrl={cert_url}'.format(cert_url=quote(self.CERT_URL, safe='')) in actual_url
97-
assert f'&organizationId={config.company_identifier}' in actual_url
81+
self.assertEqual(actual_url, expected_url)

common/djangoapps/student/tests/tests.py

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -440,28 +440,19 @@ def test_linked_in_add_to_profile_btn_with_certificate(self):
440440
assert response.status_code == 200
441441
self.assertContains(response, 'Add Certificate to LinkedIn')
442442

443-
# We can switch to this and the commented out assertContains once edx-platform reaches Python 3.8
444-
# expected_url = (
445-
# 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&'
446-
# 'name={platform}+Honor+Code+Certificate+for+Omega&certUrl={cert_url}&'
447-
# 'organizationId={company_identifier}'
448-
# ).format(
449-
# platform=quote(settings.PLATFORM_NAME.encode('utf-8')),
450-
# cert_url=quote(cert.download_url, safe=''),
451-
# company_identifier=linkedin_config.company_identifier,
452-
# )
453-
454-
# self.assertContains(response, escape(expected_url))
455-
456-
# These can be removed (in favor of the above) once we are on Python 3.8. Fails in 3.5 because of dict ordering
457-
self.assertContains(response, escape('https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME'))
458-
self.assertContains(response, escape('&name={platform}+Honor+Code+Certificate+for+Omega'.format(
459-
platform=quote(settings.PLATFORM_NAME.encode('utf-8'))
460-
)))
461-
self.assertContains(response, escape('&certUrl={cert_url}'.format(cert_url=quote(cert.download_url, safe=''))))
462-
self.assertContains(response, escape('&organizationId={company_identifier}'.format(
443+
expected_url = (
444+
'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&'
445+
'name={platform}+Honor+Code+Certificate+for+Omega&'
446+
'certUrl={cert_url}&'
447+
'organizationId={company_identifier}'
448+
).format(
449+
platform=quote(settings.PLATFORM_NAME.encode('utf-8')),
450+
cert_url=quote(cert.download_url, safe=''),
463451
company_identifier=linkedin_config.company_identifier
464-
)))
452+
)
453+
454+
# Single assertion for the expected LinkedIn URL
455+
self.assertContains(response, escape(expected_url))
465456

466457
@skip_unless_lms
467458
def test_dashboard_metadata_caching(self):

common/djangoapps/util/cache.py

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,38 +53,32 @@ def wrapper(request, *args, **kwargs):
5353
# specifically the branding index, to do authentication.
5454
# If that page is cached the authentication doesn't
5555
# happen, so we disable the cache when that feature is enabled.
56-
if (
57-
not request.user.is_authenticated
58-
):
56+
if not request.user.is_authenticated:
5957
# Use the cache. The same view accessed through different domain names may
6058
# return different things, so include the domain name in the key.
61-
domain = str(request.META.get('HTTP_HOST')) + '.'
62-
cache_key = domain + "cache_if_anonymous." + get_language() + '.' + request.path
59+
domain = request.META.get('HTTP_HOST', '') + '.'
60+
cache_key = f"{domain}cache_if_anonymous.{get_language()}.{request.path}"
6361

6462
# Include the values of GET parameters in the cache key.
6563
for get_parameter in get_parameters:
6664
parameter_value = request.GET.get(get_parameter)
6765
if parameter_value is not None:
6866
# urlencode expects data to be of type str, and doesn't deal well with Unicode data
6967
# since it doesn't provide a way to specify an encoding.
70-
cache_key = cache_key + '.' + urlencode({
71-
get_parameter: str(parameter_value).encode('utf-8')
72-
})
68+
cache_key += '.' + urlencode({get_parameter: str(parameter_value).encode('utf-8')})
7369

7470
response = cache.get(cache_key)
75-
7671
if response:
77-
# A hack to ensure that the response data is a valid text type for both Python 2 and 3.
78-
response_content = list(response._container) # lint-amnesty, pylint: disable=bad-option-value, protected-access, protected-member
79-
response.content = b''
80-
for item in response_content:
81-
response.write(item)
72+
# Ensure that response content is properly handled for caching
73+
response.content = (
74+
# pylint: disable=protected-access
75+
b''.join(response._container) if hasattr(response, '_container') else response.content
76+
)
8277
else:
8378
response = view_func(request, *args, **kwargs)
8479
cache.set(cache_key, response, 60 * 3)
8580

8681
return response
87-
8882
else:
8983
# Don't use the cache.
9084
return view_func(request, *args, **kwargs)

common/static/js/src/tooltip_manager.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,20 @@
3232
},
3333

3434
getCoords: function(pageX, pageY) {
35+
let left = pageX - 0.5 * this.tooltip.outerWidth();
36+
const top = pageY - (this.tooltip.outerHeight() + 15);
37+
// Check if the tooltip is going off the right edge of the screen
38+
if (left + this.tooltip.outerWidth() > window.innerWidth) {
39+
left = window.innerWidth - this.tooltip.outerWidth();
40+
}
41+
// Check if the tooltip is going off the left edge of the screen
42+
if (left < 0) {
43+
left = 0;
44+
}
45+
3546
return {
36-
left: pageX - 0.5 * this.tooltip.outerWidth(),
37-
top: pageY - (this.tooltip.outerHeight() + 15)
47+
left,
48+
top,
3849
};
3950
},
4051

docs/conf.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
5656
# ones.
5757
extensions = [
58-
'sphinx.ext.autodoc',
5958
'sphinx.ext.coverage',
6059
'sphinx.ext.doctest',
6160
'sphinx.ext.graphviz',
@@ -68,6 +67,18 @@
6867
'sphinx_design',
6968
'code_annotations.contrib.sphinx.extensions.featuretoggles',
7069
'code_annotations.contrib.sphinx.extensions.settings',
70+
'autoapi.extension',
71+
]
72+
73+
autoapi_type = 'python'
74+
autoapi_dirs = ['../lms', '../openedx']
75+
76+
autoapi_ignore = [
77+
'*/migrations/*',
78+
'*/tests/*',
79+
'*.pyc',
80+
'__init__.py',
81+
'**/xblock_serializer/data.py',
7182
]
7283

7384
# Rediraffe related settings.
@@ -277,13 +288,6 @@
277288
'django': ('https://docs.djangoproject.com/en/1.11/', 'https://docs.djangoproject.com/en/1.11/_objects/'),
278289
}
279290

280-
# Mock out these external modules during code import to avoid errors
281-
autodoc_mock_imports = [
282-
'MySQLdb',
283-
'django_mysql',
284-
'pymongo',
285-
]
286-
287291
# Start building a map of the directories relative to the repository root to
288292
# run sphinx-apidoc against and the directories under "docs" in which to store
289293
# the generated *.rst files

docs/lms-openapi.yaml

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9291,21 +9291,6 @@ paths:
92919291
in: path
92929292
required: true
92939293
type: string
9294-
/user/v1/skill_level/{job_id}/:
9295-
get:
9296-
operationId: user_v1_skill_level_read
9297-
description: GET /api/user/v1/skill_level/{job_id}/
9298-
parameters: []
9299-
responses:
9300-
'200':
9301-
description: ''
9302-
tags:
9303-
- user
9304-
parameters:
9305-
- name: job_id
9306-
in: path
9307-
required: true
9308-
type: string
93099294
/user/v1/user_prefs/:
93109295
get:
93119296
operationId: user_v1_user_prefs_list

lms/djangoapps/certificates/api.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ def _format_certificate_for_user(username, cert):
8282
if cert.status == CertificateStatuses.downloadable
8383
else None
8484
),
85+
"uuid": cert.verify_uuid,
8586
}
8687

8788
return None

0 commit comments

Comments
 (0)