From 00772693ef0f29a01872348124b40be4ef3a5c32 Mon Sep 17 00:00:00 2001 From: Muhammad Adeel Tajamul <77053848+muhammadadeeltajamul@users.noreply.github.com> Date: Mon, 19 Aug 2024 00:21:43 -0700 Subject: [PATCH] feat: added unsubscribe url in digest header (#35319) --- .../djangoapps/notifications/email/utils.py | 25 ++++++++------ .../notifications/digest_footer.html | 2 +- .../notifications/digest_header.html | 7 ++++ .../notifications/tests/test_views.py | 34 +++++++++++++++++-- .../core/djangoapps/notifications/views.py | 7 ++-- 5 files changed, 55 insertions(+), 20 deletions(-) diff --git a/openedx/core/djangoapps/notifications/email/utils.py b/openedx/core/djangoapps/notifications/email/utils.py index 1e0f4c81c7..da288750bb 100644 --- a/openedx/core/djangoapps/notifications/email/utils.py +++ b/openedx/core/djangoapps/notifications/email/utils.py @@ -7,7 +7,6 @@ import json from django.conf import settings from django.contrib.auth import get_user_model from django.shortcuts import get_object_or_404 -from django.urls import reverse from pytz import utc from waffle import get_waffle_flag_model # pylint: disable=invalid-django-waffle-import @@ -20,7 +19,10 @@ from openedx.core.djangoapps.notifications.base_notification import ( ) from openedx.core.djangoapps.notifications.config.waffle import ENABLE_EMAIL_NOTIFICATIONS from openedx.core.djangoapps.notifications.email_notifications import EmailCadence -from openedx.core.djangoapps.notifications.models import CourseNotificationPreference +from openedx.core.djangoapps.notifications.models import ( + CourseNotificationPreference, + get_course_notification_preference_config_version +) from xmodule.modulestore.django import modulestore from .notification_icons import NotificationTypeIcons @@ -71,15 +73,7 @@ def get_unsubscribe_link(username, patch): """ encrypted_username = encrypt_string(username) encrypted_patch = encrypt_object(patch) - kwargs = { - 'username': encrypted_username, - 'patch': encrypted_patch - } - relative_url = reverse('preference_update_from_encrypted_username_view', kwargs=kwargs) - protocol = 'https://' - if settings.DEBUG: - protocol = 'http://' - return f"{protocol}{settings.LMS_BASE}{relative_url}" + return f"{settings.LEARNING_MICROFRONTEND_URL}/preferences-unsubscribe/{encrypted_username}/{encrypted_patch}" def create_email_template_context(username): @@ -363,6 +357,14 @@ def update_user_preferences_from_patch(encrypted_username, encrypted_patch): return COURSE_NOTIFICATION_APPS[app_name]['core_email_cadence'] return COURSE_NOTIFICATION_TYPES[notification_type]['email_cadence'] + def get_updated_preference(pref): + """ + Update preference if config version doesn't match + """ + if pref.config_version != get_course_notification_preference_config_version(): + pref = pref.get_user_course_preference(pref.user_id, pref.course_id) + return pref + course_ids = CourseEnrollment.objects.filter(user=user).values_list('course_id', flat=True) CourseNotificationPreference.objects.bulk_create( [ @@ -375,6 +377,7 @@ def update_user_preferences_from_patch(encrypted_username, encrypted_patch): # pylint: disable=too-many-nested-blocks for preference in preferences: + preference = get_updated_preference(preference) preference_json = preference.notification_preference_config for app_name, app_prefs in preference_json.items(): if not is_name_match(app_name, app_value): diff --git a/openedx/core/djangoapps/notifications/templates/notifications/digest_footer.html b/openedx/core/djangoapps/notifications/templates/notifications/digest_footer.html index 0419b25665..4fa903d127 100644 --- a/openedx/core/djangoapps/notifications/templates/notifications/digest_footer.html +++ b/openedx/core/djangoapps/notifications/templates/notifications/digest_footer.html @@ -38,7 +38,7 @@ Notification Settings - Unsubscribe + Unsubscribe from email digest for learning activity

diff --git a/openedx/core/djangoapps/notifications/templates/notifications/digest_header.html b/openedx/core/djangoapps/notifications/templates/notifications/digest_header.html index 7957524e8a..1f22ced200 100644 --- a/openedx/core/djangoapps/notifications/templates/notifications/digest_header.html +++ b/openedx/core/djangoapps/notifications/templates/notifications/digest_header.html @@ -5,6 +5,13 @@ style="background: #00262b; color: white; width: 100%; padding: 1.5rem" > + + + + Unsubscribe + + + logo_url diff --git a/openedx/core/djangoapps/notifications/tests/test_views.py b/openedx/core/djangoapps/notifications/tests/test_views.py index d2968749ef..e40e520789 100644 --- a/openedx/core/djangoapps/notifications/tests/test_views.py +++ b/openedx/core/djangoapps/notifications/tests/test_views.py @@ -28,9 +28,13 @@ from openedx.core.djangoapps.django_comment_common.models import ( ) from openedx.core.djangoapps.notifications.config.waffle import ENABLE_NOTIFICATIONS from openedx.core.djangoapps.notifications.email_notifications import EmailCadence -from openedx.core.djangoapps.notifications.models import CourseNotificationPreference, Notification +from openedx.core.djangoapps.notifications.models import ( + CourseNotificationPreference, + Notification, + get_course_notification_preference_config_version +) from openedx.core.djangoapps.notifications.serializers import NotificationCourseEnrollmentSerializer -from openedx.core.djangoapps.notifications.email.utils import get_unsubscribe_link +from openedx.core.djangoapps.notifications.email.utils import encrypt_object, encrypt_string from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -910,7 +914,13 @@ class UpdatePreferenceFromEncryptedDataView(ModuleStoreTestCase): """ Tests if preference is updated when url is hit """ - url = get_unsubscribe_link(self.user.username, {'channel': 'email', 'value': False}) + user_hash = encrypt_string(self.user.username) + patch_hash = encrypt_object({'channel': 'email', 'value': False}) + url_params = { + "username": user_hash, + "patch": patch_hash + } + url = reverse("preference_update_from_encrypted_username_view", kwargs=url_params) func = getattr(self.client, request_type) response = func(url) assert response.status_code == status.HTTP_200_OK @@ -921,6 +931,24 @@ class UpdatePreferenceFromEncryptedDataView(ModuleStoreTestCase): assert type_prefs['email'] is False assert type_prefs['email_cadence'] == EmailCadence.NEVER + def test_if_config_version_is_updated(self): + """ + Tests if preference version is updated before applying patch data + """ + preference = CourseNotificationPreference.objects.get(user=self.user, course_id=self.course.id) + preference.config_version -= 1 + preference.save() + user_hash = encrypt_string(self.user.username) + patch_hash = encrypt_object({'channel': 'email', 'value': False}) + url_params = { + "username": user_hash, + "patch": patch_hash + } + url = reverse("preference_update_from_encrypted_username_view", kwargs=url_params) + self.client.get(url) + preference = CourseNotificationPreference.objects.get(user=self.user, course_id=self.course.id) + assert preference.config_version == get_course_notification_preference_config_version() + def remove_notifications_with_visibility_settings(expected_response): """ diff --git a/openedx/core/djangoapps/notifications/views.py b/openedx/core/djangoapps/notifications/views.py index ee5e282d90..fdc91c12a9 100644 --- a/openedx/core/djangoapps/notifications/views.py +++ b/openedx/core/djangoapps/notifications/views.py @@ -5,7 +5,7 @@ from datetime import datetime, timedelta from django.conf import settings from django.db.models import Count -from django.shortcuts import get_object_or_404, render +from django.shortcuts import get_object_or_404 from django.utils.translation import gettext as _ from opaque_keys.edx.keys import CourseKey from pytz import UTC @@ -441,7 +441,4 @@ def preference_update_from_encrypted_username_view(request, username, patch): username and patch must be string """ update_user_preferences_from_patch(username, patch) - context = { - "notification_preferences_url": f"{settings.ACCOUNT_MICROFRONTEND_URL}/notifications" - } - return render(request, "notifications/email_digest_preference_update.html", context=context) + return Response({"result": "success"}, status=status.HTTP_200_OK)