diff --git a/lms/djangoapps/discussion/tasks.py b/lms/djangoapps/discussion/tasks.py index 7d19cbb9cd63..ce6baaa5873d 100644 --- a/lms/djangoapps/discussion/tasks.py +++ b/lms/djangoapps/discussion/tasks.py @@ -12,6 +12,7 @@ from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user from django.contrib.sites.models import Site from edx_ace import ace +from edx_ace.channel import ChannelType from edx_ace.recipient import Recipient from edx_ace.utils import date from edx_django_utils.monitoring import set_code_owner_attribute @@ -74,6 +75,12 @@ def __init__(self, *args, **kwargs): self.options['transactional'] = True +class CommentNotification(BaseMessageType): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.options['transactional'] = True + + @shared_task(base=LoggedTask) @set_code_owner_attribute def send_ace_message(context): # lint-amnesty, pylint: disable=missing-function-docstring @@ -82,17 +89,40 @@ def send_ace_message(context): # lint-amnesty, pylint: disable=missing-function if _should_send_message(context): context['site'] = Site.objects.get(id=context['site_id']) thread_author = User.objects.get(id=context['thread_author_id']) - with emulate_http_request(site=context['site'], user=thread_author): - message_context = _build_message_context(context) + comment_author = User.objects.get(id=context['comment_author_id']) + with emulate_http_request(site=context['site'], user=comment_author): + message_context = _build_message_context(context, notification_type='forum_response') message = ResponseNotification().personalize( Recipient(thread_author.id, thread_author.email), _get_course_language(context['course_id']), message_context ) - log.info('Sending forum comment email notification with context %s', message_context) - ace.send(message) + log.info('Sending forum comment notification with context %s', message_context) + if context['comment_author_id'] != context['thread_author_id']: + limit_to_channels = None + else: + limit_to_channels = [ChannelType.PUSH] + ace.send(message, limit_to_channels=limit_to_channels) _track_notification_sent(message, context) + elif _should_send_subcomment_message(context): + context['site'] = Site.objects.get(id=context['site_id']) + comment_author = User.objects.get(id=context['comment_author_id']) + thread_author = User.objects.get(id=context['thread_author_id']) + + with emulate_http_request(site=context['site'], user=comment_author): + message_context = _build_message_context(context) + message = CommentNotification().personalize( + Recipient(thread_author.id, thread_author.email), + _get_course_language(context['course_id']), + message_context + ) + log.info('Sending forum comment notification with context %s', message_context) + ace.send(message, limit_to_channels=[ChannelType.PUSH]) + _track_notification_sent(message, context) + else: + return + @shared_task(base=LoggedTask) @set_code_owner_attribute @@ -153,8 +183,17 @@ def _should_send_message(context): cc_thread_author = cc.User(id=context['thread_author_id'], course_id=context['course_id']) return ( _is_user_subscribed_to_thread(cc_thread_author, context['thread_id']) and - _is_not_subcomment(context['comment_id']) and - _is_first_comment(context['comment_id'], context['thread_id']) + _is_not_subcomment(context['comment_id']) + ) + + +def _should_send_subcomment_message(context): + cc_thread_author = cc.User(id=context['thread_author_id'], course_id=context['course_id']) + comment_author_is_thread_author = context['comment_author_id'] == context['thread_author_id'] + return ( + _is_user_subscribed_to_thread(cc_thread_author, context['thread_id']) and + _is_subcomment(context['comment_id']) and + not comment_author_is_thread_author ) @@ -164,9 +203,13 @@ def _is_content_still_reported(context): return len(cc.Thread.find(context['thread_id']).abuse_flaggers) > 0 -def _is_not_subcomment(comment_id): +def _is_subcomment(comment_id): comment = cc.Comment.find(id=comment_id).retrieve() - return not getattr(comment, 'parent_id', None) + return getattr(comment, 'parent_id', None) + + +def _is_not_subcomment(comment_id): + return not _is_subcomment(comment_id) def _is_first_comment(comment_id, thread_id): # lint-amnesty, pylint: disable=missing-function-docstring @@ -204,7 +247,7 @@ def _get_course_language(course_id): return language -def _build_message_context(context): # lint-amnesty, pylint: disable=missing-function-docstring +def _build_message_context(context, notification_type='forum_comment'): # lint-amnesty, pylint: disable=missing-function-docstring message_context = get_base_template_context(context['site']) message_context.update(context) thread_author = User.objects.get(id=context['thread_author_id']) @@ -219,7 +262,8 @@ def _build_message_context(context): # lint-amnesty, pylint: disable=missing-fu 'comment_username': comment_author.username, 'post_link': post_link, 'push_notification_extra_context': { - 'notification_type': 'forum_comment', + 'notification_type': notification_type, + 'topic_id': context['thread_commentable_id'], 'thread_id': context['thread_id'], 'comment_id': context['comment_id'], }, diff --git a/lms/djangoapps/discussion/templates/discussion/edx_ace/commentnotification/push/body.txt b/lms/djangoapps/discussion/templates/discussion/edx_ace/commentnotification/push/body.txt new file mode 100644 index 000000000000..391e3d8ef4d7 --- /dev/null +++ b/lms/djangoapps/discussion/templates/discussion/edx_ace/commentnotification/push/body.txt @@ -0,0 +1,3 @@ +{% load i18n %} +{% blocktrans trimmed %}{{ comment_username }} commented to {{ thread_title }}:{% endblocktrans %} +{{ comment_body_text }} diff --git a/lms/djangoapps/discussion/templates/discussion/edx_ace/commentnotification/push/subject.txt b/lms/djangoapps/discussion/templates/discussion/edx_ace/commentnotification/push/subject.txt new file mode 100644 index 000000000000..d2298a812990 --- /dev/null +++ b/lms/djangoapps/discussion/templates/discussion/edx_ace/commentnotification/push/subject.txt @@ -0,0 +1,3 @@ +{% load i18n %} + +{% blocktrans %}Comment to {{ thread_title }}{% endblocktrans %} diff --git a/openedx/core/djangoapps/ace_common/settings/common.py b/openedx/core/djangoapps/ace_common/settings/common.py index dd1d5b680763..9dd0962b2d3a 100644 --- a/openedx/core/djangoapps/ace_common/settings/common.py +++ b/openedx/core/djangoapps/ace_common/settings/common.py @@ -36,7 +36,7 @@ def plugin_settings(settings): # lint-amnesty, pylint: disable=missing-function if getattr(settings, 'FIREBASE_APP', None): settings.ACE_ENABLED_CHANNELS.append(settings.ACE_CHANNEL_DEFAULT_PUSH) - settings.ACE_ENABLED_POLICIES.append(settings.ACE_CHANNEL_DEFAULT_PUSH) + settings.ACE_ENABLED_POLICIES.append('bulk_push_notification_optout') settings.PUSH_NOTIFICATIONS_SETTINGS = { 'CONFIG': 'push_notifications.conf.AppConfig', diff --git a/openedx/core/djangoapps/ace_common/settings/production.py b/openedx/core/djangoapps/ace_common/settings/production.py index d3409e13f306..0d128850f536 100644 --- a/openedx/core/djangoapps/ace_common/settings/production.py +++ b/openedx/core/djangoapps/ace_common/settings/production.py @@ -33,7 +33,7 @@ def plugin_settings(settings): settings.FIREBASE_APP = setup_firebase_app(settings.FIREBASE_CREDENTIALS, settings.FCM_APP_NAME) if settings.FIREBASE_APP: settings.ACE_ENABLED_CHANNELS.append(settings.ACE_CHANNEL_DEFAULT_PUSH) - settings.ACE_ENABLED_POLICIES.append(settings.ACE_CHANNEL_DEFAULT_PUSH) + settings.ACE_ENABLED_POLICIES.append('bulk_push_notification_optout') settings.PUSH_NOTIFICATIONS_SETTINGS = { 'CONFIG': 'push_notifications.conf.AppConfig',