-
-
Notifications
You must be signed in to change notification settings - Fork 304
Add ability to send a notification email on review of community libra… #6050
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| <!DOCTYPE html> | ||
| {% load i18n %} | ||
| {% get_current_language as LANGUAGE_CODE %} | ||
| {% get_current_language_bidi as LANGUAGE_BIDI %} | ||
| <html lang="{{ LANGUAGE_CODE }}" dir="{% if LANGUAGE_BIDI %}rtl{% else %}ltr{% endif %}"> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. praise: Setting |
||
| <head> | ||
| <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| </head> | ||
| <body> | ||
| <p>{% blocktrans with name=name %}Hello {{ name }},{% endblocktrans %}</p> | ||
|
|
||
| <p><a href="{{ channel_url }}" target="_blank">{{ channel.name }}</a> ({{ channel_url }})</p> | ||
|
|
||
| {% if approved %} | ||
| <p>{% translate "Your submission has been approved and will be added to the Community Library soon." %}</p> | ||
| {% else %} | ||
| <p>{% translate "Your submission needs changes. Please review the notes below and resubmit after all feedback has been addressed." %}</p> | ||
| {% endif %} | ||
|
|
||
| {% if feedback_notes %} | ||
| <p>{% translate "Notes from the reviewer" %}: {{ feedback_notes }}</p> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Two issues on this line. The
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're merging this to hotfixes to start with, we can come back to this during fuller string review.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Understood — deferring to the fuller string review. Thanks. |
||
| {% endif %} | ||
|
|
||
| <p> | ||
| {% translate "Thanks for using Kolibri Studio!" %} | ||
| <br> | ||
| {% translate "The Learning Equality Team" %} | ||
| </p> | ||
| </body> | ||
| </html> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
| from unittest import mock | ||
|
|
||
| import pytz | ||
| from django.core import mail | ||
| from django.urls import reverse | ||
|
|
||
| from contentcuration.constants import ( | ||
|
|
@@ -16,6 +17,7 @@ | |
| from contentcuration.tests import testdata | ||
| from contentcuration.tests.base import StudioAPITestCase | ||
| from contentcuration.tests.helpers import reverse_with_query | ||
| from contentcuration.utils.urls import canonical_url | ||
| from contentcuration.viewsets.sync.constants import ADDED_TO_COMMUNITY_LIBRARY | ||
|
|
||
|
|
||
|
|
@@ -731,6 +733,58 @@ def test_resolve_submission__accept_correct(self, apply_task_mock): | |
| channel_id=self.submission.channel.id, | ||
| ) | ||
|
|
||
| self.assertEqual(len(mail.outbox), 1) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ✅ Resolved — addressed in the current code. praise: Both paths assert against the real locmem mail backend — recipient, subject, body copy, channel name, and feedback notes — rather than mocking the mail layer. Solid end-to-end coverage of the new path. |
||
| sent_email = mail.outbox[0] | ||
| self.assertEqual(sent_email.to, [self.submission.author.email]) | ||
| self.assertIn("approved", sent_email.subject.lower()) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ✅ Resolved — addressed in the current code. suggestion: These literals are the current values of Two cheap additions while you're here: assert |
||
| self.assertIn("approved", sent_email.body.lower()) | ||
| self.assertIn(self.submission.channel.name, sent_email.body) | ||
| self.assertIn( | ||
| canonical_url( | ||
| reverse("channel", kwargs={"channel_id": self.submission.channel.pk}) | ||
| ), | ||
| sent_email.body, | ||
| ) | ||
|
|
||
| @mock.patch( | ||
| "contentcuration.viewsets.community_library_submission.apply_channel_changes_task" | ||
| ) | ||
| @mock.patch( | ||
| "contentcuration.models.CommunityLibrarySubmission.send_resolution_email", | ||
| side_effect=Exception("SMTP is down"), | ||
| ) | ||
| def test_resolve_submission__accept_correct_when_email_fails( | ||
| self, send_email_mock, apply_task_mock | ||
| ): | ||
| """A failure to notify the author shouldn't undo or fail the resolution.""" | ||
| self.client.force_authenticate(user=self.admin_user) | ||
| response = self.client.post( | ||
| reverse( | ||
| "admin-community-library-submission-resolve", | ||
| args=[self.submission.id], | ||
| ), | ||
| self.resolve_approve_metadata, | ||
| format="json", | ||
| ) | ||
| self.assertEqual(response.status_code, 200, response.content) | ||
|
|
||
| resolved_submission = CommunityLibrarySubmission.objects.get( | ||
| id=self.submission.id | ||
| ) | ||
| self.assertEqual( | ||
| resolved_submission.status, | ||
| community_library_submission_constants.STATUS_APPROVED, | ||
| ) | ||
| Change.objects.get( | ||
| channel=self.submission.channel, | ||
| change_type=ADDED_TO_COMMUNITY_LIBRARY, | ||
| ) | ||
| apply_task_mock.fetch_or_enqueue.assert_called_once_with( | ||
| self.admin_user, | ||
| channel_id=self.submission.channel.id, | ||
| ) | ||
| self.assertEqual(len(mail.outbox), 0) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nitpick: |
||
|
|
||
| @mock.patch( | ||
| "contentcuration.viewsets.community_library_submission.apply_channel_changes_task" | ||
| ) | ||
|
|
@@ -770,6 +824,20 @@ def test_resolve_submission__reject_correct(self, apply_task_mock): | |
| ) | ||
| apply_task_mock.fetch_or_enqueue.assert_not_called() | ||
|
|
||
| self.assertEqual(len(mail.outbox), 1) | ||
| sent_email = mail.outbox[0] | ||
| self.assertEqual(sent_email.to, [self.submission.author.email]) | ||
| self.assertIn("needs changes", sent_email.subject.lower()) | ||
| self.assertIn("needs changes", sent_email.body.lower()) | ||
| self.assertIn(self.submission.channel.name, sent_email.body) | ||
| self.assertIn( | ||
| canonical_url( | ||
| reverse("channel", kwargs={"channel_id": self.submission.channel.pk}) | ||
| ), | ||
| sent_email.body, | ||
| ) | ||
| self.assertIn(self.feedback_notes, sent_email.body) | ||
|
|
||
| def test_resolve_submission__reject_missing_resolution_reason(self): | ||
| self.client.force_authenticate(user=self.admin_user) | ||
| metadata = self.resolve_reject_metadata.copy() | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,6 +41,20 @@ def _get_language_info(): | |
| LANGUAGE_INFO = _get_language_info() | ||
|
|
||
|
|
||
| def closest_supported_locale(lang_code): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: No coverage for this helper or the non- A direct test (
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Relying on the caller to pass in the lang_code here is a little fragile, but as there is only one caller this is fine. |
||
| """ | ||
| Given a content language's primary code (e.g. "es", "fr"), return the | ||
| Studio UI locale in SUPPORTED_LANGUAGES that matches it, ignoring region, | ||
| or None if Studio has no UI translation for that language. | ||
| """ | ||
| if not lang_code: | ||
| return None | ||
| for supported in SUPPORTED_LANGUAGES: | ||
| if supported.split("-")[0] == lang_code: | ||
| return supported | ||
| return None | ||
|
|
||
|
|
||
| def language_globals(): | ||
| language_code = get_language() | ||
| lang_dir = "rtl" if get_language_bidi() else "ltr" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: These two subjects and the three template strings are net-new msgids —
grep -c "Community Library" contentcuration/locale/es_ES/LC_MESSAGES/django.poreturns0. The reused boilerplate (Hello %(name)s,,Thanks for using Kolibri Studio!,The Learning Equality Team) is in the catalogs.Since this PR now deliberately activates a non-English locale, a Spanish-language channel author gets Spanish greeting and sign-off wrapped around an English body — more jarring than an all-English mail.
i18n-upload.ymlisworkflow_dispatch-only so nothing structurally blocks a Crowdin round-trip onhotfixes. Is one planned before this ships? If not, consider limiting the override to locales that have these msgids.