diff --git a/course/constants.py b/course/constants.py
index a69f819da..e58e9a7c3 100644
--- a/course/constants.py
+++ b/course/constants.py
@@ -134,6 +134,8 @@ class participation_permission: # noqa
query_participation = "query_participation"
edit_participation = "edit_participation"
preapprove_participation = "preapprove_participation"
+ edit_participation_role = "edit_participation_role"
+ edit_participation_tag = "edit_participation_tag"
manage_instant_flow_requests = "manage_instant_flow_requests"
@@ -249,6 +251,12 @@ class participation_permission: # noqa
pgettext_lazy("Participation permission", "Edit participation")),
(participation_permission.preapprove_participation,
pgettext_lazy("Participation permission", "Preapprove participation")),
+ (participation_permission.edit_participation_role,
+ pgettext_lazy(
+ "Participation permission", "Edit participation role")),
+ (participation_permission.edit_participation_tag,
+ pgettext_lazy(
+ "Participation permission", "Edit participation tag")),
(participation_permission.manage_instant_flow_requests,
pgettext_lazy("Participation permission",
diff --git a/course/enrollment.py b/course/enrollment.py
index 90f06ea5b..e0e08e387 100644
--- a/course/enrollment.py
+++ b/course/enrollment.py
@@ -1124,10 +1124,12 @@ def __init__(self, add_new, *args, **kwargs):
if add_new:
self.helper.add_input(
- Submit("submit", _("Add")))
+ Submit("submit", _("Add"), css_class="btn-success"))
else:
self.helper.add_input(
- Submit("submit", _("Update")))
+ Submit("submit", _("Update"), css_class="btn-success"))
+ self.helper.add_input(
+ Submit("delete", _("Delete"), css_class="btn-danger"))
class Meta:
model = ParticipationTag
@@ -1143,18 +1145,13 @@ def view_participation_tag_list(pctx):
return render_course_page(pctx, "course/participation-tag-list.html", {
"participation_tags": participation_tags,
-
- # Wrappers used by JavaScript template (tmpl) so as not to
- # conflict with Django template's tag wrapper
- "JQ_OPEN": "{%",
- "JQ_CLOSE": "%}",
})
@course_view
def edit_participation_tag(pctx, ptag_id):
# type: (CoursePageContext, int) -> http.HttpResponse
- if not pctx.has_permission(pperm.edit_participation):
+ if not pctx.has_permission(pperm.edit_participation_tag):
raise PermissionDenied()
request = pctx.request
@@ -1176,13 +1173,23 @@ def edit_participation_tag(pctx, ptag_id):
form = EditParticipationTagForm(add_new, request.POST, instance=ptag)
try:
if form.is_valid():
- # Ref: https://stackoverflow.com/q/21458387/3437454
- with transaction.atomic():
- form.save()
- if add_new:
- msg = _("New participation tag saved.")
+ if "submit" in request.POST or "update" in request.POST:
+ # Ref: https://stackoverflow.com/q/21458387/3437454
+ with transaction.atomic():
+ form.save()
+
+ if "submit" in request.POST:
+ assert add_new
+ msg = _("New participation tag saved.")
+ else:
+ msg = _("Changes saved.")
+ elif "delete" in request.POST:
+ ptag.delete()
+ msg = (_("successfully deleted participation tag '%(tag)s'.")
+ % {"tag": ptag.name})
else:
- msg = _("Changes saved.")
+ raise SuspiciousOperation(_("invalid operation"))
+
messages.add_message(request, messages.SUCCESS, msg)
return redirect(
"relate-view_participation_tags", pctx.course.identifier)
@@ -1199,48 +1206,6 @@ def edit_participation_tag(pctx, ptag_id):
"form": form,
})
-
-@course_view
-def delete_participation_tag(pctx, ptag_id):
- # type: (CoursePageContext, int) -> http.HttpResponse
-
- if not pctx.has_permission(pperm.edit_participation):
- raise PermissionDenied()
-
- request = pctx.request
-
- if not request.is_ajax() or request.method != "POST":
- raise PermissionDenied(_("only AJAX POST is allowed"))
-
- num_ptag_id = int(ptag_id)
-
- ptag = get_object_or_404(ParticipationTag, id=num_ptag_id)
-
- if ptag.course.id != pctx.course.id:
- raise SuspiciousOperation(
- "may not delete participation tag in different course")
-
- if "delete" in request.POST:
- try:
- ptag.delete()
- except Exception as e:
- return http.JsonResponse(
- {"error": _(
- "Error when deleting participation tag '%(tag)s'."
- " %(error_type)s: %(error)s.") % {
- "tag": ptag.name,
- "error_type": type(e).__name__,
- "error": str(e)}},
- status=400)
- else:
- return http.JsonResponse(
- {"message": _("successfully deleted participation tag '%(tag)s'.")
- % {"tag": ptag.name},
- "message_level": messages.DEFAULT_TAGS[messages.SUCCESS]})
-
- else:
- raise SuspiciousOperation(_("invalid operation"))
-
# }}}
@@ -1253,10 +1218,12 @@ def __init__(self, add_new, *args, **kwargs):
if add_new:
self.helper.add_input(
- Submit("submit", _("Add")))
+ Submit("submit", _("Add"), css_class="btn-success"))
else:
self.helper.add_input(
- Submit("submit", _("Update")))
+ Submit("submit", _("Update"), css_class="btn-success"))
+ self.helper.add_input(
+ Submit("delete", _("Delete"), css_class="btn-danger"))
class Meta:
model = ParticipationRole
@@ -1272,18 +1239,13 @@ def view_participation_role_list(pctx):
return render_course_page(pctx, "course/participation-role-list.html", {
"participation_roles": participation_roles,
-
- # Wrappers used by JavaScript template (tmpl) so as not to
- # conflict with Django template's tag wrapper
- "JQ_OPEN": "{%",
- "JQ_CLOSE": "%}",
})
@course_view
def edit_participation_role(pctx, prole_id):
# type: (CoursePageContext, int) -> http.HttpResponse
- if not pctx.has_permission(pperm.edit_participation):
+ if not pctx.has_permission(pperm.edit_participation_role):
raise PermissionDenied()
request = pctx.request
@@ -1305,14 +1267,24 @@ def edit_participation_role(pctx, prole_id):
form = EditParticipationRoleForm(add_new, request.POST, instance=prole)
try:
if form.is_valid():
- # Ref: https://stackoverflow.com/q/21458387/3437454
- with transaction.atomic():
- form.save()
-
- if add_new:
- msg = _("New participation role saved.")
+ if "submit" in request.POST or "update" in request.POST:
+ # Ref: https://stackoverflow.com/q/21458387/3437454
+ with transaction.atomic():
+ form.save()
+
+ if "submit" in request.POST:
+ assert add_new
+ msg = _("New participation role saved.")
+ else:
+ msg = _("Changes saved.")
+ elif "delete" in request.POST:
+ prole.delete()
+ msg = (
+ _("successfully deleted participation role '%(role)s'.")
+ % {"role": prole.identifier})
else:
- msg = _("Changes saved.")
+ raise SuspiciousOperation(_("invalid operation"))
+
messages.add_message(request, messages.SUCCESS, msg)
return redirect(
"relate-view_participation_roles", pctx.course.identifier)
@@ -1329,48 +1301,6 @@ def edit_participation_role(pctx, prole_id):
"form": form,
})
-
-@course_view
-def delete_participation_role(pctx, prole_id):
- # type: (CoursePageContext, int) -> http.HttpResponse
-
- if not pctx.has_permission(pperm.edit_participation):
- raise PermissionDenied()
-
- request = pctx.request
-
- if not request.is_ajax() or request.method != "POST":
- raise PermissionDenied(_("only AJAX POST is allowed"))
-
- num_prole_id = int(prole_id)
-
- prole = get_object_or_404(ParticipationRole, id=num_prole_id)
-
- if prole.course.id != pctx.course.id:
- raise SuspiciousOperation(
- "may not delete participation role in different course")
-
- if "delete" in request.POST:
- try:
- prole.delete()
- except Exception as e:
- return http.JsonResponse(
- {"error": _(
- "Error when deleting participation role '%(role)s'."
- " %(error_type)s: %(error)s.") % {
- "role": prole.identifier,
- "error_type": type(e).__name__,
- "error": str(e)}},
- status=400)
- else:
- return http.JsonResponse(
- {"message": _("successfully deleted participation role '%(role)s'.")
- % {"role": prole.identifier},
- "message_level": messages.DEFAULT_TAGS[messages.SUCCESS]})
-
- else:
- raise SuspiciousOperation(_("invalid operation"))
-
# }}}
diff --git a/course/migrations/0114_added_edit_prole_and_ptag_permission.py b/course/migrations/0114_added_edit_prole_and_ptag_permission.py
new file mode 100644
index 000000000..e48378048
--- /dev/null
+++ b/course/migrations/0114_added_edit_prole_and_ptag_permission.py
@@ -0,0 +1,49 @@
+# Generated by Django 3.0.8 on 2020-10-26 08:48
+
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+def add_edit_prole_and_ptag_permission(apps, schema_editor):
+ from course.constants import participation_permission as pperm
+
+ ParticipationRolePermission = apps.get_model("course", "ParticipationRolePermission") # noqa
+
+ roles_pks = (
+ ParticipationRolePermission.objects.filter(
+ permission=pperm.preapprove_participation)
+ .values_list("role", flat=True)
+ )
+
+ if roles_pks.count():
+ for pk in roles_pks:
+ ParticipationRolePermission.objects.get_or_create(
+ role_id=pk,
+ permission=pperm.edit_participation_role
+ )
+ ParticipationRolePermission.objects.get_or_create(
+ role_id=pk,
+ permission=pperm.edit_participation_tag
+ )
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('course', '0113_merge_20190919_1408'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='participationpermission',
+ name='permission',
+ field=models.CharField(choices=[('edit_course', 'Edit course'), ('use_admin_interface', 'Use admin interface'), ('manage_authentication_tokens', 'Manage authentication tokens'), ('impersonate_role', 'Impersonate role'), ('set_fake_time', 'Set fake time'), ('set_pretend_facility', 'Pretend to be in facility'), ('edit_course_permissions', 'Edit course permissions'), ('view_hidden_course_page', 'View hidden course page'), ('view_calendar', 'View calendar'), ('send_instant_message', 'Send instant message'), ('access_files_for', 'Access files for'), ('included_in_grade_statistics', 'Included in grade statistics'), ('skip_during_manual_grading', 'Skip during manual grading'), ('edit_exam', 'Edit exam'), ('issue_exam_ticket', 'Issue exam ticket'), ('batch_issue_exam_ticket', 'Batch issue exam ticket'), ('view_participant_masked_profile', "View participants' masked profile only"), ('view_flow_sessions_from_role', 'View flow sessions from role'), ('view_gradebook', 'View gradebook'), ('edit_grading_opportunity', 'Edit grading opportunity'), ('assign_grade', 'Assign grade'), ('view_grader_stats', 'View grader stats'), ('batch_import_grade', 'Batch-import grades'), ('batch_export_grade', 'Batch-export grades'), ('batch_download_submission', 'Batch-download submissions'), ('impose_flow_session_deadline', 'Impose flow session deadline'), ('batch_impose_flow_session_deadline', 'Batch-impose flow session deadline'), ('end_flow_session', 'End flow session'), ('batch_end_flow_session', 'Batch-end flow sessions'), ('regrade_flow_session', 'Regrade flow session'), ('batch_regrade_flow_session', 'Batch-regrade flow sessions'), ('recalculate_flow_session_grade', 'Recalculate flow session grade'), ('batch_recalculate_flow_session_grade', 'Batch-recalculate flow sesssion grades'), ('reopen_flow_session', 'Reopen flow session'), ('grant_exception', 'Grant exception'), ('view_analytics', 'View analytics'), ('preview_content', 'Preview content'), ('update_content', 'Update content'), ('use_git_endpoint', 'Use direct git endpoint'), ('use_markup_sandbox', 'Use markup sandbox'), ('use_page_sandbox', 'Use page sandbox'), ('test_flow', 'Test flow'), ('edit_events', 'Edit events'), ('query_participation', 'Query participation'), ('edit_participation', 'Edit participation'), ('preapprove_participation', 'Preapprove participation'), ('edit_participation_role', 'Edit participation role'), ('edit_participation_tag', 'Edit participation tag'), ('manage_instant_flow_requests', 'Manage instant flow requests')], db_index=True, max_length=200, verbose_name='Permission'),
+ ),
+ migrations.AlterField(
+ model_name='participationrolepermission',
+ name='permission',
+ field=models.CharField(choices=[('edit_course', 'Edit course'), ('use_admin_interface', 'Use admin interface'), ('manage_authentication_tokens', 'Manage authentication tokens'), ('impersonate_role', 'Impersonate role'), ('set_fake_time', 'Set fake time'), ('set_pretend_facility', 'Pretend to be in facility'), ('edit_course_permissions', 'Edit course permissions'), ('view_hidden_course_page', 'View hidden course page'), ('view_calendar', 'View calendar'), ('send_instant_message', 'Send instant message'), ('access_files_for', 'Access files for'), ('included_in_grade_statistics', 'Included in grade statistics'), ('skip_during_manual_grading', 'Skip during manual grading'), ('edit_exam', 'Edit exam'), ('issue_exam_ticket', 'Issue exam ticket'), ('batch_issue_exam_ticket', 'Batch issue exam ticket'), ('view_participant_masked_profile', "View participants' masked profile only"), ('view_flow_sessions_from_role', 'View flow sessions from role'), ('view_gradebook', 'View gradebook'), ('edit_grading_opportunity', 'Edit grading opportunity'), ('assign_grade', 'Assign grade'), ('view_grader_stats', 'View grader stats'), ('batch_import_grade', 'Batch-import grades'), ('batch_export_grade', 'Batch-export grades'), ('batch_download_submission', 'Batch-download submissions'), ('impose_flow_session_deadline', 'Impose flow session deadline'), ('batch_impose_flow_session_deadline', 'Batch-impose flow session deadline'), ('end_flow_session', 'End flow session'), ('batch_end_flow_session', 'Batch-end flow sessions'), ('regrade_flow_session', 'Regrade flow session'), ('batch_regrade_flow_session', 'Batch-regrade flow sessions'), ('recalculate_flow_session_grade', 'Recalculate flow session grade'), ('batch_recalculate_flow_session_grade', 'Batch-recalculate flow sesssion grades'), ('reopen_flow_session', 'Reopen flow session'), ('grant_exception', 'Grant exception'), ('view_analytics', 'View analytics'), ('preview_content', 'Preview content'), ('update_content', 'Update content'), ('use_git_endpoint', 'Use direct git endpoint'), ('use_markup_sandbox', 'Use markup sandbox'), ('use_page_sandbox', 'Use page sandbox'), ('test_flow', 'Test flow'), ('edit_events', 'Edit events'), ('query_participation', 'Query participation'), ('edit_participation', 'Edit participation'), ('preapprove_participation', 'Preapprove participation'), ('edit_participation_role', 'Edit participation role'), ('edit_participation_tag', 'Edit participation tag'), ('manage_instant_flow_requests', 'Manage instant flow requests')], db_index=True, max_length=200, verbose_name='Permission'),
+ ),
+ migrations.RunPython(add_edit_prole_and_ptag_permission),
+ ]
diff --git a/course/models.py b/course/models.py
index 2926fe810..42c06d6fb 100644
--- a/course/models.py
+++ b/course/models.py
@@ -736,6 +736,8 @@ def add_instructor_permissions(role):
rpm(role=role, permission=pp.edit_events).save()
rpm(role=role, permission=pp.manage_instant_flow_requests).save()
rpm(role=role, permission=pp.preapprove_participation).save()
+ rpm(role=role, permission=pp.edit_participation_tag).save()
+ rpm(role=role, permission=pp.edit_participation_role).save()
add_teaching_assistant_permissions(role)
diff --git a/course/templates/course/course-base.html b/course/templates/course/course-base.html
index 8e15836b8..d9db3f0d4 100644
--- a/course/templates/course/course-base.html
+++ b/course/templates/course/course-base.html
@@ -52,13 +52,16 @@
{% trans "Grading" context "menu item" %}
{% endif %}
- {% if pperm.query_participation or pperm.manage_instant_flow_requests or pperm.preapprove_participation %}
+ {% if pperm.manage_instant_flow_requests or pperm.preapprove_participation %}
{% if not pperm.view_participant_masked_profile %}
{% trans "Instructor" context "menu item" %}
@@ -153,10 +156,6 @@
{% if pperm.preapprove_participation %}
{% trans "Preapprove enrollments" context "menu item" %}
{% endif %}
- {% if pperm.query_participation %}
- {% trans "Query participations" context "menu item" %}
- {% endif %}
-
{% if pperm.manage_instant_flow_requests %}
diff --git a/course/templates/course/gradebook-participant-list.html b/course/templates/course/gradebook-participant-list.html
index c7d043518..0d268dd22 100644
--- a/course/templates/course/gradebook-participant-list.html
+++ b/course/templates/course/gradebook-participant-list.html
@@ -4,7 +4,7 @@
{% load static %}
{% block title %}
- {% trans "List of participants" %} - {{ relate_site_name }}
+ {% trans "List of Participants" %} - {{ relate_site_name }}
{% endblock %}
{% block header_extra %}
diff --git a/course/templates/course/participation-role-list.html b/course/templates/course/participation-role-list.html
index 6fecb3bc7..fbd5a848f 100644
--- a/course/templates/course/participation-role-list.html
+++ b/course/templates/course/participation-role-list.html
@@ -4,7 +4,7 @@
{% load static %}
{% block title %}
- {% trans "Participation roles" %} - {{ relate_site_name }}
+ {% trans "Participation Roles" %} - {{ relate_site_name }}
{% endblock %}
{% block header_extra %}
@@ -19,7 +19,3 @@ {% trans "Participation roles" %}
{% include "course/participation-role-table.html" with participation_roles=participation_roles %}
{% endblock %}
-
-{% block page_bottom_javascript_extra %}
-
-{% endblock %}
diff --git a/course/templates/course/participation-role-table.html b/course/templates/course/participation-role-table.html
index 4798ad784..204f903b4 100644
--- a/course/templates/course/participation-role-table.html
+++ b/course/templates/course/participation-role-table.html
@@ -6,7 +6,7 @@
{% trans "Role name" %}
{% trans "Default for new participants" %}
{% trans "Default for unenrolled" %}
- {% if pperm.edit_participation %}
+ {% if pperm.edit_participation_role %}
{% trans "Actions" %}
{% endif %}
@@ -29,10 +29,9 @@
{% endif %}
- {% if pperm.edit_participation %}
+ {% if pperm.edit_participation_role %}
{% trans "Edit" %}
- {% trans "Delete" %}
{% endif %}
@@ -55,56 +54,4 @@
"language": {url: '{% static "datatables-i18n/i18n/" %}{{LANG}}.json'},
});
-
-
-
-
-
{% endblock %}
diff --git a/course/templates/course/participation-tag-list.html b/course/templates/course/participation-tag-list.html
index d97193aa4..31ab74761 100644
--- a/course/templates/course/participation-tag-list.html
+++ b/course/templates/course/participation-tag-list.html
@@ -19,7 +19,3 @@ {% trans "Participant Tags" %}
{% include "course/participation-tag-table.html" with participation_tags=participation_tags %}
{% endblock %}
-
-{% block page_bottom_javascript_extra %}
-
-{% endblock %}
diff --git a/course/templates/course/participation-tag-table.html b/course/templates/course/participation-tag-table.html
index 68f7e7e98..ad0b8be8b 100644
--- a/course/templates/course/participation-tag-table.html
+++ b/course/templates/course/participation-tag-table.html
@@ -4,7 +4,7 @@
{% trans "Tag name" %}
{% trans "Shown to participant" %}
- {% if pperm.edit_participation %}
+ {% if pperm.edit_participation_tag %}
{% trans "Actions" %}
{% endif %}
@@ -19,10 +19,9 @@
{% endif %}
- {% if pperm.edit_participation %}
+ {% if pperm.edit_participation_tag %}
{% trans "Edit" %}
- {% trans "Delete" %}
{% endif %}
@@ -46,55 +45,4 @@
});
-
-
-
-
{% endblock %}
diff --git a/relate/urls.py b/relate/urls.py
index 55b8e26a9..cf2aa8f8b 100644
--- a/relate/urls.py
+++ b/relate/urls.py
@@ -313,13 +313,6 @@
"/$",
course.enrollment.edit_participation_tag,
name="relate-edit_participation_tag"),
- url(r"^course"
- "/" + COURSE_ID_REGEX
- + "/delete-participation-tag"
- "/(?P[-0-9]+)"
- "/$",
- course.enrollment.delete_participation_tag,
- name="relate-delete_participation_tag"),
url(r"^course"
"/" + COURSE_ID_REGEX
@@ -333,13 +326,6 @@
"/$",
course.enrollment.edit_participation_role,
name="relate-edit_participation_role"),
- url(r"^course"
- "/" + COURSE_ID_REGEX
- + "/delete-participation-role"
- "/(?P[-0-9]+)"
- "/$",
- course.enrollment.delete_participation_role,
- name="relate-delete_participation_role"),
# }}}
diff --git a/tests/test_enrollment.py b/tests/test_enrollment.py
index 811cc90c7..19f9291df 100644
--- a/tests/test_enrollment.py
+++ b/tests/test_enrollment.py
@@ -144,11 +144,6 @@ def get_participation_tag_edit_url(cls, ptag_id):
return reverse("relate-edit_participation_tag",
args=[cls.course.identifier, ptag_id])
- @classmethod
- def get_participation_tag_delete_url(cls, ptag_id):
- return reverse("relate-delete_participation_tag",
- args=[cls.course.identifier, ptag_id])
-
@ classmethod
def get_participation_role_list_url(cls):
return reverse("relate-view_participation_roles",
@@ -159,11 +154,6 @@ def get_participation_role_edit_url(cls, prole_id):
return reverse("relate-edit_participation_role",
args=[cls.course.identifier, prole_id])
- @classmethod
- def get_participation_role_delete_url(cls, prole_id):
- return reverse("relate-delete_participation_role",
- args=[cls.course.identifier, prole_id])
-
def get_participation_count_by_status(self, status):
return Participation.objects.filter(
course__identifier=self.course.identifier,
@@ -1941,15 +1931,27 @@ def setUp(self):
super(ParticipationTagCRUDTest, self).setUp()
self.c.force_login(self.instructor_participation.user)
- def get_default_edit_ptag_post_data(self, **kwargs):
+ @staticmethod
+ def get_default_edit_ptag_post_data(**kwargs):
data = {"name": "a_tag"}
data.update(kwargs)
return data
+ def get_default_create_ptag_post_data(self, **kwargs):
+ data = self.get_default_edit_ptag_post_data()
+ data["submit"] = ""
+ data.update(kwargs)
+ return data
+
+ def get_default_update_ptag_post_data(self, **kwargs):
+ data = self.get_default_edit_ptag_post_data()
+ data["update"] = ""
+ data.update(kwargs)
+ return data
+
def get_default_delete_ptag_post_data(self, **kwargs):
- data = {}
- if not kwargs.pop("no_delete_in_post", None):
- data = {"delete": True}
+ data = self.get_default_edit_ptag_post_data()
+ data["delete"] = ""
data.update(kwargs)
return data
@@ -2028,7 +2030,7 @@ def test_edit_ptag_post_update(self):
resp = self.c.post(
self.get_participation_tag_edit_url(ptag.id),
- data=self.get_default_edit_ptag_post_data(name=expected_ptag_name),
+ data=self.get_default_update_ptag_post_data(name=expected_ptag_name),
)
self.assertEqual(ParticipationTag.objects.count(), n_tags)
self.assertEqual(resp.status_code, 302)
@@ -2063,7 +2065,7 @@ def test_edit_ptag_post_update_integrity_error(self):
resp = self.c.post(
self.get_participation_tag_edit_url(ptag.id),
- data=self.get_default_edit_ptag_post_data(name=exist_ptag_name),
+ data=self.get_default_update_ptag_post_data(name=exist_ptag_name),
)
self.assertEqual(ParticipationTag.objects.count(), n_tags)
@@ -2081,7 +2083,7 @@ def test_edit_ptag_post_create_new_success(self):
resp = self.c.post(
self.get_participation_tag_edit_url(-1),
- data=self.get_default_edit_ptag_post_data(),
+ data=self.get_default_create_ptag_post_data(),
)
self.assertEqual(ParticipationTag.objects.count(), n_tags+1)
@@ -2096,7 +2098,7 @@ def test_edit_ptag_post_create_new_integrity_error(self):
resp = self.c.post(
self.get_participation_tag_edit_url(-1),
- data=self.get_default_edit_ptag_post_data(name=ptags[0].name),
+ data=self.get_default_create_ptag_post_data(name=ptags[0].name),
)
self.assertEqual(ParticipationTag.objects.count(), n_tags)
@@ -2113,7 +2115,7 @@ def test_edit_ptag_post_form_invalid(self):
# Spaces are not allowed in ptag
resp = self.c.post(
self.get_participation_tag_edit_url(-1),
- data=self.get_default_edit_ptag_post_data(name="a tag"),
+ data=self.get_default_create_ptag_post_data(name="a tag"),
)
self.assertEqual(ParticipationTag.objects.count(), n_tags)
@@ -2127,93 +2129,55 @@ def test_delete_ptag_permission_denied(self):
size=n_tags, course=self.course)
with self.temporarily_switch_to_user(self.student_participation.user):
- resp = self.c.get(self.get_participation_tag_delete_url(ptags[0].id))
+ resp = self.c.get(self.get_participation_tag_edit_url(ptags[0].id))
self.assertEqual(resp.status_code, 403)
resp = self.c.post(
- self.get_participation_tag_delete_url(ptags[0].id),
+ self.get_participation_tag_edit_url(ptags[0].id),
data=self.get_default_delete_ptag_post_data(),
- HTTP_X_REQUESTED_WITH='XMLHttpRequest'
)
self.assertEqual(resp.status_code, 403)
self.assertEqual(ParticipationTag.objects.count(), n_tags)
- def test_delete_ptag_get_or_non_ajax_post_not_allowed(self):
- n_tags = randint(2, 10)
- ptags = factories.ParticipationTagFactory.create_batch(
- size=n_tags, course=self.course)
-
- resp = self.c.get(self.get_participation_tag_delete_url(ptags[0].id))
- self.assertEqual(resp.status_code, 403)
-
- resp = self.c.post(
- self.get_participation_tag_delete_url(ptags[0].id),
- data=self.get_default_delete_ptag_post_data(),
- )
- self.assertEqual(resp.status_code, 403)
- self.assertEqual(ParticipationTag.objects.count(), n_tags)
-
def test_delete_ptag_success(self):
n_tags = randint(2, 10)
ptags = factories.ParticipationTagFactory.create_batch(
size=n_tags, course=self.course)
resp = self.c.post(
- self.get_participation_tag_delete_url(ptags[0].id),
+ self.get_participation_tag_edit_url(ptags[0].id),
data=self.get_default_delete_ptag_post_data(),
- HTTP_X_REQUESTED_WITH='XMLHttpRequest'
)
- self.assertEqual(resp.status_code, 200)
+ self.assertEqual(resp.status_code, 302)
self.assertEqual(ParticipationTag.objects.count(), n_tags-1)
- def test_delete_ptag_suspicious(self):
- n_tags = randint(2, 10)
- ptags = factories.ParticipationTagFactory.create_batch(
- size=n_tags, course=self.course)
-
- resp = self.c.post(
- self.get_participation_tag_delete_url(ptags[0].id),
- data=self.get_default_delete_ptag_post_data(
- no_delete_in_post=True, some_action=True),
- HTTP_X_REQUESTED_WITH='XMLHttpRequest'
- )
- self.assertEqual(resp.status_code, 400)
- self.assertEqual(ParticipationTag.objects.count(), n_tags)
-
def test_delete_ptag_from_another_course(self):
ptag = factories.ParticipationTagFactory(
course=factories.CourseFactory(identifier="another-course"))
resp = self.c.post(
- self.get_participation_tag_delete_url(ptag.id),
+ self.get_participation_tag_edit_url(ptag.id),
data=self.get_default_delete_ptag_post_data(),
- HTTP_X_REQUESTED_WITH='XMLHttpRequest'
)
self.assertEqual(resp.status_code, 400)
self.assertEqual(ParticipationTag.objects.count(), 1)
- def test_delete_ptag_exception_raised(self):
+ def test_edit_ptag_suspicious(self):
n_tags = randint(2, 10)
ptags = factories.ParticipationTagFactory.create_batch(
size=n_tags, course=self.course)
- with mock.patch("course.models.ParticipationTag.delete") as mock_ptag_del:
- mock_ptag_del.side_effect = (
- RuntimeError("some error"))
-
+ with mock.patch("course.models.ParticipationTag.save") as mock_ptag_save:
resp = self.c.post(
- self.get_participation_tag_delete_url(ptags[0].id),
- data=self.get_default_delete_ptag_post_data(),
- HTTP_X_REQUESTED_WITH='XMLHttpRequest'
+ self.get_participation_tag_edit_url(ptags[0].id),
+ # No submit/update/delete
+ data=self.get_default_edit_ptag_post_data(),
)
- self.assertEqual(resp.status_code, 400)
+ self.assertEqual(mock_ptag_save.call_count, 0)
- import json
- self.assertIn(
- "Error when deleting participation tag",
- json.loads(resp.content.decode())["error"]
- )
+ self.assertEqual(resp.status_code, 400)
+ self.assertEqual(ParticipationTag.objects.count(), n_tags)
class ParticipationRoleCRUDTest(
@@ -2223,15 +2187,27 @@ def setUp(self):
self.c.force_login(self.instructor_participation.user)
self.default_number_of_roles = ParticipationRole.objects.count()
- def get_default_edit_role_post_data(self, **kwargs):
+ @staticmethod
+ def get_default_edit_role_post_data(**kwargs):
data = {"identifier": "a_role", "name": "the name"}
data.update(kwargs)
return data
+ def get_default_create_role_post_data(self, **kwargs):
+ data = self.get_default_edit_role_post_data()
+ data["submit"] = ""
+ data.update(kwargs)
+ return data
+
+ def get_default_update_role_post_data(self, **kwargs):
+ data = self.get_default_edit_role_post_data()
+ data["update"] = ""
+ data.update(kwargs)
+ return data
+
def get_default_delete_role_post_data(self, **kwargs):
- data = {}
- if not kwargs.pop("no_delete_in_post", None):
- data = {"delete": True}
+ data = self.get_default_edit_role_post_data()
+ data["delete"] = ""
data.update(kwargs)
return data
@@ -2303,7 +2279,7 @@ def test_edit_prole_post_update(self):
resp = self.c.post(
self.get_participation_role_edit_url(prole.id),
- data=self.get_default_edit_role_post_data(
+ data=self.get_default_update_role_post_data(
identifier=expected_prole_identifier),
)
self.assertEqual(ParticipationRole.objects.count(),
@@ -2336,7 +2312,7 @@ def test_edit_prole_post_update_integrity_error(self):
resp = self.c.post(
self.get_participation_role_edit_url(prole.id),
- data=self.get_default_edit_role_post_data(
+ data=self.get_default_update_role_post_data(
identifier=new_prole_identifier),
)
@@ -2352,7 +2328,7 @@ def test_edit_prole_post_update_integrity_error(self):
def test_edit_prole_post_create_new_success(self):
resp = self.c.post(
self.get_participation_role_edit_url(-1),
- data=self.get_default_edit_role_post_data(),
+ data=self.get_default_create_role_post_data(),
)
self.assertEqual(ParticipationRole.objects.count(),
@@ -2367,7 +2343,7 @@ def test_edit_prole_post_create_new_integrity_error(self):
resp = self.c.post(
self.get_participation_role_edit_url(-1),
- data=self.get_default_edit_role_post_data(identifier=prole.identifier),
+ data=self.get_default_create_role_post_data(identifier=prole.identifier),
)
self.assertEqual(ParticipationRole.objects.count(),
@@ -2381,7 +2357,7 @@ def test_edit_prole_post_form_invalid(self):
# Spaces are not allowed in prole
resp = self.c.post(
self.get_participation_role_edit_url(-1),
- data=self.get_default_edit_role_post_data(identifier="a role"),
+ data=self.get_default_create_role_post_data(identifier="a role"),
)
self.assertEqual(ParticipationRole.objects.count(),
@@ -2395,60 +2371,29 @@ def test_delete_prole_permission_denied(self):
identifier="some_role", course=self.course)
with self.temporarily_switch_to_user(self.student_participation.user):
- resp = self.c.get(self.get_participation_role_delete_url(prole.id))
+ resp = self.c.get(self.get_participation_role_edit_url(prole.id))
self.assertEqual(resp.status_code, 403)
resp = self.c.post(
- self.get_participation_role_delete_url(prole.id),
+ self.get_participation_role_edit_url(prole.id),
data=self.get_default_delete_role_post_data(),
- HTTP_X_REQUESTED_WITH='XMLHttpRequest'
)
self.assertEqual(resp.status_code, 403)
self.assertEqual(ParticipationRole.objects.count(),
self.default_number_of_roles + 1)
- def test_delete_prole_get_or_non_ajax_post_not_allowed(self):
- prole = factories.ParticipationRoleFactory(
- identifier="some_role", course=self.course)
-
- resp = self.c.get(self.get_participation_role_delete_url(prole.id))
- self.assertEqual(resp.status_code, 403)
-
- resp = self.c.post(
- self.get_participation_role_delete_url(prole.id),
- data=self.get_default_delete_role_post_data(),
- )
- self.assertEqual(resp.status_code, 403)
- self.assertEqual(ParticipationRole.objects.count(),
- self.default_number_of_roles + 1)
-
def test_delete_prole_success(self):
prole = factories.ParticipationRoleFactory(
identifier="some_role", course=self.course)
resp = self.c.post(
- self.get_participation_role_delete_url(prole.id),
+ self.get_participation_role_edit_url(prole.id),
data=self.get_default_delete_role_post_data(),
- HTTP_X_REQUESTED_WITH='XMLHttpRequest'
)
- self.assertEqual(resp.status_code, 200)
+ self.assertEqual(resp.status_code, 302)
self.assertEqual(ParticipationRole.objects.count(),
self.default_number_of_roles)
- def test_delete_prole_suspicious(self):
- prole = factories.ParticipationRoleFactory(
- identifier="some_role", course=self.course)
-
- resp = self.c.post(
- self.get_participation_role_delete_url(prole.id),
- data=self.get_default_delete_role_post_data(
- no_delete_in_post=True, some_action=True),
- HTTP_X_REQUESTED_WITH='XMLHttpRequest'
- )
- self.assertEqual(resp.status_code, 400)
- self.assertEqual(ParticipationRole.objects.count(),
- self.default_number_of_roles + 1)
-
def test_delete_prole_from_another_course(self):
prole = factories.ParticipationRoleFactory(
course=factories.CourseFactory(identifier="another-course"))
@@ -2456,9 +2401,8 @@ def test_delete_prole_from_another_course(self):
prole_counts = ParticipationRole.objects.count()
resp = self.c.post(
- self.get_participation_role_delete_url(prole.id),
+ self.get_participation_role_edit_url(prole.id),
data=self.get_default_delete_role_post_data(),
- HTTP_X_REQUESTED_WITH='XMLHttpRequest'
)
self.assertEqual(resp.status_code, 400)
@@ -2473,17 +2417,31 @@ def test_delete_prole_exception_raised(self):
RuntimeError("some error"))
resp = self.c.post(
- self.get_participation_role_delete_url(prole.id),
+ self.get_participation_role_edit_url(prole.id),
data=self.get_default_delete_role_post_data(),
- HTTP_X_REQUESTED_WITH='XMLHttpRequest'
)
- self.assertEqual(resp.status_code, 400)
+ self.assertEqual(resp.status_code, 200)
+ prole_counts = ParticipationRole.objects.count()
+ self.assertEqual(ParticipationRole.objects.count(), prole_counts)
- import json
- self.assertIn(
- "Error when deleting participation role",
- json.loads(resp.content.decode())["error"]
+ self.assertAddMessageCallCount(1)
+ self.assertAddMessageCalledWith(
+ "Invalid operation. RuntimeError: some error.")
+
+ def test_edit_prole_suspicious(self):
+ prole = factories.ParticipationRoleFactory(
+ identifier="some_role", course=self.course)
+
+ with mock.patch("course.models.ParticipationRole.save") as mock_prole_save:
+ resp = self.c.post(
+ self.get_participation_role_edit_url(prole.id),
+ # No submit/update/delete
+ data=self.get_default_edit_role_post_data(),
)
+ self.assertEqual(resp.status_code, 400)
+ self.assertEqual(ParticipationRole.objects.count(),
+ self.default_number_of_roles + 1)
+ self.assertEqual(mock_prole_save.call_count, 0)
# vim: foldmethod=marker