Skip to content

Commit

Permalink
Merge branch 'master' into renovate/style-loader-4.x
Browse files Browse the repository at this point in the history
  • Loading branch information
BilalQamar95 authored Nov 18, 2024
2 parents 5e6d086 + f722e3c commit a37a04c
Show file tree
Hide file tree
Showing 53 changed files with 572 additions and 1,239 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/verify-dunder-init.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: CI
name: Verify Dunder __init__.py Files

on:
pull_request:
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-22.04"
tools:
python: "3.12"
python: "3.11"

sphinx:
configuration: docs/conf.py
Expand Down
6 changes: 5 additions & 1 deletion cms/static/js/views/pages/container_subviews.js
Original file line number Diff line number Diff line change
Expand Up @@ -506,9 +506,13 @@ function($, _, gettext, BaseView, ViewUtils, XBlockViewUtils, MoveXBlockUtils, H
},

renderTagElements: function(tags, depth, parentId) {
/* This function displays the tags in the sidebar of the legacy Unit Outline Page.
* It is not used when the Authoring MFE iframes a component in the Unit Outline. */
const parentElement = document.querySelector(`.content-tags-${parentId}`);
if (!parentElement) return;

const tagListElement = this;
tags.forEach(function(tag) {
const parentElement = document.querySelector(`.content-tags-${parentId}`);
var tagContentElement = document.createElement('div'),
tagValueElement = document.createElement('span');

Expand Down
60 changes: 22 additions & 38 deletions common/djangoapps/student/tests/test_linkedin.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,20 @@ class LinkedInAddToProfileUrlTests(TestCase):
def test_linked_in_url(self, cert_mode, expected_cert_name):
config = LinkedInAddToProfileConfigurationFactory()

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

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

# We can switch to this instead of the assertIn once edx-platform reaches Python 3.8
# There was a problem with dict ordering in the add_to_profile_url function that will go away then.
# self.assertEqual(actual_url, expected_url)

assert 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME' in actual_url
assert f'&name={quote(settings.PLATFORM_NAME.encode("utf-8"))}+{expected_cert_name}' in actual_url
assert '&certUrl={cert_url}'.format(cert_url=quote(self.CERT_URL, safe='')) in actual_url
assert f'&organizationId={config.company_identifier}' in actual_url
self.assertEqual(actual_url, expected_url)

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

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

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

# We can switch to this instead of the assertIn once edx-platform reaches Python 3.8
# There was a problem with dict ordering in the add_to_profile_url function that will go away then.
# self.assertEqual(actual_url, expected_url)

assert 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME' in actual_url
assert f'&name={quote(settings.PLATFORM_NAME.encode("utf-8"))}+{expected_cert_name}' in actual_url
assert '&certUrl={cert_url}'.format(cert_url=quote(self.CERT_URL, safe='')) in actual_url
assert f'&organizationId={config.company_identifier}' in actual_url
self.assertEqual(actual_url, expected_url)
33 changes: 12 additions & 21 deletions common/djangoapps/student/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,28 +440,19 @@ def test_linked_in_add_to_profile_btn_with_certificate(self):
assert response.status_code == 200
self.assertContains(response, 'Add Certificate to LinkedIn')

# We can switch to this and the commented out assertContains once edx-platform reaches Python 3.8
# expected_url = (
# 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&'
# 'name={platform}+Honor+Code+Certificate+for+Omega&certUrl={cert_url}&'
# 'organizationId={company_identifier}'
# ).format(
# platform=quote(settings.PLATFORM_NAME.encode('utf-8')),
# cert_url=quote(cert.download_url, safe=''),
# company_identifier=linkedin_config.company_identifier,
# )

# self.assertContains(response, escape(expected_url))

# These can be removed (in favor of the above) once we are on Python 3.8. Fails in 3.5 because of dict ordering
self.assertContains(response, escape('https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME'))
self.assertContains(response, escape('&name={platform}+Honor+Code+Certificate+for+Omega'.format(
platform=quote(settings.PLATFORM_NAME.encode('utf-8'))
)))
self.assertContains(response, escape('&certUrl={cert_url}'.format(cert_url=quote(cert.download_url, safe=''))))
self.assertContains(response, escape('&organizationId={company_identifier}'.format(
expected_url = (
'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&'
'name={platform}+Honor+Code+Certificate+for+Omega&'
'certUrl={cert_url}&'
'organizationId={company_identifier}'
).format(
platform=quote(settings.PLATFORM_NAME.encode('utf-8')),
cert_url=quote(cert.download_url, safe=''),
company_identifier=linkedin_config.company_identifier
)))
)

# Single assertion for the expected LinkedIn URL
self.assertContains(response, escape(expected_url))

@skip_unless_lms
def test_dashboard_metadata_caching(self):
Expand Down
24 changes: 9 additions & 15 deletions common/djangoapps/util/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,38 +53,32 @@ def wrapper(request, *args, **kwargs):
# specifically the branding index, to do authentication.
# If that page is cached the authentication doesn't
# happen, so we disable the cache when that feature is enabled.
if (
not request.user.is_authenticated
):
if not request.user.is_authenticated:
# Use the cache. The same view accessed through different domain names may
# return different things, so include the domain name in the key.
domain = str(request.META.get('HTTP_HOST')) + '.'
cache_key = domain + "cache_if_anonymous." + get_language() + '.' + request.path
domain = request.META.get('HTTP_HOST', '') + '.'
cache_key = f"{domain}cache_if_anonymous.{get_language()}.{request.path}"

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

response = cache.get(cache_key)

if response:
# A hack to ensure that the response data is a valid text type for both Python 2 and 3.
response_content = list(response._container) # lint-amnesty, pylint: disable=bad-option-value, protected-access, protected-member
response.content = b''
for item in response_content:
response.write(item)
# Ensure that response content is properly handled for caching
response.content = (
# pylint: disable=protected-access
b''.join(response._container) if hasattr(response, '_container') else response.content
)
else:
response = view_func(request, *args, **kwargs)
cache.set(cache_key, response, 60 * 3)

return response

else:
# Don't use the cache.
return view_func(request, *args, **kwargs)
Expand Down
15 changes: 13 additions & 2 deletions common/static/js/src/tooltip_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,20 @@
},

getCoords: function(pageX, pageY) {
let left = pageX - 0.5 * this.tooltip.outerWidth();
const top = pageY - (this.tooltip.outerHeight() + 15);
// Check if the tooltip is going off the right edge of the screen
if (left + this.tooltip.outerWidth() > window.innerWidth) {
left = window.innerWidth - this.tooltip.outerWidth();
}
// Check if the tooltip is going off the left edge of the screen
if (left < 0) {
left = 0;
}

return {
left: pageX - 0.5 * this.tooltip.outerWidth(),
top: pageY - (this.tooltip.outerHeight() + 15)
left,
top,
};
},

Expand Down
20 changes: 12 additions & 8 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.coverage',
'sphinx.ext.doctest',
'sphinx.ext.graphviz',
Expand All @@ -68,6 +67,18 @@
'sphinx_design',
'code_annotations.contrib.sphinx.extensions.featuretoggles',
'code_annotations.contrib.sphinx.extensions.settings',
'autoapi.extension',
]

autoapi_type = 'python'
autoapi_dirs = ['../lms', '../openedx']

autoapi_ignore = [
'*/migrations/*',
'*/tests/*',
'*.pyc',
'__init__.py',
'**/xblock_serializer/data.py',
]

# Rediraffe related settings.
Expand Down Expand Up @@ -277,13 +288,6 @@
'django': ('https://docs.djangoproject.com/en/1.11/', 'https://docs.djangoproject.com/en/1.11/_objects/'),
}

# Mock out these external modules during code import to avoid errors
autodoc_mock_imports = [
'MySQLdb',
'django_mysql',
'pymongo',
]

# Start building a map of the directories relative to the repository root to
# run sphinx-apidoc against and the directories under "docs" in which to store
# the generated *.rst files
Expand Down
15 changes: 0 additions & 15 deletions docs/lms-openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9291,21 +9291,6 @@ paths:
in: path
required: true
type: string
/user/v1/skill_level/{job_id}/:
get:
operationId: user_v1_skill_level_read
description: GET /api/user/v1/skill_level/{job_id}/
parameters: []
responses:
'200':
description: ''
tags:
- user
parameters:
- name: job_id
in: path
required: true
type: string
/user/v1/user_prefs/:
get:
operationId: user_v1_user_prefs_list
Expand Down
1 change: 1 addition & 0 deletions lms/djangoapps/certificates/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ def _format_certificate_for_user(username, cert):
if cert.status == CertificateStatuses.downloadable
else None
),
"uuid": cert.verify_uuid,
}

return None
Expand Down
35 changes: 24 additions & 11 deletions lms/djangoapps/certificates/apis/v0/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,17 +173,22 @@ def get_url(self, username):

def assert_success_response_for_student(self, response, download_url='www.google.com'):
""" This method is required by AuthAndScopesTestMixin. """
assert response.data ==\
[{'username': self.student.username,
'course_id': str(self.course.id),
'course_display_name': self.course.display_name,
'course_organization': self.course.org,
'certificate_type': CourseMode.VERIFIED,
'created_date': self.now,
'modified_date': self.now,
'status': CertificateStatuses.downloadable,
'is_passing': True,
'download_url': download_url, 'grade': '0.88'}]
assert response.data == [
{
'username': self.student.username,
'course_id': str(self.course.id),
'course_display_name': self.course.display_name,
'course_organization': self.course.org,
'certificate_type': CourseMode.VERIFIED,
'created_date': self.now,
'modified_date': self.now,
'status': CertificateStatuses.downloadable,
'is_passing': True,
'download_url': download_url,
'grade': '0.88',
'uuid': str(self.cert.verify_uuid)
}
]

@patch('edx_rest_framework_extensions.permissions.log')
@ddt.data(*list(AuthType))
Expand Down Expand Up @@ -212,6 +217,7 @@ def test_another_user_with_certs_shared_public(self, auth_type):

assert resp.status_code == status.HTTP_200_OK
assert len(resp.data) == 1
assert 'uuid' in resp.data[0]

def test_owner_can_access_its_certs(self):
"""
Expand All @@ -227,6 +233,7 @@ def test_owner_can_access_its_certs(self):

resp = self.get_response(AuthType.session, requesting_user=self.student)
assert resp.status_code == status.HTTP_200_OK
assert 'uuid' in resp.data[0]

# verifies that other than owner cert list api is not accessible
resp = self.get_response(AuthType.session, requesting_user=self.other_student)
Expand All @@ -246,12 +253,15 @@ def test_public_profile_certs_is_accessible(self):

resp = self.get_response(AuthType.session, requesting_user=self.student)
assert resp.status_code == status.HTTP_200_OK
assert 'uuid' in resp.data[0]

resp = self.get_response(AuthType.session, requesting_user=self.other_student)
assert resp.status_code == status.HTTP_200_OK
assert 'uuid' in resp.data[0]

resp = self.get_response(AuthType.session, requesting_user=self.global_staff)
assert resp.status_code == status.HTTP_200_OK
assert 'uuid' in resp.data[0]

@ddt.data(*list(AuthType))
def test_another_user_with_certs_shared_custom(self, auth_type):
Expand All @@ -276,6 +286,7 @@ def test_another_user_with_certs_shared_custom(self, auth_type):

assert resp.status_code == status.HTTP_200_OK
assert len(resp.data) == 1
assert 'uuid' in resp.data[0]

@patch('edx_rest_framework_extensions.permissions.log')
@ddt.data(*JWT_AUTH_TYPES)
Expand All @@ -290,6 +301,7 @@ def test_jwt_on_behalf_of_other_user(self, auth_type, mock_log):
else:
assert resp.status_code == status.HTTP_200_OK
assert len(resp.data) == 1
assert 'uuid' in resp.data[0]

@patch('edx_rest_framework_extensions.permissions.log')
@ddt.data(*JWT_AUTH_TYPES)
Expand Down Expand Up @@ -422,3 +434,4 @@ def test_certificate_without_course(self, mock_get_course_run_details):
assert response.status_code == status.HTTP_200_OK
self.assertContains(response, cert_for_deleted_course.download_url)
self.assertContains(response, expected_course_name)
assert 'uuid' in response.data[0]
1 change: 1 addition & 0 deletions lms/djangoapps/certificates/apis/v0/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ def get(self, request, username):
'is_passing': user_cert.get('is_passing'),
'download_url': user_cert.get('download_url'),
'grade': user_cert.get('grade'),
'uuid': user_cert.get('uuid'),
})
return Response(user_certs)

Expand Down
Loading

0 comments on commit a37a04c

Please sign in to comment.