Repository: e-valuation/EvaP

Issue:
Save and approve for staff
The course edit view for staff users should get a `Save and approve` button just like the contributors' page which moves the course into the `approved` state.



Discussion:
should this appear for lecturerApproved courses, or also for new and prepared ones, allowing to skip the responsible altogether? this might happen accidentally.

the button should be visible in all states before `approved`, but a warning should be displayed above it if the editor didn't approve the course yet.


Resolving patch:
diff --git a/evap/contributor/views.py b/evap/contributor/views.py
--- a/evap/contributor/views.py
+++ b/evap/contributor/views.py
@@ -1,5 +1,5 @@
 from django.contrib import messages
-from django.core.exceptions import PermissionDenied
+from django.core.exceptions import PermissionDenied, SuspiciousOperation
 from django.forms.models import inlineformset_factory
 from django.shortcuts import get_object_or_404, redirect, render
 from django.utils.translation import ugettext as _
@@ -87,7 +87,7 @@ def course_edit(request, course_id):
 
     if course_form.is_valid() and formset.is_valid():
         if operation not in ('save', 'approve'):
-            raise PermissionDenied
+            raise SuspiciousOperation("Invalid POST operation")
 
         course_form.save(user=user)
         formset.save()
diff --git a/evap/staff/views.py b/evap/staff/views.py
--- a/evap/staff/views.py
+++ b/evap/staff/views.py
@@ -1,5 +1,5 @@
 from django.contrib import messages
-from django.core.exceptions import PermissionDenied
+from django.core.exceptions import PermissionDenied, SuspiciousOperation
 from django.db.models import Max, Count
 from django.forms.models import inlineformset_factory, modelformset_factory
 from django.shortcuts import get_object_or_404, redirect, render
@@ -266,7 +266,7 @@ def semester_import(request, semester_id):
     if form.is_valid():
         operation = request.POST.get('operation')
         if operation not in ('test', 'import'):
-            raise PermissionDenied
+            raise SuspiciousOperation("Invalid POST operation")
 
         # extract data from form
         excel_file = form.cleaned_data['excel_file']
@@ -428,13 +428,24 @@ def helper_course_edit(request, semester, course):
     form = CourseForm(request.POST or None, instance=course)
     formset = InlineContributionFormset(request.POST or None, instance=course, queryset=course.contributions.exclude(contributor=None))
 
+    operation = request.POST.get('operation')
+
     if form.is_valid() and formset.is_valid():
+        if operation not in ('save', 'approve'):
+            raise SuspiciousOperation("Invalid POST operation")
         if course.state in ['evaluated', 'reviewed'] and course.is_in_evaluation_period:
             course.reopen_evaluation()
         form.save(user=request.user)
         formset.save()
 
-        messages.success(request, _("Successfully updated course."))
+        if operation == 'approve':
+            # approve course
+            course.staff_approve()
+            course.save()
+            messages.success(request, _("Successfully updated and approved course."))
+        else:
+            messages.success(request, _("Successfully updated course."))
+
         return custom_redirect('staff:semester_view', semester.id)
     else:
         sort_formset(request, formset)
@@ -759,7 +770,7 @@ def user_import(request):
 
     if form.is_valid():
         if operation not in ('test', 'import'):
-            raise PermissionDenied
+            raise SuspiciousOperation("Invalid POST operation")
 
         test_run = operation == 'test'
         excel_file = form.cleaned_data['excel_file']


Test patch:
diff --git a/evap/staff/tests.py b/evap/staff/tests.py
--- a/evap/staff/tests.py
+++ b/evap/staff/tests.py
@@ -320,9 +320,9 @@ def get_assert_403(self, url, user):
         except AppError as e:
             self.fail('url "{}" failed with user "{}"'.format(url, user))
 
-    def get_submit_assert_302(self, url, user):
+    def get_submit_assert_302(self, url, user, name="", value=""):
         response = self.get_assert_200(url, user)
-        response = response.forms[2].submit("")
+        response = response.forms[2].submit(name=name, value=value)
         self.assertEqual(response.status_code, 302, 'url "{}" failed with user "{}"'.format(url, user))
         return response
 
@@ -494,7 +494,7 @@ def test_staff_semester_x_lottery__nodata_success(self):
         self.get_submit_assert_200("/staff/semester/1/lottery", "evap")
 
     def test_staff_semester_x_course_y_edit__nodata_success(self):
-        self.get_submit_assert_302("/staff/semester/1/course/1/edit", "evap")
+        self.get_submit_assert_302("/staff/semester/1/course/1/edit", "evap", name="operation", value="save")
 
     def test_staff_semester_x_course_y_delete__nodata_success(self):
         self.get_submit_assert_302("/staff/semester/1/course/1/delete", "evap"),