Commit de83e87f by Waheed Ahmed

Added mark as reviewed functionality for parent course.

ECOM-6347
parent a297c8e0
...@@ -425,7 +425,8 @@ class ChangeCourseStateViewTests(TestCase): ...@@ -425,7 +425,8 @@ class ChangeCourseStateViewTests(TestCase):
self.assertEqual(self.course_state.name, CourseStateChoices.Review) self.assertEqual(self.course_state.name, CourseStateChoices.Review)
self.assertEqual(self.course_state.owner_role, PublisherUserRole.CourseTeam) self.assertEqual(self.course_state.owner_role, PublisherUserRole.CourseTeam)
self._assert_email_sent(course_team_user) subject = 'Changes to {title} are ready for review'.format(title=self.course.title)
self._assert_email_sent(course_team_user, subject)
def test_change_course_state_with_course_team(self): def test_change_course_state_with_course_team(self):
""" """
...@@ -461,10 +462,10 @@ class ChangeCourseStateViewTests(TestCase): ...@@ -461,10 +462,10 @@ class ChangeCourseStateViewTests(TestCase):
self.assertEqual(self.course_state.owner_role, PublisherUserRole.MarketingReviewer) self.assertEqual(self.course_state.owner_role, PublisherUserRole.MarketingReviewer)
self.assertGreater(self.course_state.owner_role_modified, old_owner_role_modified) self.assertGreater(self.course_state.owner_role_modified, old_owner_role_modified)
self._assert_email_sent(marketing_user)
def _assert_email_sent(self, user):
subject = 'Changes to {title} are ready for review'.format(title=self.course.title) subject = 'Changes to {title} are ready for review'.format(title=self.course.title)
self._assert_email_sent(marketing_user, subject)
def _assert_email_sent(self, user, subject):
self.assertEqual(len(mail.outbox), 1) self.assertEqual(len(mail.outbox), 1)
self.assertEqual([user.email], mail.outbox[0].to) self.assertEqual([user.email], mail.outbox[0].to)
self.assertEqual(str(mail.outbox[0].subject), subject) self.assertEqual(str(mail.outbox[0].subject), subject)
...@@ -494,6 +495,37 @@ class ChangeCourseStateViewTests(TestCase): ...@@ -494,6 +495,37 @@ class ChangeCourseStateViewTests(TestCase):
self.assertEqual(response.data, expected) self.assertEqual(response.data, expected)
def test_mark_as_reviewed(self):
"""
Verify that user can mark course as reviewed.
"""
self.course_state.name = CourseStateChoices.Review
self.course_state.save()
factories.CourseUserRoleFactory(
course=self.course, role=PublisherUserRole.MarketingReviewer, user=self.user
)
course_team_user = UserFactory()
factories.CourseUserRoleFactory(
course=self.course, role=PublisherUserRole.CourseTeam, user=course_team_user
)
response = self.client.patch(
self.change_state_url,
data=json.dumps({'name': CourseStateChoices.Approved}),
content_type=JSON_CONTENT_TYPE
)
self.assertEqual(response.status_code, 200)
self.course_state = CourseState.objects.get(course=self.course)
self.assertEqual(self.course_state.name, CourseStateChoices.Approved)
subject = 'Changes to {title} has been approved'.format(title=self.course.title)
self._assert_email_sent(course_team_user, subject)
class ChangeCourseRunStateViewTests(TestCase): class ChangeCourseRunStateViewTests(TestCase):
......
...@@ -8,6 +8,7 @@ from django.template.loader import get_template ...@@ -8,6 +8,7 @@ from django.template.loader import get_template
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from course_discovery.apps.publisher.choices import PublisherUserRole from course_discovery.apps.publisher.choices import PublisherUserRole
from course_discovery.apps.publisher.utils import is_email_notification_enabled
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -155,16 +156,50 @@ def send_email_for_send_for_review(course, user): ...@@ -155,16 +156,50 @@ def send_email_for_send_for_review(course, user):
course (Object): Course object course (Object): Course object
user (Object): User object user (Object): User object
""" """
txt_template = 'publisher/email/course/send_for_review.txt'
html_template = 'publisher/email/course/send_for_review.html'
subject = _('Changes to {title} are ready for review').format(title=course.title) # pylint: disable=no-member
try: try:
txt_template = 'publisher/email/send_for_review.txt' send_course_workflow_email(course, user, subject, txt_template, html_template)
html_template = 'publisher/email/send_for_review.html' except Exception: # pylint: disable=broad-except
logger.exception('Failed to send email notifications send for review of course %s', course.id)
recipient_user = course.marketing_reviewer def send_email_for_mark_as_reviewed(course, user):
user_role = course.course_user_roles.get(user=user) """ Send email when course is marked as reviewed.
if user_role.role == PublisherUserRole.MarketingReviewer:
recipient_user = course.course_team_admin Arguments:
course (Object): Course object
user (Object): User object
"""
txt_template = 'publisher/email/course/mark_as_reviewed.txt'
html_template = 'publisher/email/course/mark_as_reviewed.html'
subject = _('Changes to {title} has been approved').format(title=course.title) # pylint: disable=no-member
try:
send_course_workflow_email(course, user, subject, txt_template, html_template)
except Exception: # pylint: disable=broad-except
logger.exception('Failed to send email notifications mark as reviewed of course %s', course.id)
def send_course_workflow_email(course, user, subject, txt_template, html_template):
""" Send email for course workflow state change.
Arguments:
course (Object): Course object
user (Object): User object
subject (String): Email subject
txt_template: (String): Email text template path
html_template: (String): Email html template path
"""
recipient_user = course.marketing_reviewer
user_role = course.course_user_roles.get(user=user)
if user_role.role == PublisherUserRole.MarketingReviewer:
recipient_user = course.course_team_admin
if is_email_notification_enabled(recipient_user):
partner_coordinator = course.partner_coordinator
to_addresses = [recipient_user.email] to_addresses = [recipient_user.email]
from_address = settings.PUBLISHER_FROM_EMAIL from_address = settings.PUBLISHER_FROM_EMAIL
page_path = reverse('publisher:publisher_course_detail', kwargs={'pk': course.id}) page_path = reverse('publisher:publisher_course_detail', kwargs={'pk': course.id})
...@@ -172,6 +207,7 @@ def send_email_for_send_for_review(course, user): ...@@ -172,6 +207,7 @@ def send_email_for_send_for_review(course, user):
'recipient_name': recipient_user.full_name or recipient_user.username if recipient_user else '', 'recipient_name': recipient_user.full_name or recipient_user.username if recipient_user else '',
'sender_name': user.full_name or user.username, 'sender_name': user.full_name or user.username,
'course_name': course.title, 'course_name': course.title,
'contact_us_email': partner_coordinator.email if partner_coordinator else '',
'course_page_url': 'https://{host}{path}'.format( 'course_page_url': 'https://{host}{path}'.format(
host=Site.objects.get_current().domain.strip('/'), path=page_path host=Site.objects.get_current().domain.strip('/'), path=page_path
) )
...@@ -181,12 +217,8 @@ def send_email_for_send_for_review(course, user): ...@@ -181,12 +217,8 @@ def send_email_for_send_for_review(course, user):
template = get_template(html_template) template = get_template(html_template)
html_content = template.render(context) html_content = template.render(context)
subject = _('Changes to {title} are ready for review').format(title=course.title) # pylint: disable=no-member
email_msg = EmailMultiAlternatives( email_msg = EmailMultiAlternatives(
subject, plain_content, from_address, to_addresses subject, plain_content, from_address, to_addresses
) )
email_msg.attach_alternative(html_content, 'text/html') email_msg.attach_alternative(html_content, 'text/html')
email_msg.send() email_msg.send()
except Exception: # pylint: disable=broad-except
logger.exception('Failed to send email notifications send for review of course %s', course.id)
...@@ -23,7 +23,8 @@ from course_discovery.apps.course_metadata.models import LevelType, Organization ...@@ -23,7 +23,8 @@ from course_discovery.apps.course_metadata.models import LevelType, Organization
from course_discovery.apps.course_metadata.utils import UploadToFieldNamePath from course_discovery.apps.course_metadata.utils import UploadToFieldNamePath
from course_discovery.apps.ietf_language_tags.models import LanguageTag from course_discovery.apps.ietf_language_tags.models import LanguageTag
from course_discovery.apps.publisher.choices import CourseRunStateChoices, CourseStateChoices, PublisherUserRole from course_discovery.apps.publisher.choices import CourseRunStateChoices, CourseStateChoices, PublisherUserRole
from course_discovery.apps.publisher.emails import send_email_for_change_state, send_email_for_send_for_review from course_discovery.apps.publisher.emails import (send_email_for_change_state, send_email_for_mark_as_reviewed,
send_email_for_send_for_review)
from course_discovery.apps.publisher.utils import is_email_notification_enabled from course_discovery.apps.publisher.utils import is_email_notification_enabled
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -207,6 +208,14 @@ class Course(TimeStampedModel, ChangedByMixin): ...@@ -207,6 +208,14 @@ class Course(TimeStampedModel, ChangedByMixin):
except CourseUserRole.DoesNotExist: except CourseUserRole.DoesNotExist:
return None return None
@property
def organization_extension(self):
organization = self.organizations.all().first()
if organization:
return organization.organization_extension
return None
class CourseRun(TimeStampedModel, ChangedByMixin): class CourseRun(TimeStampedModel, ChangedByMixin):
""" Publisher CourseRun model. It contains fields related to the course run intake form.""" """ Publisher CourseRun model. It contains fields related to the course run intake form."""
...@@ -554,6 +563,9 @@ class CourseState(TimeStampedModel, ChangedByMixin): ...@@ -554,6 +563,9 @@ class CourseState(TimeStampedModel, ChangedByMixin):
elif state == CourseStateChoices.Approved: elif state == CourseStateChoices.Approved:
self.approved() self.approved()
if waffle.switch_is_active('enable_publisher_email_notifications'):
send_email_for_mark_as_reviewed(self.course, user)
self.save() self.save()
......
...@@ -259,3 +259,51 @@ class CourseCreatedEmailTests(TestCase): ...@@ -259,3 +259,51 @@ class CourseCreatedEmailTests(TestCase):
self.assertIn('{dashboard_url}'.format(dashboard_url=reverse('publisher:publisher_dashboard')), body) self.assertIn('{dashboard_url}'.format(dashboard_url=reverse('publisher:publisher_dashboard')), body)
self.assertIn('Please create a Studio instance for this course', body) self.assertIn('Please create a Studio instance for this course', body)
self.assertIn('Thanks', body) self.assertIn('Thanks', body)
class SendForReviewEmailTests(TestCase):
""" Tests for the email functionality for send for review. """
def setUp(self):
super(SendForReviewEmailTests, self).setUp()
self.user = UserFactory()
self.course_state = factories.CourseStateFactory()
def test_email_with_error(self):
""" Verify that email failure log error message."""
with LogCapture(emails.logger.name) as l:
emails.send_email_for_send_for_review(self.course_state.course, self.user)
l.check(
(
emails.logger.name,
'ERROR',
'Failed to send email notifications send for review of course {}'.format(
self.course_state.course.id
)
)
)
class MarkAsReviewedEmailTests(TestCase):
""" Tests for the email functionality for mark as reviewed. """
def setUp(self):
super(MarkAsReviewedEmailTests, self).setUp()
self.user = UserFactory()
self.course_state = factories.CourseStateFactory()
def test_email_with_error(self):
""" Verify that email failure log error message."""
with LogCapture(emails.logger.name) as l:
emails.send_email_for_mark_as_reviewed(self.course_state.course, self.user)
l.check(
(
emails.logger.name,
'ERROR',
'Failed to send email notifications mark as reviewed of course {}'.format(
self.course_state.course.id
)
)
)
...@@ -672,6 +672,7 @@ class CourseRunDetailTests(TestCase): ...@@ -672,6 +672,7 @@ class CourseRunDetailTests(TestCase):
self.user.groups.add(Group.objects.get(name=ADMIN_GROUP_NAME)) self.user.groups.add(Group.objects.get(name=ADMIN_GROUP_NAME))
self.client.login(username=self.user.username, password=USER_PASSWORD) self.client.login(username=self.user.username, password=USER_PASSWORD)
self.course_run = factories.CourseRunFactory(course=self.course) self.course_run = factories.CourseRunFactory(course=self.course)
self.course_run_state = factories.CourseRunStateFactory(course_run=self.course_run)
self.organization_extension = factories.OrganizationExtensionFactory() self.organization_extension = factories.OrganizationExtensionFactory()
self.course.organizations.add(self.organization_extension.organization) self.course.organizations.add(self.organization_extension.organization)
...@@ -702,6 +703,7 @@ class CourseRunDetailTests(TestCase): ...@@ -702,6 +703,7 @@ class CourseRunDetailTests(TestCase):
available for that course-run. available for that course-run.
""" """
course_run = factories.CourseRunFactory(course=self.course) course_run = factories.CourseRunFactory(course=self.course)
factories.CourseRunStateFactory(course_run=course_run)
assign_perm( assign_perm(
OrganizationExtension.VIEW_COURSE_RUN, self.organization_extension.group, self.organization_extension OrganizationExtension.VIEW_COURSE_RUN, self.organization_extension.group, self.organization_extension
) )
...@@ -888,14 +890,20 @@ class CourseRunDetailTests(TestCase): ...@@ -888,14 +890,20 @@ class CourseRunDetailTests(TestCase):
expected_roles = [] expected_roles = []
for user_course_role in self.course.course_user_roles.all(): for user_course_role in self.course.course_user_roles.all():
expected_roles.append( expected_roles.append(
{'user_course_role': user_course_role, 'heading': ROLE_WIDGET_HEADINGS.get(user_course_role.role)} {
'course_role': user_course_role,
'heading': ROLE_WIDGET_HEADINGS.get(user_course_role.role),
'change_state_url': reverse(
'publisher:api:change_course_run_state', kwargs={'pk': self.course_run_state.id}
),
'user_list': get_internal_users()
}
) )
self.assertEqual(response.context['role_widgets'], expected_roles)
self.assertEqual(list(response.context['user_list']), list(get_internal_users())) self.assertEqual(response.context['role_widgets'], expected_roles)
def test_detail_page_role_assignment_with_non_internal_user(self): def test_detail_page_approval_widget_with_non_internal_user(self):
""" Verify that user can't see change role assignment widget without permissions. """ """ Verify that user can see change approval widget. """
# Create a user and assign course view permission. # Create a user and assign course view permission.
user = UserFactory() user = UserFactory()
...@@ -909,8 +917,8 @@ class CourseRunDetailTests(TestCase): ...@@ -909,8 +917,8 @@ class CourseRunDetailTests(TestCase):
response = self.client.get(self.page_url) response = self.client.get(self.page_url)
self.assertNotIn('role_widgets', response.context) self.assertIn('role_widgets', response.context)
self.assertNotIn('user_list', response.context) self.assertContains(response, 'APPROVALS')
def test_details_page_with_edit_permission(self): def test_details_page_with_edit_permission(self):
""" Test that user can see edit button on course run detail page. """ """ Test that user can see edit button on course run detail page. """
...@@ -1651,9 +1659,9 @@ class CourseDetailViewTests(TestCase): ...@@ -1651,9 +1659,9 @@ class CourseDetailViewTests(TestCase):
# Verify that `Send for Review` button is enabled # Verify that `Send for Review` button is enabled
self.assertContains(response, self.get_expected_data(CourseStateChoices.Review)) self.assertContains(response, self.get_expected_data(CourseStateChoices.Review))
def test_course_approval_widget_with_reviewed(self): def test_course_with_mark_as_reviewed(self):
""" """
Verify that user can see approval widget on course detail page with `Reviewed`. Verify that user can see approval widget on course detail page with `Mark as Reviewed`.
""" """
factories.CourseUserRoleFactory( factories.CourseUserRoleFactory(
course=self.course, user=self.user, role=PublisherUserRole.MarketingReviewer course=self.course, user=self.user, role=PublisherUserRole.MarketingReviewer
...@@ -1674,7 +1682,7 @@ class CourseDetailViewTests(TestCase): ...@@ -1674,7 +1682,7 @@ class CourseDetailViewTests(TestCase):
response = self.client.get(self.detail_page_url) response = self.client.get(self.detail_page_url)
# Verify that content is sent for review and user can see Reviewed button. # Verify that content is sent for review and user can see Reviewed button.
self.assertContains(response, 'Reviewed') self.assertContains(response, 'Mark as Reviewed')
self.assertContains(response, '<span class="icon fa fa-check" aria-hidden="true">') self.assertContains(response, '<span class="icon fa fa-check" aria-hidden="true">')
self.assertContains(response, 'Send for Review') self.assertContains(response, 'Send for Review')
self.assertContains(response, self.get_expected_data(CourseStateChoices.Approved)) self.assertContains(response, self.get_expected_data(CourseStateChoices.Approved))
...@@ -1689,6 +1697,37 @@ class CourseDetailViewTests(TestCase): ...@@ -1689,6 +1697,37 @@ class CourseDetailViewTests(TestCase):
return expected return expected
def test_course_with_reviewed(self):
"""
Verify that user can see approval widget on course detail page with `Reviewed`.
"""
factories.CourseUserRoleFactory(
course=self.course, user=self.user, role=PublisherUserRole.MarketingReviewer
)
self.course_state.owner_role = PublisherUserRole.MarketingReviewer
self.course_state.save()
new_user = UserFactory()
factories.CourseUserRoleFactory(
course=self.course, user=new_user, role=PublisherUserRole.CourseTeam
)
# To create history objects for both `Review` and `Approved` states
self.course.course_state.name = CourseStateChoices.Review
self.course.course_state.save()
self.course.course_state.name = CourseStateChoices.Approved
self.course.course_state.save()
self.user.groups.add(self.organization_extension.group)
assign_perm(OrganizationExtension.VIEW_COURSE, self.organization_extension.group, self.organization_extension)
response = self.client.get(self.detail_page_url)
# Verify that content is sent for review and user can see Reviewed button.
self.assertNotContains(response, 'Mark as Reviewed')
self.assertContains(response, 'Reviewed', count=1)
self.assertContains(response, '<span class="icon fa fa-check" aria-hidden="true">', count=2)
self.assertContains(response, 'Send for Review', count=1)
class CourseEditViewTests(TestCase): class CourseEditViewTests(TestCase):
""" Tests for the course edit view. """ """ Tests for the course edit view. """
...@@ -1922,6 +1961,8 @@ class CourseRunEditViewTests(TestCase): ...@@ -1922,6 +1961,8 @@ class CourseRunEditViewTests(TestCase):
self.new_course = Course.objects.get(number=data['number']) self.new_course = Course.objects.get(number=data['number'])
self.new_course_run = self.new_course.course_runs.first() self.new_course_run = self.new_course.course_runs.first()
factories.CourseRunStateFactory(course_run=self.new_course_run)
# assert edit page is loading sucesfully. # assert edit page is loading sucesfully.
self.edit_page_url = reverse('publisher:publisher_course_runs_edit', kwargs={'pk': self.new_course_run.id}) self.edit_page_url = reverse('publisher:publisher_course_runs_edit', kwargs={'pk': self.new_course_run.id})
response = self.client.get(self.edit_page_url) response = self.client.get(self.edit_page_url)
......
...@@ -43,7 +43,7 @@ ROLE_WIDGET_HEADINGS = { ...@@ -43,7 +43,7 @@ ROLE_WIDGET_HEADINGS = {
STATE_BUTTONS = { STATE_BUTTONS = {
CourseStateChoices.Draft: {'text': _('Send for Review'), 'value': CourseStateChoices.Review}, CourseStateChoices.Draft: {'text': _('Send for Review'), 'value': CourseStateChoices.Review},
CourseStateChoices.Review: {'text': _('Reviewed'), 'value': CourseStateChoices.Approved} CourseStateChoices.Review: {'text': _('Mark as Reviewed'), 'value': CourseStateChoices.Approved}
} }
...@@ -128,8 +128,12 @@ class CourseRunDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionM ...@@ -128,8 +128,12 @@ class CourseRunDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionM
for course_role in course_roles: for course_role in course_roles:
role_widgets.append( role_widgets.append(
{ {
'user_course_role': course_role, 'course_role': course_role,
'heading': ROLE_WIDGET_HEADINGS.get(course_role.role) 'heading': ROLE_WIDGET_HEADINGS.get(course_role.role),
'change_state_url': reverse(
'publisher:api:change_course_run_state', kwargs={'pk': self.object.course_run_state.id}
),
'user_list': get_internal_users()
} }
) )
...@@ -145,14 +149,11 @@ class CourseRunDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionM ...@@ -145,14 +149,11 @@ class CourseRunDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionM
context['post_back_url'] = reverse('publisher:publisher_course_run_detail', kwargs={'pk': course_run.id}) context['post_back_url'] = reverse('publisher:publisher_course_run_detail', kwargs={'pk': course_run.id})
context['can_edit'] = mixins.check_course_organization_permission( context['can_edit'] = mixins.check_course_organization_permission(
self.request.user, course_run.course, OrganizationExtension.EDIT_COURSE_RUN user, course_run.course, OrganizationExtension.EDIT_COURSE_RUN
) )
# Show role assignment widgets if user is an internal user. course_roles = course_run.course.course_user_roles.exclude(role=PublisherUserRole.CourseTeam)
if is_internal_user(user): context['role_widgets'] = self.get_role_widgets_data(course_roles)
course_roles = course_run.course.course_user_roles.exclude(role=PublisherUserRole.CourseTeam)
context['role_widgets'] = self.get_role_widgets_data(course_roles)
context['user_list'] = get_internal_users()
context['breadcrumbs'] = make_bread_crumbs( context['breadcrumbs'] = make_bread_crumbs(
[ [
...@@ -167,7 +168,7 @@ class CourseRunDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionM ...@@ -167,7 +168,7 @@ class CourseRunDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionM
] ]
) )
context['can_view_all_tabs'] = mixins.check_roles_access(self.request.user) context['can_view_all_tabs'] = mixins.check_roles_access(user)
context['publisher_hide_features_for_pilot'] = waffle.switch_is_active('publisher_hide_features_for_pilot') context['publisher_hide_features_for_pilot'] = waffle.switch_is_active('publisher_hide_features_for_pilot')
context['publisher_comment_widget_feature'] = waffle.switch_is_active('publisher_comment_widget_feature') context['publisher_comment_widget_feature'] = waffle.switch_is_active('publisher_comment_widget_feature')
context['publisher_approval_widget_feature'] = waffle.switch_is_active('publisher_approval_widget_feature') context['publisher_approval_widget_feature'] = waffle.switch_is_active('publisher_approval_widget_feature')
...@@ -395,9 +396,18 @@ class CourseDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMixi ...@@ -395,9 +396,18 @@ class CourseDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMixi
for course_role in self.object.course_user_roles.order_by('role'): for course_role in self.object.course_user_roles.order_by('role'):
role_widget = { role_widget = {
'course_role': course_role, 'course_role': course_role,
'heading': ROLE_WIDGET_HEADINGS.get(course_role.role) 'heading': ROLE_WIDGET_HEADINGS.get(course_role.role),
'change_state_url': reverse(
'publisher:api:change_course_state', kwargs={'pk': course_state.id}
)
} }
if is_internal_user(user):
role_widget['user_list'] = get_internal_users()
if course_role.role == PublisherUserRole.CourseTeam:
role_widget['user_list'] = self.object.organization_extension.group.user_set.all()
if course_state.owner_role == course_role.role: if course_state.owner_role == course_role.role:
role_widget['ownership'] = timezone.now() - course_state.owner_role_modified role_widget['ownership'] = timezone.now() - course_state.owner_role_modified
if user == course_role.user: if user == course_role.user:
...@@ -407,8 +417,18 @@ class CourseDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMixi ...@@ -407,8 +417,18 @@ class CourseDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMixi
role_widget['button_disabled'] = True role_widget['button_disabled'] = True
if course_role.role in [PublisherUserRole.CourseTeam, PublisherUserRole.MarketingReviewer]: if course_role.role in [PublisherUserRole.CourseTeam, PublisherUserRole.MarketingReviewer]:
if course_state.owner_role != course_role.role and course_state.name == CourseStateChoices.Review: if course_state.owner_role != course_role.role:
role_widget['sent_for_review'] = course_state.modified if course_state.name != CourseStateChoices.Draft:
history_records = course_state.history.filter(
name=CourseStateChoices.Review
).order_by('-modified')
role_widget['sent_for_review'] = history_records.first().modified
if course_state.name == CourseStateChoices.Approved:
history_records = course_state.history.filter(
name=CourseStateChoices.Approved
).order_by('-modified')
role_widget['reviewed'] = history_records.first().modified
role_widgets.append(role_widget) role_widgets.append(role_widget)
......
...@@ -498,6 +498,11 @@ msgstr "" ...@@ -498,6 +498,11 @@ msgstr ""
msgid "Changes to {title} are ready for review" msgid "Changes to {title} are ready for review"
msgstr "" msgstr ""
#: apps/publisher/emails.py
#, python-brace-format
msgid "Changes to {title} has been approved"
msgstr ""
#: apps/publisher/forms.py #: apps/publisher/forms.py
msgid "Remove Image" msgid "Remove Image"
msgstr "" msgstr ""
...@@ -747,13 +752,12 @@ msgstr "" ...@@ -747,13 +752,12 @@ msgstr ""
msgid "PUBLISHER" msgid "PUBLISHER"
msgstr "" msgstr ""
#: apps/publisher/views.py #: apps/publisher/views.py templates/publisher/_approval_widget.html
#: templates/publisher/course_detail/_approval_widget.html
msgid "Send for Review" msgid "Send for Review"
msgstr "" msgstr ""
#: apps/publisher/views.py #: apps/publisher/views.py
msgid "Reviewed" msgid "Mark as Reviewed"
msgstr "" msgstr ""
#: apps/publisher/views.py #: apps/publisher/views.py
...@@ -949,6 +953,26 @@ msgstr "" ...@@ -949,6 +953,26 @@ msgstr ""
msgid "Add Staff Member" msgid "Add Staff Member"
msgstr "" msgstr ""
#: templates/publisher/_approval_widget.html
msgid "APPROVALS"
msgstr ""
#: templates/publisher/_approval_widget.html
msgid "Reviewed"
msgstr ""
#: templates/publisher/_approval_widget.html
msgid "day in ownership"
msgstr ""
#: templates/publisher/_approval_widget.html
msgid "change owner"
msgstr ""
#: templates/publisher/_approval_widget.html
msgid "CHANGE"
msgstr ""
#: templates/publisher/_footer.html #: templates/publisher/_footer.html
msgid "Terms of Service" msgid "Terms of Service"
msgstr "" msgstr ""
...@@ -1715,16 +1739,8 @@ msgstr "" ...@@ -1715,16 +1739,8 @@ msgstr ""
msgid "Course Level" msgid "Course Level"
msgstr "" msgstr ""
#: templates/publisher/course_detail/_approval_widget.html
msgid "APPROVALS"
msgstr ""
#: templates/publisher/course_detail/_approval_widget.html
msgid "day in ownership"
msgstr ""
#: templates/publisher/course_detail/_widgets.html #: templates/publisher/course_detail/_widgets.html
#: templates/publisher/course_run_detail/_approval_widget.html #: templates/publisher/course_run_detail/_widgets.html
msgid "EDIT" msgid "EDIT"
msgstr "" msgstr ""
...@@ -1888,14 +1904,6 @@ msgstr "" ...@@ -1888,14 +1904,6 @@ msgstr ""
msgid "Course Length (Weeks)" msgid "Course Length (Weeks)"
msgstr "" msgstr ""
#: templates/publisher/course_run_detail/_approval_widget.html
msgid "change owner"
msgstr ""
#: templates/publisher/course_run_detail/_approval_widget.html
msgid "CHANGE"
msgstr ""
#: templates/publisher/course_run_detail/_cat.html #: templates/publisher/course_run_detail/_cat.html
#: templates/publisher/course_run_detail/_drupal.html #: templates/publisher/course_run_detail/_drupal.html
msgid "Course ID" msgid "Course ID"
...@@ -2296,30 +2304,28 @@ msgstr "" ...@@ -2296,30 +2304,28 @@ msgstr ""
msgid "View comment: " msgid "View comment: "
msgstr "" msgstr ""
#. Translators: partner_coordinator_name is a member name. #: templates/publisher/email/course/mark_as_reviewed.html
#: templates/publisher/email/course_created.html #: templates/publisher/email/course/mark_as_reviewed.txt
#: templates/publisher/email/course/send_for_review.html
#: templates/publisher/email/course/send_for_review.txt
#, python-format #, python-format
msgid "Dear %(partner_coordinator_name)s," msgid "Dear %(recipient_name)s,"
msgstr "" msgstr ""
#: templates/publisher/email/course_created.html #: templates/publisher/email/course/mark_as_reviewed.html
#, python-format #, python-format
msgid "" msgid ""
"%(course_team_name)s created the " "Changes to %(link_start)s%(course_page_url)s%(link_middle)s %(course_name)s "
"%(link_start)s%(dashboard_url)s%(link_middle)s %(course_title)s %(link_end)s" "%(link_end)s has been approved."
" course in Publisher on %(date)s at %(time)s."
msgstr ""
#: templates/publisher/email/course_created.html
#: templates/publisher/email/course_created.txt
msgid "Please create a Studio instance for this course."
msgstr "" msgstr ""
#. Translators: It's closing of mail. #. Translators: It's closing of mail.
#: templates/publisher/email/course/mark_as_reviewed.html
#: templates/publisher/email/course/mark_as_reviewed.txt
#: templates/publisher/email/course/send_for_review.html
#: templates/publisher/email/course/send_for_review.txt
#: templates/publisher/email/course_created.html #: templates/publisher/email/course_created.html
#: templates/publisher/email/course_created.txt #: templates/publisher/email/course_created.txt
#: templates/publisher/email/send_for_review.html
#: templates/publisher/email/send_for_review.txt
#: templates/publisher/email/studio_instance_created.html #: templates/publisher/email/studio_instance_created.html
#: templates/publisher/email/studio_instance_created.txt #: templates/publisher/email/studio_instance_created.txt
#: templates/publisher/email/studio_instance_needed.html #: templates/publisher/email/studio_instance_needed.html
...@@ -2327,26 +2333,30 @@ msgstr "" ...@@ -2327,26 +2333,30 @@ msgstr ""
msgid "Thanks," msgid "Thanks,"
msgstr "" msgstr ""
#: templates/publisher/email/course_created.txt #: templates/publisher/email/course/mark_as_reviewed.html
#: templates/publisher/email/studio_instance_needed.html #: templates/publisher/email/course/send_for_review.html
#: templates/publisher/email/studio_instance_needed.txt #: templates/publisher/email/studio_instance_created.html
msgid "Dear" #, python-format
msgid ""
"<p>Note: This email address is unable to receive replies. For questions or "
"comments, contact %(contact_us_email)s.</p>"
msgstr "" msgstr ""
#: templates/publisher/email/course_created.txt #: templates/publisher/email/course/mark_as_reviewed.txt
#, python-format #, python-format
msgid "" msgid "Changes to %(course_name)s has been approved. %(course_page_url)s"
"%(course_team_name)s created the %(course_title)s : %(dashboard_url)s course"
" in Publisher on %(date)s at %(time)s."
msgstr "" msgstr ""
#: templates/publisher/email/send_for_review.html #: templates/publisher/email/course/mark_as_reviewed.txt
#: templates/publisher/email/send_for_review.txt #: templates/publisher/email/course/send_for_review.txt
#: templates/publisher/email/studio_instance_created.txt
#, python-format #, python-format
msgid "Dear %(recipient_name)s," msgid ""
"Note: This email address is unable to receive replies. For questions or "
"comments, contact %(contact_us_email)s."
msgstr "" msgstr ""
#: templates/publisher/email/send_for_review.html #: templates/publisher/email/course/send_for_review.html
#, python-format #, python-format
msgid "" msgid ""
"New changes to %(link_start)s%(course_page_url)s%(link_middle)s " "New changes to %(link_start)s%(course_page_url)s%(link_middle)s "
...@@ -2355,28 +2365,44 @@ msgid "" ...@@ -2355,28 +2365,44 @@ msgid ""
"Publisher %(link_end)s to approve or decline the changes." "Publisher %(link_end)s to approve or decline the changes."
msgstr "" msgstr ""
#: templates/publisher/email/send_for_review.html #: templates/publisher/email/course/send_for_review.txt
#: templates/publisher/email/studio_instance_created.html
#, python-format #, python-format
msgid "" msgid ""
"<p>Note: This email address is unable to receive replies. For questions or " "New changes to %(course_name)s are ready for your review. "
"comments, contact %(contact_us_email)s.</p>" "%(course_page_url)s View this course in Publisher to approve or decline the "
"changes."
msgstr ""
#. Translators: partner_coordinator_name is a member name.
#: templates/publisher/email/course_created.html
#, python-format
msgid "Dear %(partner_coordinator_name)s,"
msgstr "" msgstr ""
#: templates/publisher/email/send_for_review.txt #: templates/publisher/email/course_created.html
#, python-format #, python-format
msgid "" msgid ""
"New changes to %(course_name)s are ready for your review. " "%(course_team_name)s created the "
"%(course_page_url)s View this course in Publisher to approve or decline the " "%(link_start)s%(dashboard_url)s%(link_middle)s %(course_title)s %(link_end)s"
"changes." " course in Publisher on %(date)s at %(time)s."
msgstr "" msgstr ""
#: templates/publisher/email/send_for_review.txt #: templates/publisher/email/course_created.html
#: templates/publisher/email/studio_instance_created.txt #: templates/publisher/email/course_created.txt
msgid "Please create a Studio instance for this course."
msgstr ""
#: templates/publisher/email/course_created.txt
#: templates/publisher/email/studio_instance_needed.html
#: templates/publisher/email/studio_instance_needed.txt
msgid "Dear"
msgstr ""
#: templates/publisher/email/course_created.txt
#, python-format #, python-format
msgid "" msgid ""
"Note: This email address is unable to receive replies. For questions or " "%(course_team_name)s created the %(course_title)s : %(dashboard_url)s course"
"comments, contact %(contact_us_email)s." " in Publisher on %(date)s at %(time)s."
msgstr "" msgstr ""
#. Translators: course_team_name is course team member name. #. Translators: course_team_name is course team member name.
......
...@@ -618,6 +618,12 @@ msgstr "" ...@@ -618,6 +618,12 @@ msgstr ""
"Çhängés tö {title} äré réädý för révïéw Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, " "Çhängés tö {title} äré réädý för révïéw Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, "
"¢σηѕє¢тєт#" "¢σηѕє¢тєт#"
#: apps/publisher/emails.py
#, python-brace-format
msgid "Changes to {title} has been approved"
msgstr ""
"Çhängés tö {title} häs ßéén äpprövéd Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тє#"
#: apps/publisher/forms.py #: apps/publisher/forms.py
msgid "Remove Image" msgid "Remove Image"
msgstr "Rémövé Ìmägé Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#" msgstr "Rémövé Ìmägé Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
...@@ -881,14 +887,13 @@ msgstr "MÀRKÉTÌNG Ⱡ'σяєм ιρѕυм ∂σł#" ...@@ -881,14 +887,13 @@ msgstr "MÀRKÉTÌNG Ⱡ'σяєм ιρѕυм ∂σł#"
msgid "PUBLISHER" msgid "PUBLISHER"
msgstr "PÛBLÌSHÉR Ⱡ'σяєм ιρѕυм ∂σł#" msgstr "PÛBLÌSHÉR Ⱡ'σяєм ιρѕυм ∂σł#"
#: apps/publisher/views.py #: apps/publisher/views.py templates/publisher/_approval_widget.html
#: templates/publisher/course_detail/_approval_widget.html
msgid "Send for Review" msgid "Send for Review"
msgstr "Sénd för Révïéw Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт α#" msgstr "Sénd för Révïéw Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт α#"
#: apps/publisher/views.py #: apps/publisher/views.py
msgid "Reviewed" msgid "Mark as Reviewed"
msgstr "Révïéwéd Ⱡ'σяєм ιρѕυм ∂#" msgstr "Märk äs Révïéwéd Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αм#"
#: apps/publisher/views.py #: apps/publisher/views.py
msgid "" msgid ""
...@@ -1104,6 +1109,26 @@ msgstr "öptïönäl - öné pér lïné Ⱡ'σяєм ιρѕυм ∂σłσя ѕ ...@@ -1104,6 +1109,26 @@ msgstr "öptïönäl - öné pér lïné Ⱡ'σяєм ιρѕυм ∂σłσя ѕ
msgid "Add Staff Member" msgid "Add Staff Member"
msgstr "Àdd Stäff Mémßér Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αм#" msgstr "Àdd Stäff Mémßér Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αм#"
#: templates/publisher/_approval_widget.html
msgid "APPROVALS"
msgstr "ÀPPRÖVÀLS Ⱡ'σяєм ιρѕυм ∂σł#"
#: templates/publisher/_approval_widget.html
msgid "Reviewed"
msgstr "Révïéwéd Ⱡ'σяєм ιρѕυм ∂#"
#: templates/publisher/_approval_widget.html
msgid "day in ownership"
msgstr "däý ïn öwnérshïp Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αм#"
#: templates/publisher/_approval_widget.html
msgid "change owner"
msgstr "çhängé öwnér Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
#: templates/publisher/_approval_widget.html
msgid "CHANGE"
msgstr "ÇHÀNGÉ Ⱡ'σяєм ιρѕυ#"
#: templates/publisher/_footer.html #: templates/publisher/_footer.html
msgid "Terms of Service" msgid "Terms of Service"
msgstr "Térms öf Sérvïçé Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αм#" msgstr "Térms öf Sérvïçé Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αм#"
...@@ -2060,16 +2085,8 @@ msgstr "Çöürsé Ìmägé Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#" ...@@ -2060,16 +2085,8 @@ msgstr "Çöürsé Ìmägé Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
msgid "Course Level" msgid "Course Level"
msgstr "Çöürsé Lévél Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#" msgstr "Çöürsé Lévél Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
#: templates/publisher/course_detail/_approval_widget.html
msgid "APPROVALS"
msgstr "ÀPPRÖVÀLS Ⱡ'σяєм ιρѕυм ∂σł#"
#: templates/publisher/course_detail/_approval_widget.html
msgid "day in ownership"
msgstr "däý ïn öwnérshïp Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αм#"
#: templates/publisher/course_detail/_widgets.html #: templates/publisher/course_detail/_widgets.html
#: templates/publisher/course_run_detail/_approval_widget.html #: templates/publisher/course_run_detail/_widgets.html
msgid "EDIT" msgid "EDIT"
msgstr "ÉDÌT Ⱡ'σяєм ι#" msgstr "ÉDÌT Ⱡ'σяєм ι#"
...@@ -2253,14 +2270,6 @@ msgstr "" ...@@ -2253,14 +2270,6 @@ msgstr ""
msgid "Course Length (Weeks)" msgid "Course Length (Weeks)"
msgstr "Çöürsé Léngth (Wééks) Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, #" msgstr "Çöürsé Léngth (Wééks) Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, #"
#: templates/publisher/course_run_detail/_approval_widget.html
msgid "change owner"
msgstr "çhängé öwnér Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
#: templates/publisher/course_run_detail/_approval_widget.html
msgid "CHANGE"
msgstr "ÇHÀNGÉ Ⱡ'σяєм ιρѕυ#"
#: templates/publisher/course_run_detail/_cat.html #: templates/publisher/course_run_detail/_cat.html
#: templates/publisher/course_run_detail/_drupal.html #: templates/publisher/course_run_detail/_drupal.html
msgid "Course ID" msgid "Course ID"
...@@ -2691,36 +2700,30 @@ msgstr "Vïéw çömmént Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#" ...@@ -2691,36 +2700,30 @@ msgstr "Vïéw çömmént Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
msgid "View comment: " msgid "View comment: "
msgstr "Vïéw çömmént: Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт#" msgstr "Vïéw çömmént: Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт#"
#. Translators: partner_coordinator_name is a member name. #: templates/publisher/email/course/mark_as_reviewed.html
#: templates/publisher/email/course_created.html #: templates/publisher/email/course/mark_as_reviewed.txt
#: templates/publisher/email/course/send_for_review.html
#: templates/publisher/email/course/send_for_review.txt
#, python-format #, python-format
msgid "Dear %(partner_coordinator_name)s," msgid "Dear %(recipient_name)s,"
msgstr "Déär %(partner_coordinator_name)s, Ⱡ'σяєм ιρѕυм ∂σł#" msgstr "Déär %(recipient_name)s, Ⱡ'σяєм ιρѕυм ∂σł#"
#: templates/publisher/email/course_created.html #: templates/publisher/email/course/mark_as_reviewed.html
#, python-format #, python-format
msgid "" msgid ""
"%(course_team_name)s created the " "Changes to %(link_start)s%(course_page_url)s%(link_middle)s %(course_name)s "
"%(link_start)s%(dashboard_url)s%(link_middle)s %(course_title)s %(link_end)s" "%(link_end)s has been approved."
" course in Publisher on %(date)s at %(time)s."
msgstr ""
"%(course_team_name)s çréätéd thé "
"%(link_start)s%(dashboard_url)s%(link_middle)s %(course_title)s %(link_end)s"
" çöürsé ïn Püßlïshér ön %(date)s ät %(time)s. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, "
"¢σηѕє¢тєтυя #"
#: templates/publisher/email/course_created.html
#: templates/publisher/email/course_created.txt
msgid "Please create a Studio instance for this course."
msgstr "" msgstr ""
"Pléäsé çréäté ä Stüdïö ïnstänçé för thïs çöürsé. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт " "Çhängés tö %(link_start)s%(course_page_url)s%(link_middle)s %(course_name)s "
"αмєт, ¢σηѕє¢тєтυя α#" "%(link_end)s häs ßéén äpprövéd. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α#"
#. Translators: It's closing of mail. #. Translators: It's closing of mail.
#: templates/publisher/email/course/mark_as_reviewed.html
#: templates/publisher/email/course/mark_as_reviewed.txt
#: templates/publisher/email/course/send_for_review.html
#: templates/publisher/email/course/send_for_review.txt
#: templates/publisher/email/course_created.html #: templates/publisher/email/course_created.html
#: templates/publisher/email/course_created.txt #: templates/publisher/email/course_created.txt
#: templates/publisher/email/send_for_review.html
#: templates/publisher/email/send_for_review.txt
#: templates/publisher/email/studio_instance_created.html #: templates/publisher/email/studio_instance_created.html
#: templates/publisher/email/studio_instance_created.txt #: templates/publisher/email/studio_instance_created.txt
#: templates/publisher/email/studio_instance_needed.html #: templates/publisher/email/studio_instance_needed.html
...@@ -2728,29 +2731,36 @@ msgstr "" ...@@ -2728,29 +2731,36 @@ msgstr ""
msgid "Thanks," msgid "Thanks,"
msgstr "Thänks, Ⱡ'σяєм ιρѕυм #" msgstr "Thänks, Ⱡ'σяєм ιρѕυм #"
#: templates/publisher/email/course_created.txt #: templates/publisher/email/course/mark_as_reviewed.html
#: templates/publisher/email/studio_instance_needed.html #: templates/publisher/email/course/send_for_review.html
#: templates/publisher/email/studio_instance_needed.txt #: templates/publisher/email/studio_instance_created.html
msgid "Dear"
msgstr "Déär Ⱡ'σяєм ι#"
#: templates/publisher/email/course_created.txt
#, python-format #, python-format
msgid "" msgid ""
"%(course_team_name)s created the %(course_title)s : %(dashboard_url)s course" "<p>Note: This email address is unable to receive replies. For questions or "
" in Publisher on %(date)s at %(time)s." "comments, contact %(contact_us_email)s.</p>"
msgstr "" msgstr ""
"%(course_team_name)s çréätéd thé %(course_title)s : %(dashboard_url)s çöürsé" "<p>Nöté: Thïs émäïl äddréss ïs ünäßlé tö réçéïvé réplïés. För qüéstïöns ör "
" ïn Püßlïshér ön %(date)s ät %(time)s. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, " "çömménts, çöntäçt %(contact_us_email)s.</p> Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт#"
"¢σηѕє¢тєтυя α#"
#: templates/publisher/email/send_for_review.html #: templates/publisher/email/course/mark_as_reviewed.txt
#: templates/publisher/email/send_for_review.txt
#, python-format #, python-format
msgid "Dear %(recipient_name)s," msgid "Changes to %(course_name)s has been approved. %(course_page_url)s"
msgstr "Déär %(recipient_name)s, Ⱡ'σяєм ιρѕυм ∂σł#" msgstr ""
"Çhängés tö %(course_name)s häs ßéén äpprövéd. %(course_page_url)s Ⱡ'σяєм "
"ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυ#"
#: templates/publisher/email/send_for_review.html #: templates/publisher/email/course/mark_as_reviewed.txt
#: templates/publisher/email/course/send_for_review.txt
#: templates/publisher/email/studio_instance_created.txt
#, python-format
msgid ""
"Note: This email address is unable to receive replies. For questions or "
"comments, contact %(contact_us_email)s."
msgstr ""
"Nöté: Thïs émäïl äddréss ïs ünäßlé tö réçéïvé réplïés. För qüéstïöns ör "
"çömménts, çöntäçt %(contact_us_email)s. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢#"
#: templates/publisher/email/course/send_for_review.html
#, python-format #, python-format
msgid "" msgid ""
"New changes to %(link_start)s%(course_page_url)s%(link_middle)s " "New changes to %(link_start)s%(course_page_url)s%(link_middle)s "
...@@ -2769,17 +2779,7 @@ msgstr "" ...@@ -2769,17 +2779,7 @@ msgstr ""
"ƒυgιαт ηυłłα ραяιαтυя. єχ¢єρтєυя ѕιηт σ¢¢αє¢αт ¢υρι∂αтαт ηση ρяσι∂єηт, ѕυηт " "ƒυgιαт ηυłłα ραяιαтυя. єχ¢єρтєυя ѕιηт σ¢¢αє¢αт ¢υρι∂αтαт ηση ρяσι∂єηт, ѕυηт "
"ιη ¢υłρα qυι σƒƒι¢ια ∂єѕєяυηт мσłłιт αηιм ι∂ єѕт łαв#" "ιη ¢υłρα qυι σƒƒι¢ια ∂єѕєяυηт мσłłιт αηιм ι∂ єѕт łαв#"
#: templates/publisher/email/send_for_review.html #: templates/publisher/email/course/send_for_review.txt
#: templates/publisher/email/studio_instance_created.html
#, python-format
msgid ""
"<p>Note: This email address is unable to receive replies. For questions or "
"comments, contact %(contact_us_email)s.</p>"
msgstr ""
"<p>Nöté: Thïs émäïl äddréss ïs ünäßlé tö réçéïvé réplïés. För qüéstïöns ör "
"çömménts, çöntäçt %(contact_us_email)s.</p> Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт#"
#: templates/publisher/email/send_for_review.txt
#, python-format #, python-format
msgid "" msgid ""
"New changes to %(course_name)s are ready for your review. " "New changes to %(course_name)s are ready for your review. "
...@@ -2790,15 +2790,46 @@ msgstr "" ...@@ -2790,15 +2790,46 @@ msgstr ""
"%(course_page_url)s Vïéw thïs çöürsé ïn Püßlïshér tö äpprövé ör déçlïné thé " "%(course_page_url)s Vïéw thïs çöürsé ïn Püßlïshér tö äpprövé ör déçlïné thé "
"çhängés. Ⱡ'σяєм ιρѕυм ∂σł#" "çhängés. Ⱡ'σяєм ιρѕυм ∂σł#"
#: templates/publisher/email/send_for_review.txt #. Translators: partner_coordinator_name is a member name.
#: templates/publisher/email/studio_instance_created.txt #: templates/publisher/email/course_created.html
#, python-format
msgid "Dear %(partner_coordinator_name)s,"
msgstr "Déär %(partner_coordinator_name)s, Ⱡ'σяєм ιρѕυм ∂σł#"
#: templates/publisher/email/course_created.html
#, python-format #, python-format
msgid "" msgid ""
"Note: This email address is unable to receive replies. For questions or " "%(course_team_name)s created the "
"comments, contact %(contact_us_email)s." "%(link_start)s%(dashboard_url)s%(link_middle)s %(course_title)s %(link_end)s"
" course in Publisher on %(date)s at %(time)s."
msgstr "" msgstr ""
"Nöté: Thïs émäïl äddréss ïs ünäßlé tö réçéïvé réplïés. För qüéstïöns ör " "%(course_team_name)s çréätéd thé "
"çömménts, çöntäçt %(contact_us_email)s. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢#" "%(link_start)s%(dashboard_url)s%(link_middle)s %(course_title)s %(link_end)s"
" çöürsé ïn Püßlïshér ön %(date)s ät %(time)s. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, "
"¢σηѕє¢тєтυя #"
#: templates/publisher/email/course_created.html
#: templates/publisher/email/course_created.txt
msgid "Please create a Studio instance for this course."
msgstr ""
"Pléäsé çréäté ä Stüdïö ïnstänçé för thïs çöürsé. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт "
"αмєт, ¢σηѕє¢тєтυя α#"
#: templates/publisher/email/course_created.txt
#: templates/publisher/email/studio_instance_needed.html
#: templates/publisher/email/studio_instance_needed.txt
msgid "Dear"
msgstr "Déär Ⱡ'σяєм ι#"
#: templates/publisher/email/course_created.txt
#, python-format
msgid ""
"%(course_team_name)s created the %(course_title)s : %(dashboard_url)s course"
" in Publisher on %(date)s at %(time)s."
msgstr ""
"%(course_team_name)s çréätéd thé %(course_title)s : %(dashboard_url)s çöürsé"
" ïn Püßlïshér ön %(date)s ät %(time)s. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, "
"¢σηѕє¢тєтυя α#"
#. Translators: course_team_name is course team member name. #. Translators: course_team_name is course team member name.
#: templates/publisher/email/studio_instance_created.html #: templates/publisher/email/studio_instance_created.html
......
...@@ -401,6 +401,7 @@ ...@@ -401,6 +401,7 @@
} }
.btn-change-state { .btn-change-state {
@include padding(2px, 10px, 3px, 10px);
margin-top: 15px; margin-top: 15px;
} }
......
...@@ -10,15 +10,23 @@ ...@@ -10,15 +10,23 @@
<div class="layout-1q3q layout-reversed"> <div class="layout-1q3q layout-reversed">
<div class="layout-col layout-col-a"> <div class="layout-col layout-col-a">
{% if role_widget.state_button %} {% if role_widget.state_button %}
<button class="btn btn-neutral btn-change-state" data-change-state-url="{% url 'publisher:api:change_course_state' object.course_state.id %}" data-state-name="{{ role_widget.state_button.value }}"{% if role_widget.button_disabled %} disabled{% endif %} type="button"> <button class="btn btn-neutral btn-change-state" data-change-state-url="{{ role_widget.change_state_url }}" data-state-name="{{ role_widget.state_button.value }}"{% if role_widget.button_disabled %} disabled{% endif %} type="button">
{{ role_widget.state_button.text }} {{ role_widget.state_button.text }}
</button> </button>
{% elif role_widget.sent_for_review %} {% else %}
<span class="state-status"> {% if role_widget.sent_for_review %}
<span class="icon fa fa-check" aria-hidden="true"></span> <span class="state-status">
{% trans "Send for Review" %}<br> <span class="icon fa fa-check" aria-hidden="true"></span>
{{ role_widget.sent_for_review|date:'m/d/y H:i a' }} {% trans "Send for Review" %}<br>
</span> {{ role_widget.sent_for_review|date:'m/d/y H:i a' }}
</span>
{% elif role_widget.reviewed %}
<span class="state-status">
<span class="icon fa fa-check" aria-hidden="true"></span>
{% trans "Reviewed" %}<br>
{{ role_widget.reviewed|date:'m/d/y H:i a' }}
</span>
{% endif %}
{% endif %} {% endif %}
</div> </div>
<div class="layout-col layout-col-b"> <div class="layout-col layout-col-b">
...@@ -28,12 +36,37 @@ ...@@ -28,12 +36,37 @@
{% if role_widget.ownership %} {% if role_widget.ownership %}
<span>{{ role_widget.ownership.days }} {% trans "day in ownership" %}</span> <span>{{ role_widget.ownership.days }} {% trans "day in ownership" %}</span>
{% endif %} {% endif %}
<div class="field-readonly user-full-name"> <div class="role-assignment-container">
{% if role_widget.course_role.user.full_name %} <div id="userRoleContainer-{{ role_widget.course_role.role }}">
{{ role_widget.course_role.user.full_name }} <span id="userFullName-{{ role_widget.course_role.role }}" class="field-readonly user-full-name">
{% else %} {% if role_widget.course_role.user.full_name %}
{{ role_widget.course_role.user.username }} {{ role_widget.course_role.user.full_name }}
{% endif %} {% else %}
{{ role_widget.course_role.user.username }}
{% endif %}
</span>
<a class="change-role-assignment" data-role="{{ role_widget.course_role.role }}" href="#">
{% trans "change owner" %}
</a>
</div>
<div class="change-role-container" id="changeRoleContainer-{{ role_widget.course_role.role }}">
<select class="select-users-by-role" id="selectUsers-{{ role_widget.course_role.role }}">
<option value="-----------">-----------</option>
{% for user in role_widget.user_list %}
<option value="{{ user.id }}">
{% if user.full_name %}
{{ user.full_name }}
{% else %}
{{ user.username }}
{% endif %}
</option>
{% endfor %}
</select>
<input type="hidden" id="roleName" value="{{ role_widget.course_role.role }}">
<button type="button" class="btn-neutral btn-change-assignment" data-role="{{ role_widget.course_role.role }}" data-api-endpoint="{% url 'publisher:api:course_role_assignments' role_widget.course_role.id %}">
{% trans "CHANGE" %}
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -132,6 +132,7 @@ ...@@ -132,6 +132,7 @@
{% endblock %} {% endblock %}
{% block extra_js %} {% block extra_js %}
<script src="{% static 'bower_components/google-diff-match-patch/diff_match_patch.js' %}"></script> <script src="{% static 'bower_components/google-diff-match-patch/diff_match_patch.js' %}"></script>
<script src="{% static 'js/publisher/views/course_detail.js' %}"></script>
<script src="{% static 'js/publisher/publisher.js' %}"></script> <script src="{% static 'js/publisher/publisher.js' %}"></script>
<script src="{% static 'js/publisher/comparing-objects.js' %}"></script> <script src="{% static 'js/publisher/comparing-objects.js' %}"></script>
<script src="{% static 'js/publisher/jquery-dateFormat.min.js' %}"></script> <script src="{% static 'js/publisher/jquery-dateFormat.min.js' %}"></script>
......
...@@ -43,6 +43,6 @@ ...@@ -43,6 +43,6 @@
{% include 'publisher/_history_widget.html' %} {% include 'publisher/_history_widget.html' %}
</div> </div>
<div class="approval-widget {% if not publisher_approval_widget_feature %}hidden{% endif %}"> <div class="approval-widget {% if not publisher_approval_widget_feature %}hidden{% endif %}">
{% include 'publisher/course_detail/_approval_widget.html' %} {% include 'publisher/_approval_widget.html' %}
</div> </div>
</div> </div>
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
<aside id="right-panel" class="layout-col layout-col-a layout-col-a-custom"> <aside id="right-panel" class="layout-col layout-col-a layout-col-a-custom">
<div id="approval-widget" class="approval-widget {% if not publisher_approval_widget_feature %}hidden{% endif %}"> <div id="approval-widget" class="approval-widget {% if not publisher_approval_widget_feature %}hidden{% endif %}">
{% include 'publisher/course_run_detail/_approval_widget.html' %} {% include 'publisher/course_run_detail/_widgets.html' %}
</div> </div>
<div id="comments-widget" class="comment-container {% if not publisher_comment_widget_feature %}hidden{% endif %}"> <div id="comments-widget" class="comment-container {% if not publisher_comment_widget_feature %}hidden{% endif %}">
{% include 'comments/add_auth_comments.html' %} {% include 'comments/add_auth_comments.html' %}
......
{% load i18n %}
<div class="approval-widget">
{% if can_edit %}
<a href="{% url 'publisher:publisher_course_runs_edit' pk=object.id %}" class="btn btn-neutral btn-courserun-edit">
{% trans "EDIT" %}
</a>
<div class="clearfix"></div>
{% endif %}
{% for role_widget in role_widgets %}
<div class="role-widget">
<span class="role-heading">
<strong>{{ role_widget.heading }}</strong>
</span>
<div class="role-assignment-container">
<div id="userRoleContainer-{{ role_widget.user_course_role.role }}">
<span id="userFullName-{{ role_widget.user_course_role.role }}" class="field-readonly user-full-name">
{% if role_widget.user_course_role.user.full_name %}
{{ role_widget.user_course_role.user.full_name }}
{% else %}
{{ role_widget.user_course_role.user.username }}
{% endif %}
</span>
<a class="change-role-assignment" data-role="{{ role_widget.user_course_role.role }}" href="#">
{% trans "change owner" %}
</a>
</div>
<div class="change-role-container" id="changeRoleContainer-{{ role_widget.user_course_role.role }}">
<select class="select-users-by-role" id="selectUsers-{{ role_widget.user_course_role.role }}">
{% for user in user_list %}
<option {% if role_widget.user_course_role.user == user%}selected="selected"{% endif %} value="{{ user.id }}">
{{ user.full_name }}
</option>
{% endfor %}
</select>
<input type="hidden" id="roleName" value="{{ role_widget.user_course_role.role }}">
<button type="button" class="btn-neutral btn-change-assignment" data-role="{{ role_widget.user_course_role.role }}" data-api-endpoint="{% url 'publisher:api:course_role_assignments' role_widget.user_course_role.id %}">
{% trans "CHANGE" %}
</button>
</div>
</div>
</div>
<hr>
{% endfor %}
<div class="actions">
<form action="{% url 'publisher:publisher_change_state' pk=object.id %}" method="post"> {% csrf_token %}
<button type="submit" value="{{ object.change_state_button.value }}" class="btn-brand btn-small btn-states" name="state">{{ object.change_state_button.text }}</button>
</form>
</div>
</div>
{% load i18n %}
<div class="course-widgets">
{% if can_edit %}
<a href="{% url 'publisher:publisher_course_runs_edit' pk=object.id %}" class="btn btn-neutral btn-courserun-edit">
{% trans "EDIT" %}
</a>
<div class="clearfix"></div>
{% endif %}
<div class="approval-widget {% if not publisher_approval_widget_feature %}hidden{% endif %}">
{% include 'publisher/_approval_widget.html' %}
</div>
</div>
{% extends "publisher/email/email_base.html" %}
{% load i18n %}
{% block body %}
<!-- Message Body -->
<p>
{% blocktrans trimmed %}
Dear {{ recipient_name }},
{% endblocktrans %}
<p>
{% blocktrans with link_start='<a href="' link_middle='">' link_end='</a>' trimmed %}
Changes to {{ link_start }}{{ course_page_url }}{{ link_middle }} {{ course_name }} {{ link_end }} has been approved.
{% endblocktrans %}
</p>
{% comment %}Translators: It's closing of mail.{% endcomment %}
{% trans "Thanks," %}<br>
{{ sender_name }}
{% blocktrans trimmed %}
<p>Note: This email address is unable to receive replies. For questions or comments, contact {{ contact_us_email }}.</p>
{% endblocktrans %}
<!-- End Message Body -->
{% endblock body %}
{% load i18n %}
{% blocktrans trimmed %}
Dear {{ recipient_name }},
{% endblocktrans %}
{% blocktrans trimmed %}
Changes to {{ course_name }} has been approved. {{ course_page_url }}
{% endblocktrans %}
{% trans "Thanks," %}
{{ sender_name }}
{% blocktrans trimmed %}
Note: This email address is unable to receive replies. For questions or comments, contact {{ contact_us_email }}.
{% endblocktrans %}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment