Commit 3e42af4f by tasawernawaz Committed by Tasawer Nawaz

course run edit and change ownership

ECOM-7301
parent 74435a79
......@@ -25,7 +25,8 @@ 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.constants import (ADMIN_GROUP_NAME, INTERNAL_USER_GROUP_NAME,
PROJECT_COORDINATOR_GROUP_NAME, REVIEWER_GROUP_NAME)
from course_discovery.apps.publisher.models import Course, CourseRun, CourseState, OrganizationExtension, Seat
from course_discovery.apps.publisher.models import (Course, CourseRun, CourseRunState, CourseState,
OrganizationExtension, Seat)
from course_discovery.apps.publisher.tests import factories
from course_discovery.apps.publisher.tests.utils import create_non_staff_user_and_login
from course_discovery.apps.publisher.utils import is_email_notification_enabled
......@@ -776,6 +777,11 @@ class CourseRunDetailTests(TestCase):
self.assert_can_edit_permission()
factories.CourseUserRoleFactory(course=self.course, user=user, role=PublisherUserRole.CourseTeam)
assign_perm(
OrganizationExtension.VIEW_COURSE_RUN, organization_extension.group, organization_extension
)
assign_perm(
OrganizationExtension.EDIT_COURSE_RUN, organization_extension.group, organization_extension
)
......@@ -1120,6 +1126,35 @@ class CourseRunDetailTests(TestCase):
self.assertContains(response, expected)
self.assertNotContains(response, '<button class="btn-brand btn-base btn-publish"')
def test_edit_permission_with_owner_role(self):
"""
Test that user can see edit button if he has permission and has role for course.
"""
course_team_role = factories.CourseUserRoleFactory(
course=self.course, user=self.user, role=PublisherUserRole.CourseTeam
)
self.user.groups.add(self.organization_extension.group)
assign_perm(OrganizationExtension.VIEW_COURSE_RUN, self.organization_extension.group,
self.organization_extension)
assign_perm(OrganizationExtension.EDIT_COURSE_RUN, self.organization_extension.group,
self.organization_extension)
# verify popup message will not added in context
response = self.client.get(self.page_url)
self.assertEqual(response.context['can_edit'], True)
self.assertNotIn('add_warning_popup', response.context)
self.assertNotIn('current_team_name', response.context)
self.assertNotIn('team_name', response.context)
# Assign new user to course team role.
course_team_role.user = UserFactory()
course_team_role.save()
# Verify that user cannot see edit button if he has no role for course.
self.assert_can_edit_permission(can_edit=False)
# pylint: disable=attribute-defined-outside-init
@ddt.ddt
......@@ -2052,6 +2087,9 @@ class CourseRunEditViewTests(TestCase):
self.new_course = Course.objects.get(number=data['number'])
self.new_course_run = self.new_course.course_runs.first()
assign_perm(OrganizationExtension.EDIT_COURSE_RUN, self.group, self.organization_extension)
assign_perm(OrganizationExtension.VIEW_COURSE_RUN, self.group, self.organization_extension)
# assert edit page is loading sucesfully.
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)
......@@ -2130,43 +2168,6 @@ class CourseRunEditViewTests(TestCase):
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'Edit Course')
@ddt.data(INTERNAL_USER_GROUP_NAME, ADMIN_GROUP_NAME)
def test_update_course_run_without_seat(self, publisher_group):
""" Verify that internal users can update the data from course run edit page."""
self.assertEqual(self.new_course_run.history.all().count(), 1)
self.assertEqual(self.new_course_run.course.history.all().count(), 1)
self.client.logout()
user, __ = create_non_staff_user_and_login(self)
self.assertNotEqual(self.course_run.changed_by, user)
user.groups.add(Group.objects.get(name=publisher_group))
response = self.client.post(self.edit_page_url, self.updated_dict)
self.assertRedirects(
response,
expected_url=reverse('publisher:publisher_course_run_detail', kwargs={'pk': self.new_course_run.id}),
status_code=302,
target_status_code=200
)
updated_course = Course.objects.get(id=self.new_course.id)
self.assertEqual(updated_course.full_description, 'This is testing description.')
course_run = CourseRun.objects.get(id=self.new_course_run.id)
self.assertEqual(course_run.changed_by, user)
self.assertEqual(course_run.xseries_name, 'Test XSeries')
self.assertFalse(course_run.seats.all().exists())
# no mail will be send because course-run state is already draft.
# 1st email is of course-creation
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(course_run.history.all().count(), 2)
self.assertEqual(course_run.course.history.all().count(), 2)
@ddt.data('start', 'end', 'pacing_type')
def test_update_with_errors(self, field):
""" Verify that course run edit page throws error in case of missing required field."""
......@@ -2201,10 +2202,9 @@ class CourseRunEditViewTests(TestCase):
""" Verify that course run edit page create seat object also if not exists previously."""
self.client.logout()
user, __ = create_non_staff_user_and_login(self)
user = self.new_course_run.course.course_user_roles.get(role=PublisherUserRole.CourseTeam).user
self.client.login(username=user.username, password=USER_PASSWORD)
self.assertNotEqual(self.course_run.changed_by, user)
user.groups.add(Group.objects.get(name=ADMIN_GROUP_NAME))
# post data without seat
data = {'full_description': 'This is testing description.', 'image': ''}
......@@ -2458,6 +2458,46 @@ class CourseRunEditViewTests(TestCase):
response = self.client.get(self.edit_page_url)
self.assertContains(response, '<div id="about-page" class="course-information ">')
def test_owner_role_change_on_edit(self):
""" Verify that when a user made changes in course run, course will be assign to him,
and state will be changed to `Draft`. """
self.new_course_run.course_run_state.name = CourseRunStateChoices.Review
self.new_course_run.save()
# check that current owner is course team
self.assertEqual(self.new_course_run.course_run_state.owner_role, PublisherUserRole.CourseTeam)
pc_user = self.new_course_run.course.course_user_roles.get(role=PublisherUserRole.ProjectCoordinator).user
pc_user.groups.add(self.group_project_coordinator)
pc_user.groups.add(Group.objects.get(name=INTERNAL_USER_GROUP_NAME))
assign_perm(
OrganizationExtension.EDIT_COURSE_RUN, self.group_project_coordinator, self.organization_extension
)
assign_perm(
OrganizationExtension.VIEW_COURSE_RUN, self.group_project_coordinator, self.organization_extension
)
self.client.logout()
self.client.login(username=pc_user.username, password=USER_PASSWORD)
response = self.client.get(reverse('publisher:publisher_course_run_detail', args=[self.new_course_run.id]))
self.assertEqual(response.context['add_warning_popup'], True)
self.assertEqual(response.context['current_team_name'], 'course team')
self.assertEqual(response.context['team_name'], 'project coordinator')
response = self.client.post(self.edit_page_url, self.updated_dict)
self.assertRedirects(
response,
expected_url=reverse('publisher:publisher_course_run_detail', kwargs={'pk': self.new_course_run.id}),
status_code=302,
target_status_code=200
)
course_run_state = CourseRunState.objects.get(id=self.new_course_run.course_run_state.id)
self.assertEqual(course_run_state.name, CourseRunStateChoices.Draft)
self.assertEqual(course_run_state.owner_role, PublisherUserRole.ProjectCoordinator)
class CourseRevisionViewTests(TestCase):
""" Tests for CourseReview"""
......
......@@ -139,7 +139,8 @@ class CourseRunDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionM
context['can_edit'] = mixins.check_course_organization_permission(
user, course_run.course, OrganizationExtension.EDIT_COURSE_RUN
)
) and has_role_for_course(course_run.course, user)
context['role_widgets'] = get_course_role_widgets_data(
user, course_run.course, course_run.course_run_state, 'publisher:api:change_course_run_state'
)
......@@ -174,6 +175,18 @@ class CourseRunDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionM
context['publisher_approval_widget_feature'] = waffle.switch_is_active('publisher_approval_widget_feature')
context['publish_state_name'] = CourseRunStateChoices.Published
if context['can_edit']:
current_owner_role = course_run.course.course_user_roles.get(role=course_run.course_run_state.owner_role)
user_role = course_run.course.course_user_roles.get(user=user)
if user_role.role != current_owner_role.role:
context['add_warning_popup'] = True
context['current_team_name'] = (_('course team')
if current_owner_role.role == PublisherUserRole.CourseTeam
else _('project coordinator'))
context['team_name'] = (_('course team')
if current_owner_role.role == PublisherUserRole.ProjectCoordinator
else _('project coordinator'))
return context
......@@ -628,6 +641,12 @@ class CourseRunEditView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMix
# pylint: disable=no-member
messages.success(request, _('Course run updated successfully.'))
# after editing course owner role will be changed to current user
user_role = course_run.course.course_user_roles.get(user=user).role
if user_role != course_run.course_run_state.owner_role:
course_run.course_run_state.change_owner_role(user_role)
return HttpResponseRedirect(reverse(self.success_url, kwargs={'pk': course_run.id}))
except Exception as e: # pylint: disable=broad-except
# pylint: disable=no-member
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-21 12:56+0500\n"
"POT-Creation-Date: 2017-03-21 16:21+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -781,6 +781,14 @@ msgid "Mark as Reviewed"
msgstr ""
#: apps/publisher/views.py
msgid "course team"
msgstr ""
#: apps/publisher/views.py
msgid "project coordinator"
msgstr ""
#: apps/publisher/views.py
msgid ""
"You have successfully created a course. You can edit the course information "
"or enter information for the course About page at any time. An edX project "
......@@ -908,6 +916,7 @@ msgstr ""
#: templates/publisher/add_update_course_form.html
#: templates/publisher/course_detail/_edit_warning_popup.html
#: templates/publisher/course_edit_form.html
#: templates/publisher/course_run_detail/_edit_warning.html
#: templates/publisher/course_run_detail/_preview_accept_popup.html
msgid "Cancel"
msgstr ""
......@@ -1859,6 +1868,7 @@ msgid "Course Level"
msgstr ""
#: templates/publisher/course_detail/_edit_warning_popup.html
#: templates/publisher/course_run_detail/_edit_warning.html
msgid "CAUTION"
msgstr ""
......@@ -1871,6 +1881,7 @@ msgid ""
msgstr ""
#: templates/publisher/course_detail/_edit_warning_popup.html
#: templates/publisher/course_run_detail/_edit_warning.html
#: templates/publisher/course_run_detail/_widgets.html
#: templates/publisher/courses.html
msgid "Edit"
......@@ -2138,6 +2149,14 @@ msgstr ""
msgid "Seats"
msgstr ""
#: templates/publisher/course_run_detail/_edit_warning.html
#, python-format
msgid ""
"The %(current_team_name)s is currently reviewing this course run. If you "
"edit course run information, you might overwrite the team’s changes, and you"
" will have to send the course run to the %(team_name)s for review again."
msgstr ""
#: templates/publisher/course_run_detail/_preview_accept_popup.html
msgid "Publish Course Run"
msgstr ""
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-21 12:56+0500\n"
"POT-Creation-Date: 2017-03-21 16:21+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-21 12:56+0500\n"
"POT-Creation-Date: 2017-03-21 16:21+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -923,6 +923,14 @@ msgid "Mark as Reviewed"
msgstr "Märk äs Révïéwéd Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αм#"
#: apps/publisher/views.py
msgid "course team"
msgstr "çöürsé téäm Ⱡ'σяєм ιρѕυм ∂σłσя #"
#: apps/publisher/views.py
msgid "project coordinator"
msgstr "pröjéçt çöördïnätör Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт,#"
#: apps/publisher/views.py
msgid ""
"You have successfully created a course. You can edit the course information "
"or enter information for the course About page at any time. An edX project "
......@@ -1069,6 +1077,7 @@ msgstr "Sävé Ⱡ'σяєм ι#"
#: templates/publisher/add_update_course_form.html
#: templates/publisher/course_detail/_edit_warning_popup.html
#: templates/publisher/course_edit_form.html
#: templates/publisher/course_run_detail/_edit_warning.html
#: templates/publisher/course_run_detail/_preview_accept_popup.html
msgid "Cancel"
msgstr "Çänçél Ⱡ'σяєм ιρѕυ#"
......@@ -2226,6 +2235,7 @@ msgid "Course Level"
msgstr "Çöürsé Lévél Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
#: templates/publisher/course_detail/_edit_warning_popup.html
#: templates/publisher/course_run_detail/_edit_warning.html
msgid "CAUTION"
msgstr "ÇÀÛTÌÖN Ⱡ'σяєм ιρѕυм #"
......@@ -2241,6 +2251,7 @@ msgstr ""
" Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α∂ιριѕι¢ιηg єłιт, ѕє∂ ∂σ єιυѕмσ∂ тємρσя ιη¢ι∂ι∂υηт υт łαвσяє єт ∂σłσяє мαgηα αłιqυα. υт єηιм α∂ мιηιм νєηιαм, qυιѕ ησѕтяυ∂ єχєя¢ιтαтιση υłłαм¢σ łαвσяιѕ ηιѕι υт αłιqυιρ єχ єα ¢σммσ∂σ ¢σηѕєqυαт. ∂υιѕ αυтє ιяυяє ∂σłσя ιη яєρяєнєη∂єяιт ιη νσłυρтαтє νєłιт єѕѕє ¢ιłłυм ∂σłσяє єυ ƒυgιαт ηυłłα#"
#: templates/publisher/course_detail/_edit_warning_popup.html
#: templates/publisher/course_run_detail/_edit_warning.html
#: templates/publisher/course_run_detail/_widgets.html
#: templates/publisher/courses.html
msgid "Edit"
......@@ -2520,6 +2531,22 @@ msgstr "Énröllménts Ⱡ'σяєм ιρѕυм ∂σłσя #"
msgid "Seats"
msgstr "Séäts Ⱡ'σяєм ιρѕ#"
#: templates/publisher/course_run_detail/_edit_warning.html
#, python-format
msgid ""
"The %(current_team_name)s is currently reviewing this course run. If you "
"edit course run information, you might overwrite the team’s changes, and you"
" will have to send the course run to the %(team_name)s for review again."
msgstr ""
"Thé %(current_team_name)s ïs çürréntlý révïéwïng thïs çöürsé rün. Ìf ýöü "
"édït çöürsé rün ïnförmätïön, ýöü mïght övérwrïté thé téäm’s çhängés, änd ýöü"
" wïll hävé tö sénd thé çöürsé rün tö thé %(team_name)s för révïéw ägäïn. "
"Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α∂ιριѕι¢ιηg єłιт, ѕє∂ ∂σ єιυѕмσ∂ "
"тємρσя ιη¢ι∂ι∂υηт υт łαвσяє єт ∂σłσяє мαgηα αłιqυα. υт єηιм α∂ мιηιм νєηιαм,"
" qυιѕ ησѕтяυ∂ єχєя¢ιтαтιση υłłαм¢σ łαвσяιѕ ηιѕι υт αłιqυιρ єχ єα ¢σммσ∂σ "
"¢σηѕєqυαт. ∂υιѕ αυтє ιяυяє ∂σłσя ιη яєρяєнєη∂єяιт ιη νσłυρтαтє νєłιт єѕѕє "
"¢ιłłυм ∂σłσяє єυ ƒυgιαт ηυłłα ραяιαтυя. єχ¢єρтєυя ѕιηт σ¢¢αє¢αт ¢υρι∂αтαт #"
#: templates/publisher/course_run_detail/_preview_accept_popup.html
msgid "Publish Course Run"
msgstr "Püßlïsh Çöürsé Rün Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт#"
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-21 12:56+0500\n"
"POT-Creation-Date: 2017-03-21 16:21+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......
......@@ -16,6 +16,11 @@ $(document).ready(function(){
$('body').addClass('stopScroll');
});
$('.btn-courserun-edit').click(function(e){
$('#editCourseRun').show();
$('body').addClass('stopScroll');
});
$('.btn-preview-accept').click(function(e){
$('#acceptPreviewModal').show();
$('body').addClass('stopScroll');
......
{% load i18n %}
<div id="editCourseRun" class="modal">
<div class="modal-content">
<h2 class="hd-2 emphasized">
<span class="icon fa fa-exclamation-triangle" aria-hidden="true"></span> {% trans "CAUTION" %}
</h2>
<span class="sr-only">CAUTION</span>
<p>
{% blocktrans trimmed with owner=object.course_run_state.owner_role reviewer_user='tasawer'%}
The {{ current_team_name }} is currently reviewing this course run. If you edit course run information, you might overwrite the team’s changes, and you will have to send the course run to the {{ team_name }} for review again.
{% endblocktrans %}
</p>
<div class="actions">
<a class="btn-cancel closeModal" href="#">{% trans "Cancel" %}</a>
<a class="btn-brand btn-base btn-accept" type="button" href="{% url 'publisher:publisher_course_runs_edit' object.id %}">{% trans "Edit" %}</a>
</div>
</div>
</div>
......@@ -2,7 +2,8 @@
<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">
{% url 'publisher:publisher_courses_edit' pk=object.id as edit_page_url %}
<a href="{% if add_warning_popup %}#{% else %}{{ edit_page_url }}{% endif %}" class="btn btn-neutral btn-courserun-edit">
{% trans "EDIT" %}
</a>
<div class="clearfix"></div>
......@@ -69,5 +70,9 @@
{% include 'comments/add_auth_comments.html' with comment_type='decline_preview' box_label=decline_reason btn_label=submit %}
</div>
{% include 'publisher/course_run_detail/_preview_accept_popup.html' %}
{% if add_warning_popup %}
{% include 'publisher/course_run_detail/_edit_warning.html' %}
{% endif %}
</div>
</div>
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